diff --git a/package/lienol/luci-app-passwall/Makefile b/package/lienol/luci-app-passwall/Makefile index 8614606e8e..c9c1f091b9 100644 --- a/package/lienol/luci-app-passwall/Makefile +++ b/package/lienol/luci-app-passwall/Makefile @@ -6,9 +6,9 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-passwall -PKG_VERSION:=3.9 -PKG_RELEASE:=72 -PKG_DATE:=20201012 +PKG_VERSION:=4 +PKG_RELEASE:=1 +PKG_DATE:=20201204 PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION) @@ -33,9 +33,13 @@ config PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Server bool "Include ShadowsocksR Server" default y +config PACKAGE_$(PKG_NAME)_INCLUDE_Xray + bool "Include Xray" + default y if i386||x86_64||arm||aarch64 + config PACKAGE_$(PKG_NAME)_INCLUDE_V2ray bool "Include V2ray" - default y if i386||x86_64||arm||aarch64 + default n config PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_Plus bool "Include Trojan_Plus" @@ -48,7 +52,7 @@ config PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_GO config PACKAGE_$(PKG_NAME)_INCLUDE_Brook bool "Include Brook" default n - + config PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy bool "Include NaiveProxy" default n @@ -60,19 +64,19 @@ config PACKAGE_$(PKG_NAME)_INCLUDE_kcptun config PACKAGE_$(PKG_NAME)_INCLUDE_haproxy bool "Include haproxy" default y - + config PACKAGE_$(PKG_NAME)_INCLUDE_ChinaDNS_NG bool "Include ChinaDNS-NG" default n - + config PACKAGE_$(PKG_NAME)_INCLUDE_https_dns_proxy bool "Include Https DNS Proxy(DoH)" default y - + config PACKAGE_$(PKG_NAME)_INCLUDE_dns2socks bool "Include dns2socks" default n - + config PACKAGE_$(PKG_NAME)_INCLUDE_v2ray-plugin bool "Include v2ray-plugin (Shadowsocks plugin)" default y if i386||x86_64||arm||aarch64 @@ -101,6 +105,7 @@ define Package/$(PKG_NAME) +PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR:shadowsocksr-libev-alt \ +PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR:shadowsocksr-libev-ssr-local \ +PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Server:shadowsocksr-libev-server \ + +PACKAGE_$(PKG_NAME)_INCLUDE_Xray:xray \ +PACKAGE_$(PKG_NAME)_INCLUDE_V2ray:v2ray \ +PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_Plus:trojan-plus \ +PACKAGE_$(PKG_NAME)_INCLUDE_Trojan_GO:trojan-go \ @@ -137,21 +142,20 @@ define Package/$(PKG_NAME)/install $(INSTALL_DIR) $(1)/etc/config $(INSTALL_CONF) ./root/etc/config/passwall $(1)/etc/config/passwall $(INSTALL_CONF) ./root/etc/config/passwall_server $(1)/etc/config/passwall_server - + $(INSTALL_DIR) $(1)/etc/init.d $(INSTALL_BIN) ./root/etc/init.d/passwall $(1)/etc/init.d/passwall $(INSTALL_BIN) ./root/etc/init.d/passwall_server $(1)/etc/init.d/passwall_server - + $(INSTALL_DIR) $(1)/etc/uci-defaults $(INSTALL_CONF) ./root/etc/uci-defaults/* $(1)/etc/uci-defaults - + $(INSTALL_DIR) $(1)/usr/share/passwall cp -pR ./root/usr/share/passwall/* $(1)/usr/share/passwall $(INSTALL_CONF) ./root/etc/config/passwall $(1)/usr/share/passwall/config.default - $(INSTALL_DIR) $(1)/usr/lib/lua/luci cp -pR ./luasrc/* $(1)/usr/lib/lua/luci/ - + $(INSTALL_DIR) $(1)/usr/lib/lua/luci/i18n po2lmo ./po/zh-cn/passwall.po $(1)/usr/lib/lua/luci/i18n/passwall.zh-cn.lmo endef diff --git a/package/lienol/luci-app-passwall/luasrc/controller/passwall.lua b/package/lienol/luci-app-passwall/luasrc/controller/passwall.lua index 85ab30b8e2..c1b2f7482d 100644 --- a/package/lienol/luci-app-passwall/luasrc/controller/passwall.lua +++ b/package/lienol/luci-app-passwall/luasrc/controller/passwall.lua @@ -4,8 +4,10 @@ local appname = "passwall" local ucic = luci.model.uci.cursor() local http = require "luci.http" local util = require "luci.util" +local api = require "luci.model.cbi.passwall.api.api" local kcptun = require "luci.model.cbi.passwall.api.kcptun" local brook = require "luci.model.cbi.passwall.api.brook" +local xray = require "luci.model.cbi.passwall.api.xray" local v2ray = require "luci.model.cbi.passwall.api.v2ray" local trojan_go = require "luci.model.cbi.passwall.api.trojan_go" @@ -65,6 +67,8 @@ function index() entry({"admin", "services", appname, "kcptun_update"}, call("kcptun_update")).leaf = true entry({"admin", "services", appname, "brook_check"}, call("brook_check")).leaf = true entry({"admin", "services", appname, "brook_update"}, call("brook_update")).leaf = true + entry({"admin", "services", appname, "xray_check"}, call("xray_check")).leaf = true + entry({"admin", "services", appname, "xray_update"}, call("xray_update")).leaf = true entry({"admin", "services", appname, "v2ray_check"}, call("v2ray_check")).leaf = true entry({"admin", "services", appname, "v2ray_update"}, call("v2ray_update")).leaf = true entry({"admin", "services", appname, "trojan_go_check"}, call("trojan_go_check")).leaf = true @@ -239,10 +243,22 @@ function set_node() end function copy_node() - local e = {} local section = luci.http.formvalue("section") - luci.http.prepare_content("application/json") - luci.http.write_json(e) + local uuid = api.gen_uuid() + ucic:section(appname, "nodes", uuid) + for k, v in pairs(ucic:get_all(appname, section)) do + local filter = k:find("%.") + if filter and filter == 1 then + else + xpcall(function() + ucic:set(appname, uuid, k, v) + end, + function(e) + end) + end + end + ucic:commit(appname) + luci.http.redirect(luci.dispatcher.build_url("admin", "services", appname, "node_config", uuid)) end function clear_all_nodes() @@ -285,11 +301,11 @@ function check_port() ucic:foreach(appname, "nodes", function(s) local ret = "" local tcp_socket - if (s.use_kcp and s.use_kcp == "1" and s.kcp_port) or - (s.v2ray_transport and s.v2ray_transport == "mkcp" and s.port) then + if (s.use_kcp and s.use_kcp == "1" and s.kcp_port) or (s.transport and s.transport == "mkcp" and s.port) then else local type = s.type - if type and type ~= "V2ray_balancing" and type ~= "V2ray_shunt" and + local protocol = s.protocol + if type and (protocol and protocol ~= "_balancing" and protocol ~= "_shunt") and s.address and s.port and s.remarks then node_name = "%s:[%s] %s:%s" % {s.type, s.remarks, s.address, s.port} tcp_socket = nixio.socket("inet", "stream") @@ -366,6 +382,25 @@ function brook_update() http_write_json(json) end +function xray_check() + local json = xray.to_check("") + http_write_json(json) +end + +function xray_update() + local json = nil + local task = http.formvalue("task") + if task == "extract" then + json = xray.to_extract(http.formvalue("file"), http.formvalue("subfix")) + elseif task == "move" then + json = xray.to_move(http.formvalue("file")) + else + json = xray.to_download(http.formvalue("url")) + end + + http_write_json(json) +end + function v2ray_check() local json = v2ray.to_check("") http_write_json(json) diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/api.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/api.lua index 51061d51b3..a408a69f6b 100644 --- a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/api.lua +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/api.lua @@ -19,7 +19,7 @@ function get_valid_nodes() local nodes = {} uci:foreach(appname, "nodes", function(e) if e.type and e.remarks then - if e.type == "V2ray" and (e.protocol == "_balancing" or e.protocol == "_shunt") then + if e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt") then e.remarks_name = "%s:[%s] " % {i18n.translatef(e.type .. e.protocol), e.remarks} e.node_type = "special" nodes[#nodes + 1] = e @@ -79,19 +79,55 @@ function get_customed_path(e) end function is_finded(e) - return luci.sys.exec('type -t -p "%s/%s" -p "/usr/bin/v2ray/%s" "%s"' % {get_customed_path(e), e, e, e}) ~= "" and true or false + return luci.sys.exec('type -t -p "%s/%s" "%s"' % {get_customed_path(e), e, e}) ~= "" and true or false +end + +function get_xray_path() + local path = uci_get_type("global_app", "xray_file") + return path +end + +function get_xray_version(file) + if file == nil then file = get_xray_path() end + chmod_755(file) + if fs.access(file) then + if file == get_xray_path() then + local md5 = sys.exec("echo -n $(md5sum " .. file .. " | awk '{print $1}')") + if fs.access("/tmp/psw_" .. md5) then + return sys.exec("cat /tmp/psw_" .. md5) + else + local version = sys.exec("echo -n $(%s -version | awk '{print $2}' | sed -n 1P)" % file) + sys.call("echo '" .. version .. "' > " .. "/tmp/psw_" .. md5) + return version + end + else + return sys.exec("echo -n $(%s -version | awk '{print $2}' | sed -n 1P)" % file) + end + end + return "" end function get_v2ray_path() local path = uci_get_type("global_app", "v2ray_file") - return path .. "/v2ray" + return path end function get_v2ray_version(file) if file == nil then file = get_v2ray_path() end chmod_755(file) if fs.access(file) then - return sys.exec("echo -n $(%s -version | awk '{print $2}' | sed -n 1P)" % file) + if file == get_v2ray_path() then + local md5 = sys.exec("echo -n $(md5sum " .. file .. " | awk '{print $1}')") + if fs.access("/tmp/psw_" .. md5) then + return sys.exec("cat /tmp/psw_" .. md5) + else + local version = sys.exec("echo -n $(%s -version | awk '{print $2}' | sed -n 1P)" % file) + sys.call("echo '" .. version .. "' > " .. "/tmp/psw_" .. md5) + return version + end + else + return sys.exec("echo -n $(%s -version | awk '{print $2}' | sed -n 1P)" % file) + end end return "" end @@ -105,7 +141,18 @@ function get_trojan_go_version(file) if file == nil then file = get_trojan_go_path() end chmod_755(file) if fs.access(file) then - return sys.exec("echo -n $(%s -version | awk '{print $2}' | sed -n 1P)" % file) + if file == get_trojan_go_path() then + local md5 = sys.exec("echo -n $(md5sum " .. file .. " | awk '{print $1}')") + if fs.access("/tmp/psw_" .. md5) then + return sys.exec("cat /tmp/psw_" .. md5) + else + local version = sys.exec("echo -n $(%s -version | awk '{print $2}' | sed -n 1P)" % file) + sys.call("echo '" .. version .. "' > " .. "/tmp/psw_" .. md5) + return version + end + else + return sys.exec("echo -n $(%s -version | awk '{print $2}' | sed -n 1P)" % file) + end end return "" end @@ -119,7 +166,18 @@ function get_kcptun_version(file) if file == nil then file = get_kcptun_path() end chmod_755(file) if fs.access(file) then - return sys.exec("echo -n $(%s -v | awk '{print $3}')" % file) + if file == get_kcptun_path() then + local md5 = sys.exec("echo -n $(md5sum " .. file .. " | awk '{print $1}')") + if fs.access("/tmp/psw_" .. md5) then + return sys.exec("cat /tmp/psw_" .. md5) + else + local version = sys.exec("echo -n $(%s -v | awk '{print $3}')" % file) + sys.call("echo '" .. version .. "' > " .. "/tmp/psw_" .. md5) + return version + end + else + return sys.exec("echo -n $(%s -v | awk '{print $3}')" % file) + end end return "" end @@ -133,11 +191,38 @@ function get_brook_version(file) if file == nil then file = get_brook_path() end chmod_755(file) if fs.access(file) then - return sys.exec("echo -n $(%s -v | awk '{print $3}')" % file) + if file == get_brook_path() then + local md5 = sys.exec("echo -n $(md5sum " .. file .. " | awk '{print $1}')") + if fs.access("/tmp/psw_" .. md5) then + return sys.exec("cat /tmp/psw_" .. md5) + else + local version = sys.exec("echo -n $(%s -v | awk '{print $3}')" % file) + sys.call("echo '" .. version .. "' > " .. "/tmp/psw_" .. md5) + return version + end + else + return sys.exec("echo -n $(%s -v | awk '{print $3}')" % file) + end end return "" end +function get_free_space(dir) + if dir == nil then dir = "/" end + if sys.call("df -k " .. dir .. " >/dev/null") == 0 then + return tonumber(sys.exec("echo -n $(df -k " .. dir .. " | awk 'NR>1' | awk '{print $4}')")) + end + return 0 +end + +function get_file_space(file) + if file == nil then return 0 end + if fs.access(file) then + return tonumber(sys.exec("echo -n $(du -k " .. file .. " | awk '{print $1}')")) + end + return 0 +end + function _unpack(t, i) i = i or 1 if t[i] ~= nil then return t[i], _unpack(t, i + 1) end diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/brook.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/brook.lua index 3051858f9c..4213ccb7b5 100644 --- a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/brook.lua +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/brook.lua @@ -8,6 +8,13 @@ local api = require "luci.model.cbi.passwall.api.api" local brook_api = "https://api.github.com/repos/txthinking/brook/releases/latest" function to_check(arch) + local app_path = api.get_brook_path() or "" + if app_path == "" then + return { + code = 1, + error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Brook") + } + end if not arch or arch == "" then arch = api.auto_get_arch() end local file_tree, sub_version = api.get_file_info(arch) @@ -15,8 +22,7 @@ function to_check(arch) if file_tree == "" then return { code = 1, - error = i18n.translate( - "Can't determine ARCH, or ARCH not supported.") + error = i18n.translate("Can't determine ARCH, or ARCH not supported.") } end @@ -66,6 +72,13 @@ function to_check(arch) end function to_download(url) + local app_path = api.get_brook_path() or "" + if app_path == "" then + return { + code = 1, + error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Brook") + } + end if not url or url == "" then return {code = 1, error = i18n.translate("Download url is required.")} end @@ -88,13 +101,20 @@ function to_download(url) end function to_move(file) + local app_path = api.get_brook_path() or "" + if app_path == "" then + return { + code = 1, + error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Brook") + } + end if not file or file == "" or not fs.access(file) then sys.call("/bin/rm -rf /tmp/brook_download.*") return {code = 1, error = i18n.translate("Client file is required.")} end - local version = api.get_brook_version(file) - if version == "" then + local new_version = api.get_brook_version(file) + if new_version == "" then sys.call("/bin/rm -rf /tmp/brook_download.*") return { code = 1, @@ -102,30 +122,29 @@ function to_move(file) } end - local client_file = api.get_brook_path() - local client_file_bak + local app_path_bak - if fs.access(client_file) then - client_file_bak = client_file .. ".bak" - api.exec("/bin/mv", {"-f", client_file, client_file_bak}) + if fs.access(app_path) then + app_path_bak = app_path .. ".bak" + api.exec("/bin/mv", {"-f", app_path, app_path_bak}) end - local result = api.exec("/bin/mv", {"-f", file, client_file}, nil, api.command_timeout) == 0 + local result = api.exec("/bin/mv", {"-f", file, app_path}, nil, api.command_timeout) == 0 - if not result or not fs.access(client_file) then + if not result or not fs.access(app_path) then sys.call("/bin/rm -rf /tmp/brook_download.*") - if client_file_bak then - api.exec("/bin/mv", {"-f", client_file_bak, client_file}) + if app_path_bak then + api.exec("/bin/mv", {"-f", app_path_bak, app_path}) end return { code = 1, - error = i18n.translatef("Can't move new file to path: %s", client_file) + error = i18n.translatef("Can't move new file to path: %s", app_path) } end - api.exec("/bin/chmod", {"755", client_file}) + api.exec("/bin/chmod", {"755", app_path}) - if client_file_bak then api.exec("/bin/rm", {"-f", client_file_bak}) end + if app_path_bak then api.exec("/bin/rm", {"-f", app_path_bak}) end sys.call("/bin/rm -rf /tmp/brook_download.*") diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/gen_v2ray.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/gen_v2ray.lua index 583428ef34..0b06599d2f 100644 --- a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/gen_v2ray.lua +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/gen_v2ray.lua @@ -56,10 +56,14 @@ function gen_outbound(node, tag, relay_port) node.address = "127.0.0.1" end node.stream_security = "none" - end - - if node.transport == "mkcp" or node.transport == "quic" then - node.stream_security = "none" + else + if node.tls and node.tls == "1" then + node.stream_security = "tls" + end + + if node.transport == "mkcp" or node.transport == "quic" then + node.stream_security = "none" + end end result = { @@ -73,10 +77,6 @@ function gen_outbound(node, tag, relay_port) streamSettings = (node.protocol == "vmess" or node.protocol == "vless" or node.protocol == "socks" or node.protocol == "shadowsocks" or node.protocol == "trojan") and { network = node.transport, security = node.stream_security, - xtlsSettings = (node.stream_security == "xtls") and { - serverName = node.tls_serverName, - allowInsecure = (node.tls_allowInsecure == "1") and true or false - } or nil, tlsSettings = (node.stream_security == "tls") and { serverName = node.tls_serverName, allowInsecure = (node.tls_allowInsecure == "1") and true or false @@ -265,7 +265,10 @@ if node then end end - routing = {domainStrategy = "IPOnDemand", rules = rules} + routing = { + domainStrategy = node.domainStrategy or "AsIs", + rules = rules + } elseif node.protocol == "_balancing" then if node.balancing_node then @@ -277,7 +280,7 @@ if node then if outbound then table.insert(outbounds, outbound) end end routing = { - domainStrategy = "IPOnDemand", + domainStrategy = node.domainStrategy or "AsIs", balancers = {{tag = "balancer", selector = nodes}}, rules = { {type = "field", network = "tcp,udp", balancerTag = "balancer"} diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/gen_xray.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/gen_xray.lua new file mode 100644 index 0000000000..24653d59fd --- /dev/null +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/gen_xray.lua @@ -0,0 +1,318 @@ +module("luci.model.cbi.passwall.api.gen_xray", package.seeall) +local ucursor = require"luci.model.uci".cursor() +local sys = require "luci.sys" +local json = require "luci.jsonc" +local appname = "passwall" +local inbounds = {} +local outbounds = {} +local routing = nil + +local node_section = arg[1] or "nil" +local proto = arg[2] +local redir_port = arg[3] +local socks_proxy_port = arg[4] +local node = ucursor:get_all(appname, node_section) +local network = proto +local new_port + +local function get_new_port() + if new_port then + new_port = tonumber(sys.exec(string.format("echo -n $(/usr/share/%s/app.sh get_new_port %s tcp)", appname, new_port + 1))) + else + new_port = tonumber(sys.exec(string.format("echo -n $(/usr/share/%s/app.sh get_new_port auto tcp)", appname))) + end + return new_port +end + +function gen_outbound(node, tag, relay_port) + local result = nil + if node then + local node_id = node[".name"] + if tag == nil then + tag = node_id + end + if node.type ~= "Xray" then + if node.type == "Socks" then + node.protocol = "socks" + node.transport = "tcp" + else + local node_type = (proto and proto ~= "nil") and proto or "socks" + new_port = get_new_port() + node.port = new_port + sys.call(string.format('/usr/share/%s/app.sh run_socks "%s" "%s" "%s" "%s" "%s" "%s" "%s" "%s"> /dev/null', + appname, + node_id, + "127.0.0.1", + new_port, + string.format("/var/etc/%s/v2_%s_%s.json", appname, node_type, node_id), + "0", + "nil", + "4", + relay_port and tostring(relay_port) or "" + ) + ) + node.protocol = "socks" + node.transport = "tcp" + node.address = "127.0.0.1" + end + node.stream_security = "none" + else + if node.tls and node.tls == "1" then + node.stream_security = "tls" + if node.xtls and node.xtls == "1" then + node.stream_security = "xtls" + end + end + + if node.transport == "mkcp" or node.transport == "quic" then + node.stream_security = "none" + end + end + + result = { + tag = tag, + protocol = node.protocol, + mux = (node.stream_security ~= "xtls") and { + enabled = (node.mux == "1") and true or false, + concurrency = (node.mux_concurrency) and tonumber(node.mux_concurrency) or 8 + } or nil, + -- 底层传输配置 + streamSettings = (node.protocol == "vmess" or node.protocol == "vless" or node.protocol == "socks" or node.protocol == "shadowsocks" or node.protocol == "trojan") and { + network = node.transport, + security = node.stream_security, + xtlsSettings = (node.stream_security == "xtls") and { + serverName = node.tls_serverName, + allowInsecure = (node.tls_allowInsecure == "1") and true or false + } or nil, + tlsSettings = (node.stream_security == "tls") and { + serverName = node.tls_serverName, + allowInsecure = (node.tls_allowInsecure == "1") and true or false + } or nil, + tcpSettings = (node.transport == "tcp" and node.protocol ~= "socks") and { + header = { + type = node.tcp_guise, + request = (node.tcp_guise == "http") and { + path = node.tcp_guise_http_path or {"/"}, + headers = { + Host = node.tcp_guise_http_host or {} + } + } or nil + } + } or nil, + kcpSettings = (node.transport == "mkcp") and { + mtu = tonumber(node.mkcp_mtu), + tti = tonumber(node.mkcp_tti), + uplinkCapacity = tonumber(node.mkcp_uplinkCapacity), + downlinkCapacity = tonumber(node.mkcp_downlinkCapacity), + congestion = (node.mkcp_congestion == "1") and true or false, + readBufferSize = tonumber(node.mkcp_readBufferSize), + writeBufferSize = tonumber(node.mkcp_writeBufferSize), + seed = (node.mkcp_seed and node.mkcp_seed ~= "") and node.mkcp_seed or nil, + header = {type = node.mkcp_guise} + } or nil, + wsSettings = (node.transport == "ws") and { + path = node.ws_path or "", + headers = (node.ws_host ~= nil) and + {Host = node.ws_host} or nil + } or nil, + httpSettings = (node.transport == "h2") and + {path = node.h2_path, host = node.h2_host} or + nil, + dsSettings = (node.transport == "ds") and + {path = node.ds_path} or nil, + quicSettings = (node.transport == "quic") and { + security = node.quic_security, + key = node.quic_key, + header = {type = node.quic_guise} + } or nil + } or nil, + settings = { + vnext = (node.protocol == "vmess" or node.protocol == "vless") and { + { + address = node.address, + port = tonumber(node.port), + users = { + { + id = node.uuid, + alterId = tonumber(node.alter_id), + level = node.level and tonumber(node.level) or 0, + security = (node.protocol == "vmess") and node.security or nil, + encryption = node.encryption or "none", + flow = node.flow or nil + } + } + } + } or nil, + servers = (node.protocol == "socks" or node.protocol == "http" or node.protocol == "shadowsocks" or node.protocol == "trojan") and { + { + address = node.address, + port = tonumber(node.port), + method = node.method or nil, + password = node.password or "", + users = (node.username and node.password) and + {{user = node.username, pass = node.password}} or nil + } + } or nil + } + } + end + return result +end + +if node then + if socks_proxy_port ~= "nil" then + table.insert(inbounds, { + listen = "0.0.0.0", + port = tonumber(socks_proxy_port), + protocol = "socks", + settings = {auth = "noauth", udp = true, ip = "127.0.0.1"} + }) + network = "tcp,udp" + end + + if redir_port ~= "nil" then + table.insert(inbounds, { + port = tonumber(redir_port), + protocol = "dokodemo-door", + settings = {network = proto, followRedirect = true}, + sniffing = {enabled = true, destOverride = {"http", "tls"}} + }) + if proto == "tcp" and node.tcp_socks == "1" then + table.insert(inbounds, { + listen = "0.0.0.0", + port = tonumber(node.tcp_socks_port), + protocol = "socks", + settings = { + auth = node.tcp_socks_auth, + accounts = (node.tcp_socks_auth == "password") and { + { + user = node.tcp_socks_auth_username, + pass = node.tcp_socks_auth_password + } + } or nil, + udp = true + } + }) + end + end + + if node.protocol == "_shunt" then + local rules = {} + ucursor:foreach(appname, "shunt_rules", function(e) + local name = e[".name"] + local _node_id = node[name] or nil + if _node_id and _node_id ~= "nil" then + local _node = ucursor:get_all(appname, _node_id) + local is_proxy = node[name .. "_proxy"] + local relay_port + if is_proxy and is_proxy == "1" then + new_port = get_new_port() + relay_port = new_port + table.insert(inbounds, { + tag = "proxy_" .. name, + listen = "127.0.0.1", + port = new_port, + protocol = "dokodemo-door", + settings = {network = "tcp,udp", address = _node.address, port = tonumber(_node.port)} + }) + if _node.tls_serverName == nil then + _node.tls_serverName = _node.address + end + _node.address = "127.0.0.1" + _node.port = new_port + end + local _outbound = gen_outbound(_node, name, relay_port) + if _outbound then + table.insert(outbounds, _outbound) + if is_proxy and is_proxy == "1" then + table.insert(rules, { + type = "field", + inboundTag = {"proxy_" .. name}, + outboundTag = "default" + }) + end + if e.domain_list then + local _domain = {} + string.gsub(e.domain_list, '[^' .. "\r\n" .. ']+', function(w) + table.insert(_domain, w) + end) + table.insert(rules, { + type = "field", + outboundTag = name, + domain = _domain + }) + end + if e.ip_list then + local _ip = {} + string.gsub(e.ip_list, '[^' .. "\r\n" .. ']+', function(w) + table.insert(_ip, w) + end) + table.insert(rules, { + type = "field", + outboundTag = name, + ip = _ip + }) + end + end + end + end) + + local default_node_id = node.default_node or nil + if default_node_id and default_node_id ~= "nil" then + local default_node = ucursor:get_all(appname, default_node_id) + local default_outbound = gen_outbound(default_node, "default") + if default_outbound then + table.insert(outbounds, default_outbound) + local rule = { + type = "field", + outboundTag = "default", + network = network + } + table.insert(rules, rule) + end + end + + routing = { + domainStrategy = node.domainStrategy or "AsIs", + rules = rules + } + + elseif node.protocol == "_balancing" then + if node.balancing_node then + local nodes = node.balancing_node + local length = #nodes + for i = 1, length do + local node = ucursor:get_all(appname, nodes[i]) + local outbound = gen_outbound(node) + if outbound then table.insert(outbounds, outbound) end + end + routing = { + domainStrategy = node.domainStrategy or "AsIs", + balancers = {{tag = "balancer", selector = nodes}}, + rules = { + {type = "field", network = "tcp,udp", balancerTag = "balancer"} + } + } + end + else + local outbound = gen_outbound(node) + if outbound then table.insert(outbounds, outbound) end + end + + -- 额外传出连接 + table.insert(outbounds, {protocol = "freedom", tag = "direct", settings = {keep = ""}}) + + local xray = { + log = { + -- error = string.format("/var/etc/passwall/%s.log", node[".name"]), + loglevel = "warning" + }, + -- 传入连接 + inbounds = inbounds, + -- 传出连接 + outbounds = outbounds, + -- 路由 + routing = routing + } + print(json.stringify(xray, 1)) +end diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/gen_xray_proto.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/gen_xray_proto.lua new file mode 100644 index 0000000000..6d6582f61c --- /dev/null +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/gen_xray_proto.lua @@ -0,0 +1,80 @@ +local json = require "luci.jsonc" +local inbounds = {} +local outbounds = {} +local routing = nil + +local local_proto = arg[1] +local local_address = arg[2] +local local_port = arg[3] +local server_proto = arg[4] +local server_address = arg[5] +local server_port = arg[6] +local server_username = arg[7] or "nil" +local server_password = arg[8] or "nil" + +function gen_outbound(proto, address, port, username, password) + local result = { + protocol = proto, + streamSettings = { + network = "tcp", + security = "none" + }, + settings = { + servers = { + { + address = address, + port = tonumber(port), + users = (username ~= "nil" and password ~= "nil") and { + { + user = username, + pass = password + } + } or nil + } + } + } + } + return result +end + +if local_proto ~= "nil" and local_address ~= "nil" and local_port ~= "nil" then + local inbound = { + listen = local_address, + port = tonumber(local_port), + protocol = local_proto, + settings = { + accounts = nil + } + } + if local_proto == "socks" then + inbound.settings.auth = "noauth" + inbound.settings.udp = true + elseif local_proto == "http" then + inbound.settings.allowTransparent = false + end + table.insert(inbounds, inbound) +end + +if server_proto ~= "nil" and server_address ~= "nil" and server_port ~= "nil" then + local outbound = gen_outbound(server_proto, server_address, server_port, server_username, server_password) + if outbound then table.insert(outbounds, outbound) end +end + +-- 额外传出连接 +table.insert(outbounds, { + protocol = "freedom", tag = "direct", settings = {keep = ""} +}) + +local xray = { + log = { + -- error = string.format("/var/etc/passwall/%s.log", node[".name"]), + loglevel = "warning" + }, + -- 传入连接 + inbounds = inbounds, + -- 传出连接 + outbounds = outbounds, + -- 路由 + routing = routing +} +print(json.stringify(xray, 1)) diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/kcptun.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/kcptun.lua index 8ad20f894f..867875a120 100644 --- a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/kcptun.lua +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/kcptun.lua @@ -7,30 +7,14 @@ local api = require "luci.model.cbi.passwall.api.api" local kcptun_api = "https://api.github.com/repos/xtaci/kcptun/releases/latest" -function get_kcptun_file_path() - return api.uci_get_type("global_app", "kcptun_client_file") -end - -function get_kcptun_version(file) - if file == nil then file = get_kcptun_file_path() end - - if file and file ~= "" then - if not fs.access(file, "rwx", "rx", "rx") then - fs.chmod(file, 755) - end - - local info = util.trim(sys.exec("%s -v 2>/dev/null" % file)) - - if info ~= "" then - local tb = util.split(info, "%s+", nil, true) - return tb[1] == "kcptun" and tb[3] or "" - end - end - - return "" -end - function to_check(arch) + local app_path = api.get_kcptun_path() or "" + if app_path == "" then + return { + code = 1, + error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Kcptun") + } + end if not arch or arch == "" then arch = api.auto_get_arch() end local file_tree, sub_version = api.get_file_info(arch) @@ -38,8 +22,7 @@ function to_check(arch) if file_tree == "" then return { code = 1, - error = i18n.translate( - "Can't determine ARCH, or ARCH not supported.") + error = i18n.translate("Can't determine ARCH, or ARCH not supported.") } end @@ -52,12 +35,9 @@ function to_check(arch) } end + local now_version = api.get_kcptun_version() local remote_version = json.tag_name:match("[^v]+") - - local client_file = get_kcptun_file_path() - - local needs_update = api.compare_versions(get_kcptun_version(client_file), - "<", remote_version) + local needs_update = api.compare_versions(now_version, "<", remote_version) local html_url, download_url if needs_update then @@ -73,24 +53,30 @@ function to_check(arch) if needs_update and not download_url then return { code = 1, - now_version = get_kcptun_version(client_file), + now_version = now_version, version = remote_version, html_url = html_url, - error = i18n.translate( - "New version found, but failed to get new version download url.") + error = i18n.translate("New version found, but failed to get new version download url.") } end return { code = 0, update = needs_update, - now_version = get_kcptun_version(client_file), + now_version = now_version, version = remote_version, url = {html = html_url, download = download_url} } end function to_download(url) + local app_path = api.get_kcptun_path() or "" + if app_path == "" then + return { + code = 1, + error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Kcptun") + } + end if not url or url == "" then return {code = 1, error = i18n.translate("Download url is required.")} end @@ -113,6 +99,13 @@ function to_download(url) end function to_extract(file, subfix) + local app_path = api.get_kcptun_path() or "" + if app_path == "" then + return { + code = 1, + error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Kcptun") + } + end if not file or file == "" or not fs.access(file) then return {code = 1, error = i18n.translate("File path required.")} end @@ -157,47 +150,50 @@ function to_extract(file, subfix) end function to_move(file) + local app_path = api.get_kcptun_path() or "" + if app_path == "" then + return { + code = 1, + error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Kcptun") + } + end if not file or file == "" or not fs.access(file) then sys.call("/bin/rm -rf /tmp/kcptun_extract.*") return {code = 1, error = i18n.translate("Client file is required.")} end - local version = get_kcptun_version(file) - if version == "" then + local new_version = api.get_kcptun_version(file) + if new_version == "" then sys.call("/bin/rm -rf /tmp/kcptun_extract.*") return { code = 1, - error = i18n.translate( - "The client file is not suitable for current device.") + error = i18n.translate("The client file is not suitable for current device.") } end - local client_file = get_kcptun_file_path() - local client_file_bak + local app_path_bak - if fs.access(client_file) then - client_file_bak = client_file .. ".bak" - api.exec("/bin/mv", {"-f", client_file, client_file_bak}) + if fs.access(app_path) then + app_path_bak = app_path .. ".bak" + api.exec("/bin/mv", {"-f", app_path, app_path_bak}) end - local result = api.exec("/bin/mv", {"-f", file, client_file}, nil, - api.command_timeout) == 0 + local result = api.exec("/bin/mv", {"-f", file, app_path}, nil, api.command_timeout) == 0 - if not result or not fs.access(client_file) then + if not result or not fs.access(app_path) then sys.call("/bin/rm -rf /tmp/kcptun_extract.*") - if client_file_bak then - api.exec("/bin/mv", {"-f", client_file_bak, client_file}) + if app_path_bak then + api.exec("/bin/mv", {"-f", app_path_bak, app_path}) end return { code = 1, - error = i18n.translatef("Can't move new file to path: %s", - client_file) + error = i18n.translatef("Can't move new file to path: %s", app_path) } end - api.exec("/bin/chmod", {"755", client_file}) + api.exec("/bin/chmod", {"755", app_path}) - if client_file_bak then api.exec("/bin/rm", {"-f", client_file_bak}) end + if app_path_bak then api.exec("/bin/rm", {"-f", app_path_bak}) end sys.call("/bin/rm -rf /tmp/kcptun_extract.*") diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/trojan_go.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/trojan_go.lua index 0ea54d4e75..f6a4ca3d4d 100644 --- a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/trojan_go.lua +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/trojan_go.lua @@ -3,12 +3,18 @@ local fs = require "nixio.fs" local sys = require "luci.sys" local util = require "luci.util" local i18n = require "luci.i18n" -local ipkg = require("luci.model.ipkg") local api = require "luci.model.cbi.passwall.api.api" local trojan_go_api = api.uci_get_type("global_app", "trojan_go_latest", "https://api.github.com/repos/trojan-gfw/trojan-go/releases/latest") function to_check(arch) + local app_path = api.get_trojan_go_path() or "" + if app_path == "" then + return { + code = 1, + error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Trojan-GO") + } + end if not arch or arch == "" then arch = api.auto_get_arch() end local file_tree, sub_version = api.get_file_info(arch) @@ -16,8 +22,7 @@ function to_check(arch) if file_tree == "" then return { code = 1, - error = i18n.translate( - "Can't determine ARCH, or ARCH not supported.") + error = i18n.translate("Can't determine ARCH, or ARCH not supported.") } end @@ -59,8 +64,7 @@ function to_check(arch) now_version = now_version, version = remote_version, html_url = html_url, - error = i18n.translate( - "New version found, but failed to get new version download url.") .. " [linux-" .. file_tree .. ".zip]" + error = i18n.translate("New version found, but failed to get new version download url.") .. " [linux-" .. file_tree .. ".zip]" } end @@ -74,6 +78,13 @@ function to_check(arch) end function to_download(url) + local app_path = api.get_trojan_go_path() or "" + if app_path == "" then + return { + code = 1, + error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Trojan-GO") + } + end if not url or url == "" then return {code = 1, error = i18n.translate("Download url is required.")} end @@ -96,10 +107,19 @@ function to_download(url) end function to_extract(file, subfix) - local isinstall_unzip = ipkg.installed("unzip") - if isinstall_unzip == nil then - ipkg.update() - ipkg.install("unzip") + local app_path = api.get_trojan_go_path() or "" + if app_path == "" then + return { + code = 1, + error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Trojan-GO") + } + end + if sys.exec("echo -n $(opkg list-installed | grep -c unzip)") ~= "1" then + api.exec("/bin/rm", {"-f", file}) + return { + code = 1, + error = i18n.translate("Not installed unzip, Can't unzip!") + } end if not file or file == "" or not fs.access(file) then @@ -121,29 +141,35 @@ function to_extract(file, subfix) end function to_move(file) + local app_path = api.get_trojan_go_path() or "" + if app_path == "" then + return { + code = 1, + error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Trojan-GO") + } + end if not file or file == "" then sys.call("/bin/rm -rf /tmp/trojan-go_extract.*") return {code = 1, error = i18n.translate("Client file is required.")} end - local client_file = api.get_trojan_go_path() - local client_file_bak + local app_path_bak - if fs.access(client_file) then - client_file_bak = client_file .. ".bak" - api.exec("/bin/mv", {"-f", client_file, client_file_bak}) + if fs.access(app_path) then + app_path_bak = app_path .. ".bak" + api.exec("/bin/mv", {"-f", app_path, app_path_bak}) end - local result = api.exec("/bin/mv", { "-f", file .. "/trojan-go", client_file }, nil, api.command_timeout) == 0 + local result = api.exec("/bin/mv", { "-f", file .. "/trojan-go", app_path }, nil, api.command_timeout) == 0 sys.call("/bin/rm -rf /tmp/trojan-go_extract.*") - if not result or not fs.access(client_file) then + if not result or not fs.access(app_path) then return { code = 1, - error = i18n.translatef("Can't move new file to path: %s", client_file) + error = i18n.translatef("Can't move new file to path: %s", app_path) } end - api.exec("/bin/chmod", {"-R", "755", client_file}) + api.exec("/bin/chmod", {"-R", "755", app_path}) return {code = 0} end diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/v2ray.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/v2ray.lua index e5339acba2..60841d6bcb 100644 --- a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/v2ray.lua +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/v2ray.lua @@ -3,26 +3,19 @@ local fs = require "nixio.fs" local sys = require "luci.sys" local util = require "luci.util" local i18n = require "luci.i18n" -local ipkg = require("luci.model.ipkg") local api = require "luci.model.cbi.passwall.api.api" local v2ray_api = "https://api.github.com/repos/v2fly/v2ray-core/releases/latest" local is_armv7 = false -function get_v2ray_file_path() - return api.uci_get_type("global_app", "v2ray_file") -end - -function get_v2ray_version() - if get_v2ray_file_path() and get_v2ray_file_path() ~= "" then - if fs.access(get_v2ray_file_path() .. "/v2ray") then - return sys.exec("echo -n $(" .. get_v2ray_file_path() .. "/v2ray -version | awk '{print $2}' | sed -n 1P" .. ")") - end - end - return "" -end - function to_check(arch) + local app_path = api.get_v2ray_path() or "" + if app_path == "" then + return { + code = 1, + error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "V2ray") + } + end if not arch or arch == "" then arch = api.auto_get_arch() end local file_tree, sub_version = api.get_file_info(arch) @@ -31,8 +24,7 @@ function to_check(arch) if file_tree == "" then return { code = 1, - error = i18n.translate( - "Can't determine ARCH, or ARCH not supported.") + error = i18n.translate("Can't determine ARCH, or ARCH not supported.") } end @@ -48,8 +40,9 @@ function to_check(arch) } end + local now_version = api.get_v2ray_version() local remote_version = json.tag_name:match("[^v]+") - local needs_update = api.compare_versions(get_v2ray_version(), "<", remote_version) + local needs_update = api.compare_versions(now_version, "<", remote_version) local html_url, download_url if needs_update then @@ -65,24 +58,30 @@ function to_check(arch) if needs_update and not download_url then return { code = 1, - now_version = get_v2ray_version(), + now_version = now_version, version = remote_version, html_url = html_url, - error = i18n.translate( - "New version found, but failed to get new version download url.") + error = i18n.translate("New version found, but failed to get new version download url.") } end return { code = 0, update = needs_update, - now_version = get_v2ray_version(), + now_version = now_version, version = remote_version, url = {html = html_url, download = download_url} } end function to_download(url) + local app_path = api.get_v2ray_path() or "" + if app_path == "" then + return { + code = 1, + error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "V2ray") + } + end if not url or url == "" then return {code = 1, error = i18n.translate("Download url is required.")} end @@ -105,16 +104,26 @@ function to_download(url) end function to_extract(file, subfix) - local isinstall_unzip = ipkg.installed("unzip") - if isinstall_unzip == nil then - ipkg.update() - ipkg.install("unzip") + local app_path = api.get_v2ray_path() or "" + if app_path == "" then + return { + code = 1, + error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "V2ray") + } end if not file or file == "" or not fs.access(file) then return {code = 1, error = i18n.translate("File path required.")} end + if sys.exec("echo -n $(opkg list-installed | grep -c unzip)") ~= "1" then + api.exec("/bin/rm", {"-f", file}) + return { + code = 1, + error = i18n.translate("Not installed unzip, Can't unzip!") + } + end + sys.call("/bin/rm -rf /tmp/v2ray_extract.*") local tmp_dir = util.trim(util.exec("mktemp -d -t v2ray_extract.XXXXXX")) @@ -130,37 +139,35 @@ function to_extract(file, subfix) end function to_move(file) + local app_path = api.get_v2ray_path() or "" + if app_path == "" then + return { + code = 1, + error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "V2ray") + } + end if not file or file == "" then sys.call("/bin/rm -rf /tmp/v2ray_extract.*") return {code = 1, error = i18n.translate("Client file is required.")} end - local client_file = get_v2ray_file_path() - - sys.call("mkdir -p " .. client_file) - if not arch or arch == "" then arch = api.auto_get_arch() end local file_tree, sub_version = api.get_file_info(arch) - if sub_version == "7" then is_armv7 = true end + local t = "" + sys.call("/etc/init.d/passwall stop") local result = nil - if is_armv7 and is_armv7 == true then - result = api.exec("/bin/mv", { - "-f", file .. "/v2ray_armv7", file .. "/v2ctl_armv7", client_file - }, nil, api.command_timeout) == 0 - else - result = api.exec("/bin/mv", { - "-f", file .. "/v2ray", file .. "/v2ctl", client_file - }, nil, api.command_timeout) == 0 - end + if sub_version and sub_version == "7" then t = "_armv7" end + result = api.exec("/bin/mv", { "-f", file .. "/v2ray" .. t, app_path }, nil, api.command_timeout) == 0 sys.call("/bin/rm -rf /tmp/v2ray_extract.*") - if not result or not fs.access(client_file) then + if not result or not fs.access(app_path) then return { code = 1, - error = i18n.translatef("Can't move new file to path: %s", client_file) + error = i18n.translatef("Can't move new file to path: %s", app_path) } end - - api.exec("/bin/chmod", {"-R", "755", client_file}) + + api.chmod_755(app_path) + sys.call("/etc/init.d/passwall restart >/dev/null 2>&1 &") return {code = 0} end diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/xray.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/xray.lua new file mode 100644 index 0000000000..0666d4dc00 --- /dev/null +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/api/xray.lua @@ -0,0 +1,173 @@ +module("luci.model.cbi.passwall.api.xray", package.seeall) +local fs = require "nixio.fs" +local sys = require "luci.sys" +local util = require "luci.util" +local i18n = require "luci.i18n" +local api = require "luci.model.cbi.passwall.api.api" + +local xray_api = "https://api.github.com/repos/XTLS/Xray-core/releases/latest" +local is_armv7 = false + +function to_check(arch) + local app_path = api.get_xray_path() or "" + if app_path == "" then + return { + code = 1, + error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Xray") + } + end + if not arch or arch == "" then arch = api.auto_get_arch() end + + local file_tree, sub_version = api.get_file_info(arch) + if sub_version == "7" then is_armv7 = true end + + if file_tree == "" then + return { + code = 1, + error = i18n.translate("Can't determine ARCH, or ARCH not supported.") + } + end + + if file_tree == "amd64" then file_tree = "64" end + if file_tree == "386" then file_tree = "32" end + + local json = api.get_api_json(xray_api) + + if json.tag_name == nil then + return { + code = 1, + error = i18n.translate("Get remote version info failed.") + } + end + + local now_version = api.get_xray_version() + local remote_version = json.tag_name:match("[^v]+") + local needs_update = api.compare_versions(now_version, "<", remote_version) + local html_url, download_url + + if needs_update then + html_url = json.html_url + for _, v in ipairs(json.assets) do + if v.name and v.name:match("linux%-" .. file_tree) then + download_url = v.browser_download_url + break + end + end + end + + if needs_update and not download_url then + return { + code = 1, + now_version = now_version, + version = remote_version, + html_url = html_url, + error = i18n.translate("New version found, but failed to get new version download url.") + } + end + + return { + code = 0, + update = needs_update, + now_version = now_version, + version = remote_version, + url = {html = html_url, download = download_url} + } +end + +function to_download(url) + local app_path = api.get_xray_path() or "" + if app_path == "" then + return { + code = 1, + error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Xray") + } + end + if not url or url == "" then + return {code = 1, error = i18n.translate("Download url is required.")} + end + + sys.call("/bin/rm -f /tmp/xray_download.*") + + local tmp_file = util.trim(util.exec("mktemp -u -t xray_download.XXXXXX")) + + local result = api.exec(api.curl, {api._unpack(api.curl_args), "-o", tmp_file, url}, nil, api.command_timeout) == 0 + + if not result then + api.exec("/bin/rm", {"-f", tmp_file}) + return { + code = 1, + error = i18n.translatef("File download failed or timed out: %s", url) + } + end + + return {code = 0, file = tmp_file} +end + +function to_extract(file, subfix) + local app_path = api.get_xray_path() or "" + if app_path == "" then + return { + code = 1, + error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Xray") + } + end + + if not file or file == "" or not fs.access(file) then + return {code = 1, error = i18n.translate("File path required.")} + end + + if sys.exec("echo -n $(opkg list-installed | grep -c unzip)") ~= "1" then + api.exec("/bin/rm", {"-f", file}) + return { + code = 1, + error = i18n.translate("Not installed unzip, Can't unzip!") + } + end + + sys.call("/bin/rm -rf /tmp/xray_extract.*") + local tmp_dir = util.trim(util.exec("mktemp -d -t xray_extract.XXXXXX")) + + local output = {} + api.exec("/usr/bin/unzip", {"-o", file, "-d", tmp_dir}, + function(chunk) output[#output + 1] = chunk end) + + local files = util.split(table.concat(output)) + + api.exec("/bin/rm", {"-f", file}) + + return {code = 0, file = tmp_dir} +end + +function to_move(file) + local app_path = api.get_xray_path() or "" + if app_path == "" then + return { + code = 1, + error = i18n.translatef("You did not fill in the %s path. Please save and apply then update manually.", "Xray") + } + end + if not file or file == "" then + sys.call("/bin/rm -rf /tmp/xray_extract.*") + return {code = 1, error = i18n.translate("Client file is required.")} + end + + if not arch or arch == "" then arch = api.auto_get_arch() end + local file_tree, sub_version = api.get_file_info(arch) + local t = "" + sys.call("/etc/init.d/passwall stop") + local result = nil + if sub_version and sub_version == "7" then t = "_armv7" end + result = api.exec("/bin/mv", { "-f", file .. "/xray" .. t, app_path }, nil, api.command_timeout) == 0 + sys.call("/bin/rm -rf /tmp/xray_extract.*") + if not result or not fs.access(app_path) then + return { + code = 1, + error = i18n.translatef("Can't move new file to path: %s", app_path) + } + end + + api.chmod_755(app_path) + sys.call("/etc/init.d/passwall restart >/dev/null 2>&1 &") + + return {code = 0} +end diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/app_update.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/app_update.lua index 8a1713a769..dbbad8e5b0 100644 --- a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/app_update.lua +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/app_update.lua @@ -9,39 +9,39 @@ s = m:section(TypedSection, "global_app", translate("App Update"), translate("Please confirm that your firmware supports FPU.") .. "") s.anonymous = true +s:append(Template(appname .. "/app_update/xray_version")) s:append(Template(appname .. "/app_update/v2ray_version")) s:append(Template(appname .. "/app_update/trojan_go_version")) s:append(Template(appname .. "/app_update/kcptun_version")) s:append(Template(appname .. "/app_update/brook_version")) ----- V2ray Path -o = s:option(Value, "v2ray_file", translate("V2ray Path"), translatef("if you want to run from memory, change the path, such as %s, Then save the application and update it manually.", "/tmp/v2ray/")) -o.default = "/usr/bin/v2ray/" +o = s:option(Value, "xray_file", translatef("%s App Path", "Xray")) +o.default = "/usr/bin/xray" o.rmempty = false ----- Trojan-Go Path -o = s:option(Value, "trojan_go_file", translate("Trojan-Go Path"), translatef("if you want to run from memory, change the path, such as %s, Then save the application and update it manually.", "/tmp/trojan-go")) +o = s:option(Value, "v2ray_file", translatef("%s App Path", "V2ray")) +o.default = "/usr/bin/v2ray" +o.rmempty = false + +o = s:option(Value, "trojan_go_file", translatef("%s App Path", "Trojan-Go")) o.default = "/usr/bin/trojan-go" o.rmempty = false -o = s:option(Value, "trojan_go_latest", translate("Trojan-Go Version API"), translate("alternate API URL for version checking")) +o = s:option(Value, "trojan_go_latest", translatef("Trojan-Go Version API"), translate("alternate API URL for version checking")) o.default = "https://api.github.com/repos/peter-tank/trojan-go/releases/latest" ----- Kcptun client Path -o = s:option(Value, "kcptun_client_file", translate("Kcptun Client Path"), translatef("if you want to run from memory, change the path, such as %s, Then save the application and update it manually.", "/tmp/kcptun-client")) +o = s:option(Value, "kcptun_client_file", translatef("%s Client App Path", "Kcptun")) o.default = "/usr/bin/kcptun-client" o.rmempty = false ---[[ -o = s:option(Button, "_check_kcptun", translate("Manually update"), translatef("Make sure there is enough space to install %s", "kcptun")) -o.template = appname .. "/kcptun" -o.inputstyle = "apply" -o.btnclick = "onBtnClick_kcptun(this);" -o.id = "_kcptun-check_btn"]] -- - ----- Brook Path -o = s:option(Value, "brook_file", translate("Brook Path"), translatef("if you want to run from memory, change the path, such as %s, Then save the application and update it manually.", "/tmp/brook")) +o = s:option(Value, "brook_file", translatef("%s App Path", "Brook")) o.default = "/usr/bin/brook" o.rmempty = false +o = s:option(DummyValue, "tips", " ") +o.rawhtml = true +o.cfgvalue = function(t, n) + return string.format('%s', translate("if you want to run from memory, change the path, /tmp beginning then save the application and update it manually.")) +end + return m diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/auto_switch.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/auto_switch.lua index 2e7006202b..e9baecd5ec 100644 --- a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/auto_switch.lua +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/auto_switch.lua @@ -39,6 +39,8 @@ for i = 1, tcp_node_num, 1 do for k, v in pairs(nodes_table) do o:value(v.id, v.remarks) end + + o = s:option(Flag, "restore_switch" .. i, "TCP " .. i .. " " .. translate("Restore Switch"), translate("When detects main node is available, switch back to the main node.")) end return m diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/global.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/global.lua index 0c89f015c9..9788ef6dbd 100644 --- a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/global.lua +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/global.lua @@ -76,17 +76,17 @@ o.rmempty = false ---- TCP Node local tcp_node_num = tonumber(m:get("@global_other[0]", "tcp_node_num") or 1) for i = 1, tcp_node_num, 1 do + o = s:taboption("Main", ListValue, "tcp_node" .. i, translate("TCP Node") .. " " .. i) if i == 1 then - o = s:taboption("Main", ListValue, "tcp_node" .. i, translate("TCP Node")) - o.description = translate("For proxy specific list.") - + o.title = translate("TCP Node") + o.description = translate("For proxy specific list.") .. o.description if tonumber(m:get("@auto_switch[0]", "enable") or 0) == 1 then - local now_node = luci.sys.exec(string.format("[ -f '/var/etc/%s/id/TCP_%s' ] && echo -n $(cat /var/etc/%s/id/TCP_%s)", appname, i, appname, i)) - if now_node and now_node ~= "" then - local e = uci:get_all(appname, now_node) + local current_node = luci.sys.exec(string.format("[ -f '/var/etc/%s/id/TCP_%s' ] && echo -n $(cat /var/etc/%s/id/TCP_%s)", appname, i, appname, i)) + if current_node and current_node ~= "" and current_node ~= "nil" then + local e = uci:get_all(appname, current_node) if e then local remarks = "" - if e.type == "V2ray" and (e.protocol == "_balancing" or e.protocol == "_shunt") then + if e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt") then remarks = "%s:[%s] " % {translatef(e.type .. e.protocol), e.remarks} else if e.use_kcp and e.use_kcp == "1" then @@ -95,12 +95,10 @@ for i = 1, tcp_node_num, 1 do remarks = "%s:[%s] %s:%s" % {e.type, e.remarks, e.address, e.port} end end - o.description = o.description .. "
" ..translatef("Current node: %s", remarks) + o.description = translate("For proxy specific list.") .. "
" .. translatef("Current node: %s", '' .. remarks .. '') end end end - else - o = s:taboption("Main", ListValue, "tcp_node" .. i, translate("TCP Node") .. " " .. i) end o:value("nil", translate("Close")) for k, v in pairs(nodes_table) do o:value(v.id, v.remarks) end @@ -109,16 +107,14 @@ end ---- UDP Node local udp_node_num = tonumber(m:get("@global_other[0]", "udp_node_num") or 1) for i = 1, udp_node_num, 1 do + o = s:taboption("Main", ListValue, "udp_node" .. i, translate("UDP Node") .. " " .. i) + o:value("nil", translate("Close")) if i == 1 then - o = s:taboption("Main", ListValue, "udp_node" .. i, translate("UDP Node")) - o.description = translate("For proxy game network, DNS hijack etc.") .. translate(" The selected server will not use Kcptun.") - o:value("nil", translate("Close")) + o.title = translate("UDP Node") + o.description = translate("For proxy game network, DNS hijack etc.") .. o.description .. "
" .. translate("The selected server will not use Kcptun.") o:value("tcp_", translate("Same as the tcp node")) --o:value("tcp", translate("Same as the tcp node")) --o:value("tcp_", translate("Same as the tcp node") .. "(" .. translate("New process") .. ")") - else - o = s:taboption("Main", ListValue, "udp_node" .. i, translate("UDP Node") .. " " .. i) - o:value("nil", translate("Close")) end for k, v in pairs(nodes_table) do o:value(v.id, v.remarks) end end @@ -334,7 +330,7 @@ o.default = 9050 o.datatype = "port" o.rmempty = false -if api.is_finded("v2ray") then +if api.is_finded("xray") or api.is_finded("v2ray") then o = s:option(Value, "http_port", "HTTP" .. translate("Listen Port") .. " " .. translate("0 is not use")) o.default = 0 o.datatype = "port" diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/haproxy.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/haproxy.lua index d2ddedfd4b..69a3c8af89 100644 --- a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/haproxy.lua +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/haproxy.lua @@ -47,7 +47,10 @@ o:depends("balancing_enable", 1) -- [[ Balancing Settings ]]-- s = m:section(TypedSection, "haproxy_config", "", - "" .. translate("Add a node, Export Of Multi WAN Only support Multi Wan. Load specific gravity range 1-256. Multiple primary servers can be load balanced, standby will only be enabled when the primary server is offline! Multiple groups can be set, Haproxy port same one for each group.").."") + "" .. + translate("Add a node, Export Of Multi WAN Only support Multi Wan. Load specific gravity range 1-256. Multiple primary servers can be load balanced, standby will only be enabled when the primary server is offline! Multiple groups can be set, Haproxy port same one for each group.") .. + "\n" .. translate("Note that the node configuration parameters for load balancing must be consistent, otherwise problems can arise!") .. + "") s.template = "cbi/tblsection" s.sortable = true s.anonymous = true diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/node_config.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/node_config.lua index a14a6f2838..02c785f935 100644 --- a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/node_config.lua +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/node_config.lua @@ -79,6 +79,10 @@ end if api.is_finded("ssr-redir") then type:value("SSR", translate("ShadowsocksR")) end +if api.is_finded("xray") then + type:value("Xray", translate("Xray")) + type.description = translate("Xray is currently directly compatible with V2ray and used.") +end if api.is_finded("v2ray") then type:value("V2ray", translate("V2ray")) end @@ -110,6 +114,7 @@ protocol:value("trojan", translate("Trojan")) protocol:value("_balancing", translate("Balancing")) protocol:value("_shunt", translate("Shunt")) protocol:depends("type", "V2ray") +protocol:depends("type", "Xray") local nodes_table = {} for k, e in ipairs(api.get_valid_nodes()) do @@ -128,7 +133,7 @@ balancing_node:depends("protocol", "_balancing") -- 分流 uci:foreach(appname, "shunt_rules", function(e) - o = s:option(ListValue, e[".name"], translate(e.remarks)) + o = s:option(ListValue, e[".name"], '' .. translate(e.remarks) .. "") o:value("nil", translate("Close")) for k, v in pairs(nodes_table) do o:value(v.id, v.remarks) end o:depends("protocol", "_shunt") @@ -138,11 +143,29 @@ uci:foreach(appname, "shunt_rules", function(e) o:depends("protocol", "_shunt") end) +shunt_tips = s:option(DummyValue, "shunt_tips", " ") +shunt_tips.rawhtml = true +shunt_tips.cfgvalue = function(t, n) + return string.format('%s', translate("No shunt rules? Click me to go to add.")) +end +shunt_tips:depends("protocol", "_shunt") + default_node = s:option(ListValue, "default_node", translate("Default") .. " " .. translate("Node")) default_node:value("nil", translate("Close")) for k, v in pairs(nodes_table) do default_node:value(v.id, v.remarks) end default_node:depends("protocol", "_shunt") +domainStrategy = s:option(ListValue, "domainStrategy", translate("Domain Strategy")) +domainStrategy:value("AsIs") +domainStrategy:value("IPIfNonMatch") +domainStrategy:value("IPOnDemand") +domainStrategy.description = "
" +domainStrategy:depends("protocol", "_balancing") +domainStrategy:depends("protocol", "_shunt") + -- Brook协议 brook_protocol = s:option(ListValue, "brook_protocol", translate("Protocol")) brook_protocol:value("client", translate("Brook")) @@ -180,6 +203,12 @@ address:depends("type", "Trojan") address:depends("type", "Trojan-Plus") address:depends("type", "Trojan-Go") address:depends("type", "Naiveproxy") +address:depends({ type = "Xray", protocol = "vmess" }) +address:depends({ type = "Xray", protocol = "vless" }) +address:depends({ type = "Xray", protocol = "http" }) +address:depends({ type = "Xray", protocol = "socks" }) +address:depends({ type = "Xray", protocol = "shadowsocks" }) +address:depends({ type = "Xray", protocol = "trojan" }) address:depends({ type = "V2ray", protocol = "vmess" }) address:depends({ type = "V2ray", protocol = "vless" }) address:depends({ type = "V2ray", protocol = "http" }) @@ -197,6 +226,12 @@ use_ipv6:depends("type", "Brook") use_ipv6:depends("type", "Trojan") use_ipv6:depends("type", "Trojan-Plus") use_ipv6:depends("type", "Trojan-Go") +use_ipv6:depends({ type = "Xray", protocol = "vmess" }) +use_ipv6:depends({ type = "Xray", protocol = "vless" }) +use_ipv6:depends({ type = "Xray", protocol = "http" }) +use_ipv6:depends({ type = "Xray", protocol = "socks" }) +use_ipv6:depends({ type = "Xray", protocol = "shadowsocks" }) +use_ipv6:depends({ type = "Xray", protocol = "trojan" }) use_ipv6:depends({ type = "V2ray", protocol = "vmess" }) use_ipv6:depends({ type = "V2ray", protocol = "vless" }) use_ipv6:depends({ type = "V2ray", protocol = "http" }) @@ -216,6 +251,12 @@ port:depends("type", "Trojan") port:depends("type", "Trojan-Plus") port:depends("type", "Trojan-Go") port:depends("type", "Naiveproxy") +port:depends({ type = "Xray", protocol = "vmess" }) +port:depends({ type = "Xray", protocol = "vless" }) +port:depends({ type = "Xray", protocol = "http" }) +port:depends({ type = "Xray", protocol = "socks" }) +port:depends({ type = "Xray", protocol = "shadowsocks" }) +port:depends({ type = "Xray", protocol = "trojan" }) port:depends({ type = "V2ray", protocol = "vmess" }) port:depends({ type = "V2ray", protocol = "vless" }) port:depends({ type = "V2ray", protocol = "http" }) @@ -226,6 +267,8 @@ port:depends({ type = "V2ray", protocol = "trojan" }) username = s:option(Value, "username", translate("Username")) username:depends("type", "Socks") username:depends("type", "Naiveproxy") +username:depends({ type = "Xray", protocol = "http" }) +username:depends({ type = "Xray", protocol = "socks" }) username:depends({ type = "V2ray", protocol = "http" }) username:depends({ type = "V2ray", protocol = "socks" }) @@ -239,6 +282,10 @@ password:depends("type", "Trojan") password:depends("type", "Trojan-Plus") password:depends("type", "Trojan-Go") password:depends("type", "Naiveproxy") +password:depends({ type = "Xray", protocol = "http" }) +password:depends({ type = "Xray", protocol = "socks" }) +password:depends({ type = "Xray", protocol = "shadowsocks" }) +password:depends({ type = "Xray", protocol = "trojan" }) password:depends({ type = "V2ray", protocol = "http" }) password:depends({ type = "V2ray", protocol = "socks" }) password:depends({ type = "V2ray", protocol = "shadowsocks" }) @@ -266,10 +313,12 @@ end security = s:option(ListValue, "security", translate("Encrypt Method")) for a, t in ipairs(security_list) do security:value(t) end +security:depends({ type = "Xray", protocol = "vmess" }) security:depends({ type = "V2ray", protocol = "vmess" }) encryption = s:option(Value, "encryption", translate("Encrypt Method")) encryption.default = "none" +encryption:depends({ type = "Xray", protocol = "vless" }) encryption:depends({ type = "V2ray", protocol = "vless" }) v_ss_encrypt_method = s:option(ListValue, "v_ss_encrypt_method", translate("Encrypt Method")) @@ -360,6 +409,8 @@ kcp_opts:depends("use_kcp", "1") uuid = s:option(Value, "uuid", translate("ID")) uuid.password = true +uuid:depends({ type = "Xray", protocol = "vmess" }) +uuid:depends({ type = "Xray", protocol = "vless" }) uuid:depends({ type = "V2ray", protocol = "vmess" }) uuid:depends({ type = "V2ray", protocol = "vless" }) @@ -368,64 +419,77 @@ alter_id:depends("protocol", "vmess") level = s:option(Value, "level", translate("User Level")) level.default = 1 +level:depends({ type = "Xray", protocol = "vmess" }) +level:depends({ type = "Xray", protocol = "vless" }) +level:depends({ type = "Xray", protocol = "trojan" }) level:depends({ type = "V2ray", protocol = "vmess" }) level:depends({ type = "V2ray", protocol = "vless" }) level:depends({ type = "V2ray", protocol = "trojan" }) -flow = s:option(Value, "flow", translate("flow")) -flow.placeholder = "xtls-rprx-origin" ---flow:value("xtls-rprx-origin") ---flow:value("xtls-rprx-origin-udp443") ---flow:value("xtls-rprx-direct") ---flow:value("xtls-rprx-direct-udp443") -flow:depends({ type = "V2ray", protocol = "vless" }) - -stream_security = s:option(ListValue, "stream_security", translate("Transport Layer Encryption"), translate('Whether or not transport layer encryption is enabled, "none" for unencrypted, "tls" for using TLS, "xtls" for using XTLS.')) -stream_security:value("none", "none") -stream_security:value("tls", "tls") -stream_security:value("xtls", "xtls") -stream_security.default = "tls" -stream_security:depends({ type = "V2ray", protocol = "vmess" }) -stream_security:depends({ type = "V2ray", protocol = "vless" }) -stream_security:depends({ type = "V2ray", protocol = "socks" }) -stream_security:depends({ type = "V2ray", protocol = "shadowsocks" }) -stream_security:depends({ type = "V2ray", protocol = "trojan" }) -stream_security:depends("type", "Trojan") -stream_security:depends("type", "Trojan-Plus") -stream_security:depends("type", "Trojan-Go") -stream_security.validate = function(self, value) - if value == "none" and (type:formvalue(arg[1]) == "Trojan" or type:formvalue(arg[1]) == "Trojan-Plus") then - return nil, translate("'none' not supported for original Trojan, please choose 'tls'.") +tls = s:option(Flag, "tls", translate("TLS")) +tls.default = 0 +tls.validate = function(self, value, t) + if value then + local type = type:formvalue(t) or "" + if value == "0" and (type == "Trojan" or type == "Trojan-Plus") then + return nil, translate("Original Trojan only supported 'tls', please choose 'tls'.") + end + return value end - return value end +tls:depends({ type = "Xray", protocol = "vmess" }) +tls:depends({ type = "Xray", protocol = "vless" }) +tls:depends({ type = "Xray", protocol = "socks" }) +tls:depends({ type = "Xray", protocol = "trojan" }) +tls:depends({ type = "Xray", protocol = "shadowsocks" }) +tls:depends({ type = "V2ray", protocol = "vmess" }) +tls:depends({ type = "V2ray", protocol = "vless" }) +tls:depends({ type = "V2ray", protocol = "socks" }) +tls:depends({ type = "V2ray", protocol = "shadowsocks" }) +tls:depends({ type = "Xray", protocol = "trojan" }) +tls:depends("type", "Trojan") +tls:depends("type", "Trojan-Plus") +tls:depends("type", "Trojan-Go") + +xtls = s:option(Flag, "xtls", translate("XTLS")) +xtls.default = 0 +xtls:depends({ type = "Xray", protocol = "vless", tls = "1" }) + +flow = s:option(Value, "flow", translate("flow")) +flow:value("xtls-rprx-origin") +flow:value("xtls-rprx-origin-udp443") +flow:value("xtls-rprx-direct") +flow:value("xtls-rprx-direct-udp443") +flow:value("xtls-rprx-splice") +flow:value("xtls-rprx-splice-udp443") +flow:depends("xtls", "1") -- [[ TLS部分 ]] -- tls_sessionTicket = s:option(Flag, "tls_sessionTicket", translate("Session Ticket")) tls_sessionTicket.default = "0" -tls_sessionTicket:depends({ type = "Trojan", stream_security = "tls" }) -tls_sessionTicket:depends({ type = "Trojan-Plus", stream_security = "tls" }) -tls_sessionTicket:depends({ type = "Trojan-Go", stream_security = "tls" }) +tls_sessionTicket:depends({ type = "Trojan", tls = "1" }) +tls_sessionTicket:depends({ type = "Trojan-Plus", tls = "1" }) +tls_sessionTicket:depends({ type = "Trojan-Go", tls = "1" }) -- [[ Trojan TLS ]]-- trojan_force_fp = s:option(ListValue, "fingerprint", translate("Finger Print")) for a, t in ipairs(force_fp) do trojan_force_fp:value(t) end trojan_force_fp.default = "firefox" -trojan_force_fp:depends({ type = "Trojan-Go", stream_security = "tls" }) +trojan_force_fp:depends({ type = "Trojan-Go", tls = "1" }) tls_serverName = s:option(Value, "tls_serverName", translate("Domain")) -tls_serverName:depends("stream_security", "tls") -tls_serverName:depends("stream_security", "xtls") +tls_serverName:depends("tls", "1") +tls_serverName:depends("xtls", "1") tls_allowInsecure = s:option(Flag, "tls_allowInsecure", translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped.")) tls_allowInsecure.default = "0" -tls_allowInsecure:depends("stream_security", "tls") -tls_allowInsecure:depends("stream_security", "xtls") +tls_allowInsecure:depends("tls", "1") +tls_allowInsecure:depends("xtls", "1") -- [[ Trojan Cert ]]-- trojan_cert_path = s:option(Value, "trojan_cert_path", translate("Trojan Cert Path")) trojan_cert_path.default = "" -trojan_cert_path:depends({ stream_security = "tls", tls_allowInsecure = false }) +trojan_cert_path:depends({ tls = "1", tls_allowInsecure = false }) trojan_transport = s:option(ListValue, "trojan_transport", translate("Transport")) trojan_transport:value("original", "Original") @@ -440,7 +504,7 @@ trojan_plugin:value("plaintext", "Plain Text") trojan_plugin:value("shadowsocks", "ShadowSocks") trojan_plugin:value("other", "Other") trojan_plugin.default = "plaintext" -trojan_plugin:depends({ stream_security = "none", trojan_transport = "original" }) +trojan_plugin:depends({ tls = "0", trojan_transport = "original" }) trojan_plugin_cmd = s:option(Value, "plugin_cmd", translate("Plugin Binary")) trojan_plugin_cmd.placeholder = "eg: /usr/bin/v2ray-plugin" @@ -464,6 +528,11 @@ transport:value("ws", "WebSocket") transport:value("h2", "HTTP/2") transport:value("ds", "DomainSocket") transport:value("quic", "QUIC") +transport:depends({ type = "Xray", protocol = "vmess" }) +transport:depends({ type = "Xray", protocol = "vless" }) +transport:depends({ type = "Xray", protocol = "socks" }) +transport:depends({ type = "Xray", protocol = "shadowsocks" }) +transport:depends({ type = "Xray", protocol = "trojan" }) transport:depends({ type = "V2ray", protocol = "vmess" }) transport:depends({ type = "V2ray", protocol = "vless" }) transport:depends({ type = "V2ray", protocol = "socks" }) @@ -475,6 +544,7 @@ ss_transport = s:option(ListValue, "ss_transport", translate("Transport")) ss_transport:value("ws", "WebSocket") ss_transport:value("h2", "HTTP/2") ss_transport:value("h2+ws", "HTTP/2 & WebSocket") +ss_transport:depends({ type = "Xray", protocol = "shadowsocks" }) ss_transport:depends({ type = "V2ray", protocol = "shadowsocks" }) ]]-- @@ -557,7 +627,7 @@ h2_path:depends("trojan_transport", "h2+ws") h2_path:depends("trojan_transport", "h2") -- [[ DomainSocket部分 ]]-- -ds_path = s:option(Value, "ds_path", "Path", translate("A legal file path. This file must not exist before running V2Ray.")) +ds_path = s:option(Value, "ds_path", "Path", translate("A legal file path. This file must not exist before running.")) ds_path:depends("transport", "ds") -- [[ QUIC部分 ]]-- @@ -590,6 +660,11 @@ ss_aead_pwd:depends("ss_aead", "1") -- [[ Mux ]]-- mux = s:option(Flag, "mux", translate("Mux")) +mux:depends({ type = "Xray", protocol = "vmess" }) +mux:depends({ type = "Xray", protocol = "vless", xtls = false }) +mux:depends({ type = "Xray", protocol = "http" }) +mux:depends({ type = "Xray", protocol = "socks" }) +mux:depends({ type = "Xray", protocol = "shadowsocks" }) mux:depends({ type = "V2ray", protocol = "vmess" }) mux:depends({ type = "V2ray", protocol = "vless" }) mux:depends({ type = "V2ray", protocol = "http" }) @@ -605,6 +680,7 @@ mux_concurrency:depends("mux", "1") --[[ tcp_socks = s:option(Flag, "tcp_socks", translate("TCP Open Socks"), translate("When using this TCP node, whether to open the socks proxy at the same time")) tcp_socks.default = 0 +tcp_socks:depends("type", "Xray") tcp_socks:depends("type", "V2ray") tcp_socks_port = s:option(Value, "tcp_socks_port", "Socks " .. translate("Port"), translate("Do not conflict with other ports")) diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/node_list.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/node_list.lua index 4a2a73aee4..2252c49258 100644 --- a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/node_list.lua +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/node_list.lua @@ -63,7 +63,7 @@ if nodes_display:find("compact_display_nodes") then local remarks = m:get(n, "remarks") or "" local type = m:get(n, "type") or "" str = str .. string.format("", appname, n, type) - if type == "V2ray" then + if type == "Xray" or type == "V2ray" then local protocol = m:get(n, "protocol") if protocol == "_balancing" then type = type .. " 负载均衡" @@ -75,7 +75,11 @@ if nodes_display:find("compact_display_nodes") then local port = m:get(n, "port") or "" str = str .. translate(type) .. ":" .. remarks if address ~= "" and port ~= "" then - str = str .. string.format("(%s:%s)", address, port) + if datatypes.ip6addr(address) then + str = str .. string.format("([%s]:%s)", address, port) + else + str = str .. string.format("(%s:%s)", address, port) + end str = str .. string.format("", appname, n, address) str = str .. string.format("", appname, n, port) end @@ -102,7 +106,7 @@ else local v = Value.cfgvalue(t, n) if v then result = translate(v) - if v == "V2ray" then + if v == "Xray" or v == "V2ray" then local protocol = m:get(n, "protocol") if protocol == "_balancing" then result = result .. " 负载均衡" diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/rule.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/rule.lua index ccc0033994..51fb136942 100644 --- a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/rule.lua +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/rule.lua @@ -49,7 +49,7 @@ for e = 0, 23 do o:value(e, e .. translate("oclock")) end o.default = 0 o:depends("auto_update", 1) -s = m:section(TypedSection, "shunt_rules", "V2ray" .. translate("Shunt") .. translate("Rule")) +s = m:section(TypedSection, "shunt_rules", "Xray/V2ray" .. translate("Shunt") .. translate("Rule")) s.template = "cbi/tblsection" s.anonymous = false s.addremove = true diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/shunt_rules.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/shunt_rules.lua index 21626344d3..ce6b3cc34f 100644 --- a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/shunt_rules.lua +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/client/shunt_rules.lua @@ -1,7 +1,7 @@ local d = require "luci.dispatcher" local appname = "passwall" -m = Map(appname, "V2ray" .. translate("Shunt") .. translate("Rule")) +m = Map(appname, "Xray/V2ray" .. translate("Shunt") .. translate("Rule")) m.redirect = d.build_url("admin", "services", appname) s = m:section(NamedSection, arg[1], "shunt_rules", "") @@ -13,31 +13,61 @@ remarks.default = arg[1] remarks.rmempty = false domain_list = s:option(TextValue, "domain_list", translate("Domain")) -domain_list.rows = 15 +domain_list.rows = 10 domain_list.wrap = "off" domain_list.validate = function(self, value) local hosts= {} string.gsub(value, '[^' .. "\r\n" .. ']+', function(w) table.insert(hosts, w) end) for index, host in ipairs(hosts) do - if not datatypes.hostname(host) then - return nil, host .. " " .. translate("Not valid domain name, please re-enter!") + local flag = 1 + local tmp_host = host + if host:find("regexp:") and host:find("regexp:") == 1 then + flag = 0 + elseif host:find("domain:.") and host:find("domain:.") == 1 then + tmp_host = host:gsub("domain:", "") + elseif host:find("full:.") and host:find("full:.") == 1 then + tmp_host = host:gsub("full:", "") + elseif host:find("geosite:") and host:find("geosite:") == 1 then + flag = 0 + elseif host:find("ext:") and host:find("ext:") == 1 then + flag = 0 + end + if flag == 1 then + if not datatypes.hostname(tmp_host) then + return nil, tmp_host .. " " .. translate("Not valid domain name, please re-enter!") + end end end return value end - +domain_list.description = "
" ip_list = s:option(TextValue, "ip_list", "IP") -ip_list.rows = 15 +ip_list.rows = 10 ip_list.wrap = "off" ip_list.validate = function(self, value) local ipmasks= {} string.gsub(value, '[^' .. "\r\n" .. ']+', function(w) table.insert(ipmasks, w) end) for index, ipmask in ipairs(ipmasks) do - if not datatypes.ipmask4(ipmask) then - return nil, ipmask .. " " .. translate("Not valid IP format, please re-enter!") + if ipmask:find("geoip:") and ipmask:find("geoip:") == 1 then + elseif ipmask:find("ext:") and ipmask:find("ext:") == 1 then + else + if not datatypes.ipmask4(ipmask) then + return nil, ipmask .. " " .. translate("Not valid IP format, please re-enter!") + end end end return value end +ip_list.description = "
" return m diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/server/api/app.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/server/api/app.lua old mode 100644 new mode 100755 index 39ca61ec29..cab85d501d --- a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/server/api/app.lua +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/server/api/app.lua @@ -89,6 +89,9 @@ local function start() end type = type:lower() bin = ln_start("/usr/bin/" .. type .. "-server", type .. "-server", "-c " .. config_file .. " " .. udp_param) + elseif type == "Xray" then + config = require("luci.model.cbi.passwall.server.api.xray").gen_config(user) + bin = ln_start(_api.get_xray_path(), "xray", "-config=" .. config_file) elseif type == "V2ray" then config = require("luci.model.cbi.passwall.server.api.v2ray").gen_config(user) bin = ln_start(_api.get_v2ray_path(), "v2ray", "-config=" .. config_file) diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/server/api/v2ray.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/server/api/v2ray.lua index dcd1ec7a34..519700fc2b 100644 --- a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/server/api/v2ray.lua +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/server/api/v2ray.lua @@ -14,7 +14,7 @@ function gen_config(user) for i = 1, #user.uuid do clients[i] = { id = user.uuid[i], - flow = (user.xtls and user.xtls == "1") and user.flow or nil, + flow = user.flow or nil, level = tonumber(user.level), alterId = tonumber(user.alter_id) } @@ -109,16 +109,6 @@ function gen_config(user) streamSettings = { network = user.transport, security = "none", - xtlsSettings = (user.tls and user.tls == "1" and user.xtls and user.xtls == "1") and { - --alpn = {"http/1.1"}, - disableSystemRoot = false, - certificates = { - { - certificateFile = user.tls_certificateFile, - keyFile = user.tls_keyFile - } - } - } or nil, tlsSettings = (user.tls and user.tls == "1") and { disableSystemRoot = false, certificates = { @@ -176,10 +166,6 @@ function gen_config(user) if user.tls and user.tls == "1" then config.inbounds[1].streamSettings.security = "tls" - if user.xtls and user.xtls == "1" then - config.inbounds[1].streamSettings.security = "xtls" - config.inbounds[1].streamSettings.tlsSettings = nil - end end if user.transport == "mkcp" or user.transport == "quic" then diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/server/api/xray.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/server/api/xray.lua new file mode 100644 index 0000000000..0132ab03c2 --- /dev/null +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/server/api/xray.lua @@ -0,0 +1,191 @@ +module("luci.model.cbi.passwall.server.api.xray", package.seeall) +local ucic = require"luci.model.uci".cursor() + +function gen_config(user) + local settings = nil + local routing = nil + local outbounds = { + {protocol = "freedom", tag = "direct"}, {protocol = "blackhole", tag = "blocked"} + } + + if user.protocol == "vmess" or user.protocol == "vless" then + if user.uuid then + local clients = {} + for i = 1, #user.uuid do + clients[i] = { + id = user.uuid[i], + flow = (user.xtls and user.xtls == "1") and user.flow or nil, + level = tonumber(user.level), + alterId = tonumber(user.alter_id) + } + end + settings = { + clients = clients, + decryption = user.decryption or "none" + } + end + elseif user.protocol == "socks" then + settings = { + auth = (user.auth and user.auth == "1") and "password" or "noauth", + accounts = (user.auth and user.auth == "1") and { + { + user = user.username, + pass = user.password + } + } + } + elseif user.protocol == "http" then + settings = { + allowTransparent = false, + accounts = (user.auth and user.auth == "1") and { + { + user = user.username, + pass = user.password + } + } + } + user.transport = "tcp" + user.tcp_guise = "none" + elseif user.protocol == "shadowsocks" then + settings = { + method = user.method, + password = user.password, + level = tonumber(user.level) or 1, + network = user.ss_network or "TCP,UDP" + } + elseif user.protocol == "trojan" then + if user.uuid then + local clients = {} + for i = 1, #user.uuid do + clients[i] = { + password = user.uuid[i], + level = tonumber(user.level) + } + end + settings = { + clients = clients + } + end + elseif user.protocol == "mtproto" then + settings = { + users = { + { + level = tonumber(user.level) or 1, + secret = (user.password == nil) and "" or user.password + } + } + } + end + + routing = { + domainStrategy = "IPOnDemand", + rules = { + { + type = "field", + ip = {"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"}, + outboundTag = (user.accept_lan == nil or user.accept_lan == "0") and "blocked" or "direct" + } + } + } + + if user.transit_node and user.transit_node ~= "nil" then + local gen_xray = require("luci.model.cbi.passwall.api.gen_xray") + local client = gen_xray.gen_outbound(ucic:get_all("passwall", user.transit_node), "transit") + table.insert(outbounds, 1, client) + end + + local config = { + log = { + -- error = "/var/etc/passwall_server/log/" .. user[".name"] .. ".log", + loglevel = "warning" + }, + -- 传入连接 + inbounds = { + { + listen = (user.bind_local == "1") and "127.0.0.1" or nil, + port = tonumber(user.port), + protocol = user.protocol, + settings = settings, + streamSettings = { + network = user.transport, + security = "none", + xtlsSettings = (user.tls and user.tls == "1" and user.xtls and user.xtls == "1") and { + --alpn = {"http/1.1"}, + disableSystemRoot = false, + certificates = { + { + certificateFile = user.tls_certificateFile, + keyFile = user.tls_keyFile + } + } + } or nil, + tlsSettings = (user.tls and user.tls == "1") and { + disableSystemRoot = false, + certificates = { + { + certificateFile = user.tls_certificateFile, + keyFile = user.tls_keyFile + } + } + } or nil, + tcpSettings = (user.transport == "tcp") and { + header = { + type = user.tcp_guise, + request = (user.tcp_guise == "http") and { + path = user.tcp_guise_http_path or {"/"}, + headers = { + Host = user.tcp_guise_http_host or {} + } + } or nil + } + } or nil, + kcpSettings = (user.transport == "mkcp") and { + mtu = tonumber(user.mkcp_mtu), + tti = tonumber(user.mkcp_tti), + uplinkCapacity = tonumber(user.mkcp_uplinkCapacity), + downlinkCapacity = tonumber(user.mkcp_downlinkCapacity), + congestion = (user.mkcp_congestion == "1") and true or false, + readBufferSize = tonumber(user.mkcp_readBufferSize), + writeBufferSize = tonumber(user.mkcp_writeBufferSize), + seed = (user.mkcp_seed and user.mkcp_seed ~= "") and user.mkcp_seed or nil, + header = {type = user.mkcp_guise} + } or nil, + wsSettings = (user.transport == "ws") and { + acceptProxyProtocol = false, + headers = (user.ws_host) and {Host = user.ws_host} or nil, + path = user.ws_path + } or nil, + httpSettings = (user.transport == "h2") and { + path = user.h2_path, host = user.h2_host + } or nil, + dsSettings = (user.transport == "ds") and { + path = user.ds_path + } or nil, + quicSettings = (user.transport == "quic") and { + security = user.quic_security, + key = user.quic_key, + header = {type = user.quic_guise} + } or nil + } + } + }, + -- 传出连接 + outbounds = outbounds, + routing = routing + } + + if user.tls and user.tls == "1" then + config.inbounds[1].streamSettings.security = "tls" + if user.xtls and user.xtls == "1" then + config.inbounds[1].streamSettings.security = "xtls" + config.inbounds[1].streamSettings.tlsSettings = nil + end + end + + if user.transport == "mkcp" or user.transport == "quic" then + config.inbounds[1].streamSettings.security = "none" + config.inbounds[1].streamSettings.tlsSettings = nil + end + + return config +end diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/server/index.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/server/index.lua index 2825a30fb7..81e0f5e27e 100644 --- a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/server/index.lua +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/server/index.lua @@ -47,7 +47,7 @@ e = t:option(DummyValue, "type", translate("Type")) e.cfgvalue = function(t, n) local v = Value.cfgvalue(t, n) if v then - if v == "V2ray" then + if v == "Xray" or v == "V2ray" then local protocol = m:get(n, "protocol") return v .. " -> " .. protocol end diff --git a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/server/user.lua b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/server/user.lua index 94c99413ca..8ee9626432 100644 --- a/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/server/user.lua +++ b/package/lienol/luci-app-passwall/luasrc/model/cbi/passwall/server/user.lua @@ -1,5 +1,4 @@ local d = require "luci.dispatcher" -local uci = require"luci.model.uci".cursor() local api = require "luci.model.cbi.passwall.api.api" local ss_encrypt_method_list = { @@ -78,6 +77,9 @@ end if api.is_finded("ssr-server") then type:value("SSR", translate("ShadowsocksR")) end +if api.is_finded("xray") then + type:value("Xray", translate("Xray")) +end if api.is_finded("v2ray") then type:value("V2ray", translate("V2ray")) end @@ -104,6 +106,7 @@ protocol:value("socks", "Socks") protocol:value("shadowsocks", "Shadowsocks") protocol:value("trojan", "Trojan") protocol:value("mtproto", "MTProto") +protocol:depends("type", "Xray") protocol:depends("type", "V2ray") -- Brook协议 @@ -137,6 +140,8 @@ auth.validate = function(self, value, t) return value end auth:depends("type", "Socks") +auth:depends({ type = "Xray", protocol = "socks" }) +auth:depends({ type = "Xray", protocol = "http" }) auth:depends({ type = "V2ray", protocol = "socks" }) auth:depends({ type = "V2ray", protocol = "http" }) @@ -149,9 +154,11 @@ password:depends("auth", "1") password:depends("type", "SS") password:depends("type", "SSR") password:depends("type", "Brook") +password:depends({ type = "Xray", protocol = "shadowsocks" }) password:depends({ type = "V2ray", protocol = "shadowsocks" }) mtproto_password = s:option(Value, "mtproto_password", translate("Password"), translate("The MTProto protocol must be 32 characters and can only contain characters from 0 to 9 and a to f.")) +mtproto_password:depends({ type = "Xray", protocol = "mtproto" }) mtproto_password:depends({ type = "V2ray", protocol = "mtproto" }) mtproto_password.default = arg[1] function mtproto_password.cfgvalue(self, section) @@ -163,6 +170,7 @@ end decryption = s:option(Value, "decryption", translate("Encrypt Method")) decryption.default = "none" +decryption:depends({ type = "Xray", protocol = "vless" }) decryption:depends({ type = "V2ray", protocol = "vless" }) ss_encrypt_method = s:option(ListValue, "ss_encrypt_method", translate("Encrypt Method")) @@ -187,6 +195,7 @@ end v_ss_encrypt_method = s:option(ListValue, "v_ss_encrypt_method", translate("Encrypt Method")) for a, t in ipairs(v_ss_encrypt_method_list) do v_ss_encrypt_method:value(t) end +v_ss_encrypt_method:depends({ type = "Xray", protocol = "shadowsocks" }) v_ss_encrypt_method:depends({ type = "V2ray", protocol = "shadowsocks" }) function v_ss_encrypt_method.cfgvalue(self, section) return m:get(section, "method") @@ -200,6 +209,7 @@ ss_network.default = "tcp,udp" ss_network:value("tcp", "TCP") ss_network:value("udp", "UDP") ss_network:value("tcp,udp", "TCP,UDP") +ss_network:depends({ type = "Xray", protocol = "shadowsocks" }) ss_network:depends({ type = "V2ray", protocol = "shadowsocks" }) ssr_protocol = s:option(ListValue, "ssr_protocol", translate("Protocol")) @@ -237,6 +247,9 @@ uuid = s:option(DynamicList, "uuid", translate("ID") .. "/" .. translate("Passwo for i = 1, 3 do uuid:value(api.gen_uuid(1)) end +uuid:depends({ type = "Xray", protocol = "vmess" }) +uuid:depends({ type = "Xray", protocol = "vless" }) +uuid:depends({ type = "Xray", protocol = "trojan" }) uuid:depends({ type = "V2ray", protocol = "vmess" }) uuid:depends({ type = "V2ray", protocol = "vless" }) uuid:depends({ type = "V2ray", protocol = "trojan" }) @@ -246,10 +259,16 @@ uuid:depends("type", "Trojan-Plus") alter_id = s:option(Value, "alter_id", translate("Alter ID")) alter_id.default = 16 +alter_id:depends({ type = "Xray", protocol = "vmess" }) alter_id:depends({ type = "V2ray", protocol = "vmess" }) level = s:option(Value, "level", translate("User Level")) level.default = 1 +level:depends({ type = "Xray", protocol = "vmess" }) +level:depends({ type = "Xray", protocol = "vless" }) +level:depends({ type = "Xray", protocol = "shadowsocks" }) +level:depends({ type = "Xray", protocol = "trojan" }) +level:depends({ type = "Xray", protocol = "mtproto" }) level:depends({ type = "V2ray", protocol = "vmess" }) level:depends({ type = "V2ray", protocol = "vless" }) level:depends({ type = "V2ray", protocol = "shadowsocks" }) @@ -274,6 +293,10 @@ tls.validate = function(self, value, t) return value end end +tls:depends({ type = "Xray", protocol = "vmess" }) +tls:depends({ type = "Xray", protocol = "vless" }) +tls:depends({ type = "Xray", protocol = "socks" }) +tls:depends({ type = "Xray", protocol = "shadowsocks" }) tls:depends({ type = "V2ray", protocol = "vmess" }) tls:depends({ type = "V2ray", protocol = "vless" }) tls:depends({ type = "V2ray", protocol = "socks" }) @@ -284,12 +307,16 @@ tls:depends("type", "Trojan-Go") xtls = s:option(Flag, "xtls", translate("XTLS")) xtls.default = 0 -xtls:depends({ type = "V2ray", protocol = "vless", tls = "1" }) +xtls:depends({ type = "Xray", protocol = "vless", tls = "1" }) flow = s:option(Value, "flow", translate("flow")) flow.default = "xtls-rprx-origin" flow:value("xtls-rprx-origin") flow:value("xtls-rprx-origin-udp443") +flow:value("xtls-rprx-direct") +flow:value("xtls-rprx-direct-udp443") +flow:value("xtls-rprx-splice") +flow:value("xtls-rprx-splice-udp443") flow:depends("xtls", "1") -- [[ TLS部分 ]] -- @@ -342,6 +369,11 @@ transport:value("ws", "WebSocket") transport:value("h2", "HTTP/2") transport:value("ds", "DomainSocket") transport:value("quic", "QUIC") +transport:depends({ type = "Xray", protocol = "vmess" }) +transport:depends({ type = "Xray", protocol = "vless" }) +transport:depends({ type = "Xray", protocol = "socks" }) +transport:depends({ type = "Xray", protocol = "shadowsocks" }) +transport:depends({ type = "Xray", protocol = "trojan" }) transport:depends({ type = "V2ray", protocol = "vmess" }) transport:depends({ type = "V2ray", protocol = "vless" }) transport:depends({ type = "V2ray", protocol = "socks" }) @@ -460,7 +492,7 @@ mkcp_seed:depends("transport", "mkcp") -- [[ DomainSocket部分 ]]-- -ds_path = s:option(Value, "ds_path", "Path", translate("A legal file path. This file must not exist before running V2Ray.")) +ds_path = s:option(Value, "ds_path", "Path", translate("A legal file path. This file must not exist before running.")) ds_path:depends("transport", "ds") -- [[ QUIC部分 ]]-- @@ -480,6 +512,7 @@ quic_guise:depends("transport", "quic") -- [[ VLESS Fallback部分 ]]-- --[[ fallback = s:option(Flag, "fallback", translate("Fallback")) +fallback:depends({ type = "Xray", protocol = "vless", transport = "tcp", tls = "1" }) fallback:depends({ type = "V2ray", protocol = "vless", transport = "tcp", tls = "1" }) fallback_alpn = s:option(Value, "fallback_alpn", "Fallback alpn") @@ -517,7 +550,7 @@ tcp_fast_open:depends("type", "Trojan") tcp_fast_open:depends("type", "Trojan-Plus") tcp_fast_open:depends("type", "Trojan-Go") -remote_enable = s:option(Flag, "remote_enable", translate("Enable Remote"), translate("You can forward to Nginx/Caddy/V2ray WebSocket and more.")) +remote_enable = s:option(Flag, "remote_enable", translate("Enable Remote"), translate("You can forward to Nginx/Caddy/Xray/V2ray WebSocket and more.")) remote_enable.default = "1" remote_enable.rmempty = false remote_enable:depends("type", "Trojan") @@ -535,16 +568,18 @@ remote_port:depends("remote_enable", 1) bind_local = s:option(Flag, "bind_local", translate("Bind Local"), translate("When selected, it can only be accessed locally,It is recommended to turn on when using reverse proxies.")) bind_local.default = "0" +bind_local:depends("type", "Xray") bind_local:depends("type", "V2ray") accept_lan = s:option(Flag, "accept_lan", translate("Accept LAN Access"), translate("When selected, it can accessed lan , this will not be safe!")) accept_lan.default = "0" accept_lan.rmempty = false +accept_lan:depends("type", "Xray") accept_lan:depends("type", "V2ray") local nodes_table = {} for k, e in ipairs(api.get_valid_nodes()) do - if e.node_type == "normal" and e.type == "V2ray" then + if e.node_type == "normal" and e.type == "Xray" or e.type == "V2ray" then nodes_table[#nodes_table + 1] = { id = e[".name"], remarks = e.remarks_name @@ -556,6 +591,7 @@ transit_node = s:option(ListValue, "transit_node", translate("transit node")) transit_node:value("nil", translate("Close")) for k, v in pairs(nodes_table) do transit_node:value(v.id, v.remarks) end transit_node.default = "nil" +transit_node:depends("type", "Xray") transit_node:depends("type", "V2ray") return m diff --git a/package/lienol/luci-app-passwall/luasrc/view/passwall/app_update/brook_version.htm b/package/lienol/luci-app-passwall/luasrc/view/passwall/app_update/brook_version.htm index 96956cf390..4fb2cfb9b4 100644 --- a/package/lienol/luci-app-passwall/luasrc/view/passwall/app_update/brook_version.htm +++ b/package/lienol/luci-app-passwall/luasrc/view/passwall/app_update/brook_version.htm @@ -6,6 +6,7 @@ local brook_version = require "luci.model.cbi.passwall.api.api".get_brook_versio //'; + var manuallyUpdateText = '<%:Manually update%>'; var noUpdateText = '<%:It is the latest version%>'; var updateSuccessText = '<%:Update successful%>'; var clickToUpdateText = '<%:Click to update%>'; @@ -48,7 +49,7 @@ local brook_version = require "luci.model.cbi.passwall.api.api".get_brook_versio function onRequestError_brook(btn, errorMessage) { btn.disabled = false; - btn.value = btn.placeholder; + btn.value = manuallyUpdateText; if(errorMessage) { alert(errorMessage); diff --git a/package/lienol/luci-app-passwall/luasrc/view/passwall/app_update/kcptun_version.htm b/package/lienol/luci-app-passwall/luasrc/view/passwall/app_update/kcptun_version.htm index 3abf588e66..6a3a1a63ca 100644 --- a/package/lienol/luci-app-passwall/luasrc/view/passwall/app_update/kcptun_version.htm +++ b/package/lienol/luci-app-passwall/luasrc/view/passwall/app_update/kcptun_version.htm @@ -6,6 +6,7 @@ local kcptun_version = require "luci.model.cbi.passwall.api.api".get_kcptun_vers //'; + var manuallyUpdateText = '<%:Manually update%>'; var noUpdateText = '<%:It is the latest version%>'; var updateSuccessText = '<%:Update successful%>'; var clickToUpdateText = '<%:Click to update%>'; @@ -48,7 +49,7 @@ local kcptun_version = require "luci.model.cbi.passwall.api.api".get_kcptun_vers function onRequestError_kcptun(btn, errorMessage) { btn.disabled = false; - btn.value = btn.placeholder; + btn.value = manuallyUpdateText; if(errorMessage) { alert(errorMessage); diff --git a/package/lienol/luci-app-passwall/luasrc/view/passwall/app_update/trojan_go_version.htm b/package/lienol/luci-app-passwall/luasrc/view/passwall/app_update/trojan_go_version.htm index d6d2c44246..878020044b 100644 --- a/package/lienol/luci-app-passwall/luasrc/view/passwall/app_update/trojan_go_version.htm +++ b/package/lienol/luci-app-passwall/luasrc/view/passwall/app_update/trojan_go_version.htm @@ -6,6 +6,7 @@ local trojan_go_version = require "luci.model.cbi.passwall.api.api".get_trojan_g //'; + var manuallyUpdateText = '<%:Manually update%>'; var noUpdateText = '<%:It is the latest version%>'; var updateSuccessText = '<%:Update successful%>'; var clickToUpdateText = '<%:Click to update%>'; @@ -48,7 +49,7 @@ local trojan_go_version = require "luci.model.cbi.passwall.api.api".get_trojan_g function onRequestError_trojan(btn, errorMessage) { btn.disabled = false; - btn.value = btn.placeholder; + btn.value = manuallyUpdateText; if(errorMessage) { alert(errorMessage); diff --git a/package/lienol/luci-app-passwall/luasrc/view/passwall/app_update/v2ray_version.htm b/package/lienol/luci-app-passwall/luasrc/view/passwall/app_update/v2ray_version.htm index a2694c398d..467a5ad229 100644 --- a/package/lienol/luci-app-passwall/luasrc/view/passwall/app_update/v2ray_version.htm +++ b/package/lienol/luci-app-passwall/luasrc/view/passwall/app_update/v2ray_version.htm @@ -6,6 +6,7 @@ local v2ray_version = require "luci.model.cbi.passwall.api.api".get_v2ray_versio //'; + var manuallyUpdateText = '<%:Manually update%>'; var noUpdateText = '<%:It is the latest version%>'; var updateSuccessText = '<%:Update successful%>'; var clickToUpdateText = '<%:Click to update%>'; @@ -48,7 +49,7 @@ local v2ray_version = require "luci.model.cbi.passwall.api.api".get_v2ray_versio function onRequestError_v2ray(btn, errorMessage) { btn.disabled = false; - btn.value = btn.placeholder; + btn.value = manuallyUpdateText; if(errorMessage) { alert(errorMessage); diff --git a/package/lienol/luci-app-passwall/luasrc/view/passwall/app_update/xray_version.htm b/package/lienol/luci-app-passwall/luasrc/view/passwall/app_update/xray_version.htm new file mode 100644 index 0000000000..64dbea2d46 --- /dev/null +++ b/package/lienol/luci-app-passwall/luasrc/view/passwall/app_update/xray_version.htm @@ -0,0 +1,172 @@ +<% +local xray_version = require "luci.model.cbi.passwall.api.api".get_xray_version() +-%> + + + +
+ +
+
+ 【 <%=xray_version%> 】 + + +
+
+
\ No newline at end of file diff --git a/package/lienol/luci-app-passwall/luasrc/view/passwall/global/footer.htm b/package/lienol/luci-app-passwall/luasrc/view/passwall/global/footer.htm index 9d7fc006e4..2a0d0b482d 100644 --- a/package/lienol/luci-app-passwall/luasrc/view/passwall/global/footer.htm +++ b/package/lienol/luci-app-passwall/luasrc/view/passwall/global/footer.htm @@ -1,3 +1,9 @@ +<% +local api = require "luci.model.cbi.passwall.api.api" +local tcp_node_num = api.uci_get_type("global_other", "tcp_node_num", 1) +local udp_node_num = api.uci_get_type("global_other", "udp_node_num", 1) +local auto_switch = api.uci_get_type("auto_switch", "enable", 0) +-%> \ No newline at end of file diff --git a/package/lienol/luci-app-passwall/luasrc/view/passwall/node_list/link_share_man.htm b/package/lienol/luci-app-passwall/luasrc/view/passwall/node_list/link_share_man.htm index 2c2f6efc01..d8e0c670de 100644 --- a/package/lienol/luci-app-passwall/luasrc/view/passwall/node_list/link_share_man.htm +++ b/package/lienol/luci-app-passwall/luasrc/view/passwall/node_list/link_share_man.htm @@ -172,14 +172,14 @@ local dsp = require "luci.dispatcher" "&protoparam=" + b64encsafe(v_protocol_param.value) + "&remarks=" + b64encutf8safe(v_alias.value); url = b64encsafe(ssr_str); - } else if (v_type === "Trojan" || v_type === "Trojan-Go") { + } else if (v_type === "Trojan" || v_type === "Trojan-Plus" || v_type === "Trojan-Go") { var v_password = opt.get(!opt.client && v_type === "Trojan-Go" ? "passwords" : "password"); var v_server = opt.get("address"); var v_port = opt.get("port"); url = encodeURIComponent(v_password.value) + "@" + v_server.value + ":" + v_port.value + "/?"; - if (opt.get("stream_security").value === "tls") { + if (opt.get("tls").checked) { url += "tls=1"; url += opt.query("sni", "tls_serverName"); url += opt.query("allowinsecure", "tls_allowInsecure"); @@ -187,7 +187,7 @@ local dsp = require "luci.dispatcher" url += "tls=0"; } if (v_type === "Trojan-Go") { - if (opt.get("stream_security").value === "none" && opt.get("trojan_transport").value === "original") { + if (!opt.get("tls").checked && opt.get("trojan_transport").value === "original") { var plugin = {}; plugin.type = opt.get("plugin_type").value; if (plugin.type !== "plaintext") { @@ -432,8 +432,8 @@ local dsp = require "luci.dispatcher" tls = queryParam.plugin === undefined; } if (tls === false) { alert("TODO: plugin params for trojan-go."); } - opt.set('stream_security', tls ? "tls" : "none"); - opt.get('stream_security').dispatchEvent(event); + opt.set('tls', tls); + opt.get('tls').dispatchEvent(event); if (tls) { opt.set('tls_serverName', queryParam.peer || queryParam.sni || ''); opt.set('tls_allowInsecure', queryParam.allowinsecure === '1'); @@ -490,8 +490,8 @@ local dsp = require "luci.dispatcher" opt.set('address', m.hostname); opt.set('port', m.port || "443"); opt.set(opt.client ? 'password' : 'passwords', decodeURIComponent(password)); - opt.set('stream_security', (queryParam.tls && queryParam.tls === '1') ? 'tls' : 'none'); - opt.get('stream_security').dispatchEvent(event); + opt.set('tls', (queryParam.tls && queryParam.tls === '1')); + opt.get('tls').dispatchEvent(event); var plugin = queryParam.plugin !== undefined; if (plugin) { opt.set('trojan_transport', 'original'); @@ -509,8 +509,8 @@ local dsp = require "luci.dispatcher" alert(queryParam.plugin); } var tls = !plugin && queryParam.tls === '1'; - opt.set('stream_security', tls ? "tls" : "none"); - opt.get('stream_security').dispatchEvent(event); + opt.set('tls', tls); + opt.get('tls').dispatchEvent(event); if (tls) { opt.set('tls_serverName', queryParam.peer || queryParam.sni || ''); opt.set('tls_allowInsecure', queryParam.allowinsecure === '1'); @@ -562,13 +562,14 @@ local dsp = require "luci.dispatcher" param = sstr.substr(ploc + 2); } var ssm = JSON.parse(sstr); + console.log(ssm); opt.set('remarks', ssm.ps); opt.set('address', ssm.add); opt.set('port', ssm.port); opt.set('alter_id', ssm.aid); opt.set('uuid', ssm.id); - opt.set('stream_security', ssm.tls === "tls" ? "tls" : "none"); - opt.get('stream_security').dispatchEvent(event); + opt.set('tls', ssm.tls === "tls"); + opt.get('tls').dispatchEvent(event); if (ssm.tls === "tls") { opt.set('tls_serverName', ssm.host); } diff --git a/package/lienol/luci-app-passwall/luasrc/view/passwall/node_list/node_list.htm b/package/lienol/luci-app-passwall/luasrc/view/passwall/node_list/node_list.htm index 2545d28266..4ef10331ef 100644 --- a/package/lienol/luci-app-passwall/luasrc/view/passwall/node_list/node_list.htm +++ b/package/lienol/luci-app-passwall/luasrc/view/passwall/node_list/node_list.htm @@ -97,6 +97,10 @@ table td, .table .td { } } + function copy_node(cbi_id) { + window.location.href = '<%=dsp.build_url("admin/services/passwall/copy_node")%>' + "?section=" + cbi_id; + } + var section = ""; function open_set_node_div(cbi_id) { section = cbi_id; @@ -212,7 +216,7 @@ table td, .table .td { catch(err){} //判断是否含有汉字 var reg = new RegExp("[\\u4E00-\\u9FFF]+","g"); - if ((address != null && address != "") && (port != null && port != "") && reg.test(address) == false && (address.indexOf(".") != -1 && address.charAt(address.address - 1) != ".")) { + if ((address != null && address != "") && (port != null && port != "") && reg.test(address) == false) { return { address: address, port: port }; } else { return null; @@ -408,12 +412,14 @@ table td, .table .td { var id = onclick_str.substring(onclick_str.lastIndexOf('/') + 1, onclick_str.length - 1); var td = edit_btn[i].parentNode; var new_div = ""; - //添加"置顶"按钮到"修改"按钮前 + //添加"置顶"按钮 new_div += '  '; - //添加"选择"按钮到"修改"按钮前 + //添加"选择"按钮 new_div += '  '; - //添加"应用"按钮到"修改"按钮前 + //添加"应用"按钮 new_div += '  '; + //添加"复制"按钮 + new_div += '  '; td.innerHTML = new_div + td.innerHTML; } catch(err) { diff --git a/package/lienol/luci-app-passwall/po/zh-cn/passwall.po b/package/lienol/luci-app-passwall/po/zh-cn/passwall.po index f55a8c8b55..c3428b81e2 100644 --- a/package/lienol/luci-app-passwall/po/zh-cn/passwall.po +++ b/package/lienol/luci-app-passwall/po/zh-cn/passwall.po @@ -112,6 +112,9 @@ msgstr "TCP节点" msgid "UDP Node" msgstr "UDP节点" +msgid "Edit Current Node" +msgstr "编辑当前节点" + msgid "Socks Config" msgstr "Socks配置" @@ -391,12 +394,18 @@ msgstr "类型" msgid "Balancing" msgstr "负载均衡" +msgid "Xray_balancing" +msgstr "Xray 负载均衡" + msgid "V2ray_balancing" msgstr "V2ray 负载均衡" msgid "Shunt" msgstr "分流" +msgid "Xray_shunt" +msgstr "Xray 分流" + msgid "V2ray_shunt" msgstr "V2ray 分流" @@ -406,6 +415,21 @@ msgstr "前置代理" msgid "Use the default node for the transit." msgstr "使用默认节点代理转发。" +msgid "No shunt rules? Click me to go to add." +msgstr "没有分流规则?点我前往去添加。" + +msgid "Domain Strategy" +msgstr "域名解析策略" + +msgid "'AsIs': Only use domain for routing. Default value." +msgstr "AsIs:只使用域名进行路由选择。默认值。" + +msgid "'IPIfNonMatch': When no rule matches current domain, resolves it into IP addresses (A or AAAA records) and try all rules again." +msgstr "IPIfNonMatch:当域名没有匹配任何规则时,将域名解析成 IP(A 记录或 AAAA 记录)再次进行匹配。" + +msgid "'IPOnDemand': As long as there is a IP-based rule, resolves the domain into IP immediately." +msgstr "IPOnDemand:当匹配时碰到任何基于 IP 的规则,将域名立即解析为 IP 进行匹配。" + msgid "Load balancing node list" msgstr "负载均衡节点列表" @@ -505,6 +529,9 @@ msgstr "应用" msgid "Use" msgstr "使用" +msgid "Copy" +msgstr "复制" + msgid "Delay Settings" msgstr "定时配置" @@ -613,6 +640,12 @@ msgstr "主节点" msgid "List of backup nodes" msgstr "备用节点的列表" +msgid "Restore Switch" +msgstr "恢复切换" + +msgid "When detects main node is available, switch back to the main node." +msgstr "当检测到主节点可用时,切换回主节点。" + msgid "Configure this node with 127.0.0.1: this port" msgstr "使用127.0.0.1和此端口配置节点" @@ -637,6 +670,9 @@ msgstr "负载均衡端口" msgid "Add a node, Export Of Multi WAN Only support Multi Wan. Load specific gravity range 1-256. Multiple primary servers can be load balanced, standby will only be enabled when the primary server is offline! Multiple groups can be set, Haproxy port same one for each group." msgstr "添加节点,指定出口功能是为多WAN用户准备的。负载比重范围1-256。多个主服务器可以负载均衡,备用只有在主服务器离线时才会启用!可以设置多个组,负载均衡端口相同则为一组。" +msgid "Note that the node configuration parameters for load balancing must be consistent, otherwise problems can arise!" +msgstr "注意,负载均衡的节点配置参数必须一致,否则会出问题!" + msgid "Node" msgstr "节点" @@ -739,23 +775,20 @@ msgstr "组件更新" msgid "Please confirm that your firmware supports FPU." msgstr "请确认你的固件支持FPU。" -msgid "if you want to run from memory, change the path, such as %s, Then save the application and update it manually." -msgstr "如果你希望从内存中运行,请更改路径,例如%s,然后保存应用后,再手动更新。" +msgid "if you want to run from memory, change the path, /tmp beginning then save the application and update it manually." +msgstr "如果你希望从内存中运行,请更改路径,/tmp 开头,然后保存应用后,再手动更新。" msgid "Make sure there is enough space to install %s" -msgstr "确保有足够的空间安装%s" +msgstr "确保有足够的空间安装 %s" -msgid "V2ray Path" -msgstr "V2ray 路径" +msgid "App Path" +msgstr "程序路径" -msgid "Trojan-Go Path" -msgstr "Trojan-Go 路径" +msgid "%s App Path" +msgstr "%s 程序路径" -msgid "Kcptun Client Path" -msgstr "Kcptun 客户端路径" - -msgid "Brook Path" -msgstr "Brook 路径" +msgid "%s Client App Path" +msgstr "%s 客户端程序路径" msgid "Trojan-Go Version API" msgstr "Trojan-Go 版本 API" @@ -838,6 +871,36 @@ msgstr "不是有效域名,请重新输入!" msgid "Not valid IP format, please re-enter!" msgstr "不是有效IP格式,请重新输入!" +msgid "Plaintext: If this string matches any part of the targeting domain, this rule takes effet. Example: rule 'sina.com' matches targeting domain 'sina.com', 'sina.com.cn' and 'www.sina.com', but not 'sina.cn'." +msgstr "纯字符串: 当此字符串匹配目标域名中任意部分,该规则生效。比如'sina.com'可以匹配'sina.com'、'sina.com.cn'和'www.sina.com',但不匹配'sina.cn'。" + +msgid "Regular expression: Begining with 'regexp:', the rest is a regular expression. When the regexp matches targeting domain, this rule takes effect. Example: rule 'regexp:\\.goo.*\\.com$' matches 'www.google.com' and 'fonts.googleapis.com', but not 'google.com'." +msgstr "正则表达式: 由'regexp:'开始,余下部分是一个正则表达式。当此正则表达式匹配目标域名时,该规则生效。例如'regexp:\\.goo.*\\.com$'匹配'www.google.com'、'fonts.googleapis.com',但不匹配'google.com'。" + +msgid "Subdomain (recommended): Begining with 'domain:' and the rest is a domain. When the targeting domain is exactly the value, or is a subdomain of the value, this rule takes effect. Example: rule 'domain:v2ray.com' matches 'www.v2ray.com', 'v2ray.com', but not 'xv2ray.com'." +msgstr "子域名 (推荐): 由'domain:'开始,余下部分是一个域名。当此域名是目标域名或其子域名时,该规则生效。例如'domain:v2ray.com'匹配'www.v2ray.com'、'v2ray.com',但不匹配'xv2ray.com'。" + +msgid "Full domain: Begining with 'full:' and the rest is a domain. When the targeting domain is exactly the value, the rule takes effect. Example: rule 'domain:v2ray.com' matches 'v2ray.com', but not 'www.v2ray.com'." +msgstr "完整匹配: 由'full:'开始,余下部分是一个域名。当此域名完整匹配目标域名时,该规则生效。例如'full:v2ray.com'匹配'v2ray.com'但不匹配'www.v2ray.com'。" + +msgid "Pre-defined domain list: Begining with 'geosite:' and the rest is a name, such as geosite:google or geosite:cn." +msgstr "预定义域名列表:由'geosite:'开头,余下部分是一个名称,如geosite:google或者geosite:cn。" + +msgid "Domains from file: Such as 'ext:file:tag'. The value must begin with ext: (lowercase), and followed by filename and tag. The file is placed in resource directory, and has the same format of geosite.dat. The tag must exist in the file." +msgstr "从文件中加载域名: 形如'ext:file:tag',必须以ext:(小写)开头,后面跟文件名和标签,文件存放在资源目录中,文件格式与geosite.dat相同,标签必须在文件中存在。" + +msgid "IP: such as '127.0.0.1'." +msgstr "IP: 形如'127.0.0.1'。" + +msgid "CIDR: such as '127.0.0.0/8'." +msgstr "CIDR: 形如'10.0.0.0/8'." + +msgid "GeoIP: such as 'geoip:cn'. It begins with geoip: (lower case) and followed by two letter of country code." +msgstr "GeoIP: 形如'geoip:cn',必须以geoip:(小写)开头,后面跟双字符国家代码,支持几乎所有可以上网的国家。" + +msgid "IPs from file: Such as 'ext:file:tag'. The value must begin with ext: (lowercase), and followed by filename and tag. The file is placed in resource directory, and has the same format of geoip.dat. The tag must exist in the file." +msgstr "从文件中加载 IP: 形如'ext:file:tag',必须以ext:(小写)开头,后面跟文件名和标签,文件存放在资源目录中,文件格式与geoip.dat相同标签必须在文件中存在。" + msgid "Clear logs" msgstr "清空日志" @@ -868,6 +931,9 @@ msgstr "插件" msgid "opts" msgstr "插件选项" +msgid "Xray is currently directly compatible with V2ray and used." +msgstr "Xray 目前可直接兼容 V2ray 并使用。" + msgid "Protocol" msgstr "协议名称" @@ -955,8 +1021,8 @@ msgstr "是否允许不安全连接。当勾选时,将跳过证书验证。" msgid "
none: default, no masquerade, data sent is packets with no characteristics.
srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).
utp: packets disguised as uTP will be recognized as bittorrent downloaded data.
wechat-video: packets disguised as WeChat video calls.
dtls: disguised as DTLS 1.2 packet.
wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)" msgstr "
none:默认值,不进行伪装,发送的数据是没有特征的数据包。
srtp:伪装成 SRTP 数据包,会被识别为视频通话数据(如 FaceTime)。
utp:伪装成 uTP 数据包,会被识别为 BT 下载数据。
wechat-video:伪装成微信视频通话的数据包。
dtls:伪装成 DTLS 1.2 数据包。
wireguard:伪装成 WireGuard 数据包。(并不是真正的 WireGuard 协议)" -msgid "A legal file path. This file must not exist before running V2Ray." -msgstr "一个合法的文件路径。在运行 V2Ray 之前,这个文件必须不存在。" +msgid "A legal file path. This file must not exist before running." +msgstr "一个合法的文件路径。在运行之前,这个文件必须不存在。" msgid "TCP Open Socks" msgstr "开启Socks" @@ -1069,6 +1135,12 @@ msgstr "日志" msgid "UDP Forward" msgstr "UDP转发" +msgid "You did not fill in the %s path. Please save and apply then update manually." +msgstr "您没有填写 %s 路径。请保存应用后再手动更新。" + +msgid "Not installed unzip, Can't unzip!" +msgstr "未安装unzip,无法解压。" + msgid "Can't determine ARCH, or ARCH not supported." msgstr "无法确认ARCH架构,或是不支持。" diff --git a/package/lienol/luci-app-passwall/root/etc/config/passwall b/package/lienol/luci-app-passwall/root/etc/config/passwall index b95c03068b..8d8401e160 100644 --- a/package/lienol/luci-app-passwall/root/etc/config/passwall +++ b/package/lienol/luci-app-passwall/root/etc/config/passwall @@ -48,6 +48,7 @@ config global_rules option chnroute6_url 'https://ispip.clang.cn/all_cn_ipv6.txt' config global_app + option xray_file '/usr/bin/xray/' option v2ray_file '/usr/bin/v2ray/' option trojan_go_file '/usr/bin/trojan-go' option kcptun_client_file '/usr/bin/kcptun-client' diff --git a/package/lienol/luci-app-passwall/root/etc/uci-defaults/luci-app-passwall b/package/lienol/luci-app-passwall/root/etc/uci-defaults/luci-app-passwall index f6838de529..4eb5ac2dd8 100755 --- a/package/lienol/luci-app-passwall/root/etc/uci-defaults/luci-app-passwall +++ b/package/lienol/luci-app-passwall/root/etc/uci-defaults/luci-app-passwall @@ -25,5 +25,25 @@ uci -q batch <<-EOF >/dev/null commit uhttpd EOF +tmp=$(uci -q get passwall.@global_app[0].xray_file) +if [ -z "$tmp" ]; then + uci set passwall.@global_app[0].xray_file="/usr/bin/xray" +else + [ -n "$(echo $tmp | grep -E "^.*/xray/$")" ] && { + uci set passwall.@global_app[0].xray_file="${tmp%?}" + } +fi +tmp=$(uci -q get passwall.@global_app[0].v2ray_file) +if [ -z "$tmp" ]; then + uci set passwall.@global_app[0].v2ray_file="/usr/bin/v2ray" +else + [ -n "$(echo $tmp | grep -E "^.*/v2ray/$")" ] && { + uci set passwall.@global_app[0].v2ray_file="${tmp%?}" + } +fi +uci commit passwall + +/etc/init.d/https-dns-proxy stop >/dev/null 2>&1 & +/etc/init.d/https-dns-proxy disable >/dev/null 2>&1 & rm -rf /tmp/luci-*cache exit 0 diff --git a/package/lienol/luci-app-passwall/root/usr/share/passwall/app.sh b/package/lienol/luci-app-passwall/root/usr/share/passwall/app.sh index cb5034e31b..9522313318 100755 --- a/package/lienol/luci-app-passwall/root/usr/share/passwall/app.sh +++ b/package/lienol/luci-app-passwall/root/usr/share/passwall/app.sh @@ -26,6 +26,8 @@ use_tcp_node_resolve_dns=0 use_udp_node_resolve_dns=0 LUA_API_PATH=/usr/lib/lua/luci/model/cbi/$CONFIG/api API_GEN_SS=$LUA_API_PATH/gen_shadowsocks.lua +API_GEN_XRAY=$LUA_API_PATH/gen_xray.lua +API_GEN_XRAY_PROTO=$LUA_API_PATH/gen_xray_proto.lua API_GEN_V2RAY=$LUA_API_PATH/gen_v2ray.lua API_GEN_V2RAY_PROTO=$LUA_API_PATH/gen_v2ray_proto.lua API_GEN_TROJAN=$LUA_API_PATH/gen_trojan.lua @@ -209,7 +211,7 @@ get_new_port() { first_type() { local path_name=${1} - type -t -p "/bin/${path_name}" -p "${TMP_BIN_PATH}/${path_name}" -p "${path_name}" -p "/usr/bin/v2ray/{path_name}" "$@" | head -n1 + type -t -p "/bin/${path_name}" -p "${TMP_BIN_PATH}/${path_name}" -p "${path_name}" "$@" | head -n1 } ln_start_bin() { @@ -333,7 +335,7 @@ run_socks() { msg="某种原因,此 Socks 服务的相关配置已失联,启动中止!" fi - if [ "$type" == "v2ray" ] && ([ -n "$(config_n_get $node balancing_node)" ] || [ "$(config_n_get $node default_node)" != "nil" ]); then + if [ "$type" == "xray" -o "$type" == "v2ray" ] && ([ -n "$(config_n_get $node balancing_node)" ] || [ "$(config_n_get $node default_node)" != "nil" ]); then unset msg fi @@ -350,6 +352,10 @@ run_socks() { [ -n "$_username" ] && [ -n "$_password" ] && local _auth="--uname $_username --passwd $_password" ln_start_bin "$(first_type ssocks)" ssocks_SOCKS_$id --listen $socks_port --socks $server_host:$port $_auth ;; + xray) + lua $API_GEN_XRAY $node nil nil $socks_port > $config_file + ln_start_bin "$(first_type $(config_t_get global_app xray_file notset)/xray xray)" xray -config="$config_file" + ;; v2ray) lua $API_GEN_V2RAY $node nil nil $socks_port > $config_file ln_start_bin "$(first_type $(config_t_get global_app v2ray_file notset)/v2ray v2ray)" v2ray -config="$config_file" @@ -423,6 +429,10 @@ run_redir() { eval port=\$UDP_REDIR_PORT$6 ln_start_bin "$(first_type ipt2socks)" "ipt2socks_udp_$6" -U -l "$port" -b 0.0.0.0 -s "$node_address" -p "$node_port" -R ;; + xray) + lua $API_GEN_XRAY $node udp $local_port nil > $config_file + ln_start_bin "$(first_type $(config_t_get global_app xray_file notset)/xray xray)" xray -config="$config_file" + ;; v2ray) lua $API_GEN_V2RAY $node udp $local_port nil > $config_file ln_start_bin "$(first_type $(config_t_get global_app v2ray_file notset)/v2ray v2ray)" v2ray -config="$config_file" @@ -480,6 +490,12 @@ run_redir() { _socks_username=$(config_n_get $node username) _socks_password=$(config_n_get $node password) ;; + xray) + local extra_param="tcp" + [ "$6" == 1 ] && [ "$UDP_NODE1" == "tcp" ] && extra_param="tcp,udp" + lua $API_GEN_XRAY $node $extra_param $local_port nil > $config_file + ln_start_bin "$(first_type $(config_t_get global_app xray_file notset)/xray xray)" xray -config="$config_file" + ;; v2ray) local extra_param="tcp" [ "$6" == 1 ] && [ "$UDP_NODE1" == "tcp" ] && extra_param="tcp,udp" @@ -603,15 +619,22 @@ start_socks() { clean_log() { logsnum=$(cat $LOG_FILE 2>/dev/null | wc -l) - [ "$logsnum" -gt 300 ] && { + [ "$logsnum" -gt 1000 ] && { echo "" > $LOG_FILE echolog "日志文件过长,清空处理!" } } -start_crontab() { +clean_crontab() { touch /etc/crontabs/root - sed -i "/$CONFIG/d" /etc/crontabs/root >/dev/null 2>&1 & + #sed -i "/${CONFIG}/d" /etc/crontabs/root >/dev/null 2>&1 & + sed -i "/$(echo "/etc/init.d/${CONFIG}" | sed 's#\/#\\\/#g')/d" /etc/crontabs/root >/dev/null 2>&1 & + sed -i "/$(echo "lua ${APP_PATH}/rule_update.lua log" | sed 's#\/#\\\/#g')/d" /etc/crontabs/root >/dev/null 2>&1 & + sed -i "/$(echo "lua ${APP_PATH}/subscribe.lua start log" | sed 's#\/#\\\/#g')/d" /etc/crontabs/root >/dev/null 2>&1 & +} + +start_crontab() { + clean_crontab auto_on=$(config_t_get global_delay auto_on 0) if [ "$auto_on" = "1" ]; then time_off=$(config_t_get global_delay time_off) @@ -666,8 +689,7 @@ start_crontab() { } stop_crontab() { - touch /etc/crontabs/root - sed -i "/$CONFIG/d" /etc/crontabs/root >/dev/null 2>&1 & + clean_crontab ps | grep "$APP_PATH/test.sh" | grep -v "grep" | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 & /etc/init.d/cron restart #echolog "清除定时执行命令。" @@ -1118,7 +1140,7 @@ start_haproxy() { unset msg failcount=0 while [ "$failcount" -lt "3" ]; do - ubus list network.interface.${export} >/dev/null 2>&1 + ip route show dev ${export} >/dev/null 2>&1 if [ $? -ne 0 ]; then let "failcount++" echolog " - 找不到出口接口:$export,1分钟后再重试(${failcount}/3),${bip}" diff --git a/package/lienol/luci-app-passwall/root/usr/share/passwall/rule_update.lua b/package/lienol/luci-app-passwall/root/usr/share/passwall/rule_update.lua old mode 100644 new mode 100755 diff --git a/package/lienol/luci-app-passwall/root/usr/share/passwall/subscribe.lua b/package/lienol/luci-app-passwall/root/usr/share/passwall/subscribe.lua old mode 100644 new mode 100755 index 049a321b8a..8c8ecc069d --- a/package/lienol/luci-app-passwall/root/usr/share/passwall/subscribe.lua +++ b/package/lienol/luci-app-passwall/root/usr/share/passwall/subscribe.lua @@ -8,7 +8,7 @@ require 'luci.model.uci' require 'luci.util' require 'luci.jsonc' require 'luci.sys' -local _api = require "luci.model.cbi.passwall.api.api" +local api = require "luci.model.cbi.passwall.api.api" -- these global functions are accessed all the time by the event handler -- so caching them is worth the effort @@ -135,7 +135,7 @@ do end ucic2:foreach(application, uciType, function(node) - if node.type == 'V2ray' and node.protocol == '_shunt' then + if node.protocol and node.protocol == '_shunt' then local node_id = node[".name"] ucic2:foreach(application, "shunt_rules", function(e) local _node_id = node[e[".name"]] or nil @@ -146,7 +146,7 @@ do CONFIG[#CONFIG + 1] = { log = false, currentNode = _node, - remarks = "V2ray分流" .. e.remarks .. "节点", + remarks = "分流" .. e.remarks .. "节点", set = function(server) ucic2:set(application, node_id, e[".name"], server) end @@ -161,12 +161,12 @@ do CONFIG[#CONFIG + 1] = { log = false, currentNode = default_node, - remarks = "V2ray分流默认节点", + remarks = "分流默认节点", set = function(server) ucic2:set(application, node_id, "default_node", server) end } - elseif node.type == 'V2ray' and node.protocol == '_balancing' then + elseif node.protocol and node.protocol == '_balancing' then local node_id = node[".name"] local nodes = {} local new_nodes = {} @@ -184,7 +184,7 @@ do remarks = node, set = function(server) for kk, vv in pairs(CONFIG) do - if (vv.remarks == "V2ray负载均衡节点列表" .. node_id) then + if (vv.remarks == "负载均衡节点列表" .. node_id) then table.insert(vv.new_nodes, server) end end @@ -193,13 +193,13 @@ do end end CONFIG[#CONFIG + 1] = { - remarks = "V2ray负载均衡节点列表" .. node_id, + remarks = "负载均衡节点列表" .. node_id, nodes = nodes, new_nodes = new_nodes, set = function() for kk, vv in pairs(CONFIG) do - if (vv.remarks == "V2ray负载均衡节点列表" .. node_id) then - log("刷新V2ray负载均衡节点列表") + if (vv.remarks == "负载均衡节点列表" .. node_id) then + log("刷新负载均衡节点列表") ucic2:foreach(application, uciType, function(node2) if node2[".name"] == node[".name"] then local index = node2[".index"] @@ -315,7 +315,7 @@ local function base64Decode(text) end -- 处理数据 local function processData(szType, content, add_mode) - log(content, add_mode) + --log(content, add_mode) local result = { timeout = 60, add_mode = add_mode, @@ -344,6 +344,9 @@ local function processData(szType, content, add_mode) elseif szType == 'vmess' then local info = jsonParse(content) result.type = 'V2ray' + if api.is_finded("xray") then + result.type = 'Xray' + end result.address = info.add result.port = info.port result.protocol = 'vmess' @@ -385,11 +388,11 @@ local function processData(szType, content, add_mode) end if not info.security then result.security = "auto" end if info.tls == "tls" or info.tls == "1" then - result.stream_security = "tls" + result.tls = "1" result.tls_serverName = info.host result.tls_allowInsecure = allowInsecure_default and "1" or "0" else - result.stream_security = "none" + result.tls = "0" end elseif szType == "ss" then local idx_sp = 0 @@ -506,7 +509,7 @@ local function processData(szType, content, add_mode) result.type = "Trojan-Go" result.fingerprint = "firefox" end - result.stream_security = 'tls' + result.tls = '1' result.tls_serverName = peer and peer or sni result.tls_allowInsecure = allowInsecure and "1" or "0" end @@ -567,7 +570,7 @@ local function processData(szType, content, add_mode) end result.port = port result.fingerprint = "firefox" - result.stream_security = 'tls' + result.tls = '1' result.tls_serverName = peer and peer or sni result.tls_allowInsecure = allowInsecure and "1" or "0" end @@ -647,21 +650,21 @@ end local function select_node(nodes, config) local server if config.currentNode then - -- 特别优先级 V2ray分流 + 备注 - if config.currentNode.type == 'V2ray' and config.currentNode.protocol == '_shunt' then + -- 特别优先级 分流 + 备注 + if config.currentNode.protocol and config.currentNode.protocol == '_shunt' then for id, node in pairs(nodes) do if node.remarks == config.currentNode.remarks then - log('选择【' .. config.remarks .. '】V2ray分流匹配节点:' .. node.remarks) + log('选择【' .. config.remarks .. '】分流匹配节点:' .. node.remarks) server = id break end end end - -- 特别优先级 V2ray负载均衡 + 备注 - if config.currentNode.type == 'V2ray' and config.currentNode.protocol == '_balancing' then + -- 特别优先级 负载均衡 + 备注 + if config.currentNode.protocol and config.currentNode.protocol == '_balancing' then for id, node in pairs(nodes) do if node.remarks == config.currentNode.remarks then - log('选择【' .. config.remarks .. '】V2ray负载均衡匹配节点:' .. node.remarks) + log('选择【' .. config.remarks .. '】负载均衡匹配节点:' .. node.remarks) server = id break end @@ -679,11 +682,11 @@ local function select_node(nodes, config) end end end - -- 第二优先级 IP + 端口 + -- 第二优先级 类型 + IP + 端口 if not server then for id, node in pairs(nodes) do - if node.address and node.port then - if node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port then + if node.type and node.address and node.port then + if node.type == config.currentNode.type and (node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port) then if config.log == nil or config.log == true then log('选择【' .. config.remarks .. '】第二匹配节点:' .. node.remarks) end @@ -693,11 +696,11 @@ local function select_node(nodes, config) end end end - -- 第三优先级 IP + -- 第三优先级 IP + 端口 if not server then for id, node in pairs(nodes) do - if node.address then - if node.address == config.currentNode.address then + if node.address and node.port then + if node.address .. ':' .. node.port == config.currentNode.address .. ':' .. config.currentNode.port then if config.log == nil or config.log == true then log('选择【' .. config.remarks .. '】第三匹配节点:' .. node.remarks) end @@ -707,13 +710,27 @@ local function select_node(nodes, config) end end end - -- 第四优先级备注 + -- 第四优先级 IP + if not server then + for id, node in pairs(nodes) do + if node.address then + if node.address == config.currentNode.address then + if config.log == nil or config.log == true then + log('选择【' .. config.remarks .. '】第四匹配节点:' .. node.remarks) + end + server = id + break + end + end + end + end + -- 第五优先级备注 if not server then for id, node in pairs(nodes) do if node.remarks then if node.remarks == config.currentNode.remarks then if config.log == nil or config.log == true then - log('选择【' .. config.remarks .. '】第四匹配节点:' .. node.remarks) + log('选择【' .. config.remarks .. '】第五匹配节点:' .. node.remarks) end server = id break @@ -751,7 +768,7 @@ local function update_node(manual) end) for _, v in ipairs(nodeResult) do for _, vv in ipairs(v) do - local uuid = _api.gen_uuid() + local uuid = api.gen_uuid() local cfgid = ucic2:section(application, uciType, uuid) cfgid = uuid for kkk, vvv in pairs(vv) do @@ -858,7 +875,7 @@ local function parse_link(raw, remark, manual) not result.address:find("%.") or -- 虽然没有.也算域,不过应该没有人会这样干吧 result.address:sub(#result.address) == "." -- 结尾是. then - log('丢弃无效节点: ' .. result.type .. ' 节点, ' .. result.remarks) + log('丢弃过滤节点: ' .. result.type .. ' 节点, ' .. result.remarks) else tinsert(all_nodes, result) end diff --git a/package/lienol/luci-app-passwall/root/usr/share/passwall/test.sh b/package/lienol/luci-app-passwall/root/usr/share/passwall/test.sh index 8baca8a613..35beb8d2b7 100755 --- a/package/lienol/luci-app-passwall/root/usr/share/passwall/test.sh +++ b/package/lienol/luci-app-passwall/root/usr/share/passwall/test.sh @@ -67,40 +67,15 @@ test_auto_switch() { fi status=$(test_proxy) - if [ "$status" == 0 ]; then - echolog "自动切换检测:${type}_${index}节点$(config_n_get $now_node type) $(config_n_get $now_node address) $(config_n_get $now_node port)正常。" - #检测主节点是否能使用 - local main_node=$(config_t_get auto_switch tcp_main1) - if [ "$now_node" != "$main_node" ]; then - local node_type=$(echo $(config_n_get $main_node type) | tr 'A-Z' 'a-z') - if [ "$node_type" == "socks" ]; then - local node_address=$(config_n_get $main_node address) - local node_port=$(config_n_get $main_node port) - [ -n "$node_address" ] && [ -n "$node_port" ] && local curlx="socks5h://$node_address:$node_port" - else - local tmp_port=$(/usr/share/passwall/app.sh get_new_port 61080 tcp) - /usr/share/passwall/app.sh run_socks "$main_node" "127.0.0.1" "$tmp_port" "/var/etc/passwall/auto_switch_$index.json" "10" - local curlx="socks5h://127.0.0.1:$tmp_port" - fi - sleep 10s - proxy_status=$(test_url "https://www.google.com/generate_204" 3 3 "-x $curlx") - ps -w | grep -v "grep" | grep "/var/etc/passwall/auto_switch_$index.json" | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 - if [ "$proxy_status" -eq 200 ]; then - #主节点正常,切换到主节点 - echolog "自动切换检测:${type}_${index}主节点正常,切换到主节点!" - /usr/share/passwall/app.sh node_switch $type $2 $index $main_node - return 0 - fi - fi - return 0 - elif [ "$status" == 2 ]; then + if [ "$status" == 2 ]; then echolog "自动切换检测:无法连接到网络,请检查网络是否正常!" return 2 - elif [ "$status" == 1 ]; then - echolog "自动切换检测:${type}_${index}节点异常,开始切换节点!" - + fi + + local restore_switch=$(config_t_get auto_switch restore_switch${index} 0) + if [ "$restore_switch" == "1" ]; then #检测主节点是否能使用 - local main_node=$(config_t_get auto_switch tcp_main1) + local main_node=$(config_t_get auto_switch tcp_main${index}) if [ "$now_node" != "$main_node" ]; then local node_type=$(echo $(config_n_get $main_node type) | tr 'A-Z' 'a-z') if [ "$node_type" == "socks" ]; then @@ -122,7 +97,13 @@ test_auto_switch() { return 0 fi fi - + fi + + if [ "$status" == 0 ]; then + echolog "自动切换检测:${type}_${index}节点$(config_n_get $now_node type) $(config_n_get $now_node address) $(config_n_get $now_node port)正常。" + return 0 + elif [ "$status" == 1 ]; then + echolog "自动切换检测:${type}_${index}节点异常,开始切换节点!" local new_node in_backup_nodes=$(echo $b_tcp_nodes | grep $now_node) # 判断当前节点是否存在于备用节点列表里