├── .github ├── ISSUE_TEMPLATE │ ├── bug.yml │ ├── config.yml │ └── feature.yml └── workflows │ ├── Auto compile with openwrt sdk.yml │ └── Close stale issues and Prs.yml └── luci-app-passwall2 ├── Makefile ├── htdocs └── luci-static │ └── resources │ └── qrcode.min.js ├── luasrc ├── controller │ └── passwall2.lua ├── model │ └── cbi │ │ └── passwall2 │ │ ├── client │ │ ├── acl.lua │ │ ├── acl_config.lua │ │ ├── app_update.lua │ │ ├── global.lua │ │ ├── haproxy.lua │ │ ├── log.lua │ │ ├── node_config.lua │ │ ├── node_list.lua │ │ ├── node_subscribe.lua │ │ ├── node_subscribe_config.lua │ │ ├── other.lua │ │ ├── rule.lua │ │ ├── shunt_rules.lua │ │ ├── socks_config.lua │ │ └── type │ │ │ ├── hysteria2.lua │ │ │ ├── naive.lua │ │ │ ├── ray.lua │ │ │ ├── sing-box.lua │ │ │ ├── ss-rust.lua │ │ │ ├── ss.lua │ │ │ ├── ssr.lua │ │ │ └── tuic.lua │ │ └── server │ │ ├── index.lua │ │ ├── type │ │ ├── hysteria2.lua │ │ ├── ray.lua │ │ ├── sing-box.lua │ │ ├── ss-rust.lua │ │ ├── ss.lua │ │ └── ssr.lua │ │ └── user.lua ├── passwall2 │ ├── api.lua │ ├── com.lua │ ├── server_app.lua │ ├── util_hysteria2.lua │ ├── util_naiveproxy.lua │ ├── util_shadowsocks.lua │ ├── util_sing-box.lua │ ├── util_tuic.lua │ └── util_xray.lua └── view │ └── passwall2 │ ├── app_update │ └── app_version.htm │ ├── cbi │ └── hidevalue.htm │ ├── global │ ├── faq.htm │ ├── footer.htm │ └── status.htm │ ├── haproxy │ └── status.htm │ ├── log │ ├── backup_restore.htm │ └── log.htm │ ├── node_list │ ├── link_add_node.htm │ ├── link_share_man.htm │ └── node_list.htm │ ├── rule │ └── rule_version.htm │ ├── server │ ├── log.htm │ └── users_list_status.htm │ └── socks_auto_switch │ └── btn.htm ├── po ├── zh-cn │ └── passwall2.po └── zh_Hans └── root ├── etc ├── config │ └── passwall2_server ├── hotplug.d │ └── iface │ │ └── 98-passwall2 ├── init.d │ ├── passwall2 │ └── passwall2_server └── uci-defaults │ └── luci-passwall2 └── usr └── share ├── passwall2 ├── 0_default_config ├── app.sh ├── domains_excluded ├── haproxy.lua ├── haproxy_check.sh ├── helper_dnsmasq.lua ├── iptables.sh ├── monitor.sh ├── nftables.sh ├── rule_update.lua ├── socks_auto_switch.sh ├── subscribe.lua ├── tasks.sh └── test.sh ├── rpcd └── acl.d │ └── luci-app-passwall2.json └── ucitrack ├── luci-app-passwall2-server.json └── luci-app-passwall2.json /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | name: Bug Reporting 2 | description: Report any Bugs you encounter to help us improve. 3 | title: "[Bug]: " 4 | labels: bug 5 | 6 | body: 7 | - type: markdown 8 | id: tips 9 | attributes: 10 | value: | 11 | Please fill in this specification. Effective feedback will help us better solve your problem. 12 | 13 | - type: textarea 14 | id: description 15 | attributes: 16 | label: Describe the Bug you encountered 17 | description: Please describe the problem as simply and clearly as possible. 18 | validations: 19 | required: true 20 | 21 | - type: textarea 22 | id: steps 23 | attributes: 24 | label: Steps to reproduce this Bug 25 | placeholder: | 26 | 1. Select Menu'...' 27 | 2. Click the button'....' 28 | 3. Then... 29 | 4. Appear: 30 | validations: 31 | required: true 32 | 33 | - type: textarea 34 | id: purpose 35 | attributes: 36 | label: What you want to implement 37 | description: Please describe what you expect to happen as simply and clearly as possible. 38 | validations: 39 | required: true 40 | 41 | - type: textarea 42 | id: logs 43 | attributes: 44 | label: Log information 45 | description: Submit all system log information related to the problem (this is very important). 46 | validations: 47 | required: true 48 | 49 | - type: textarea 50 | id: pics 51 | attributes: 52 | label: Screenshot 53 | description: If applicable, you can add screenshots to better explain your problem. 54 | validations: 55 | required: false 56 | 57 | - type: textarea 58 | id: environment 59 | attributes: 60 | label: System related information 61 | description: Fill in relevant information to better identify the problem. 62 | placeholder: | 63 | - Passwall2 version: 64 | - Browser version (such as Chrome 96.0.4664.45 64-bit official version): 65 | - Other: 66 | validations: 67 | required: true 68 | 69 | - type: textarea 70 | id: supplements 71 | attributes: 72 | label: Other Information 73 | description: Other information related to this Bug. 74 | validations: 75 | required: false 76 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.yml: -------------------------------------------------------------------------------- 1 | name: Feature Requests 2 | description: Propose new feature ideas for this project. 3 | title: "[Feature Request]: " 4 | labels: enhancement 5 | 6 | body: 7 | - type: textarea 8 | id: description 9 | attributes: 10 | label: Describe the new feature you want 11 | description: Describe the new feature you want or the problem you want solved as simply and clearly as possible. 12 | validations: 13 | required: true 14 | 15 | - type: textarea 16 | id: ideal-solution 17 | attributes: 18 | label: Describe the solution you want 19 | description: Describe the new feature you want as simply and clearly as possible. 20 | validations: 21 | required: true 22 | 23 | - type: textarea 24 | id: other-solutions 25 | attributes: 26 | label: Describe the alternatives you considered 27 | description: Describe any alternative solutions or features you considered as simply and clearly as possible. 28 | validations: 29 | required: false 30 | 31 | - type: textarea 32 | id: supplements 33 | attributes: 34 | label: Other Information 35 | description: Add any other information or screenshots about your feature request here. 36 | validations: 37 | required: false 38 | -------------------------------------------------------------------------------- /.github/workflows/Close stale issues and Prs.yml: -------------------------------------------------------------------------------- 1 | name: "Close stale issues and PRs" 2 | on: 3 | schedule: 4 | - cron: "30 1 * * *" 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-22.04 9 | steps: 10 | - uses: actions/stale@v7.0.0 11 | with: 12 | stale-issue-message: "Stale Issue" 13 | stale-pr-message: "Stale PR" 14 | stale-issue-label: "no-issue-activity" 15 | exempt-issue-labels: "awaiting-approval,awaiting,work-in-progress" 16 | stale-pr-label: "no-pr-activity" 17 | exempt-pr-labels: "awaiting-approval,awaiting,work-in-progress,automated-pr" 18 | # only-labels: 'bug,enhancement' 19 | days-before-issue-stale: 10 20 | days-before-pr-stale: 10 21 | days-before-issue-close: 5 22 | days-before-pr-close: -1 23 | operations-per-run: 500 24 | 25 | # - name: Delete workflow runs 26 | # uses: Mattraks/delete-workflow-runs@main 27 | # with: 28 | # token: ${{ github.token }} 29 | # repository: ${{ github.repository }} 30 | # retain_days: 1 31 | # keep_minimum_runs: 0 32 | -------------------------------------------------------------------------------- /luci-app-passwall2/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022-2025 xiaorouji 2 | # 3 | # This is free software, licensed under the GNU General Public License v3. 4 | 5 | include $(TOPDIR)/rules.mk 6 | 7 | PKG_NAME:=luci-app-passwall2 8 | PKG_VERSION:=25.5.15 9 | PKG_RELEASE:=1 10 | 11 | PKG_CONFIG_DEPENDS:= \ 12 | CONFIG_PACKAGE_$(PKG_NAME)_Iptables_Transparent_Proxy \ 13 | CONFIG_PACKAGE_$(PKG_NAME)_Nftables_Transparent_Proxy \ 14 | CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy \ 15 | CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria \ 16 | CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_IPv6_Nat \ 17 | CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy \ 18 | CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Client \ 19 | CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Server \ 20 | CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Client \ 21 | CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Server \ 22 | CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Client \ 23 | CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Server \ 24 | CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Simple_Obfs \ 25 | CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_SingBox \ 26 | CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_tuic_client \ 27 | CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Plugin 28 | 29 | LUCI_TITLE:=LuCI support for PassWall 2 30 | LUCI_PKGARCH:=all 31 | LUCI_DEPENDS:=+coreutils +coreutils-base64 +coreutils-nohup +curl \ 32 | +ip-full +libuci-lua +lua +luci-compat +luci-lib-jsonc +resolveip +tcping \ 33 | +xray-core +geoview +v2ray-geoip +v2ray-geosite \ 34 | +unzip \ 35 | +PACKAGE_$(PKG_NAME)_INCLUDE_IPv6_Nat:ip6tables-mod-nat 36 | 37 | define Package/$(PKG_NAME)/config 38 | menu "Configuration" 39 | 40 | config PACKAGE_$(PKG_NAME)_INCLUDE_IPv6_Nat 41 | depends on PACKAGE_ip6tables 42 | bool "Include IPv6 Nat" 43 | default n 44 | 45 | if PACKAGE_$(PKG_NAME) 46 | 47 | config PACKAGE_$(PKG_NAME)_Iptables_Transparent_Proxy 48 | bool "Iptables Transparent Proxy" 49 | select PACKAGE_chinadns-ng 50 | select PACKAGE_dnsmasq-full 51 | select PACKAGE_dnsmasq_full_ipset 52 | select PACKAGE_ipset 53 | select PACKAGE_iptables 54 | select PACKAGE_iptables-nft 55 | select PACKAGE_iptables-zz-legacy 56 | select PACKAGE_iptables-mod-conntrack-extra 57 | select PACKAGE_iptables-mod-iprange 58 | select PACKAGE_iptables-mod-socket 59 | select PACKAGE_iptables-mod-tproxy 60 | select PACKAGE_kmod-ipt-nat 61 | default y if ! PACKAGE_firewall4 62 | 63 | config PACKAGE_$(PKG_NAME)_Nftables_Transparent_Proxy 64 | bool "Nftables Transparent Proxy" 65 | select PACKAGE_chinadns-ng 66 | select PACKAGE_dnsmasq-full 67 | select PACKAGE_dnsmasq_full_nftset 68 | select PACKAGE_nftables 69 | select PACKAGE_kmod-nft-socket 70 | select PACKAGE_kmod-nft-tproxy 71 | select PACKAGE_kmod-nft-nat 72 | default y if PACKAGE_firewall4 73 | 74 | config PACKAGE_$(PKG_NAME)_INCLUDE_Haproxy 75 | bool "Include Haproxy" 76 | select PACKAGE_haproxy 77 | default y if aarch64||arm||i386||x86_64 78 | 79 | config PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria 80 | bool "Include Hysteria" 81 | select PACKAGE_hysteria 82 | default n 83 | 84 | config PACKAGE_$(PKG_NAME)_INCLUDE_NaiveProxy 85 | bool "Include NaiveProxy" 86 | depends on !(arc||(arm&&TARGET_gemini)||armeb||mips||mips64||powerpc) 87 | select PACKAGE_naiveproxy 88 | default n 89 | 90 | config PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Client 91 | bool "Include Shadowsocks Libev Client" 92 | select PACKAGE_shadowsocks-libev-ss-local 93 | select PACKAGE_shadowsocks-libev-ss-redir 94 | default y 95 | 96 | config PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Libev_Server 97 | bool "Include Shadowsocks Libev Server" 98 | select PACKAGE_shadowsocks-libev-ss-server 99 | default n 100 | 101 | config PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Client 102 | bool "Include Shadowsocks Rust Client" 103 | depends on aarch64||arm||i386||mips||mipsel||x86_64 104 | select PACKAGE_shadowsocks-rust-sslocal 105 | default y if aarch64 106 | 107 | config PACKAGE_$(PKG_NAME)_INCLUDE_Shadowsocks_Rust_Server 108 | bool "Include Shadowsocks Rust Server" 109 | depends on aarch64||arm||i386||mips||mipsel||x86_64 110 | select PACKAGE_shadowsocks-rust-ssserver 111 | default n 112 | 113 | config PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Client 114 | bool "Include ShadowsocksR Libev Client" 115 | select PACKAGE_shadowsocksr-libev-ssr-local 116 | select PACKAGE_shadowsocksr-libev-ssr-redir 117 | default y 118 | 119 | config PACKAGE_$(PKG_NAME)_INCLUDE_ShadowsocksR_Libev_Server 120 | bool "Include ShadowsocksR Libev Server" 121 | select PACKAGE_shadowsocksr-libev-ssr-server 122 | default n 123 | 124 | config PACKAGE_$(PKG_NAME)_INCLUDE_Simple_Obfs 125 | bool "Include Simple-Obfs (Shadowsocks Plugin)" 126 | select PACKAGE_simple-obfs 127 | default y 128 | 129 | config PACKAGE_$(PKG_NAME)_INCLUDE_SingBox 130 | bool "Include Sing-Box" 131 | select PACKAGE_sing-box 132 | default y if aarch64||arm||i386||x86_64 133 | 134 | config PACKAGE_$(PKG_NAME)_INCLUDE_tuic_client 135 | bool "Include tuic-client" 136 | depends on aarch64||arm||i386||x86_64 137 | select PACKAGE_tuic-client 138 | default n 139 | 140 | config PACKAGE_$(PKG_NAME)_INCLUDE_V2ray_Plugin 141 | bool "Include V2ray-Plugin (Shadowsocks Plugin)" 142 | select PACKAGE_v2ray-plugin 143 | default y if aarch64||arm||i386||x86_64 144 | 145 | endif 146 | endmenu 147 | endef 148 | 149 | define Package/$(PKG_NAME)/conffiles 150 | /etc/config/passwall2 151 | /etc/config/passwall2_server 152 | /usr/share/passwall2/domains_excluded 153 | /www/luci-static/resources/qrcode.min.js 154 | endef 155 | 156 | include $(TOPDIR)/feeds/luci/luci.mk 157 | 158 | # call BuildPackage - OpenWrt buildroot signature 159 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/client/acl.lua: -------------------------------------------------------------------------------- 1 | local api = require "luci.passwall2.api" 2 | local appname = api.appname 3 | local sys = api.sys 4 | 5 | m = Map(appname) 6 | api.set_apply_on_parse(m) 7 | 8 | s = m:section(TypedSection, "global", translate("ACLs"), "" .. translate("ACLs is a tools which used to designate specific IP proxy mode.") .. "") 9 | s.anonymous = true 10 | 11 | o = s:option(Flag, "acl_enable", translate("Main switch")) 12 | o.rmempty = false 13 | o.default = false 14 | 15 | -- [[ ACLs Settings ]]-- 16 | s = m:section(TypedSection, "acl_rule") 17 | s.template = "cbi/tblsection" 18 | s.sortable = true 19 | s.anonymous = true 20 | s.addremove = true 21 | s.extedit = api.url("acl_config", "%s") 22 | function s.create(e, t) 23 | t = TypedSection.create(e, t) 24 | luci.http.redirect(e.extedit:format(t)) 25 | end 26 | 27 | ---- Enable 28 | o = s:option(Flag, "enabled", translate("Enable")) 29 | o.default = 1 30 | o.rmempty = false 31 | 32 | ---- Remarks 33 | o = s:option(Value, "remarks", translate("Remarks")) 34 | o.rmempty = true 35 | 36 | local mac_t = {} 37 | sys.net.mac_hints(function(e, t) 38 | mac_t[e] = { 39 | ip = t, 40 | mac = e 41 | } 42 | end) 43 | 44 | o = s:option(DummyValue, "sources", translate("Source")) 45 | o.rawhtml = true 46 | o.cfgvalue = function(t, n) 47 | local e = '' 48 | local v = Value.cfgvalue(t, n) or '-' 49 | string.gsub(v, '[^' .. " " .. ']+', function(w) 50 | local a = w 51 | if mac_t[w] then 52 | a = a .. ' (' .. mac_t[w].ip .. ')' 53 | end 54 | if #e > 0 then 55 | e = e .. "
" 56 | end 57 | e = e .. a 58 | end) 59 | return e 60 | end 61 | 62 | i = s:option(DummyValue, "interface", translate("Source Interface")) 63 | i.cfgvalue = function(t, n) 64 | local v = Value.cfgvalue(t, n) or '-' 65 | return v 66 | end 67 | 68 | return m 69 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/client/acl_config.lua: -------------------------------------------------------------------------------- 1 | local api = require "luci.passwall2.api" 2 | local appname = api.appname 3 | 4 | m = Map(appname) 5 | api.set_apply_on_parse(m) 6 | 7 | if not arg[1] or not m:get(arg[1]) then 8 | luci.http.redirect(api.url("acl")) 9 | end 10 | 11 | local sys = api.sys 12 | 13 | local port_validate = function(self, value, t) 14 | return value:gsub("-", ":") 15 | end 16 | 17 | local nodes_table = {} 18 | for k, e in ipairs(api.get_valid_nodes()) do 19 | nodes_table[#nodes_table + 1] = e 20 | end 21 | 22 | local dynamicList_write = function(self, section, value) 23 | local t = {} 24 | local t2 = {} 25 | if type(value) == "table" then 26 | local x 27 | for _, x in ipairs(value) do 28 | if x and #x > 0 then 29 | if not t2[x] then 30 | t2[x] = x 31 | t[#t+1] = x 32 | end 33 | end 34 | end 35 | else 36 | t = { value } 37 | end 38 | t = table.concat(t, " ") 39 | return DynamicList.write(self, section, t) 40 | end 41 | local doh_validate = function(self, value, t) 42 | if value ~= "" then 43 | local flag = 0 44 | local util = require "luci.util" 45 | local val = util.split(value, ",") 46 | local url = val[1] 47 | val[1] = nil 48 | for i = 1, #val do 49 | local v = val[i] 50 | if v then 51 | if not datatypes.ipmask4(v) then 52 | flag = 1 53 | end 54 | end 55 | end 56 | if flag == 0 then 57 | return value 58 | end 59 | end 60 | return nil, translate("DoH request address") .. " " .. translate("Format must be:") .. " URL,IP" 61 | end 62 | -- [[ ACLs Settings ]]-- 63 | s = m:section(NamedSection, arg[1], translate("ACLs"), translate("ACLs")) 64 | s.addremove = false 65 | s.dynamic = false 66 | 67 | ---- Enable 68 | o = s:option(Flag, "enabled", translate("Enable")) 69 | o.default = 1 70 | o.rmempty = false 71 | 72 | ---- Remarks 73 | o = s:option(Value, "remarks", translate("Remarks")) 74 | o.default = arg[1] 75 | o.rmempty = false 76 | 77 | o = s:option(ListValue, "interface", translate("Source Interface")) 78 | o:value("", translate("All")) 79 | local wa = require "luci.tools.webadmin" 80 | wa.cbi_add_networks(o) 81 | 82 | local mac_t = {} 83 | sys.net.mac_hints(function(e, t) 84 | mac_t[#mac_t + 1] = { 85 | ip = t, 86 | mac = e 87 | } 88 | end) 89 | table.sort(mac_t, function(a,b) 90 | if #a.ip < #b.ip then 91 | return true 92 | elseif #a.ip == #b.ip then 93 | if a.ip < b.ip then 94 | return true 95 | else 96 | return #a.ip < #b.ip 97 | end 98 | end 99 | return false 100 | end) 101 | 102 | ---- Source 103 | sources = s:option(DynamicList, "sources", translate("Source")) 104 | sources.description = "" 111 | sources.cast = "string" 112 | for _, key in pairs(mac_t) do 113 | sources:value(key.mac, "%s (%s)" % {key.mac, key.ip}) 114 | end 115 | 116 | sources.cfgvalue = function(self, section) 117 | local value 118 | if self.tag_error[section] then 119 | value = self:formvalue(section) 120 | else 121 | value = self.map:get(section, self.option) 122 | if type(value) == "string" then 123 | local value2 = {} 124 | string.gsub(value, '[^' .. " " .. ']+', function(w) table.insert(value2, w) end) 125 | value = value2 126 | end 127 | end 128 | return value 129 | end 130 | sources.validate = function(self, value, t) 131 | local err = {} 132 | for _, v in ipairs(value) do 133 | local flag = false 134 | if v:find("ipset:") and v:find("ipset:") == 1 then 135 | local ipset = v:gsub("ipset:", "") 136 | if ipset and ipset ~= "" then 137 | flag = true 138 | end 139 | end 140 | 141 | if flag == false and datatypes.macaddr(v) then 142 | flag = true 143 | end 144 | 145 | if flag == false and datatypes.ip4addr(v) then 146 | flag = true 147 | end 148 | 149 | if flag == false and api.iprange(v) then 150 | flag = true 151 | end 152 | 153 | if flag == false then 154 | err[#err + 1] = v 155 | end 156 | end 157 | 158 | if #err > 0 then 159 | self:add_error(t, "invalid", translate("Not true format, please re-enter!")) 160 | for _, v in ipairs(err) do 161 | self:add_error(t, "invalid", v) 162 | end 163 | end 164 | 165 | return value 166 | end 167 | sources.write = dynamicList_write 168 | 169 | ---- TCP No Redir Ports 170 | local TCP_NO_REDIR_PORTS = m:get("@global_forwarding[0]", "tcp_no_redir_ports") 171 | o = s:option(Value, "tcp_no_redir_ports", translate("TCP No Redir Ports")) 172 | o:value("", translate("Use global config") .. "(" .. TCP_NO_REDIR_PORTS .. ")") 173 | o:value("disable", translate("No patterns are used")) 174 | o:value("1:65535", translate("All")) 175 | o.validate = port_validate 176 | 177 | ---- UDP No Redir Ports 178 | local UDP_NO_REDIR_PORTS = m:get("@global_forwarding[0]", "udp_no_redir_ports") 179 | o = s:option(Value, "udp_no_redir_ports", translate("UDP No Redir Ports"), 180 | "" .. 181 | translate("If you don't want to let the device in the list to go proxy, please choose all.") .. 182 | "") 183 | o:value("", translate("Use global config") .. "(" .. UDP_NO_REDIR_PORTS .. ")") 184 | o:value("disable", translate("No patterns are used")) 185 | o:value("1:65535", translate("All")) 186 | o.validate = port_validate 187 | 188 | o = s:option(DummyValue, "_hide_node_option", "") 189 | o.template = "passwall2/cbi/hidevalue" 190 | o.value = "1" 191 | o:depends({ tcp_no_redir_ports = "1:65535", udp_no_redir_ports = "1:65535" }) 192 | if TCP_NO_REDIR_PORTS == "1:65535" and UDP_NO_REDIR_PORTS == "1:65535" then 193 | o:depends({ tcp_no_redir_ports = "", udp_no_redir_ports = "" }) 194 | end 195 | 196 | local GLOBAL_ENABLED = m:get("@global[0]", "enabled") 197 | local NODE = m:get("@global[0]", "node") 198 | o = s:option(ListValue, "node", "" .. translate("Node") .. "") 199 | if GLOBAL_ENABLED == "1" and NODE then 200 | o:value("", translate("Use global config") .. "(" .. api.get_node_name(NODE) .. ")") 201 | end 202 | o:depends({ _hide_node_option = "1", ['!reverse'] = true }) 203 | 204 | o = s:option(DummyValue, "_hide_dns_option", "") 205 | o.template = "passwall2/cbi/hidevalue" 206 | o.value = "1" 207 | o:depends({ node = "" }) 208 | if GLOBAL_ENABLED == "1" and NODE then 209 | o:depends({ node = NODE }) 210 | end 211 | 212 | o = s:option(DummyValue, "_xray_node", "") 213 | o.template = "passwall2/cbi/hidevalue" 214 | o.value = "1" 215 | o:depends({ __hide = true }) 216 | 217 | ---- TCP Redir Ports 218 | local TCP_REDIR_PORTS = m:get("@global_forwarding[0]", "tcp_redir_ports") 219 | o = s:option(Value, "tcp_redir_ports", translate("TCP Redir Ports")) 220 | o:value("", translate("Use global config") .. "(" .. TCP_REDIR_PORTS .. ")") 221 | o:value("1:65535", translate("All")) 222 | o:value("22,25,53,143,465,587,853,993,995,80,443", translate("Common Use")) 223 | o:value("80,443", "80,443") 224 | o.validate = port_validate 225 | o:depends({ _hide_node_option = "1", ['!reverse'] = true }) 226 | 227 | ---- UDP Redir Ports 228 | local UDP_REDIR_PORTS = m:get("@global_forwarding[0]", "udp_redir_ports") 229 | o = s:option(Value, "udp_redir_ports", translate("UDP Redir Ports")) 230 | o:value("", translate("Use global config") .. "(" .. UDP_REDIR_PORTS .. ")") 231 | o:value("1:65535", translate("All")) 232 | o.validate = port_validate 233 | o:depends({ _hide_node_option = "1", ['!reverse'] = true }) 234 | 235 | o = s:option(DummyValue, "tips", " ") 236 | o.rawhtml = true 237 | o.cfgvalue = function(t, n) 238 | return string.format('%s', 239 | translate("The port settings support single ports and ranges.
Separate multiple ports with commas (,).
Example: 21,80,443,1000:2000.")) 240 | end 241 | 242 | o = s:option(ListValue, "direct_dns_query_strategy", translate("Direct Query Strategy")) 243 | o.default = "UseIP" 244 | o:value("UseIP") 245 | o:value("UseIPv4") 246 | o:value("UseIPv6") 247 | o:depends({ _hide_dns_option = "1", ['!reverse'] = true }) 248 | 249 | o = s:option(Flag, "write_ipset_direct", translate("Direct DNS result write to IPSet"), translate("Perform the matching direct domain name rules into IP to IPSet/NFTSet, and then connect directly (not entering the core). Maybe conflict with some special circumstances.")) 250 | o.default = "1" 251 | o:depends({ direct_dns_query_strategy = "", ['!reverse'] = true }) 252 | 253 | o = s:option(ListValue, "remote_dns_protocol", translate("Remote DNS Protocol")) 254 | o:value("tcp", "TCP") 255 | o:value("doh", "DoH") 256 | o:value("udp", "UDP") 257 | o:depends({ _hide_dns_option = "1", ['!reverse'] = true }) 258 | 259 | ---- DNS Forward 260 | o = s:option(Value, "remote_dns", translate("Remote DNS")) 261 | o.datatype = "or(ipaddr,ipaddrport)" 262 | o.default = "1.1.1.1" 263 | o:value("1.1.1.1", "1.1.1.1 (CloudFlare)") 264 | o:value("1.1.1.2", "1.1.1.2 (CloudFlare-Security)") 265 | o:value("8.8.4.4", "8.8.4.4 (Google)") 266 | o:value("8.8.8.8", "8.8.8.8 (Google)") 267 | o:value("9.9.9.9", "9.9.9.9 (Quad9-Recommended)") 268 | o:value("149.112.112.112", "149.112.112.112 (Quad9-Recommended)") 269 | o:value("208.67.220.220", "208.67.220.220 (OpenDNS)") 270 | o:value("208.67.222.222", "208.67.222.222 (OpenDNS)") 271 | o:depends("remote_dns_protocol", "tcp") 272 | o:depends("remote_dns_protocol", "udp") 273 | 274 | ---- DoH 275 | o = s:option(Value, "remote_dns_doh", translate("Remote DNS DoH")) 276 | o:value("https://1.1.1.1/dns-query", "CloudFlare") 277 | o:value("https://1.1.1.2/dns-query", "CloudFlare-Security") 278 | o:value("https://8.8.4.4/dns-query", "Google 8844") 279 | o:value("https://8.8.8.8/dns-query", "Google 8888") 280 | o:value("https://9.9.9.9/dns-query", "Quad9-Recommended 9.9.9.9") 281 | o:value("https://149.112.112.112/dns-query", "Quad9-Recommended 149.112.112.112") 282 | o:value("https://208.67.222.222/dns-query", "OpenDNS") 283 | o:value("https://dns.adguard.com/dns-query,176.103.130.130", "AdGuard") 284 | o:value("https://doh.libredns.gr/dns-query,116.202.176.26", "LibreDNS") 285 | o:value("https://doh.libredns.gr/ads,116.202.176.26", "LibreDNS (No Ads)") 286 | o.default = "https://1.1.1.1/dns-query" 287 | o.validate = doh_validate 288 | o:depends("remote_dns_protocol", "doh") 289 | 290 | o = s:option(Value, "remote_dns_client_ip", translate("Remote DNS EDNS Client Subnet")) 291 | o.description = translate("Notify the DNS server when the DNS query is notified, the location of the client (cannot be a private IP address).") .. "
" .. 292 | translate("This feature requires the DNS server to support the Edns Client Subnet (RFC7871).") 293 | o.datatype = "ipaddr" 294 | o:depends("remote_dns_protocol", "tcp") 295 | o:depends("remote_dns_protocol", "doh") 296 | o:depends("remote_dns_protocol", "udp") 297 | 298 | o = s:option(ListValue, "remote_dns_detour", translate("Remote DNS Outbound")) 299 | o.default = "remote" 300 | o:value("remote", translate("Remote")) 301 | o:value("direct", translate("Direct")) 302 | o:depends("remote_dns_protocol", "tcp") 303 | o:depends("remote_dns_protocol", "doh") 304 | o:depends("remote_dns_protocol", "udp") 305 | 306 | o = s:option(Flag, "remote_fakedns", "FakeDNS", translate("Use FakeDNS work in the shunt domain that proxy.")) 307 | o.default = "0" 308 | o.rmempty = false 309 | o:depends("remote_dns_protocol", "tcp") 310 | o:depends("remote_dns_protocol", "doh") 311 | o:depends("remote_dns_protocol", "udp") 312 | 313 | o = s:option(ListValue, "remote_dns_query_strategy", translate("Remote Query Strategy")) 314 | o.default = "UseIPv4" 315 | o:value("UseIP") 316 | o:value("UseIPv4") 317 | o:value("UseIPv6") 318 | o:depends("remote_dns_protocol", "tcp") 319 | o:depends("remote_dns_protocol", "doh") 320 | o:depends("remote_dns_protocol", "udp") 321 | 322 | o = s:option(TextValue, "dns_hosts", translate("Domain Override")) 323 | o.rows = 5 324 | o.wrap = "off" 325 | o:depends({ __hide = true }) 326 | o.remove = function(self, section) 327 | local node_value = s.fields["node"]:formvalue(arg[1]) 328 | if node_value then 329 | local node_t = m:get(node_value) or {} 330 | if node_t.type == "Xray" then 331 | AbstractValue.remove(self, section) 332 | end 333 | end 334 | end 335 | 336 | for k, v in pairs(nodes_table) do 337 | s.fields["node"]:value(v.id, v["remark"]) 338 | if v.type == "Xray" then 339 | s.fields["_xray_node"]:depends({ node = v.id }) 340 | end 341 | end 342 | 343 | s.fields["dns_hosts"]:depends({ _xray_node = "1" }) 344 | 345 | return m 346 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/client/app_update.lua: -------------------------------------------------------------------------------- 1 | local api = require "luci.passwall2.api" 2 | local appname = api.appname 3 | 4 | m = Map(appname) 5 | api.set_apply_on_parse(m) 6 | 7 | -- [[ App Settings ]]-- 8 | s = m:section(TypedSection, "global_app", translate("App Update"), 9 | "" .. 10 | translate("Please confirm that your firmware supports FPU.") .. 11 | "") 12 | s.anonymous = true 13 | s:append(Template(appname .. "/app_update/app_version")) 14 | 15 | local k, v 16 | local com = require "luci.passwall2.com" 17 | for k, v in pairs(com) do 18 | o = s:option(Value, k:gsub("%-","_") .. "_file", translatef("%s App Path", v.name)) 19 | o.default = v.default_path or ("/usr/bin/" .. k) 20 | o.rmempty = false 21 | end 22 | 23 | o = s:option(DummyValue, "tips", " ") 24 | o.rawhtml = true 25 | o.cfgvalue = function(t, n) 26 | 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.")) 27 | end 28 | 29 | return m 30 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/client/haproxy.lua: -------------------------------------------------------------------------------- 1 | local api = require "luci.passwall2.api" 2 | local appname = api.appname 3 | local datatypes = api.datatypes 4 | local net = require "luci.model.network".init() 5 | 6 | local nodes_table = {} 7 | for k, e in ipairs(api.get_valid_nodes()) do 8 | if e.node_type == "normal" then 9 | nodes_table[#nodes_table + 1] = { 10 | id = e[".name"], 11 | obj = e, 12 | remarks = e["remark"] 13 | } 14 | end 15 | end 16 | 17 | m = Map(appname) 18 | api.set_apply_on_parse(m) 19 | 20 | -- [[ Haproxy Settings ]]-- 21 | s = m:section(TypedSection, "global_haproxy", translate("Basic Settings")) 22 | s.anonymous = true 23 | 24 | s:append(Template(appname .. "/haproxy/status")) 25 | 26 | ---- Balancing Enable 27 | o = s:option(Flag, "balancing_enable", translate("Enable Load Balancing")) 28 | o.rmempty = false 29 | o.default = false 30 | 31 | ---- Console Login Auth 32 | o = s:option(Flag, "console_auth", translate("Console Login Auth")) 33 | o.default = false 34 | o:depends("balancing_enable", true) 35 | 36 | ---- Console Username 37 | o = s:option(Value, "console_user", translate("Console Username")) 38 | o.default = "" 39 | o:depends("console_auth", true) 40 | 41 | ---- Console Password 42 | o = s:option(Value, "console_password", translate("Console Password")) 43 | o.password = true 44 | o.default = "" 45 | o:depends("console_auth", true) 46 | 47 | ---- Console Port 48 | o = s:option(Value, "console_port", translate("Console Port"), translate( 49 | "In the browser input routing IP plus port access, such as:192.168.1.1:1188")) 50 | o.default = "1188" 51 | o:depends("balancing_enable", true) 52 | 53 | o = s:option(Flag, "bind_local", translate("Haproxy Port") .. " " .. translate("Bind Local"), translate("When selected, it can only be accessed localhost.")) 54 | o.default = "0" 55 | o:depends("balancing_enable", true) 56 | 57 | ---- Health Check Type 58 | o = s:option(ListValue, "health_check_type", translate("Health Check Type")) 59 | o.default = "passwall_logic" 60 | o:value("tcp", "TCP") 61 | o:value("passwall_logic", translate("URL Test") .. string.format("(passwall %s)", translate("Inner implement"))) 62 | o:depends("balancing_enable", true) 63 | 64 | ---- Passwall Inner implement Probe URL 65 | o = s:option(Value, "health_probe_url", translate("Probe URL")) 66 | o.default = "https://www.google.com/generate_204" 67 | o:value("https://cp.cloudflare.com/", "Cloudflare") 68 | o:value("https://www.gstatic.com/generate_204", "Gstatic") 69 | o:value("https://www.google.com/generate_204", "Google") 70 | o:value("https://www.youtube.com/generate_204", "YouTube") 71 | o:value("https://connect.rom.miui.com/generate_204", "MIUI (CN)") 72 | o:value("https://connectivitycheck.platform.hicloud.com/generate_204", "HiCloud (CN)") 73 | o.description = translate("The URL used to detect the connection status.") 74 | o:depends("health_check_type", "passwall_logic") 75 | 76 | ---- Health Check Inter 77 | o = s:option(Value, "health_check_inter", translate("Health Check Inter"), translate("Units:seconds")) 78 | o.default = "60" 79 | o:depends("balancing_enable", true) 80 | 81 | o = s:option(DummyValue, "health_check_tips", " ") 82 | o.rawhtml = true 83 | o.cfgvalue = function(t, n) 84 | return string.format('%s', translate("When the URL test is used, the load balancing node will be converted into a Socks node. when node list set customizing, must be a Socks node, otherwise the health check will be invalid.")) 85 | end 86 | o:depends("health_check_type", "passwall_logic") 87 | 88 | -- [[ Balancing Settings ]]-- 89 | s = m:section(TypedSection, "haproxy_config", translate("Node List"), 90 | "" .. 91 | 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.") .. 92 | "\n" .. translate("Note that the node configuration parameters for load balancing must be consistent when use TCP health check type, otherwise it cannot be used normally!") .. 93 | "") 94 | s.template = "cbi/tblsection" 95 | s.sortable = true 96 | s.anonymous = true 97 | s.addremove = true 98 | 99 | s.create = function(e, t) 100 | TypedSection.create(e, api.gen_short_uuid()) 101 | end 102 | 103 | s.remove = function(self, section) 104 | for k, v in pairs(self.children) do 105 | v.rmempty = true 106 | v.validate = nil 107 | end 108 | TypedSection.remove(self, section) 109 | end 110 | 111 | ---- Enable 112 | o = s:option(Flag, "enabled", translate("Enable")) 113 | o.default = 1 114 | o.rmempty = false 115 | 116 | ---- Node Address 117 | o = s:option(Value, "lbss", translate("Node Address")) 118 | for k, v in pairs(nodes_table) do o:value(v.id, v.remarks) end 119 | o.rmempty = false 120 | o.validate = function(self, value) 121 | if not value then return nil end 122 | local t = m:get(value) or nil 123 | if t and t[".type"] == "nodes" then 124 | return value 125 | end 126 | if datatypes.hostport(value) or datatypes.ip4addrport(value) then 127 | return value 128 | end 129 | if api.is_ipv6addrport(value) then 130 | return value 131 | end 132 | return nil, value 133 | end 134 | 135 | ---- Haproxy Port 136 | o = s:option(Value, "haproxy_port", translate("Haproxy Port")) 137 | o.datatype = "port" 138 | o.default = 1181 139 | o.rmempty = false 140 | 141 | ---- Node Weight 142 | o = s:option(Value, "lbweight", translate("Node Weight")) 143 | o.datatype = "uinteger" 144 | o.default = 5 145 | o.rmempty = false 146 | 147 | ---- Export 148 | o = s:option(ListValue, "export", translate("Export Of Multi WAN")) 149 | o:value(0, translate("Auto")) 150 | local wa = require "luci.tools.webadmin" 151 | wa.cbi_add_networks(o) 152 | o.default = 0 153 | o.rmempty = false 154 | 155 | ---- Mode 156 | o = s:option(ListValue, "backup", translate("Mode")) 157 | o:value(0, translate("Primary")) 158 | o:value(1, translate("Standby")) 159 | o.rmempty = false 160 | 161 | return m 162 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/client/log.lua: -------------------------------------------------------------------------------- 1 | local api = require "luci.passwall2.api" 2 | local appname = "passwall2" 3 | local http = require "luci.http" 4 | local fs = api.fs 5 | local sys = api.sys 6 | 7 | f = SimpleForm(appname) 8 | f.reset = false 9 | f.submit = false 10 | f:append(Template(appname .. "/log/log")) 11 | 12 | fb = SimpleForm('backup-restore') 13 | fb.reset = false 14 | fb.submit = false 15 | s = fb:section(SimpleSection, translate("Backup and Restore"), translate("Backup or Restore Client and Server Configurations.") .. 16 | "
" .. 17 | translate("Note: Restoring configurations across different versions may cause compatibility issues.") .. 18 | "") 19 | 20 | s.anonymous = true 21 | s:append(Template(appname .. "/log/backup_restore")) 22 | 23 | local backup_files = { 24 | "/etc/config/passwall2", 25 | "/etc/config/passwall2_server", 26 | "/usr/share/passwall2/domains_excluded" 27 | } 28 | 29 | local file_path = '/tmp/passwall2_upload.tar.gz' 30 | local temp_dir = '/tmp/passwall2_bak' 31 | local fd 32 | http.setfilehandler(function(meta, chunk, eof) 33 | if not fd and meta and meta.name == "ulfile" and chunk then 34 | sys.call("rm -rf " .. temp_dir) 35 | fs.remove(file_path) 36 | fd = nixio.open(file_path, "w") 37 | sys.call("echo '' > /tmp/log/passwall2.log") 38 | end 39 | if fd and chunk then 40 | fd:write(chunk) 41 | end 42 | if eof and fd then 43 | fd:close() 44 | fd = nil 45 | if fs.access(file_path) then 46 | api.log(" * PassWall2 配置文件上传成功…") 47 | sys.call("mkdir -p " .. temp_dir) 48 | if sys.call("tar -xzf " .. file_path .. " -C " .. temp_dir) == 0 then 49 | for _, backup_file in ipairs(backup_files) do 50 | local temp_file = temp_dir .. backup_file 51 | if fs.access(temp_file) then 52 | sys.call("cp -f " .. temp_file .. " " .. backup_file) 53 | end 54 | end 55 | api.log(" * PassWall2 配置还原成功…") 56 | api.log(" * 重启 PassWall2 服务中…\n") 57 | sys.call('/etc/init.d/passwall2 restart > /dev/null 2>&1 &') 58 | sys.call('/etc/init.d/passwall2_server restart > /dev/null 2>&1 &') 59 | else 60 | api.log(" * PassWall2 配置文件解压失败,请重试!") 61 | end 62 | else 63 | api.log(" * PassWall2 配置文件上传失败,请重试!") 64 | end 65 | sys.call("rm -rf " .. temp_dir) 66 | fs.remove(file_path) 67 | end 68 | end) 69 | 70 | return f, fb 71 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_config.lua: -------------------------------------------------------------------------------- 1 | local api = require "luci.passwall2.api" 2 | local appname = api.appname 3 | 4 | m = Map(appname, translate("Node Config")) 5 | m.redirect = api.url() 6 | api.set_apply_on_parse(m) 7 | 8 | if not arg[1] or not m:get(arg[1]) then 9 | luci.http.redirect(api.url("node_list")) 10 | end 11 | 12 | s = m:section(NamedSection, arg[1], "nodes", "") 13 | s.addremove = false 14 | s.dynamic = false 15 | 16 | o = s:option(DummyValue, "passwall2", " ") 17 | o.rawhtml = true 18 | o.template = "passwall2/node_list/link_share_man" 19 | o.value = arg[1] 20 | 21 | o = s:option(Value, "remarks", translate("Node Remarks")) 22 | o.default = translate("Remarks") 23 | o.rmempty = false 24 | 25 | local fs = require "nixio.fs" 26 | local types_dir = "/usr/lib/lua/luci/model/cbi/passwall2/client/type/" 27 | 28 | o = s:option(ListValue, "type", translate("Type")) 29 | 30 | local type_table = {} 31 | for filename in fs.dir(types_dir) do 32 | table.insert(type_table, filename) 33 | end 34 | table.sort(type_table) 35 | 36 | for index, value in ipairs(type_table) do 37 | local p_func = loadfile(types_dir .. value) 38 | setfenv(p_func, getfenv(1))(m, s) 39 | end 40 | 41 | return m 42 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_list.lua: -------------------------------------------------------------------------------- 1 | local api = require "luci.passwall2.api" 2 | local appname = api.appname 3 | local datatypes = api.datatypes 4 | local sys = api.sys 5 | 6 | m = Map(appname) 7 | api.set_apply_on_parse(m) 8 | 9 | -- [[ Other Settings ]]-- 10 | s = m:section(TypedSection, "global_other") 11 | s.anonymous = true 12 | 13 | o = s:option(ListValue, "auto_detection_time", translate("Automatic detection delay")) 14 | o:value("0", translate("Close")) 15 | o:value("icmp", "Ping") 16 | o:value("tcping", "TCP Ping") 17 | 18 | o = s:option(Flag, "show_node_info", translate("Show server address and port")) 19 | o.default = "0" 20 | 21 | -- [[ Add the node via the link ]]-- 22 | s:append(Template(appname .. "/node_list/link_add_node")) 23 | 24 | local auto_detection_time = m:get("@global_other[0]", "auto_detection_time") or "0" 25 | local show_node_info = m:get("@global_other[0]", "show_node_info") or "0" 26 | 27 | -- [[ Node List ]]-- 28 | s = m:section(TypedSection, "nodes") 29 | s.anonymous = true 30 | s.addremove = true 31 | s.template = "cbi/tblsection" 32 | s.extedit = api.url("node_config", "%s") 33 | function s.create(e, t) 34 | local uuid = api.gen_short_uuid() 35 | t = uuid 36 | TypedSection.create(e, t) 37 | luci.http.redirect(e.extedit:format(t)) 38 | end 39 | 40 | function s.remove(e, t) 41 | m.uci:foreach(appname, "socks", function(s) 42 | if s["node"] == t then 43 | m:del(s[".name"]) 44 | end 45 | for k, v in ipairs(m:get(s[".name"], "autoswitch_backup_node") or {}) do 46 | if v and v == t then 47 | sys.call(string.format("uci -q del_list %s.%s.autoswitch_backup_node='%s'", appname, s[".name"], v)) 48 | end 49 | end 50 | end) 51 | m.uci:foreach(appname, "acl_rule", function(s) 52 | if s["node"] and s["node"] == t then 53 | m:set(s[".name"], "node", "default") 54 | end 55 | end) 56 | TypedSection.remove(e, t) 57 | local new_node 58 | local node0 = m:get("@nodes[0]") or nil 59 | if node0 then 60 | new_node = node0[".name"] 61 | end 62 | if (m:get("@global[0]", "node") or "") == t then 63 | if new_node then 64 | m:set('@global[0]', "node", new_node) 65 | else 66 | m:del('@global[0]', "node") 67 | end 68 | end 69 | end 70 | 71 | s.sortable = true 72 | -- 简洁模式 73 | o = s:option(DummyValue, "add_from", "") 74 | o.cfgvalue = function(t, n) 75 | local v = Value.cfgvalue(t, n) 76 | if v and v ~= '' then 77 | local group = m:get(n, "group") or "" 78 | if group ~= "" then 79 | v = v .. " " .. group 80 | end 81 | return v 82 | else 83 | return '' 84 | end 85 | end 86 | o = s:option(DummyValue, "remarks", translate("Remarks")) 87 | o.rawhtml = true 88 | o.cfgvalue = function(t, n) 89 | local str = "" 90 | local is_sub = m:get(n, "is_sub") or "" 91 | local group = m:get(n, "group") or "" 92 | local remarks = m:get(n, "remarks") or "" 93 | local type = m:get(n, "type") or "" 94 | str = str .. string.format("", appname, n, type) 95 | if type == "sing-box" or type == "Xray" then 96 | local protocol = m:get(n, "protocol") 97 | if protocol == "_balancing" then 98 | protocol = translate("Balancing") 99 | elseif protocol == "_urltest" then 100 | protocol = "URLTest" 101 | elseif protocol == "_shunt" then 102 | protocol = translate("Shunt") 103 | elseif protocol == "vmess" then 104 | protocol = "VMess" 105 | elseif protocol == "vless" then 106 | protocol = "VLESS" 107 | elseif protocol == "shadowsocks" then 108 | protocol = "SS" 109 | elseif protocol == "shadowsocksr" then 110 | protocol = "SSR" 111 | elseif protocol == "wireguard" then 112 | protocol = "WG" 113 | elseif protocol == "hysteria" then 114 | protocol = "HY" 115 | elseif protocol == "hysteria2" then 116 | protocol = "HY2" 117 | elseif protocol == "anytls" then 118 | protocol = "AnyTLS" 119 | else 120 | protocol = protocol:gsub("^%l",string.upper) 121 | end 122 | if type == "sing-box" then type = "Sing-Box" end 123 | type = type .. " " .. protocol 124 | end 125 | local address = m:get(n, "address") or "" 126 | local port = m:get(n, "port") or "" 127 | str = str .. translate(type) .. ":" .. remarks 128 | if address ~= "" and port ~= "" then 129 | if show_node_info == "1" then 130 | if datatypes.ip6addr(address) then 131 | str = str .. string.format("([%s]:%s)", address, port) 132 | else 133 | str = str .. string.format("(%s:%s)", address, port) 134 | end 135 | end 136 | str = str .. string.format("", appname, n, address) 137 | str = str .. string.format("", appname, n, port) 138 | end 139 | return str 140 | end 141 | 142 | ---- Ping 143 | o = s:option(DummyValue, "ping", "Ping") 144 | o.width = "8%" 145 | o.rawhtml = true 146 | o.cfgvalue = function(t, n) 147 | local result = "---" 148 | if auto_detection_time ~= "icmp" then 149 | result = string.format('%s', n, translate("Test")) 150 | else 151 | result = string.format('---', n) 152 | end 153 | return result 154 | end 155 | 156 | ---- TCP Ping 157 | o = s:option(DummyValue, "tcping", "TCPing") 158 | o.width = "8%" 159 | o.rawhtml = true 160 | o.cfgvalue = function(t, n) 161 | local result = "---" 162 | if auto_detection_time ~= "tcping" then 163 | result = string.format('%s', n, translate("Test")) 164 | else 165 | result = string.format('---', n) 166 | end 167 | return result 168 | end 169 | 170 | o = s:option(DummyValue, "_url_test", translate("URL Test")) 171 | o.width = "8%" 172 | o.rawhtml = true 173 | o.cfgvalue = function(t, n) 174 | return string.format('%s', n, translate("Test")) 175 | end 176 | 177 | m:append(Template(appname .. "/node_list/node_list")) 178 | 179 | return m 180 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua: -------------------------------------------------------------------------------- 1 | local api = require "luci.passwall2.api" 2 | local appname = api.appname 3 | local uci = api.uci 4 | local has_ss = api.is_finded("ss-redir") 5 | local has_ss_rust = api.is_finded("sslocal") 6 | local has_singbox = api.finded_com("sing-box") 7 | local has_xray = api.finded_com("xray") 8 | local has_hysteria2 = api.finded_com("hysteria") 9 | local ss_type = {} 10 | local trojan_type = {} 11 | local vmess_type = {} 12 | local vless_type = {} 13 | local hysteria2_type = {} 14 | if has_ss then 15 | local s = "shadowsocks-libev" 16 | table.insert(ss_type, s) 17 | end 18 | if has_ss_rust then 19 | local s = "shadowsocks-rust" 20 | table.insert(ss_type, s) 21 | end 22 | if has_singbox then 23 | local s = "sing-box" 24 | table.insert(trojan_type, s) 25 | table.insert(ss_type, s) 26 | table.insert(vmess_type, s) 27 | table.insert(vless_type, s) 28 | table.insert(hysteria2_type, s) 29 | end 30 | if has_xray then 31 | local s = "xray" 32 | table.insert(trojan_type, s) 33 | table.insert(ss_type, s) 34 | table.insert(vmess_type, s) 35 | table.insert(vless_type, s) 36 | end 37 | if has_hysteria2 then 38 | local s = "hysteria2" 39 | table.insert(hysteria2_type, s) 40 | end 41 | 42 | m = Map(appname) 43 | api.set_apply_on_parse(m) 44 | 45 | if api.is_js_luci() then 46 | m.on_after_apply = function(self) 47 | uci:foreach(appname, "subscribe_list", function(e) 48 | uci:delete(appname, e[".name"], "md5") 49 | end) 50 | uci:commit(appname) 51 | end 52 | end 53 | 54 | -- [[ Subscribe Settings ]]-- 55 | s = m:section(TypedSection, "global_subscribe", "") 56 | s.anonymous = true 57 | 58 | function m.commit_handler(self) 59 | if self.no_commit then 60 | return 61 | end 62 | self.uci:foreach(appname, "subscribe_list", function(e) 63 | self:del(e[".name"], "md5") 64 | end) 65 | end 66 | 67 | o = s:option(ListValue, "filter_keyword_mode", translate("Filter keyword Mode")) 68 | o:value("0", translate("Close")) 69 | o:value("1", translate("Discard List")) 70 | o:value("2", translate("Keep List")) 71 | o:value("3", translate("Discard List,But Keep List First")) 72 | o:value("4", translate("Keep List,But Discard List First")) 73 | 74 | o = s:option(DynamicList, "filter_discard_list", translate("Discard List")) 75 | 76 | o = s:option(DynamicList, "filter_keep_list", translate("Keep List")) 77 | 78 | if #ss_type > 0 then 79 | o = s:option(ListValue, "ss_type", translatef("%s Node Use Type", "Shadowsocks")) 80 | for key, value in pairs(ss_type) do 81 | o:value(value) 82 | end 83 | end 84 | 85 | if #trojan_type > 0 then 86 | o = s:option(ListValue, "trojan_type", translatef("%s Node Use Type", "Trojan")) 87 | for key, value in pairs(trojan_type) do 88 | o:value(value) 89 | end 90 | end 91 | 92 | if #vmess_type > 0 then 93 | o = s:option(ListValue, "vmess_type", translatef("%s Node Use Type", "VMess")) 94 | for key, value in pairs(vmess_type) do 95 | o:value(value) 96 | end 97 | if has_xray then 98 | o.default = "xray" 99 | end 100 | end 101 | 102 | if #vless_type > 0 then 103 | o = s:option(ListValue, "vless_type", translatef("%s Node Use Type", "VLESS")) 104 | for key, value in pairs(vless_type) do 105 | o:value(value) 106 | end 107 | if has_xray then 108 | o.default = "xray" 109 | end 110 | end 111 | 112 | if #hysteria2_type > 0 then 113 | o = s:option(ListValue, "hysteria2_type", translatef("%s Node Use Type", "Hysteria2")) 114 | for key, value in pairs(hysteria2_type) do 115 | o:value(value) 116 | end 117 | if has_hysteria2 then 118 | o.default = "hysteria2" 119 | end 120 | end 121 | 122 | o = s:option(ListValue, "domain_strategy", "Sing-box " .. translate("Domain Strategy"), translate("Set the default domain resolution strategy for the sing-box node.")) 123 | o.default = "" 124 | o:value("", translate("Auto")) 125 | o:value("prefer_ipv4", translate("Prefer IPv4")) 126 | o:value("prefer_ipv6", translate("Prefer IPv6")) 127 | o:value("ipv4_only", translate("IPv4 Only")) 128 | o:value("ipv6_only", translate("IPv6 Only")) 129 | 130 | ---- Subscribe Delete All 131 | o = s:option(Button, "_stop", translate("Delete All Subscribe Node")) 132 | o.inputstyle = "remove" 133 | function o.write(e, e) 134 | luci.sys.call("lua /usr/share/" .. appname .. "/subscribe.lua truncate > /dev/null 2>&1") 135 | m.no_commit = true 136 | end 137 | 138 | o = s:option(Button, "_update", translate("Manual subscription All")) 139 | o.inputstyle = "apply" 140 | function o.write(t, n) 141 | luci.sys.call("lua /usr/share/" .. appname .. "/subscribe.lua start > /dev/null 2>&1 &") 142 | m.no_commit = true 143 | luci.http.redirect(api.url("log")) 144 | end 145 | 146 | s = m:section(TypedSection, "subscribe_list", "", "" .. translate("Please input the subscription url first, save and submit before manual subscription.") .. "") 147 | s.addremove = true 148 | s.anonymous = true 149 | s.sortable = true 150 | s.template = "cbi/tblsection" 151 | s.extedit = api.url("node_subscribe_config", "%s") 152 | function s.create(e, t) 153 | local id = TypedSection.create(e, t) 154 | luci.http.redirect(e.extedit:format(id)) 155 | end 156 | 157 | o = s:option(Value, "remark", translate("Remarks")) 158 | o.width = "auto" 159 | o.rmempty = false 160 | o.validate = function(self, value, t) 161 | if value then 162 | local count = 0 163 | m.uci:foreach(appname, "subscribe_list", function(e) 164 | if e[".name"] ~= t and e["remark"] == value then 165 | count = count + 1 166 | end 167 | end) 168 | if count > 0 then 169 | return nil, translate("This remark already exists, please change a new remark.") 170 | end 171 | return value 172 | end 173 | end 174 | 175 | o = s:option(DummyValue, "_node_count", translate("Subscribe Info")) 176 | o.rawhtml = true 177 | o.cfgvalue = function(t, n) 178 | local remark = m:get(n, "remark") or "" 179 | local str = m:get(n, "rem_traffic") or "" 180 | local expired_date = m:get(n, "expired_date") or "" 181 | if expired_date ~= "" then 182 | str = str .. (str ~= "" and "/" or "") .. expired_date 183 | end 184 | str = str ~= "" and "
" .. str or "" 185 | local num = 0 186 | m.uci:foreach(appname, "nodes", function(s) 187 | if s["add_from"] ~= "" and s["add_from"] == remark then 188 | num = num + 1 189 | end 190 | end) 191 | return string.format("%s%s", translate("Node num") .. ": " .. num, str) 192 | end 193 | 194 | o = s:option(Value, "url", translate("Subscribe URL")) 195 | o.width = "auto" 196 | o.rmempty = false 197 | 198 | o = s:option(Button, "_remove", translate("Delete the subscribed node")) 199 | o.inputstyle = "remove" 200 | function o.write(t, n) 201 | local remark = m:get(n, "remark") or "" 202 | luci.sys.call("lua /usr/share/" .. appname .. "/subscribe.lua truncate " .. remark .. " > /dev/null 2>&1") 203 | m.no_commit = true 204 | end 205 | 206 | o = s:option(Button, "_update", translate("Manual subscription")) 207 | o.inputstyle = "apply" 208 | function o.write(t, n) 209 | luci.sys.call("lua /usr/share/" .. appname .. "/subscribe.lua start " .. n .. " > /dev/null 2>&1 &") 210 | m.no_commit = true 211 | luci.http.redirect(api.url("log")) 212 | end 213 | 214 | return m 215 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua: -------------------------------------------------------------------------------- 1 | local api = require "luci.passwall2.api" 2 | local appname = api.appname 3 | 4 | m = Map(appname) 5 | m.redirect = api.url("node_subscribe") 6 | api.set_apply_on_parse(m) 7 | 8 | if not arg[1] or not m:get(arg[1]) then 9 | luci.http.redirect(m.redirect) 10 | end 11 | 12 | local has_ss = api.is_finded("ss-redir") 13 | local has_ss_rust = api.is_finded("sslocal") 14 | local has_singbox = api.finded_com("sing-box") 15 | local has_xray = api.finded_com("xray") 16 | local has_hysteria2 = api.finded_com("hysteria") 17 | local ss_type = {} 18 | local trojan_type = {} 19 | local vmess_type = {} 20 | local vless_type = {} 21 | local hysteria2_type = {} 22 | if has_ss then 23 | local s = "shadowsocks-libev" 24 | table.insert(ss_type, s) 25 | end 26 | if has_ss_rust then 27 | local s = "shadowsocks-rust" 28 | table.insert(ss_type, s) 29 | end 30 | if has_singbox then 31 | local s = "sing-box" 32 | table.insert(trojan_type, s) 33 | table.insert(ss_type, s) 34 | table.insert(vmess_type, s) 35 | table.insert(vless_type, s) 36 | table.insert(hysteria2_type, s) 37 | end 38 | if has_xray then 39 | local s = "xray" 40 | table.insert(trojan_type, s) 41 | table.insert(ss_type, s) 42 | table.insert(vmess_type, s) 43 | table.insert(vless_type, s) 44 | end 45 | if has_hysteria2 then 46 | local s = "hysteria2" 47 | table.insert(hysteria2_type, s) 48 | end 49 | 50 | s = m:section(NamedSection, arg[1]) 51 | s.addremove = false 52 | s.dynamic = false 53 | 54 | function m.commit_handler(self) 55 | self:del(arg[1], "md5") 56 | end 57 | 58 | o = s:option(Value, "remark", translate("Subscribe Remark")) 59 | o.rmempty = false 60 | 61 | o = s:option(TextValue, "url", translate("Subscribe URL")) 62 | o.rows = 5 63 | o.rmempty = false 64 | 65 | o = s:option(Flag, "allowInsecure", translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped.")) 66 | o.default = "1" 67 | o.rmempty = false 68 | 69 | o = s:option(ListValue, "filter_keyword_mode", translate("Filter keyword Mode")) 70 | o.default = "5" 71 | o:value("0", translate("Close")) 72 | o:value("1", translate("Discard List")) 73 | o:value("2", translate("Keep List")) 74 | o:value("3", translate("Discard List,But Keep List First")) 75 | o:value("4", translate("Keep List,But Discard List First")) 76 | o:value("5", translate("Use global config")) 77 | 78 | o = s:option(DynamicList, "filter_discard_list", translate("Discard List")) 79 | o:depends("filter_keyword_mode", "1") 80 | o:depends("filter_keyword_mode", "3") 81 | o:depends("filter_keyword_mode", "4") 82 | 83 | o = s:option(DynamicList, "filter_keep_list", translate("Keep List")) 84 | o:depends("filter_keyword_mode", "2") 85 | o:depends("filter_keyword_mode", "3") 86 | o:depends("filter_keyword_mode", "4") 87 | 88 | if #ss_type > 0 then 89 | o = s:option(ListValue, "ss_type", translatef("%s Node Use Type", "Shadowsocks")) 90 | o.default = "global" 91 | o:value("global", translate("Use global config")) 92 | for key, value in pairs(ss_type) do 93 | o:value(value) 94 | end 95 | end 96 | 97 | if #trojan_type > 0 then 98 | o = s:option(ListValue, "trojan_type", translatef("%s Node Use Type", "Trojan")) 99 | o.default = "global" 100 | o:value("global", translate("Use global config")) 101 | for key, value in pairs(trojan_type) do 102 | o:value(value) 103 | end 104 | end 105 | 106 | if #vmess_type > 0 then 107 | o = s:option(ListValue, "vmess_type", translatef("%s Node Use Type", "VMess")) 108 | o.default = "global" 109 | o:value("global", translate("Use global config")) 110 | for key, value in pairs(vmess_type) do 111 | o:value(value) 112 | end 113 | end 114 | 115 | if #vless_type > 0 then 116 | o = s:option(ListValue, "vless_type", translatef("%s Node Use Type", "VLESS")) 117 | o.default = "global" 118 | o:value("global", translate("Use global config")) 119 | for key, value in pairs(vless_type) do 120 | o:value(value) 121 | end 122 | end 123 | 124 | if #hysteria2_type > 0 then 125 | o = s:option(ListValue, "hysteria2_type", translatef("%s Node Use Type", "Hysteria2")) 126 | o.default = "global" 127 | o:value("global", translate("Use global config")) 128 | for key, value in pairs(hysteria2_type) do 129 | o:value(value) 130 | end 131 | end 132 | 133 | o = s:option(ListValue, "domain_strategy", "Sing-box " .. translate("Domain Strategy"), translate("Set the default domain resolution strategy for the sing-box node.")) 134 | o.default = "global" 135 | o:value("global", translate("Use global config")) 136 | o:value("", translate("Auto")) 137 | o:value("prefer_ipv4", translate("Prefer IPv4")) 138 | o:value("prefer_ipv6", translate("Prefer IPv6")) 139 | o:value("ipv4_only", translate("IPv4 Only")) 140 | o:value("ipv6_only", translate("IPv6 Only")) 141 | 142 | ---- Enable auto update subscribe 143 | o = s:option(Flag, "auto_update", translate("Enable auto update subscribe")) 144 | o.default = 0 145 | o.rmempty = false 146 | 147 | ---- Week Update 148 | o = s:option(ListValue, "week_update", translate("Update Mode")) 149 | o:value(8, translate("Loop Mode")) 150 | o:value(7, translate("Every day")) 151 | o:value(1, translate("Every Monday")) 152 | o:value(2, translate("Every Tuesday")) 153 | o:value(3, translate("Every Wednesday")) 154 | o:value(4, translate("Every Thursday")) 155 | o:value(5, translate("Every Friday")) 156 | o:value(6, translate("Every Saturday")) 157 | o:value(0, translate("Every Sunday")) 158 | o.default = 7 159 | o:depends("auto_update", true) 160 | o.rmempty = true 161 | 162 | ---- Time Update 163 | o = s:option(ListValue, "time_update", translate("Update Time(every day)")) 164 | for t = 0, 23 do o:value(t, t .. ":00") end 165 | o.default = 0 166 | o:depends("week_update", "0") 167 | o:depends("week_update", "1") 168 | o:depends("week_update", "2") 169 | o:depends("week_update", "3") 170 | o:depends("week_update", "4") 171 | o:depends("week_update", "5") 172 | o:depends("week_update", "6") 173 | o:depends("week_update", "7") 174 | o.rmempty = true 175 | 176 | ---- Interval Update 177 | o = s:option(ListValue, "interval_update", translate("Update Interval(hour)")) 178 | for t = 1, 24 do o:value(t, t .. " " .. translate("hour")) end 179 | o.default = 2 180 | o:depends("week_update", "8") 181 | o.rmempty = true 182 | 183 | o = s:option(ListValue, "access_mode", translate("Subscribe URL Access Method")) 184 | o.default = "" 185 | o:value("", translate("Auto")) 186 | o:value("direct", translate("Direct Connection")) 187 | o:value("proxy", translate("Proxy")) 188 | 189 | o = s:option(Value, "user_agent", translate("User-Agent")) 190 | o.default = "v2rayN/9.99" 191 | o:value("curl", "Curl") 192 | o:value("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0", "Edge for Linux") 193 | o:value("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0", "Edge for Windows") 194 | o:value("Passwall2/OpenWrt", "PassWall2") 195 | o:value("v2rayN/9.99", "v2rayN") 196 | 197 | return m 198 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua: -------------------------------------------------------------------------------- 1 | local api = require "luci.passwall2.api" 2 | local appname = api.appname 3 | local fs = api.fs 4 | local has_singbox = api.finded_com("sing-box") 5 | local has_xray = api.finded_com("xray") 6 | local has_fw3 = api.is_finded("fw3") 7 | local has_fw4 = api.is_finded("fw4") 8 | 9 | local port_validate = function(self, value, t) 10 | return value:gsub("-", ":") 11 | end 12 | 13 | m = Map(appname) 14 | api.set_apply_on_parse(m) 15 | 16 | -- [[ Delay Settings ]]-- 17 | s = m:section(TypedSection, "global_delay", translate("Delay Settings")) 18 | s.anonymous = true 19 | s.addremove = false 20 | 21 | ---- Open and close Daemon 22 | o = s:option(Flag, "start_daemon", translate("Open and close Daemon")) 23 | o.default = 1 24 | o.rmempty = false 25 | 26 | ---- Delay Start 27 | o = s:option(Value, "start_delay", translate("Delay Start"), translate("Units:seconds")) 28 | o.default = "1" 29 | o.rmempty = true 30 | 31 | for index, value in ipairs({"stop", "start", "restart"}) do 32 | o = s:option(ListValue, value .. "_week_mode", translate(value .. " automatically mode")) 33 | o:value("", translate("Disable")) 34 | o:value(8, translate("Loop Mode")) 35 | o:value(7, translate("Every day")) 36 | o:value(1, translate("Every Monday")) 37 | o:value(2, translate("Every Tuesday")) 38 | o:value(3, translate("Every Wednesday")) 39 | o:value(4, translate("Every Thursday")) 40 | o:value(5, translate("Every Friday")) 41 | o:value(6, translate("Every Saturday")) 42 | o:value(0, translate("Every Sunday")) 43 | 44 | o = s:option(ListValue, value .. "_time_mode", translate(value .. " Time(Every day)")) 45 | for t = 0, 23 do o:value(t, t .. ":00") end 46 | o.default = 0 47 | o:depends(value .. "_week_mode", "0") 48 | o:depends(value .. "_week_mode", "1") 49 | o:depends(value .. "_week_mode", "2") 50 | o:depends(value .. "_week_mode", "3") 51 | o:depends(value .. "_week_mode", "4") 52 | o:depends(value .. "_week_mode", "5") 53 | o:depends(value .. "_week_mode", "6") 54 | o:depends(value .. "_week_mode", "7") 55 | 56 | o = s:option(ListValue, value .. "_interval_mode", translate(value .. " Interval(Hour)")) 57 | for t = 1, 24 do o:value(t, t .. " " .. translate("Hour")) end 58 | o.default = 2 59 | o:depends(value .. "_week_mode", "8") 60 | end 61 | 62 | -- [[ Forwarding Settings ]]-- 63 | s = m:section(TypedSection, "global_forwarding", translate("Forwarding Settings")) 64 | s.anonymous = true 65 | s.addremove = false 66 | 67 | ---- TCP No Redir Ports 68 | o = s:option(Value, "tcp_no_redir_ports", translate("TCP No Redir Ports")) 69 | o.default = "disable" 70 | o:value("disable", translate("No patterns are used")) 71 | o:value("1:65535", translate("All")) 72 | o.validate = port_validate 73 | 74 | ---- UDP No Redir Ports 75 | o = s:option(Value, "udp_no_redir_ports", translate("UDP No Redir Ports"), 76 | "" .. 77 | translate("Fill in the ports you don't want to be forwarded by the agent, with the highest priority.") .. 78 | "") 79 | o.default = "disable" 80 | o:value("disable", translate("No patterns are used")) 81 | o:value("1:65535", translate("All")) 82 | o.validate = port_validate 83 | 84 | ---- TCP Redir Ports 85 | o = s:option(Value, "tcp_redir_ports", translate("TCP Redir Ports")) 86 | o.default = "22,25,53,143,465,587,853,993,995,80,443" 87 | o:value("1:65535", translate("All")) 88 | o:value("22,25,53,143,465,587,853,993,995,80,443", translate("Common Use")) 89 | o:value("80,443", translate("Only Web")) 90 | o.validate = port_validate 91 | 92 | ---- UDP Redir Ports 93 | o = s:option(Value, "udp_redir_ports", translate("UDP Redir Ports")) 94 | o.default = "1:65535" 95 | o:value("1:65535", translate("All")) 96 | o.validate = port_validate 97 | 98 | o = s:option(DummyValue, "tips", " ") 99 | o.rawhtml = true 100 | o.cfgvalue = function(t, n) 101 | return string.format('%s', 102 | translate("The port settings support single ports and ranges.
Separate multiple ports with commas (,).
Example: 21,80,443,1000:2000.")) 103 | end 104 | 105 | ---- Use nftables 106 | o = s:option(ListValue, "use_nft", translate("Firewall tools")) 107 | o.default = "0" 108 | if has_fw3 then 109 | o:value("0", "IPtables") 110 | end 111 | if has_fw4 then 112 | o:value("1", "NFtables") 113 | end 114 | 115 | if (os.execute("lsmod | grep -i REDIRECT >/dev/null") == 0 and os.execute("lsmod | grep -i TPROXY >/dev/null") == 0) or (os.execute("lsmod | grep -i nft_redir >/dev/null") == 0 and os.execute("lsmod | grep -i nft_tproxy >/dev/null") == 0) then 116 | o = s:option(ListValue, "tcp_proxy_way", translate("TCP Proxy Way")) 117 | o.default = "redirect" 118 | o:value("redirect", "REDIRECT") 119 | o:value("tproxy", "TPROXY") 120 | o:depends("ipv6_tproxy", false) 121 | o.remove = function(self, section) 122 | -- 禁止在隐藏时删除 123 | end 124 | 125 | o = s:option(ListValue, "_tcp_proxy_way", translate("TCP Proxy Way")) 126 | o.default = "tproxy" 127 | o:value("tproxy", "TPROXY") 128 | o:depends("ipv6_tproxy", true) 129 | o.write = function(self, section, value) 130 | self.map:set(section, "tcp_proxy_way", value) 131 | end 132 | 133 | if os.execute("lsmod | grep -i ip6table_mangle >/dev/null") == 0 or os.execute("lsmod | grep -i nft_tproxy >/dev/null") == 0 then 134 | ---- IPv6 TProxy 135 | o = s:option(Flag, "ipv6_tproxy", translate("IPv6 TProxy"), 136 | "" .. 137 | translate("Experimental feature. Make sure that your node supports IPv6.") .. 138 | "") 139 | o.default = 0 140 | o.rmempty = false 141 | end 142 | end 143 | 144 | o = s:option(Flag, "accept_icmp", translate("Hijacking ICMP (PING)")) 145 | o.default = 0 146 | 147 | o = s:option(Flag, "accept_icmpv6", translate("Hijacking ICMPv6 (IPv6 PING)")) 148 | o:depends("ipv6_tproxy", true) 149 | o.default = 0 150 | 151 | if has_xray then 152 | s_xray = m:section(TypedSection, "global_xray", "Xray " .. translate("Settings")) 153 | s_xray.anonymous = true 154 | s_xray.addremove = false 155 | 156 | o = s_xray:option(Flag, "fragment", translate("Fragment"), translate("TCP fragments, which can deceive the censorship system in some cases, such as bypassing SNI blacklists.")) 157 | o.default = 0 158 | 159 | o = s_xray:option(ListValue, "fragment_packets", translate("Fragment Packets"), translate(" \"1-3\" is for segmentation at TCP layer, applying to the beginning 1 to 3 data writes by the client. \"tlshello\" is for TLS client hello packet fragmentation.")) 160 | o.default = "tlshello" 161 | o:value("tlshello", "tlshello") 162 | o:value("1-1", "1-1") 163 | o:value("1-2", "1-2") 164 | o:value("1-3", "1-3") 165 | o:value("1-5", "1-5") 166 | o:depends("fragment", true) 167 | 168 | o = s_xray:option(Value, "fragment_length", translate("Fragment Length"), translate("Fragmented packet length (byte)")) 169 | o.default = "100-200" 170 | o:depends("fragment", true) 171 | 172 | o = s_xray:option(Value, "fragment_interval", translate("Fragment Interval"), translate("Fragmentation interval (ms)")) 173 | o.default = "10-20" 174 | o:depends("fragment", true) 175 | 176 | o = s_xray:option(Flag, "noise", translate("Noise"), translate("UDP noise, Under some circumstances it can bypass some UDP based protocol restrictions.")) 177 | o.default = 0 178 | 179 | o = s_xray:option(Flag, "sniffing_override_dest", translate("Override the connection destination address")) 180 | o.default = 0 181 | o.description = translate("Override the connection destination address with the sniffed domain.
Otherwise use sniffed domain for routing only.
If using shunt nodes, configure the domain shunt rules correctly.") 182 | 183 | o = s_xray:option(Flag, "route_only", translate("Sniffing Route Only")) 184 | o.default = 0 185 | o:depends("sniffing", true) 186 | 187 | local domains_excluded = string.format("/usr/share/%s/domains_excluded", appname) 188 | o = s_xray:option(TextValue, "excluded_domains", translate("Excluded Domains"), translate("If the traffic sniffing result is in this list, the destination address will not be overridden.")) 189 | o.rows = 15 190 | o.wrap = "off" 191 | o.cfgvalue = function(self, section) return fs.readfile(domains_excluded) or "" end 192 | o.write = function(self, section, value) fs.writefile(domains_excluded, value:gsub("\r\n", "\n")) end 193 | o:depends({sniffing_override_dest = true}) 194 | 195 | o = s_xray:option(Value, "buffer_size", translate("Buffer Size"), translate("Buffer size for every connection (kB)")) 196 | o.datatype = "uinteger" 197 | 198 | s_xray_noise = m:section(TypedSection, "xray_noise_packets", translate("Xray Noise Packets"),"" .. translate("To send noise packets, select \"Noise\" in Xray Settings.") .. "") 199 | s_xray_noise.template = "cbi/tblsection" 200 | s_xray_noise.sortable = true 201 | s_xray_noise.anonymous = true 202 | s_xray_noise.addremove = true 203 | 204 | s_xray_noise.create = function(e, t) 205 | TypedSection.create(e, api.gen_short_uuid()) 206 | end 207 | 208 | s_xray_noise.remove = function(self, section) 209 | for k, v in pairs(self.children) do 210 | v.rmempty = true 211 | v.validate = nil 212 | end 213 | TypedSection.remove(self, section) 214 | end 215 | 216 | o = s_xray_noise:option(Flag, "enabled", translate("Enable")) 217 | o.default = 1 218 | o.rmempty = false 219 | 220 | o = s_xray_noise:option(ListValue, "type", translate("Type")) 221 | o:value("rand", "rand") 222 | o:value("str", "str") 223 | o:value("hex", "hex") 224 | o:value("base64", "base64") 225 | 226 | o = s_xray_noise:option(Value, "packet", translate("Packet")) 227 | o.datatype = "minlength(1)" 228 | o.rmempty = false 229 | 230 | o = s_xray_noise:option(Value, "delay", translate("Delay (ms)")) 231 | o.datatype = "or(uinteger,portrange)" 232 | o.rmempty = false 233 | 234 | end 235 | 236 | if has_singbox then 237 | s = m:section(TypedSection, "global_singbox", "Sing-Box " .. translate("Settings")) 238 | s.anonymous = true 239 | s.addremove = false 240 | 241 | o = s:option(Flag, "sniff_override_destination", translate("Override the connection destination address")) 242 | o.default = 0 243 | o.rmempty = false 244 | o.description = translate("Override the connection destination address with the sniffed domain.
When enabled, traffic will match only by domain, ignoring IP rules.
If using shunt nodes, configure the domain shunt rules correctly.") 245 | 246 | o = s:option(Value, "geoip_path", translate("Custom geoip Path")) 247 | o.default = "/usr/share/singbox/geoip.db" 248 | o.rmempty = false 249 | 250 | o = s:option(Value, "geoip_url", translate("Custom geoip URL")) 251 | o.default = "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.db" 252 | o:value("https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.db") 253 | o:value("https://github.com/1715173329/sing-geoip/releases/latest/download/geoip.db") 254 | o:value("https://github.com/lyc8503/sing-box-rules/releases/latest/download/geoip.db") 255 | o.rmempty = false 256 | 257 | o = s:option(Value, "geosite_path", translate("Custom geosite Path")) 258 | o.default = "/usr/share/singbox/geosite.db" 259 | o.rmempty = false 260 | 261 | o = s:option(Value, "geosite_url", translate("Custom geosite URL")) 262 | o.default = "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.db" 263 | o:value("https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.db") 264 | o:value("https://github.com/1715173329/sing-geosite/releases/latest/download/geosite.db") 265 | o:value("https://github.com/lyc8503/sing-box-rules/releases/latest/download/geosite.db") 266 | o.rmempty = false 267 | 268 | o = s:option(Button, "_remove_resource", translate("Remove resource files")) 269 | o.description = translate("Sing-Box will automatically download resource files when starting, you can use this feature achieve upgrade resource files.") 270 | o.inputstyle = "remove" 271 | function o.write(self, section, value) 272 | local geoip_path = s.fields["geoip_path"] and s.fields["geoip_path"]:formvalue(section) or nil 273 | if geoip_path then 274 | os.remove(geoip_path) 275 | luci.sys.call("rm -f /tmp/etc/passwall2_tmp/geoip-*.json") 276 | end 277 | local geosite_path = s.fields["geosite_path"] and s.fields["geosite_path"]:formvalue(section) or nil 278 | if geosite_path then 279 | os.remove(geosite_path) 280 | end 281 | end 282 | end 283 | 284 | return m 285 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/client/rule.lua: -------------------------------------------------------------------------------- 1 | local api = require "luci.passwall2.api" 2 | local appname = api.appname 3 | 4 | m = Map(appname) 5 | api.set_apply_on_parse(m) 6 | 7 | -- [[ Rule Settings ]]-- 8 | s = m:section(TypedSection, "global_rules", translate("Rule status")) 9 | s.anonymous = true 10 | 11 | o = s:option(Value, "v2ray_location_asset", translate("Location of V2ray/Xray asset"), translate("This variable specifies a directory where geoip.dat and geosite.dat files are.")) 12 | o.default = "/usr/share/v2ray/" 13 | o.rmempty = false 14 | 15 | ---- Custom geo file url 16 | o = s:option(Value, "geoip_url", translate("Custom geoip URL")) 17 | o.default = "https://api.github.com/repos/Loyalsoldier/v2ray-rules-dat/releases/latest" 18 | o.rmempty = false 19 | 20 | o = s:option(Value, "geosite_url", translate("Custom geosite URL")) 21 | o.default = "https://api.github.com/repos/Loyalsoldier/v2ray-rules-dat/releases/latest" 22 | o.rmempty = false 23 | ---- 24 | 25 | if api.is_finded("geoview") then 26 | o = s:option(Flag, "enable_geoview", translate("Enable Geo Data Parsing")) 27 | o.default = 0 28 | o.rmempty = false 29 | o.description = "" 34 | end 35 | 36 | s:append(Template(appname .. "/rule/rule_version")) 37 | 38 | ---- Auto Update 39 | o = s:option(Flag, "auto_update", translate("Enable auto update rules")) 40 | o.default = 0 41 | o.rmempty = false 42 | 43 | ---- Week Update 44 | o = s:option(ListValue, "week_update", translate("Update Mode")) 45 | o:value(8, translate("Loop Mode")) 46 | o:value(7, translate("Every day")) 47 | o:value(1, translate("Every Monday")) 48 | o:value(2, translate("Every Tuesday")) 49 | o:value(3, translate("Every Wednesday")) 50 | o:value(4, translate("Every Thursday")) 51 | o:value(5, translate("Every Friday")) 52 | o:value(6, translate("Every Saturday")) 53 | o:value(0, translate("Every Sunday")) 54 | o.default = 7 55 | o:depends("auto_update", true) 56 | o.rmempty = true 57 | 58 | ---- Time Update 59 | o = s:option(ListValue, "time_update", translate("Update Time(every day)")) 60 | for t = 0, 23 do o:value(t, t .. ":00") end 61 | o.default = 0 62 | o:depends("week_update", "0") 63 | o:depends("week_update", "1") 64 | o:depends("week_update", "2") 65 | o:depends("week_update", "3") 66 | o:depends("week_update", "4") 67 | o:depends("week_update", "5") 68 | o:depends("week_update", "6") 69 | o:depends("week_update", "7") 70 | o.rmempty = true 71 | 72 | ---- Interval Update 73 | o = s:option(ListValue, "interval_update", translate("Update Interval(hour)")) 74 | for t = 1, 24 do o:value(t, t .. " " .. translate("hour")) end 75 | o.default = 2 76 | o:depends("week_update", "8") 77 | o.rmempty = true 78 | 79 | s = m:section(TypedSection, "shunt_rules", "Sing-Box/Xray " .. translate("Shunt Rule"), "" .. translate("Please note attention to the priority, the higher the order, the higher the priority.") .. "") 80 | s.template = "cbi/tblsection" 81 | s.anonymous = false 82 | s.addremove = true 83 | s.sortable = true 84 | s.extedit = api.url("shunt_rules", "%s") 85 | function s.create(e, t) 86 | TypedSection.create(e, t) 87 | luci.http.redirect(e.extedit:format(t)) 88 | end 89 | function s.remove(e, t) 90 | m.uci:foreach(appname, "nodes", function(s) 91 | if s["protocol"] and s["protocol"] == "_shunt" then 92 | m:del(s[".name"], t) 93 | end 94 | end) 95 | TypedSection.remove(e, t) 96 | end 97 | 98 | o = s:option(DummyValue, "remarks", translate("Remarks")) 99 | 100 | return m 101 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/client/shunt_rules.lua: -------------------------------------------------------------------------------- 1 | local api = require "luci.passwall2.api" 2 | local appname = api.appname 3 | local datatypes = api.datatypes 4 | 5 | m = Map(appname, "Sing-Box/Xray " .. translate("Shunt Rule")) 6 | m.redirect = api.url() 7 | api.set_apply_on_parse(m) 8 | 9 | s = m:section(NamedSection, arg[1], "shunt_rules", "") 10 | s.addremove = false 11 | s.dynamic = false 12 | 13 | remarks = s:option(Value, "remarks", translate("Remarks")) 14 | remarks.default = arg[1] 15 | remarks.rmempty = false 16 | 17 | protocol = s:option(MultiValue, "protocol", translate("Protocol")) 18 | protocol:value("http") 19 | protocol:value("tls") 20 | protocol:value("bittorrent") 21 | 22 | o = s:option(MultiValue, "inbound", translate("Inbound Tag")) 23 | o:value("tproxy", translate("Transparent proxy")) 24 | o:value("socks", "Socks") 25 | 26 | network = s:option(ListValue, "network", translate("Network")) 27 | network:value("tcp,udp", "TCP UDP") 28 | network:value("tcp", "TCP") 29 | network:value("udp", "UDP") 30 | 31 | source = s:option(DynamicList, "source", translate("Source")) 32 | source.description = "" 37 | source.cast = "string" 38 | source.cfgvalue = function(self, section) 39 | local value 40 | if self.tag_error[section] then 41 | value = self:formvalue(section) 42 | else 43 | value = self.map:get(section, self.option) 44 | if type(value) == "string" then 45 | local value2 = {} 46 | string.gsub(value, '[^' .. " " .. ']+', function(w) table.insert(value2, w) end) 47 | value = value2 48 | end 49 | end 50 | return value 51 | end 52 | source.validate = function(self, value, t) 53 | local err = {} 54 | for _, v in ipairs(value) do 55 | local flag = false 56 | if datatypes.ip4addr(v) then 57 | flag = true 58 | end 59 | 60 | if flag == false and v:find("geoip:") and v:find("geoip:") == 1 then 61 | flag = true 62 | end 63 | 64 | if flag == false then 65 | err[#err + 1] = v 66 | end 67 | end 68 | 69 | if #err > 0 then 70 | self:add_error(t, "invalid", translate("Not true format, please re-enter!")) 71 | for _, v in ipairs(err) do 72 | self:add_error(t, "invalid", v) 73 | end 74 | end 75 | 76 | return value 77 | end 78 | 79 | local dynamicList_write = function(self, section, value) 80 | local t = {} 81 | local t2 = {} 82 | if type(value) == "table" then 83 | local x 84 | for _, x in ipairs(value) do 85 | if x and #x > 0 then 86 | if not t2[x] then 87 | t2[x] = x 88 | t[#t+1] = x 89 | end 90 | end 91 | end 92 | else 93 | t = { value } 94 | end 95 | t = table.concat(t, " ") 96 | return DynamicList.write(self, section, t) 97 | end 98 | 99 | source.write = dynamicList_write 100 | 101 | sourcePort = s:option(Value, "sourcePort", translate("Source port")) 102 | 103 | port = s:option(Value, "port", translate("port")) 104 | 105 | domain_list = s:option(TextValue, "domain_list", translate("Domain")) 106 | domain_list.rows = 10 107 | domain_list.wrap = "off" 108 | domain_list.validate = function(self, value) 109 | local hosts= {} 110 | value = value:gsub("^%s+", ""):gsub("%s+$","\n"):gsub("\r\n","\n"):gsub("[ \t]*\n[ \t]*", "\n") 111 | string.gsub(value, "[^\r\n]+", function(w) table.insert(hosts, w) end) 112 | for index, host in ipairs(hosts) do 113 | local flag = 1 114 | local tmp_host = host 115 | if not host:find("#") and host:find("%s") then 116 | elseif host:find("regexp:") and host:find("regexp:") == 1 then 117 | flag = 0 118 | elseif host:find("domain:.") and host:find("domain:.") == 1 then 119 | tmp_host = host:gsub("domain:", "") 120 | elseif host:find("full:.") and host:find("full:.") == 1 then 121 | tmp_host = host:gsub("full:", "") 122 | elseif host:find("geosite:") and host:find("geosite:") == 1 then 123 | flag = 0 124 | elseif host:find("ext:") and host:find("ext:") == 1 then 125 | flag = 0 126 | elseif host:find("#") and host:find("#") == 1 then 127 | flag = 0 128 | end 129 | if flag == 1 then 130 | if not datatypes.hostname(tmp_host) then 131 | return nil, tmp_host .. " " .. translate("Not valid domain name, please re-enter!") 132 | end 133 | end 134 | end 135 | return value 136 | end 137 | domain_list.description = "
" 144 | ip_list = s:option(TextValue, "ip_list", "IP") 145 | ip_list.rows = 10 146 | ip_list.wrap = "off" 147 | ip_list.validate = function(self, value) 148 | local ipmasks= {} 149 | value = value:gsub("^%s+", ""):gsub("%s+$","\n"):gsub("\r\n","\n"):gsub("[ \t]*\n[ \t]*", "\n") 150 | string.gsub(value, "[^\r\n]+", function(w) table.insert(ipmasks, w) end) 151 | for index, ipmask in ipairs(ipmasks) do 152 | if ipmask:find("geoip:") and ipmask:find("geoip:") == 1 and not ipmask:find("%s") then 153 | elseif ipmask:find("ext:") and ipmask:find("ext:") == 1 and not ipmask:find("%s") then 154 | elseif ipmask:find("#") and ipmask:find("#") == 1 then 155 | else 156 | if not (datatypes.ipmask4(ipmask) or datatypes.ipmask6(ipmask)) then 157 | return nil, ipmask .. " " .. translate("Not valid IP format, please re-enter!") 158 | end 159 | end 160 | end 161 | return value 162 | end 163 | ip_list.description = "
" 168 | 169 | return m 170 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/client/socks_config.lua: -------------------------------------------------------------------------------- 1 | local api = require "luci.passwall2.api" 2 | local appname = api.appname 3 | 4 | m = Map(appname) 5 | api.set_apply_on_parse(m) 6 | 7 | if not arg[1] or not m:get(arg[1]) then 8 | luci.http.redirect(api.url()) 9 | end 10 | 11 | local has_singbox = api.finded_com("sing-box") 12 | local has_xray = api.finded_com("xray") 13 | 14 | local nodes_table = {} 15 | for k, e in ipairs(api.get_valid_nodes()) do 16 | nodes_table[#nodes_table + 1] = e 17 | end 18 | 19 | s = m:section(NamedSection, arg[1], translate("Socks Config"), translate("Socks Config")) 20 | s.addremove = false 21 | s.dynamic = false 22 | 23 | ---- Enable 24 | o = s:option(Flag, "enabled", translate("Enable")) 25 | o.default = 1 26 | o.rmempty = false 27 | 28 | local auto_switch_tip 29 | local current_node = api.get_cache_var("socks_" .. arg[1]) 30 | if current_node then 31 | local n = m:get(current_node) 32 | if n then 33 | if tonumber(m:get(arg[1], "enable_autoswitch") or 0) == 1 then 34 | if n then 35 | local remarks = api.get_node_remarks(n) 36 | local url = api.url("node_config", n[".name"]) 37 | auto_switch_tip = translatef("Current node: %s", string.format('%s', url, remarks)) .. "
" 38 | end 39 | end 40 | end 41 | end 42 | 43 | socks_node = s:option(ListValue, "node", translate("Node")) 44 | if auto_switch_tip then 45 | socks_node.description = auto_switch_tip 46 | end 47 | 48 | o = s:option(Flag, "bind_local", translate("Bind Local"), translate("When selected, it can only be accessed localhost.")) 49 | o.default = "0" 50 | 51 | local n = 1 52 | m.uci:foreach(appname, "socks", function(s) 53 | if s[".name"] == section then 54 | return false 55 | end 56 | n = n + 1 57 | end) 58 | 59 | o = s:option(Value, "port", "Socks " .. translate("Listen Port")) 60 | o.default = n + 1080 61 | o.datatype = "port" 62 | o.rmempty = false 63 | 64 | if has_singbox or has_xray then 65 | o = s:option(Value, "http_port", "HTTP " .. translate("Listen Port") .. " " .. translate("0 is not use")) 66 | o.default = 0 67 | o.datatype = "port" 68 | end 69 | 70 | o = s:option(Flag, "log", translate("Enable") .. " " .. translate("Log")) 71 | o.default = 1 72 | o.rmempty = false 73 | 74 | o = s:option(Flag, "enable_autoswitch", translate("Auto Switch")) 75 | o.default = 0 76 | o.rmempty = false 77 | 78 | o = s:option(Value, "autoswitch_testing_time", translate("How often to test"), translate("Units:seconds")) 79 | o.datatype = "min(10)" 80 | o.default = 30 81 | o:depends("enable_autoswitch", true) 82 | 83 | o = s:option(Value, "autoswitch_connect_timeout", translate("Timeout seconds"), translate("Units:seconds")) 84 | o.datatype = "min(1)" 85 | o.default = 3 86 | o:depends("enable_autoswitch", true) 87 | 88 | o = s:option(Value, "autoswitch_retry_num", translate("Timeout retry num")) 89 | o.datatype = "min(1)" 90 | o.default = 1 91 | o:depends("enable_autoswitch", true) 92 | 93 | autoswitch_backup_node = s:option(DynamicList, "autoswitch_backup_node", translate("List of backup nodes")) 94 | autoswitch_backup_node:depends("enable_autoswitch", true) 95 | function o.write(self, section, value) 96 | local t = {} 97 | local t2 = {} 98 | if type(value) == "table" then 99 | local x 100 | for _, x in ipairs(value) do 101 | if x and #x > 0 then 102 | if not t2[x] then 103 | t2[x] = x 104 | t[#t+1] = x 105 | end 106 | end 107 | end 108 | else 109 | t = { value } 110 | end 111 | return DynamicList.write(self, section, t) 112 | end 113 | 114 | o = s:option(Flag, "autoswitch_restore_switch", translate("Restore Switch"), translate("When detects main node is available, switch back to the main node.")) 115 | o:depends("enable_autoswitch", true) 116 | 117 | o = s:option(Value, "autoswitch_probe_url", translate("Probe URL"), translate("The URL used to detect the connection status.")) 118 | o.default = "https://www.google.com/generate_204" 119 | o:depends("enable_autoswitch", true) 120 | 121 | for k, v in pairs(nodes_table) do 122 | autoswitch_backup_node:value(v.id, v["remark"]) 123 | socks_node:value(v.id, v["remark"]) 124 | end 125 | 126 | o = s:option(DummyValue, "btn", " ") 127 | o.template = appname .. "/socks_auto_switch/btn" 128 | o:depends("enable_autoswitch", true) 129 | 130 | return m 131 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/hysteria2.lua: -------------------------------------------------------------------------------- 1 | local m, s = ... 2 | 3 | local api = require "luci.passwall2.api" 4 | 5 | if not api.finded_com("hysteria") then 6 | return 7 | end 8 | 9 | local type_name = "Hysteria2" 10 | 11 | local option_prefix = "hysteria2_" 12 | 13 | local function _n(name) 14 | return option_prefix .. name 15 | end 16 | 17 | -- [[ Hysteria2 ]] 18 | 19 | s.fields["type"]:value(type_name, "Hysteria2") 20 | 21 | o = s:option(ListValue, _n("protocol"), translate("Protocol")) 22 | o:value("udp", "UDP") 23 | 24 | o = s:option(Value, _n("address"), translate("Address (Support Domain Name)")) 25 | 26 | o = s:option(Value, _n("port"), translate("Port")) 27 | o.datatype = "port" 28 | 29 | o = s:option(Value, _n("hop"), translate("Port hopping range")) 30 | o.description = translate("Format as 1000:2000 or 1000-2000 Multiple groups are separated by commas (,).") 31 | o.rewrite_option = o.option 32 | 33 | o = s:option(Value, _n("obfs"), translate("Obfs Password")) 34 | o.rewrite_option = o.option 35 | 36 | o = s:option(Value, _n("auth_password"), translate("Auth Password")) 37 | o.password = true 38 | o.rewrite_option = o.option 39 | 40 | o = s:option(Flag, _n("fast_open"), translate("Fast Open")) 41 | o.default = "0" 42 | 43 | o = s:option(Value, _n("tls_serverName"), translate("Domain")) 44 | 45 | o = s:option(Flag, _n("tls_allowInsecure"), translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped.")) 46 | o.default = "0" 47 | 48 | o = s:option(Value, _n("tls_pinSHA256"), translate("PinSHA256"),translate("Certificate fingerprint")) 49 | o.rewrite_option = o.option 50 | 51 | o = s:option(Value, _n("up_mbps"), translate("Max upload Mbps")) 52 | o.rewrite_option = o.option 53 | 54 | o = s:option(Value, _n("down_mbps"), translate("Max download Mbps")) 55 | o.rewrite_option = o.option 56 | 57 | o = s:option(Value, _n("hop_interval"), translate("Hop Interval"), translate("Example:") .. "30s (≥5s)") 58 | o.rewrite_option = o.option 59 | 60 | o = s:option(Value, _n("recv_window"), translate("QUIC stream receive window")) 61 | o.rewrite_option = o.option 62 | 63 | o = s:option(Value, _n("recv_window_conn"), translate("QUIC connection receive window")) 64 | o.rewrite_option = o.option 65 | 66 | o = s:option(Value, _n("idle_timeout"), translate("Idle Timeout"), translate("Example:") .. "30s (4s-120s)") 67 | o.rewrite_option = o.option 68 | 69 | o = s:option(Flag, _n("disable_mtu_discovery"), translate("Disable MTU detection")) 70 | o.default = "0" 71 | o.rewrite_option = o.option 72 | 73 | o = s:option(Flag, _n("lazy_start"), translate("Lazy Start")) 74 | o.default = "0" 75 | o.rewrite_option = o.option 76 | 77 | api.luci_types(arg[1], m, s, type_name, option_prefix) 78 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/naive.lua: -------------------------------------------------------------------------------- 1 | local m, s = ... 2 | 3 | local api = require "luci.passwall2.api" 4 | 5 | if not api.is_finded("naive") then 6 | return 7 | end 8 | 9 | local type_name = "Naiveproxy" 10 | 11 | local option_prefix = "naive_" 12 | 13 | local function _n(name) 14 | return option_prefix .. name 15 | end 16 | 17 | -- [[ Naive ]] 18 | 19 | s.fields["type"]:value(type_name, translate("NaiveProxy")) 20 | 21 | o = s:option(ListValue, _n("protocol"), translate("Protocol")) 22 | o:value("https", translate("HTTPS")) 23 | o:value("quic", translate("QUIC")) 24 | 25 | o = s:option(Value, _n("address"), translate("Address (Support Domain Name)")) 26 | 27 | o = s:option(Value, _n("port"), translate("Port")) 28 | o.datatype = "port" 29 | 30 | o = s:option(Value, _n("username"), translate("Username")) 31 | 32 | o = s:option(Value, _n("password"), translate("Password")) 33 | o.password = true 34 | 35 | api.luci_types(arg[1], m, s, type_name, option_prefix) 36 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ss-rust.lua: -------------------------------------------------------------------------------- 1 | local m, s = ... 2 | 3 | local api = require "luci.passwall2.api" 4 | 5 | if not api.is_finded("sslocal") then 6 | return 7 | end 8 | 9 | local type_name = "SS-Rust" 10 | 11 | local option_prefix = "ssrust_" 12 | 13 | local function _n(name) 14 | return option_prefix .. name 15 | end 16 | 17 | local ssrust_encrypt_method_list = { 18 | "plain", "none", 19 | "aes-128-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", 20 | "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha8-poly1305", "2022-blake3-chacha20-poly1305" 21 | } 22 | 23 | -- [[ Shadowsocks Rust ]] 24 | 25 | s.fields["type"]:value(type_name, translate("Shadowsocks Rust")) 26 | 27 | o = s:option(Value, _n("address"), translate("Address (Support Domain Name)")) 28 | 29 | o = s:option(Value, _n("port"), translate("Port")) 30 | o.datatype = "port" 31 | 32 | o = s:option(Value, _n("password"), translate("Password")) 33 | o.password = true 34 | 35 | o = s:option(Value, _n("method"), translate("Encrypt Method")) 36 | for a, t in ipairs(ssrust_encrypt_method_list) do o:value(t) end 37 | 38 | o = s:option(Value, _n("timeout"), translate("Connection Timeout")) 39 | o.datatype = "uinteger" 40 | o.default = 300 41 | 42 | o = s:option(ListValue, _n("tcp_fast_open"), "TCP " .. translate("Fast Open"), translate("Need node support required")) 43 | o:value("false") 44 | o:value("true") 45 | 46 | o = s:option(ListValue, _n("plugin"), translate("plugin")) 47 | o:value("none", translate("none")) 48 | if api.is_finded("xray-plugin") then o:value("xray-plugin") end 49 | if api.is_finded("v2ray-plugin") then o:value("v2ray-plugin") end 50 | if api.is_finded("obfs-local") then o:value("obfs-local") end 51 | 52 | o = s:option(Value, _n("plugin_opts"), translate("opts")) 53 | o:depends({ [_n("plugin")] = "xray-plugin"}) 54 | o:depends({ [_n("plugin")] = "v2ray-plugin"}) 55 | o:depends({ [_n("plugin")] = "obfs-local"}) 56 | 57 | api.luci_types(arg[1], m, s, type_name, option_prefix) 58 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ss.lua: -------------------------------------------------------------------------------- 1 | local m, s = ... 2 | 3 | local api = require "luci.passwall2.api" 4 | 5 | if not api.is_finded("ss-local") then 6 | return 7 | end 8 | 9 | local type_name = "SS" 10 | 11 | local option_prefix = "ss_" 12 | 13 | local function _n(name) 14 | return option_prefix .. name 15 | end 16 | 17 | local ss_encrypt_method_list = { 18 | "rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", 19 | "aes-192-ctr", "aes-256-ctr", "bf-cfb", "salsa20", "chacha20", "chacha20-ietf", 20 | "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", 21 | "xchacha20-ietf-poly1305" 22 | } 23 | 24 | -- [[ Shadowsocks Libev ]] 25 | 26 | s.fields["type"]:value(type_name, translate("Shadowsocks Libev")) 27 | 28 | o = s:option(Value, _n("address"), translate("Address (Support Domain Name)")) 29 | 30 | o = s:option(Value, _n("port"), translate("Port")) 31 | o.datatype = "port" 32 | 33 | o = s:option(Value, _n("password"), translate("Password")) 34 | o.password = true 35 | 36 | o = s:option(Value, _n("method"), translate("Encrypt Method")) 37 | for a, t in ipairs(ss_encrypt_method_list) do o:value(t) end 38 | 39 | o = s:option(Value, _n("timeout"), translate("Connection Timeout")) 40 | o.datatype = "uinteger" 41 | o.default = 300 42 | 43 | o = s:option(ListValue, _n("tcp_fast_open"), "TCP " .. translate("Fast Open"), translate("Need node support required")) 44 | o:value("false") 45 | o:value("true") 46 | 47 | o = s:option(ListValue, _n("plugin"), translate("plugin")) 48 | o:value("none", translate("none")) 49 | if api.is_finded("xray-plugin") then o:value("xray-plugin") end 50 | if api.is_finded("v2ray-plugin") then o:value("v2ray-plugin") end 51 | if api.is_finded("obfs-local") then o:value("obfs-local") end 52 | 53 | o = s:option(Value, _n("plugin_opts"), translate("opts")) 54 | o:depends({ [_n("plugin")] = "xray-plugin"}) 55 | o:depends({ [_n("plugin")] = "v2ray-plugin"}) 56 | o:depends({ [_n("plugin")] = "obfs-local"}) 57 | 58 | api.luci_types(arg[1], m, s, type_name, option_prefix) 59 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ssr.lua: -------------------------------------------------------------------------------- 1 | local m, s = ... 2 | 3 | local api = require "luci.passwall2.api" 4 | 5 | if not api.is_finded("ssr-local") then 6 | return 7 | end 8 | 9 | local type_name = "SSR" 10 | 11 | local option_prefix = "ssr_" 12 | 13 | local function _n(name) 14 | return option_prefix .. name 15 | end 16 | 17 | local ssr_encrypt_method_list = { 18 | "none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb", 19 | "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", 20 | "bf-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb", 21 | "cast5-cfb", "des-cfb", "idea-cfb", "seed-cfb", "salsa20", "chacha20", 22 | "chacha20-ietf" 23 | } 24 | 25 | local ssr_protocol_list = { 26 | "origin", "verify_simple", "verify_deflate", "verify_sha1", "auth_simple", 27 | "auth_sha1", "auth_sha1_v2", "auth_sha1_v4", "auth_aes128_md5", 28 | "auth_aes128_sha1", "auth_chain_a", "auth_chain_b", "auth_chain_c", 29 | "auth_chain_d", "auth_chain_e", "auth_chain_f" 30 | } 31 | local ssr_obfs_list = { 32 | "plain", "http_simple", "http_post", "random_head", "tls_simple", 33 | "tls1.0_session_auth", "tls1.2_ticket_auth" 34 | } 35 | 36 | -- [[ ShadowsocksR Libev ]] 37 | 38 | s.fields["type"]:value(type_name, translate("ShadowsocksR Libev")) 39 | 40 | o = s:option(Value, _n("address"), translate("Address (Support Domain Name)")) 41 | 42 | o = s:option(Value, _n("port"), translate("Port")) 43 | o.datatype = "port" 44 | 45 | o = s:option(Value, _n("password"), translate("Password")) 46 | o.password = true 47 | 48 | o = s:option(ListValue, _n("method"), translate("Encrypt Method")) 49 | for a, t in ipairs(ssr_encrypt_method_list) do o:value(t) end 50 | 51 | o = s:option(ListValue, _n("protocol"), translate("Protocol")) 52 | for a, t in ipairs(ssr_protocol_list) do o:value(t) end 53 | 54 | o = s:option(Value, _n("protocol_param"), translate("Protocol_param")) 55 | 56 | o = s:option(ListValue, _n("obfs"), translate("Obfs")) 57 | for a, t in ipairs(ssr_obfs_list) do o:value(t) end 58 | 59 | o = s:option(Value, _n("obfs_param"), translate("Obfs_param")) 60 | 61 | o = s:option(Value, _n("timeout"), translate("Connection Timeout")) 62 | o.datatype = "uinteger" 63 | o.default = 300 64 | 65 | o = s:option(ListValue, _n("tcp_fast_open"), "TCP " .. translate("Fast Open"), translate("Need node support required")) 66 | o:value("false") 67 | o:value("true") 68 | 69 | api.luci_types(arg[1], m, s, type_name, option_prefix) 70 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/tuic.lua: -------------------------------------------------------------------------------- 1 | local m, s = ... 2 | 3 | local api = require "luci.passwall2.api" 4 | 5 | if not api.is_finded("tuic-client") then 6 | return 7 | end 8 | 9 | local type_name = "TUIC" 10 | 11 | local option_prefix = "tuic_" 12 | 13 | local function _n(name) 14 | return option_prefix .. name 15 | end 16 | 17 | -- [[ TUIC ]] 18 | 19 | s.fields["type"]:value(type_name, translate("TUIC")) 20 | 21 | o = s:option(Value, _n("address"), translate("Address (Support Domain Name)")) 22 | 23 | o = s:option(Value, _n("port"), translate("Port")) 24 | o.datatype = "port" 25 | 26 | o = s:option(Value, _n("uuid"), translate("ID")) 27 | o.password = true 28 | 29 | -- Tuic Password for remote server connect 30 | o = s:option(Value, _n("password"), translate("TUIC User Password For Connect Remote Server")) 31 | o.password = true 32 | o.rmempty = true 33 | o.default = "" 34 | o.rewrite_option = o.option 35 | 36 | --[[ 37 | -- Tuic username for local socks connect 38 | o = s:option(Value, _n("socks_username"), translate("TUIC UserName For Local Socks")) 39 | o.rmempty = true 40 | o.default = "" 41 | o.rewrite_option = o.option 42 | 43 | -- Tuic Password for local socks connect 44 | o = s:option(Value, _n("socks_password"), translate("TUIC Password For Local Socks")) 45 | o.password = true 46 | o.rmempty = true 47 | o.default = "" 48 | o.rewrite_option = o.option 49 | --]] 50 | 51 | o = s:option(Value, _n("ip"), translate("Set the TUIC proxy server ip address")) 52 | o.datatype = "ipaddr" 53 | o.rmempty = true 54 | o.rewrite_option = o.option 55 | 56 | o = s:option(ListValue, _n("udp_relay_mode"), translate("UDP relay mode")) 57 | o:value("native", translate("native")) 58 | o:value("quic", translate("QUIC")) 59 | o.default = "native" 60 | o.rmempty = true 61 | o.rewrite_option = o.option 62 | 63 | o = s:option(ListValue, _n("congestion_control"), translate("Congestion control algorithm")) 64 | o:value("bbr", translate("BBR")) 65 | o:value("cubic", translate("CUBIC")) 66 | o:value("new_reno", translate("New Reno")) 67 | o.default = "cubic" 68 | o.rmempty = true 69 | o.rewrite_option = o.option 70 | 71 | o = s:option(Value, _n("heartbeat"), translate("Heartbeat interval(second)")) 72 | o.datatype = "uinteger" 73 | o.default = "3" 74 | o.rmempty = true 75 | o.rewrite_option = o.option 76 | 77 | o = s:option(Value, _n("timeout"), translate("Timeout for establishing a connection to server(second)")) 78 | o.datatype = "uinteger" 79 | o.default = "8" 80 | o.rmempty = true 81 | o.rewrite_option = o.option 82 | 83 | o = s:option(Value, _n("gc_interval"), translate("Garbage collection interval(second)")) 84 | o.datatype = "uinteger" 85 | o.default = "3" 86 | o.rmempty = true 87 | o.rewrite_option = o.option 88 | 89 | o = s:option(Value, _n("gc_lifetime"), translate("Garbage collection lifetime(second)")) 90 | o.datatype = "uinteger" 91 | o.default = "15" 92 | o.rmempty = true 93 | o.rewrite_option = o.option 94 | 95 | o = s:option(Value, _n("send_window"), translate("TUIC send window")) 96 | o.datatype = "uinteger" 97 | o.default = 20971520 98 | o.rmempty = true 99 | o.rewrite_option = o.option 100 | 101 | o = s:option(Value, _n("receive_window"), translate("TUIC receive window")) 102 | o.datatype = "uinteger" 103 | o.default = 10485760 104 | o.rmempty = true 105 | o.rewrite_option = o.option 106 | 107 | o = s:option(Value, _n("max_package_size"), translate("TUIC Maximum packet size the socks5 server can receive from external, in bytes")) 108 | o.datatype = "uinteger" 109 | o.default = 1500 110 | o.rmempty = true 111 | o.rewrite_option = o.option 112 | 113 | --Tuic settings for the local inbound socks5 server 114 | o = s:option(Flag, _n("dual_stack"), translate("Set if the listening socket should be dual-stack")) 115 | o.default = 0 116 | o.rmempty = true 117 | o.rewrite_option = o.option 118 | 119 | o = s:option(Flag, _n("disable_sni"), translate("Disable SNI")) 120 | o.default = 0 121 | o.rmempty = true 122 | o.rewrite_option = o.option 123 | 124 | o = s:option(Flag, _n("zero_rtt_handshake"), translate("Enable 0-RTT QUIC handshake")) 125 | o.default = 0 126 | o.rmempty = true 127 | o.rewrite_option = o.option 128 | 129 | o = s:option(DynamicList, _n("tls_alpn"), translate("TLS ALPN")) 130 | o.rmempty = true 131 | o.rewrite_option = o.option 132 | 133 | api.luci_types(arg[1], m, s, type_name, option_prefix) 134 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/server/index.lua: -------------------------------------------------------------------------------- 1 | local api = require "luci.passwall2.api" 2 | 3 | m = Map("passwall2_server", translate("Server-Side")) 4 | api.set_apply_on_parse(m) 5 | 6 | t = m:section(NamedSection, "global", "global") 7 | t.anonymous = true 8 | t.addremove = false 9 | 10 | e = t:option(Flag, "enable", translate("Enable")) 11 | e.rmempty = false 12 | 13 | t = m:section(TypedSection, "user", translate("Users Manager")) 14 | t.anonymous = true 15 | t.addremove = true 16 | t.sortable = true 17 | t.template = "cbi/tblsection" 18 | t.extedit = api.url("server_user", "%s") 19 | function t.create(e, t) 20 | local uuid = api.gen_uuid() 21 | t = uuid 22 | TypedSection.create(e, t) 23 | luci.http.redirect(e.extedit:format(t)) 24 | end 25 | function t.remove(e, t) 26 | e.map.proceed = true 27 | e.map:del(t) 28 | luci.http.redirect(api.url("server")) 29 | end 30 | 31 | e = t:option(Flag, "enable", translate("Enable")) 32 | e.width = "5%" 33 | e.rmempty = false 34 | 35 | e = t:option(DummyValue, "status", translate("Status")) 36 | e.rawhtml = true 37 | e.cfgvalue = function(t, n) 38 | return string.format('%s', translate("Collecting data...")) 39 | end 40 | 41 | e = t:option(DummyValue, "remarks", translate("Remarks")) 42 | e.width = "15%" 43 | 44 | e = t:option(DummyValue, "type", translate("Type")) 45 | e.width = "20%" 46 | e.rawhtml = true 47 | e.cfgvalue = function(t, n) 48 | local str = "" 49 | local type = m:get(n, "type") or "" 50 | if type == "sing-box" or type == "Xray" then 51 | local protocol = m:get(n, "protocol") or "" 52 | if protocol == "vmess" then 53 | protocol = "VMess" 54 | elseif protocol == "vless" then 55 | protocol = "VLESS" 56 | elseif protocol == "shadowsocks" then 57 | protocol = "SS" 58 | elseif protocol == "shadowsocksr" then 59 | protocol = "SSR" 60 | elseif protocol == "wireguard" then 61 | protocol = "WG" 62 | elseif protocol == "hysteria" then 63 | protocol = "HY" 64 | elseif protocol == "hysteria2" then 65 | protocol = "HY2" 66 | elseif protocol == "anytls" then 67 | protocol = "AnyTLS" 68 | else 69 | protocol = protocol:gsub("^%l",string.upper) 70 | local custom = m:get(n, "custom") or "0" 71 | if custom == "1" then 72 | protocol = translate("Custom Config") 73 | end 74 | end 75 | if type == "sing-box" then type = "Sing-Box" end 76 | type = type .. " " .. protocol 77 | end 78 | str = str .. translate(type) 79 | return str 80 | end 81 | 82 | e = t:option(DummyValue, "port", translate("Port")) 83 | 84 | e = t:option(Flag, "log", translate("Log")) 85 | e.default = "1" 86 | e.rmempty = false 87 | 88 | m:append(Template("passwall2/server/log")) 89 | 90 | m:append(Template("passwall2/server/users_list_status")) 91 | return m 92 | 93 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/hysteria2.lua: -------------------------------------------------------------------------------- 1 | local m, s = ... 2 | 3 | local api = require "luci.passwall2.api" 4 | 5 | if not api.finded_com("hysteria") then 6 | return 7 | end 8 | 9 | local type_name = "Hysteria2" 10 | 11 | local option_prefix = "hysteria2_" 12 | 13 | local function _n(name) 14 | return option_prefix .. name 15 | end 16 | 17 | -- [[ Hysteria2 ]] 18 | 19 | s.fields["type"]:value(type_name, "Hysteria2") 20 | 21 | o = s:option(Flag, _n("custom"), translate("Use Custom Config")) 22 | 23 | o = s:option(Value, _n("port"), translate("Listen Port")) 24 | o.datatype = "port" 25 | o:depends({ [_n("custom")] = false }) 26 | 27 | o = s:option(Value, _n("obfs"), translate("Obfs Password")) 28 | o.rewrite_option = o.option 29 | o:depends({ [_n("custom")] = false }) 30 | 31 | o = s:option(Value, _n("auth_password"), translate("Auth Password")) 32 | o.password = true 33 | o.rewrite_option = o.option 34 | o:depends({ [_n("custom")] = false }) 35 | 36 | o = s:option(Flag, _n("udp"), translate("UDP")) 37 | o.default = "1" 38 | o.rewrite_option = o.option 39 | o:depends({ [_n("custom")] = false }) 40 | 41 | o = s:option(Value, _n("up_mbps"), translate("Max upload Mbps")) 42 | o.rewrite_option = o.option 43 | o:depends({ [_n("custom")] = false }) 44 | 45 | o = s:option(Value, _n("down_mbps"), translate("Max download Mbps")) 46 | o.rewrite_option = o.option 47 | o:depends({ [_n("custom")] = false }) 48 | 49 | o = s:option(Flag, _n("ignoreClientBandwidth"), translate("ignoreClientBandwidth")) 50 | o.default = "0" 51 | o.rewrite_option = o.option 52 | o:depends({ [_n("custom")] = false }) 53 | 54 | o = s:option(FileUpload, _n("tls_certificateFile"), translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem") 55 | o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem" 56 | if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end 57 | o.validate = function(self, value, t) 58 | if value and value ~= "" then 59 | if not nixio.fs.access(value) then 60 | return nil, translate("Can't find this file!") 61 | else 62 | return value 63 | end 64 | end 65 | return nil 66 | end 67 | o:depends({ [_n("custom")] = false }) 68 | 69 | o = s:option(FileUpload, _n("tls_keyFile"), translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key") 70 | o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key" 71 | if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end 72 | o.validate = function(self, value, t) 73 | if value and value ~= "" then 74 | if not nixio.fs.access(value) then 75 | return nil, translate("Can't find this file!") 76 | else 77 | return value 78 | end 79 | end 80 | return nil 81 | end 82 | o:depends({ [_n("custom")] = false }) 83 | 84 | o = s:option(TextValue, _n("custom_config"), translate("Custom Config")) 85 | o.rows = 10 86 | o.wrap = "off" 87 | o:depends({ [_n("custom")] = true }) 88 | o.validate = function(self, value, t) 89 | if value and api.jsonc.parse(value) then 90 | return value 91 | else 92 | return nil, translate("Must be JSON text!") 93 | end 94 | end 95 | o.custom_cfgvalue = function(self, section, value) 96 | local config_str = m:get(section, "config_str") 97 | if config_str then 98 | return api.base64Decode(config_str) 99 | end 100 | end 101 | o.custom_write = function(self, section, value) 102 | m:set(section, "config_str", api.base64Encode(value)) 103 | end 104 | 105 | o = s:option(Flag, _n("log"), translate("Log")) 106 | o.default = "1" 107 | o.rmempty = false 108 | 109 | api.luci_types(arg[1], m, s, type_name, option_prefix) 110 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ss-rust.lua: -------------------------------------------------------------------------------- 1 | local m, s = ... 2 | 3 | local api = require "luci.passwall2.api" 4 | 5 | if not api.is_finded("ssserver") then 6 | return 7 | end 8 | 9 | local type_name = "SS-Rust" 10 | 11 | local option_prefix = "ssrust_" 12 | 13 | local function _n(name) 14 | return option_prefix .. name 15 | end 16 | 17 | local ssrust_encrypt_method_list = { 18 | "plain", "none", 19 | "aes-128-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", 20 | "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha8-poly1305", "2022-blake3-chacha20-poly1305" 21 | } 22 | 23 | -- [[ Shadowsocks Rust ]] 24 | 25 | s.fields["type"]:value(type_name, translate("Shadowsocks Rust")) 26 | 27 | o = s:option(Flag, _n("custom"), translate("Use Custom Config")) 28 | 29 | o = s:option(Value, _n("port"), translate("Listen Port")) 30 | o.datatype = "port" 31 | o:depends({ [_n("custom")] = false }) 32 | 33 | o = s:option(Value, _n("password"), translate("Password")) 34 | o.password = true 35 | o:depends({ [_n("custom")] = false }) 36 | 37 | o = s:option(ListValue, _n("method"), translate("Encrypt Method")) 38 | for a, t in ipairs(ssrust_encrypt_method_list) do o:value(t) end 39 | o:depends({ [_n("custom")] = false }) 40 | 41 | o = s:option(Value, _n("timeout"), translate("Connection Timeout")) 42 | o.datatype = "uinteger" 43 | o.default = 300 44 | o:depends({ [_n("custom")] = false }) 45 | 46 | o = s:option(Flag, _n("tcp_fast_open"), "TCP " .. translate("Fast Open")) 47 | o.default = "0" 48 | o:depends({ [_n("custom")] = false }) 49 | 50 | o = s:option(TextValue, _n("custom_config"), translate("Custom Config")) 51 | o.rows = 10 52 | o.wrap = "off" 53 | o:depends({ [_n("custom")] = true }) 54 | o.validate = function(self, value, t) 55 | if value and api.jsonc.parse(value) then 56 | return value 57 | else 58 | return nil, translate("Must be JSON text!") 59 | end 60 | end 61 | o.custom_cfgvalue = function(self, section, value) 62 | local config_str = m:get(section, "config_str") 63 | if config_str then 64 | return api.base64Decode(config_str) 65 | end 66 | end 67 | o.custom_write = function(self, section, value) 68 | m:set(section, "config_str", api.base64Encode(value)) 69 | end 70 | 71 | o = s:option(Flag, _n("log"), translate("Log")) 72 | o.default = "1" 73 | o.rmempty = false 74 | 75 | api.luci_types(arg[1], m, s, type_name, option_prefix) 76 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ss.lua: -------------------------------------------------------------------------------- 1 | local m, s = ... 2 | 3 | local api = require "luci.passwall2.api" 4 | 5 | if not api.is_finded("ss-server") then 6 | return 7 | end 8 | 9 | local type_name = "SS" 10 | 11 | local option_prefix = "ss_" 12 | 13 | local function _n(name) 14 | return option_prefix .. name 15 | end 16 | 17 | local ss_encrypt_method_list = { 18 | "rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", 19 | "aes-192-ctr", "aes-256-ctr", "bf-cfb", "camellia-128-cfb", 20 | "camellia-192-cfb", "camellia-256-cfb", "salsa20", "chacha20", 21 | "chacha20-ietf", -- aead 22 | "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", 23 | "xchacha20-ietf-poly1305" 24 | } 25 | 26 | -- [[ Shadowsocks ]] 27 | 28 | s.fields["type"]:value(type_name, translate("Shadowsocks")) 29 | 30 | o = s:option(Flag, _n("custom"), translate("Use Custom Config")) 31 | 32 | o = s:option(Value, _n("port"), translate("Listen Port")) 33 | o.datatype = "port" 34 | o:depends({ [_n("custom")] = false }) 35 | 36 | o = s:option(Value, _n("password"), translate("Password")) 37 | o.password = true 38 | o:depends({ [_n("custom")] = false }) 39 | 40 | o = s:option(ListValue, _n("method"), translate("Encrypt Method")) 41 | for a, t in ipairs(ss_encrypt_method_list) do o:value(t) end 42 | o:depends({ [_n("custom")] = false }) 43 | 44 | o = s:option(Value, _n("timeout"), translate("Connection Timeout")) 45 | o.datatype = "uinteger" 46 | o.default = 300 47 | o:depends({ [_n("custom")] = false }) 48 | 49 | o = s:option(Flag, _n("tcp_fast_open"), "TCP " .. translate("Fast Open")) 50 | o.default = "0" 51 | o:depends({ [_n("custom")] = false }) 52 | 53 | o = s:option(TextValue, _n("custom_config"), translate("Custom Config")) 54 | o.rows = 10 55 | o.wrap = "off" 56 | o:depends({ [_n("custom")] = true }) 57 | o.validate = function(self, value, t) 58 | if value and api.jsonc.parse(value) then 59 | return value 60 | else 61 | return nil, translate("Must be JSON text!") 62 | end 63 | end 64 | o.custom_cfgvalue = function(self, section, value) 65 | local config_str = m:get(section, "config_str") 66 | if config_str then 67 | return api.base64Decode(config_str) 68 | end 69 | end 70 | o.custom_write = function(self, section, value) 71 | m:set(section, "config_str", api.base64Encode(value)) 72 | end 73 | 74 | o = s:option(Flag, _n("log"), translate("Log")) 75 | o.default = "1" 76 | o.rmempty = false 77 | 78 | api.luci_types(arg[1], m, s, type_name, option_prefix) 79 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ssr.lua: -------------------------------------------------------------------------------- 1 | local m, s = ... 2 | 3 | local api = require "luci.passwall2.api" 4 | 5 | if not api.is_finded("ssr-server") then 6 | return 7 | end 8 | 9 | local type_name = "SSR" 10 | 11 | local option_prefix = "ssr_" 12 | 13 | local function _n(name) 14 | return option_prefix .. name 15 | end 16 | 17 | local ssr_encrypt_method_list = { 18 | "none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb", 19 | "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", 20 | "bf-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb", 21 | "cast5-cfb", "des-cfb", "idea-cfb", "seed-cfb", "salsa20", "chacha20", 22 | "chacha20-ietf" 23 | } 24 | 25 | local ssr_protocol_list = { 26 | "origin", "verify_simple", "verify_deflate", "verify_sha1", "auth_simple", 27 | "auth_sha1", "auth_sha1_v2", "auth_sha1_v4", "auth_aes128_md5", 28 | "auth_aes128_sha1", "auth_chain_a", "auth_chain_b", "auth_chain_c", 29 | "auth_chain_d", "auth_chain_e", "auth_chain_f" 30 | } 31 | local ssr_obfs_list = { 32 | "plain", "http_simple", "http_post", "random_head", "tls_simple", 33 | "tls1.0_session_auth", "tls1.2_ticket_auth" 34 | } 35 | 36 | -- [[ ShadowsocksR ]] 37 | 38 | s.fields["type"]:value(type_name, translate("ShadowsocksR")) 39 | 40 | o = s:option(Flag, _n("custom"), translate("Use Custom Config")) 41 | 42 | o = s:option(Value, _n("port"), translate("Listen Port")) 43 | o.datatype = "port" 44 | o:depends({ [_n("custom")] = false }) 45 | 46 | o = s:option(Value, _n("password"), translate("Password")) 47 | o.password = true 48 | o:depends({ [_n("custom")] = false }) 49 | 50 | o = s:option(ListValue, _n("method"), translate("Encrypt Method")) 51 | for a, t in ipairs(ssr_encrypt_method_list) do o:value(t) end 52 | o:depends({ [_n("custom")] = false }) 53 | 54 | o = s:option(ListValue, _n("protocol"), translate("Protocol")) 55 | for a, t in ipairs(ssr_protocol_list) do o:value(t) end 56 | o:depends({ [_n("custom")] = false }) 57 | 58 | o = s:option(Value, _n("protocol_param"), translate("Protocol_param")) 59 | o:depends({ [_n("custom")] = false }) 60 | 61 | o = s:option(ListValue, _n("obfs"), translate("Obfs")) 62 | for a, t in ipairs(ssr_obfs_list) do o:value(t) end 63 | o:depends({ [_n("custom")] = false }) 64 | 65 | o = s:option(Value, _n("obfs_param"), translate("Obfs_param")) 66 | o:depends({ [_n("custom")] = false }) 67 | 68 | o = s:option(Value, _n("timeout"), translate("Connection Timeout")) 69 | o.datatype = "uinteger" 70 | o.default = 300 71 | o:depends({ [_n("custom")] = false }) 72 | 73 | o = s:option(Flag, _n("tcp_fast_open"), "TCP " .. translate("Fast Open")) 74 | o.default = "0" 75 | o:depends({ [_n("custom")] = false }) 76 | 77 | o = s:option(TextValue, _n("custom_config"), translate("Custom Config")) 78 | o.rows = 10 79 | o.wrap = "off" 80 | o:depends({ [_n("custom")] = true }) 81 | o.validate = function(self, value, t) 82 | if value and api.jsonc.parse(value) then 83 | return value 84 | else 85 | return nil, translate("Must be JSON text!") 86 | end 87 | end 88 | o.custom_cfgvalue = function(self, section, value) 89 | local config_str = m:get(section, "config_str") 90 | if config_str then 91 | return api.base64Decode(config_str) 92 | end 93 | end 94 | o.custom_write = function(self, section, value) 95 | m:set(section, "config_str", api.base64Encode(value)) 96 | end 97 | 98 | o = s:option(Flag, _n("udp_forward"), translate("UDP Forward")) 99 | o.default = "1" 100 | o.rmempty = false 101 | 102 | o = s:option(Flag, _n("log"), translate("Log")) 103 | o.default = "1" 104 | o.rmempty = false 105 | 106 | api.luci_types(arg[1], m, s, type_name, option_prefix) 107 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/model/cbi/passwall2/server/user.lua: -------------------------------------------------------------------------------- 1 | local api = require "luci.passwall2.api" 2 | local fs = require "nixio.fs" 3 | local types_dir = "/usr/lib/lua/luci/model/cbi/passwall2/server/type/" 4 | 5 | m = Map("passwall2_server", translate("Server Config")) 6 | m.redirect = api.url("server") 7 | api.set_apply_on_parse(m) 8 | 9 | s = m:section(NamedSection, arg[1], "user", "") 10 | s.addremove = false 11 | s.dynamic = false 12 | 13 | o = s:option(Flag, "enable", translate("Enable")) 14 | o.default = "1" 15 | o.rmempty = false 16 | 17 | o = s:option(Value, "remarks", translate("Remarks")) 18 | o.default = translate("Remarks") 19 | o.rmempty = false 20 | 21 | o = s:option(ListValue, "type", translate("Type")) 22 | 23 | local type_table = {} 24 | for filename in fs.dir(types_dir) do 25 | table.insert(type_table, filename) 26 | end 27 | table.sort(type_table) 28 | 29 | for index, value in ipairs(type_table) do 30 | local p_func = loadfile(types_dir .. value) 31 | setfenv(p_func, getfenv(1))(m, s) 32 | end 33 | 34 | return m 35 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/passwall2/com.lua: -------------------------------------------------------------------------------- 1 | local _M = {} 2 | 3 | local function gh_release_url(self) 4 | return "https://api.github.com/repos/" .. self.repo .. "/releases/latest" 5 | end 6 | 7 | local function gh_pre_release_url(self) 8 | return "https://api.github.com/repos/" .. self.repo .. "/releases?per_page=1" 9 | end 10 | 11 | _M.hysteria = { 12 | name = "Hysteria", 13 | repo = "HyNetwork/hysteria", 14 | get_url = gh_release_url, 15 | cmd_version = "version | awk '/^Version:/ {print $2}'", 16 | remote_version_str_replace = "app/", 17 | zipped = false, 18 | default_path = "/usr/bin/hysteria", 19 | match_fmt_str = "linux%%-%s$", 20 | file_tree = { 21 | armv6 = "arm", 22 | armv7 = "arm" 23 | } 24 | } 25 | 26 | _M["sing-box"] = { 27 | name = "Sing-Box", 28 | repo = "SagerNet/sing-box", 29 | get_url = gh_release_url, 30 | cmd_version = "version | awk '{print $3}' | sed -n 1P", 31 | zipped = true, 32 | zipped_suffix = "tar.gz", 33 | default_path = "/usr/bin/sing-box", 34 | match_fmt_str = "linux%%-%s", 35 | file_tree = { 36 | x86_64 = "amd64", 37 | mips64el = "mips64le" 38 | } 39 | } 40 | 41 | _M.xray = { 42 | name = "Xray", 43 | repo = "XTLS/Xray-core", 44 | get_url = gh_pre_release_url, 45 | cmd_version = "version | awk '{print $2}' | sed -n 1P", 46 | zipped = true, 47 | default_path = "/usr/bin/xray", 48 | match_fmt_str = "linux%%-%s", 49 | file_tree = { 50 | x86_64 = "64", 51 | x86 = "32", 52 | mips = "mips32", 53 | mipsel = "mips32le", 54 | mips64el = "mips64le" 55 | } 56 | } 57 | 58 | return _M 59 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/passwall2/server_app.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | local action = arg[1] 4 | local api = require "luci.passwall2.api" 5 | local sys = api.sys 6 | local uci = api.uci 7 | local jsonc = api.jsonc 8 | 9 | local CONFIG = "passwall2_server" 10 | local CONFIG_PATH = "/tmp/etc/" .. CONFIG 11 | local NFT_INCLUDE_FILE = CONFIG_PATH .. "/" .. CONFIG .. ".nft" 12 | local LOG_APP_FILE = "/tmp/log/" .. CONFIG .. ".log" 13 | local TMP_BIN_PATH = CONFIG_PATH .. "/bin" 14 | local require_dir = "luci.passwall2." 15 | 16 | local ipt_bin = sys.exec("echo -n $(/usr/share/passwall2/iptables.sh get_ipt_bin)") 17 | local ip6t_bin = sys.exec("echo -n $(/usr/share/passwall2/iptables.sh get_ip6t_bin)") 18 | 19 | local nft_flag = api.is_finded("fw4") and "1" or "0" 20 | 21 | local function log(...) 22 | local f, err = io.open(LOG_APP_FILE, "a") 23 | if f and err == nil then 24 | local str = os.date("%Y-%m-%d %H:%M:%S: ") .. table.concat({...}, " ") 25 | f:write(str .. "\n") 26 | f:close() 27 | end 28 | end 29 | 30 | local function cmd(cmd) 31 | sys.call(cmd) 32 | end 33 | 34 | local function ipt(arg) 35 | if ipt_bin and #ipt_bin > 0 then 36 | cmd(ipt_bin .. " -w " .. arg) 37 | end 38 | end 39 | 40 | local function ip6t(arg) 41 | if ip6t_bin and #ip6t_bin > 0 then 42 | cmd(ip6t_bin .. " -w " .. arg) 43 | end 44 | end 45 | 46 | local function ln_run(s, d, command, output) 47 | if not output then 48 | output = "/dev/null" 49 | end 50 | d = TMP_BIN_PATH .. "/" .. d 51 | cmd(string.format('[ ! -f "%s" ] && ln -s %s %s 2>/dev/null', d, s, d)) 52 | return string.format("%s >%s 2>&1 &", d .. " " ..command, output) 53 | end 54 | 55 | local function gen_include() 56 | cmd(string.format("echo '#!/bin/sh' > /tmp/etc/%s.include", CONFIG)) 57 | local function extract_rules(n, a) 58 | local _ipt = ipt_bin 59 | if n == "6" then 60 | _ipt = ip6t_bin 61 | end 62 | local result = "*" .. a 63 | result = result .. "\n" .. sys.exec(_ipt .. '-save -t ' .. a .. ' | grep "PSW2-SERVER" | sed -e "s/^-A \\(INPUT\\)/-I \\1 1/"') 64 | result = result .. "COMMIT" 65 | return result 66 | end 67 | local f, err = io.open("/tmp/etc/" .. CONFIG .. ".include", "a") 68 | if f and err == nil then 69 | if nft_flag == "0" then 70 | f:write(ipt_bin .. '-save -c | grep -v "PSW2-SERVER" | ' .. ipt_bin .. '-restore -c' .. "\n") 71 | f:write(ipt_bin .. '-restore -n <<-EOT' .. "\n") 72 | f:write(extract_rules("4", "filter") .. "\n") 73 | f:write("EOT" .. "\n") 74 | f:write(ip6t_bin .. '-save -c | grep -v "PSW2-SERVER" | ' .. ip6t_bin .. '-restore -c' .. "\n") 75 | f:write(ip6t_bin .. '-restore -n <<-EOT' .. "\n") 76 | f:write(extract_rules("6", "filter") .. "\n") 77 | f:write("EOT" .. "\n") 78 | f:close() 79 | else 80 | f:write("nft -f " .. NFT_INCLUDE_FILE .. "\n") 81 | f:close() 82 | end 83 | end 84 | end 85 | 86 | local function start() 87 | local enabled = tonumber(uci:get(CONFIG, "@global[0]", "enable") or 0) 88 | if enabled == nil or enabled == 0 then 89 | return 90 | end 91 | cmd(string.format("mkdir -p %s %s", CONFIG_PATH, TMP_BIN_PATH)) 92 | cmd(string.format("touch %s", LOG_APP_FILE)) 93 | if nft_flag == "0" then 94 | ipt("-N PSW2-SERVER") 95 | ipt("-I INPUT -j PSW2-SERVER") 96 | ip6t("-N PSW2-SERVER") 97 | ip6t("-I INPUT -j PSW2-SERVER") 98 | else 99 | nft_file, err = io.open(NFT_INCLUDE_FILE, "w") 100 | nft_file:write('#!/usr/sbin/nft -f\n') 101 | nft_file:write('add chain inet fw4 PSW2-SERVER\n') 102 | nft_file:write('flush chain inet fw4 PSW2-SERVER\n') 103 | nft_file:write('insert rule inet fw4 input position 0 jump PSW2-SERVER comment "PSW2-SERVER"\n') 104 | end 105 | uci:foreach(CONFIG, "user", function(user) 106 | local id = user[".name"] 107 | local enable = user.enable 108 | if enable and tonumber(enable) == 1 then 109 | local enable_log = user.log 110 | local log_path = nil 111 | if enable_log and enable_log == "1" then 112 | log_path = CONFIG_PATH .. "/" .. id .. ".log" 113 | else 114 | log_path = nil 115 | end 116 | local remarks = user.remarks 117 | local port = tonumber(user.port) 118 | local bin 119 | local config = {} 120 | local config_file = CONFIG_PATH .. "/" .. id .. ".json" 121 | local udp_forward = 1 122 | local type = user.type or "" 123 | if type == "SS" or type == "SSR" then 124 | if user.custom == "1" and user.config_str then 125 | config = jsonc.parse(api.base64Decode(user.config_str)) 126 | else 127 | config = require(require_dir .. "util_shadowsocks").gen_config_server(user) 128 | end 129 | local udp_param = "" 130 | udp_forward = tonumber(user.udp_forward) or 1 131 | if udp_forward == 1 then 132 | udp_param = "-u" 133 | end 134 | type = type:lower() 135 | bin = ln_run("/usr/bin/" .. type .. "-server", type .. "-server", "-c " .. config_file .. " " .. udp_param, log_path) 136 | elseif type == "SS-Rust" then 137 | if user.custom == "1" and user.config_str then 138 | config = jsonc.parse(api.base64Decode(user.config_str)) 139 | else 140 | config = require(require_dir .. "util_shadowsocks").gen_config_server(user) 141 | end 142 | bin = ln_run("/usr/bin/ssserver", "ssserver", "-c " .. config_file, log_path) 143 | elseif type == "Xray" then 144 | if user.custom == "1" and user.config_str then 145 | config = jsonc.parse(api.base64Decode(user.config_str)) 146 | if log_path then 147 | if not config.log then 148 | config.log = {} 149 | end 150 | config.log.loglevel = user.loglevel 151 | end 152 | else 153 | config = require(require_dir .. "util_xray").gen_config_server(user) 154 | end 155 | bin = ln_run(api.get_app_path("xray"), "xray", "run -c " .. config_file, log_path) 156 | elseif type == "sing-box" then 157 | if user.custom == "1" and user.config_str then 158 | config = jsonc.parse(api.base64Decode(user.config_str)) 159 | if log_path then 160 | if not config.log then 161 | config.log = {} 162 | end 163 | config.log.timestamp = true 164 | config.log.disabled = false 165 | config.log.level = user.loglevel 166 | config.log.output = log_path 167 | end 168 | else 169 | config = require(require_dir .. "util_sing-box").gen_config_server(user) 170 | end 171 | bin = ln_run(api.get_app_path("sing-box"), "sing-box", "run -c " .. config_file, log_path) 172 | elseif type == "Hysteria2" then 173 | if user.custom == "1" and user.config_str then 174 | config = jsonc.parse(api.base64Decode(user.config_str)) 175 | else 176 | config = require(require_dir .. "util_hysteria2").gen_config_server(user) 177 | end 178 | bin = ln_run(api.get_app_path("hysteria"), "hysteria", "-c " .. config_file .. " server", log_path) 179 | end 180 | 181 | if next(config) then 182 | local f, err = io.open(config_file, "w") 183 | if f and err == nil then 184 | f:write(jsonc.stringify(config, 1)) 185 | f:close() 186 | end 187 | log(string.format("%s 生成配置文件并运行 - %s", remarks, config_file)) 188 | end 189 | 190 | if bin then 191 | cmd(bin) 192 | end 193 | 194 | local bind_local = user.bind_local or 0 195 | if bind_local and tonumber(bind_local) ~= 1 and port then 196 | if nft_flag == "0" then 197 | ipt(string.format('-A PSW2-SERVER -p tcp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks)) 198 | ip6t(string.format('-A PSW2-SERVER -p tcp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks)) 199 | if udp_forward == 1 then 200 | ipt(string.format('-A PSW2-SERVER -p udp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks)) 201 | ip6t(string.format('-A PSW2-SERVER -p udp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks)) 202 | end 203 | else 204 | nft_file:write(string.format('add rule inet fw4 PSW2-SERVER meta l4proto tcp tcp dport {%s} counter accept comment "%s"\n', port, remarks)) 205 | if udp_forward == 1 then 206 | nft_file:write(string.format('add rule inet fw4 PSW2-SERVER meta l4proto udp udp dport {%s} counter accept comment "%s"\n', port, remarks)) 207 | end 208 | end 209 | end 210 | end 211 | end) 212 | if nft_flag == "1" then 213 | nft_file:write("add rule inet fw4 PSW2-SERVER return\n") 214 | nft_file:close() 215 | cmd("nft -f " .. NFT_INCLUDE_FILE) 216 | end 217 | gen_include() 218 | end 219 | 220 | local function stop() 221 | cmd(string.format("/bin/busybox top -bn1 | grep -v 'grep' | grep '%s/' | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1", CONFIG_PATH)) 222 | if nft_flag == "0" then 223 | ipt("-D INPUT -j PSW2-SERVER 2>/dev/null") 224 | ipt("-F PSW2-SERVER 2>/dev/null") 225 | ipt("-X PSW2-SERVER 2>/dev/null") 226 | ip6t("-D INPUT -j PSW2-SERVER 2>/dev/null") 227 | ip6t("-F PSW2-SERVER 2>/dev/null") 228 | ip6t("-X PSW2-SERVER 2>/dev/null") 229 | else 230 | local nft_cmd = "handles=$(nft -a list chain inet fw4 input | grep -E \"PSW2-SERVER\" | awk -F '# handle ' '{print$2}')\n for handle in $handles; do\n nft delete rule inet fw4 input handle ${handle} 2>/dev/null\n done" 231 | cmd(nft_cmd) 232 | cmd("nft flush chain inet fw4 PSW2-SERVER 2>/dev/null") 233 | cmd("nft delete chain inet fw4 PSW2-SERVER 2>/dev/null") 234 | end 235 | cmd(string.format("rm -rf %s %s /tmp/etc/%s.include", CONFIG_PATH, LOG_APP_FILE, CONFIG)) 236 | end 237 | 238 | if action then 239 | if action == "start" then 240 | start() 241 | elseif action == "stop" then 242 | stop() 243 | end 244 | end 245 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/passwall2/util_hysteria2.lua: -------------------------------------------------------------------------------- 1 | module("luci.passwall2.util_hysteria2", package.seeall) 2 | local api = require "luci.passwall2.api" 3 | local uci = api.uci 4 | local jsonc = api.jsonc 5 | 6 | function gen_config_server(node) 7 | local config = { 8 | listen = ":" .. node.port, 9 | tls = { 10 | cert = node.tls_certificateFile, 11 | key = node.tls_keyFile, 12 | }, 13 | obfs = (node.hysteria2_obfs) and { 14 | type = "salamander", 15 | salamander = { 16 | password = node.hysteria2_obfs 17 | } 18 | } or nil, 19 | auth = { 20 | type = "password", 21 | password = node.hysteria2_auth_password 22 | }, 23 | bandwidth = (node.hysteria2_up_mbps or node.hysteria2_down_mbps) and { 24 | up = node.hysteria2_up_mbps and node.hysteria2_up_mbps .. " mbps" or nil, 25 | down = node.hysteria2_down_mbps and node.hysteria2_down_mbps .. " mbps" or nil 26 | } or nil, 27 | ignoreClientBandwidth = (node.hysteria2_ignoreClientBandwidth == "1") and true or false, 28 | disableUDP = (node.hysteria2_udp == "0") and true or false, 29 | } 30 | return config 31 | end 32 | 33 | function gen_config(var) 34 | local node_id = var["-node"] 35 | if not node_id then 36 | print("-node 不能为空") 37 | return 38 | end 39 | local node = uci:get_all("passwall2", node_id) 40 | local local_socks_address = var["-local_socks_address"] or "0.0.0.0" 41 | local local_socks_port = var["-local_socks_port"] 42 | local local_socks_username = var["-local_socks_username"] 43 | local local_socks_password = var["-local_socks_password"] 44 | local local_http_address = var["-local_http_address"] or "0.0.0.0" 45 | local local_http_port = var["-local_http_port"] 46 | local local_http_username = var["-local_http_username"] 47 | local local_http_password = var["-local_http_password"] 48 | local server_host = var["-server_host"] or node.address 49 | local server_port = var["-server_port"] or node.port 50 | 51 | if api.is_ipv6(server_host) then 52 | server_host = api.get_ipv6_full(server_host) 53 | end 54 | local server = server_host .. ":" .. server_port 55 | 56 | if (node.hysteria2_hop) then 57 | server = server .. "," .. string.gsub(node.hysteria2_hop, ":", "-") 58 | end 59 | 60 | local config = { 61 | server = server, 62 | transport = { 63 | type = node.protocol or "udp", 64 | udp = { 65 | hopInterval = (function() 66 | local HopIntervalStr = tostring(node.hysteria2_hop_interval or "30s") 67 | local HopInterval = tonumber(HopIntervalStr:match("^%d+")) 68 | if HopInterval and HopInterval >= 5 then 69 | return tostring(HopInterval) .. "s" 70 | end 71 | return "30s" 72 | end)(), 73 | } 74 | }, 75 | obfs = (node.hysteria2_obfs) and { 76 | type = "salamander", 77 | salamander = { 78 | password = node.hysteria2_obfs 79 | } 80 | } or nil, 81 | auth = node.hysteria2_auth_password, 82 | tls = { 83 | sni = node.tls_serverName, 84 | insecure = (node.tls_allowInsecure == "1") and true or false, 85 | pinSHA256 = (node.hysteria2_tls_pinSHA256) and node.hysteria2_tls_pinSHA256 or nil, 86 | }, 87 | quic = { 88 | initStreamReceiveWindow = (node.hysteria2_recv_window) and tonumber(node.hysteria2_recv_window) or nil, 89 | initConnReceiveWindow = (node.hysteria2_recv_window_conn) and tonumber(node.hysteria2_recv_window_conn) or nil, 90 | maxIdleTimeout = (function() 91 | local timeoutStr = tostring(node.hysteria2_idle_timeout or "") 92 | local timeout = tonumber(timeoutStr:match("^%d+")) 93 | if timeout and timeout >= 4 and timeout <= 120 then 94 | return tostring(timeout) .. "s" 95 | end 96 | return nil 97 | end)(), 98 | disablePathMTUDiscovery = (node.hysteria2_disable_mtu_discovery) and true or false, 99 | }, 100 | bandwidth = (node.hysteria2_up_mbps or node.hysteria2_down_mbps) and { 101 | up = node.hysteria2_up_mbps and node.hysteria2_up_mbps .. " mbps" or nil, 102 | down = node.hysteria2_down_mbps and node.hysteria2_down_mbps .. " mbps" or nil 103 | } or nil, 104 | fast_open = (node.fast_open == "1") and true or false, 105 | lazy = (node.hysteria2_lazy_start == "1") and true or false, 106 | socks5 = (local_socks_address and local_socks_port) and { 107 | listen = local_socks_address .. ":" .. local_socks_port, 108 | username = (local_socks_username and local_socks_password) and local_socks_username or nil, 109 | password = (local_socks_username and local_socks_password) and local_socks_password or nil, 110 | disableUDP = false, 111 | } or nil, 112 | http = (local_http_address and local_http_port) and { 113 | listen = local_http_address .. ":" .. local_http_port, 114 | username = (local_http_username and local_http_password) and local_http_username or nil, 115 | password = (local_http_username and local_http_password) and local_http_password or nil, 116 | } or nil 117 | } 118 | 119 | return jsonc.stringify(config, 1) 120 | end 121 | 122 | _G.gen_config = gen_config 123 | 124 | if arg[1] then 125 | local func =_G[arg[1]] 126 | if func then 127 | print(func(api.get_function_args(arg))) 128 | end 129 | end 130 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/passwall2/util_naiveproxy.lua: -------------------------------------------------------------------------------- 1 | module("luci.passwall2.util_navieproxy", package.seeall) 2 | local api = require "luci.passwall2.api" 3 | local uci = api.uci 4 | local jsonc = api.jsonc 5 | 6 | function gen_config(var) 7 | local node_id = var["-node"] 8 | if not node_id then 9 | print("-node 不能为空") 10 | return 11 | end 12 | local node = uci:get_all("passwall2", node_id) 13 | local run_type = var["-run_type"] 14 | local local_addr = var["-local_addr"] 15 | local local_port = var["-local_port"] 16 | local server_host = var["-server_host"] or node.address 17 | local server_port = var["-server_port"] or node.port 18 | 19 | if api.is_ipv6(server_host) then 20 | server_host = api.get_ipv6_full(server_host) 21 | end 22 | local server = server_host .. ":" .. server_port 23 | 24 | local config = { 25 | listen = run_type .. "://" .. local_addr .. ":" .. local_port, 26 | proxy = node.protocol .. "://" .. node.username .. ":" .. node.password .. "@" .. server 27 | } 28 | 29 | return jsonc.stringify(config, 1) 30 | end 31 | 32 | _G.gen_config = gen_config 33 | 34 | if arg[1] then 35 | local func =_G[arg[1]] 36 | if func then 37 | print(func(api.get_function_args(arg))) 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/passwall2/util_shadowsocks.lua: -------------------------------------------------------------------------------- 1 | module("luci.passwall2.util_shadowsocks", package.seeall) 2 | local api = require "luci.passwall2.api" 3 | local uci = api.uci 4 | local jsonc = api.jsonc 5 | 6 | function gen_config_server(node) 7 | local config = {} 8 | config.server_port = tonumber(node.port) 9 | config.password = node.password 10 | config.timeout = tonumber(node.timeout) 11 | config.fast_open = (node.tcp_fast_open and node.tcp_fast_open == "1") and true or false 12 | config.method = node.method 13 | 14 | if node.type == "SS-Rust" then 15 | config.server = "::" 16 | config.mode = "tcp_and_udp" 17 | else 18 | config.server = {"[::0]", "0.0.0.0"} 19 | end 20 | 21 | if node.type == "SSR" then 22 | config.protocol = node.protocol 23 | config.protocol_param = node.protocol_param 24 | config.obfs = node.obfs 25 | config.obfs_param = node.obfs_param 26 | end 27 | 28 | return config 29 | end 30 | 31 | 32 | function gen_config(var) 33 | local node_id = var["-node"] 34 | if not node_id then 35 | print("-node 不能为空") 36 | return 37 | end 38 | local node = uci:get_all("passwall2", node_id) 39 | local server_host = var["-server_host"] or node.address 40 | local server_port = var["-server_port"] or node.port 41 | local local_addr = var["-local_addr"] 42 | local local_port = var["-local_port"] 43 | local mode = var["-mode"] 44 | local local_socks_address = var["-local_socks_address"] or "0.0.0.0" 45 | local local_socks_port = var["-local_socks_port"] 46 | local local_socks_username = var["-local_socks_username"] 47 | local local_socks_password = var["-local_socks_password"] 48 | local local_http_address = var["-local_http_address"] or "0.0.0.0" 49 | local local_http_port = var["-local_http_port"] 50 | local local_http_username = var["-local_http_username"] 51 | local local_http_password = var["-local_http_password"] 52 | 53 | if api.is_ipv6(server_host) then 54 | server_host = api.get_ipv6_only(server_host) 55 | end 56 | local server = server_host 57 | 58 | local config = { 59 | server = server, 60 | server_port = tonumber(server_port), 61 | local_address = local_addr, 62 | local_port = tonumber(local_port), 63 | password = node.password, 64 | method = node.method, 65 | timeout = tonumber(node.timeout), 66 | fast_open = (node.tcp_fast_open and node.tcp_fast_open == "true") and true or false, 67 | reuse_port = true 68 | } 69 | 70 | if node.type == "SS" then 71 | if node.plugin and node.plugin ~= "none" then 72 | config.plugin = node.plugin 73 | config.plugin_opts = node.plugin_opts or nil 74 | end 75 | config.mode = mode 76 | elseif node.type == "SSR" then 77 | config.protocol = node.protocol 78 | config.protocol_param = node.protocol_param 79 | config.obfs = node.obfs 80 | config.obfs_param = node.obfs_param 81 | elseif node.type == "SS-Rust" then 82 | config = { 83 | servers = { 84 | { 85 | address = server, 86 | port = tonumber(server_port), 87 | method = node.method, 88 | password = node.password, 89 | timeout = tonumber(node.timeout), 90 | plugin = (node.plugin and node.plugin ~= "none") and node.plugin or nil, 91 | plugin_opts = (node.plugin and node.plugin ~= "none") and node.plugin_opts or nil 92 | } 93 | }, 94 | locals = {}, 95 | fast_open = (node.tcp_fast_open and node.tcp_fast_open == "true") and true or false 96 | } 97 | if local_socks_address and local_socks_port then 98 | table.insert(config.locals, { 99 | local_address = local_socks_address, 100 | local_port = tonumber(local_socks_port), 101 | mode = "tcp_and_udp" 102 | }) 103 | end 104 | if local_http_address and local_http_port then 105 | table.insert(config.locals, { 106 | protocol = "http", 107 | local_address = local_http_address, 108 | local_port = tonumber(local_http_port) 109 | }) 110 | end 111 | end 112 | 113 | return jsonc.stringify(config, 1) 114 | end 115 | 116 | _G.gen_config = gen_config 117 | 118 | if arg[1] then 119 | local func =_G[arg[1]] 120 | if func then 121 | print(func(api.get_function_args(arg))) 122 | end 123 | end 124 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/passwall2/util_tuic.lua: -------------------------------------------------------------------------------- 1 | module("luci.passwall2.util_tuic", package.seeall) 2 | local api = require "luci.passwall2.api" 3 | local uci = api.uci 4 | local json = api.jsonc 5 | 6 | function gen_config(var) 7 | local node_id = var["-node"] 8 | if not node_id then 9 | print("-node 不能为空") 10 | return 11 | end 12 | local node = uci:get_all("passwall2", node_id) 13 | local local_addr = var["-local_addr"] 14 | local local_port = var["-local_port"] 15 | local server_host = var["-server_host"] or node.address 16 | local server_port = var["-server_port"] or node.port 17 | local loglevel = var["-loglevel"] or "warn" 18 | 19 | local tuic= { 20 | relay = { 21 | server = server_host .. ":" .. server_port, 22 | ip = node.tuic_ip, 23 | uuid = node.uuid, 24 | password = node.tuic_password, 25 | -- certificates = node.tuic_certificate and { node.tuic_certpath } or nil, 26 | udp_relay_mode = node.tuic_udp_relay_mode, 27 | congestion_control = node.tuic_congestion_control, 28 | heartbeat = node.tuic_heartbeat .. "s", 29 | timeout = node.tuic_timeout .. "s", 30 | gc_interval = node.tuic_gc_interval .. "s", 31 | gc_lifetime = node.tuic_gc_lifetime .. "s", 32 | alpn = node.tuic_tls_alpn, 33 | disable_sni = (node.tuic_disable_sni == "1"), 34 | zero_rtt_handshake = (node.tuic_zero_rtt_handshake == "1"), 35 | send_window = tonumber(node.tuic_send_window), 36 | receive_window = tonumber(node.tuic_receive_window) 37 | }, 38 | ["local"] = { 39 | server = "[::]:" .. local_port, 40 | username = node.tuic_socks_username, 41 | password = node.tuic_socks_password, 42 | dual_stack = (node.tuic_dual_stack == "1") and true or false, 43 | max_packet_size = tonumber(node.tuic_max_package_size) 44 | }, 45 | log_level = loglevel 46 | } 47 | return json.stringify(tuic, 1) 48 | end 49 | 50 | _G.gen_config = gen_config 51 | 52 | if arg[1] then 53 | local func =_G[arg[1]] 54 | if func then 55 | print(func(api.get_function_args(arg))) 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/view/passwall2/app_update/app_version.htm: -------------------------------------------------------------------------------- 1 | <% 2 | local api = require "luci.passwall2.api" 3 | local com = require "luci.passwall2.com" 4 | local version = {} 5 | -%> 6 | 7 | 179 | 180 |
181 | 182 |
183 |
184 | 【 <%=api.get_version()%> 】 185 | 187 | 188 |
189 |
190 |
191 | 192 | <%for k, v in pairs(com) do 193 | version[k] = api.get_app_version(k)%> 194 |
195 | 198 |
199 |
200 | 【 <%=version[k] ~="" and version[k] or translate("Null") %> 】 201 | 203 | 205 | 206 |
207 |
208 |
209 | <%end%> 210 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/view/passwall2/cbi/hidevalue.htm: -------------------------------------------------------------------------------- 1 |
" data-index="<%=self.index%>" data-depends="<%=pcdata(self:deplist2json(section))%>" style="display: none !important"> 2 | " /> 3 |
4 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/view/passwall2/global/faq.htm: -------------------------------------------------------------------------------- 1 | <% 2 | local api = require "luci.passwall2.api" 3 | -%> 4 | 31 |
32 |
33 | 44 |
45 |
46 |
47 | 48 | 67 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/view/passwall2/global/footer.htm: -------------------------------------------------------------------------------- 1 | <% 2 | local api = require "luci.passwall2.api" 3 | -%> 4 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/view/passwall2/haproxy/status.htm: -------------------------------------------------------------------------------- 1 | <% 2 | local api = require "luci.passwall2.api" 3 | local console_port = api.uci_get_type("global_haproxy", "console_port", "") 4 | -%> 5 |

6 | 7 | 27 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/view/passwall2/log/backup_restore.htm: -------------------------------------------------------------------------------- 1 | <% 2 | local api = require "luci.passwall2.api" 3 | -%> 4 | 5 |
6 | 7 |
8 | 9 |
10 |
11 | 12 |
13 | 14 |
15 | 16 |
17 |
18 | 19 |
20 | 21 |
22 | 23 |
24 |
25 | 26 | 41 | 42 | 79 | 80 | 133 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/view/passwall2/log/log.htm: -------------------------------------------------------------------------------- 1 | <% 2 | local api = require "luci.passwall2.api" 3 | -%> 4 | 27 |
28 | 29 | 30 |
31 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/view/passwall2/node_list/link_add_node.htm: -------------------------------------------------------------------------------- 1 | <% 2 | local api = require "luci.passwall2.api" 3 | -%> 4 | 5 | 76 | 77 | 90 | 91 |
92 | 93 |
94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 |
103 |
104 |
105 | 106 | 157 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/view/passwall2/rule/rule_version.htm: -------------------------------------------------------------------------------- 1 | <% 2 | local api = require "luci.passwall2.api" 3 | 4 | local geoip_update = api.uci_get_type("global_rules", "geoip_update", "1") == "1" and "checked='checked'" or "" 5 | local geosite_update = api.uci_get_type("global_rules", "geosite_update", "1") == "1" and "checked='checked'" or "" 6 | -%> 7 | 8 | 39 |
40 | 43 |
44 |
45 | 49 | 53 | 54 |
55 |
56 |
57 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/view/passwall2/server/log.htm: -------------------------------------------------------------------------------- 1 | <% 2 | local api = require "luci.passwall2.api" 3 | -%> 4 | 28 |
29 | 30 | <%:Logs%> 31 | 32 | 33 | 34 |
-------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/view/passwall2/server/users_list_status.htm: -------------------------------------------------------------------------------- 1 | <% 2 | local api = require "luci.passwall2.api" 3 | -%> 4 | -------------------------------------------------------------------------------- /luci-app-passwall2/luasrc/view/passwall2/socks_auto_switch/btn.htm: -------------------------------------------------------------------------------- 1 | <% 2 | local api = require "luci.passwall2.api" 3 | -%> 4 | 5 | 22 |
" data-index="<%=self.index%>" data-depends="<%=pcdata(self:deplist2json(section))%>"> 23 | 24 |
25 | 26 | 27 |
28 |
29 | -------------------------------------------------------------------------------- /luci-app-passwall2/po/zh_Hans: -------------------------------------------------------------------------------- 1 | zh-cn -------------------------------------------------------------------------------- /luci-app-passwall2/root/etc/config/passwall2_server: -------------------------------------------------------------------------------- 1 | 2 | config global 'global' 3 | option enable '0' 4 | 5 | -------------------------------------------------------------------------------- /luci-app-passwall2/root/etc/hotplug.d/iface/98-passwall2: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | [[ "$ACTION" == "ifup" && $(uci get "passwall2.@global[0].enabled") == "1" ]] && [ -f /var/lock/passwall2_ready.lock ] && { 4 | default_device=$(ip route | grep default | awk -F 'dev ' '{print $2}' | awk '{print $1}') 5 | [ "$default_device" == "$DEVICE" ] && { 6 | LOCK_FILE_DIR=/var/lock 7 | [ ! -d ${LOCK_FILE_DIR} ] && mkdir -p ${LOCK_FILE_DIR} 8 | LOCK_FILE="${LOCK_FILE_DIR}/passwall2_ifup.lock" 9 | if [ -s ${LOCK_FILE} ]; then 10 | SPID=$(cat ${LOCK_FILE}) 11 | if [ -e /proc/${SPID}/status ]; then 12 | exit 1 13 | fi 14 | cat /dev/null > ${LOCK_FILE} 15 | fi 16 | echo $$ > ${LOCK_FILE} 17 | 18 | /etc/init.d/passwall2 restart >/dev/null 2>&1 & 19 | logger -p notice -t network -s "passwall2: restart when $INTERFACE ifup" 20 | 21 | rm -rf ${LOCK_FILE} 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /luci-app-passwall2/root/etc/init.d/passwall2: -------------------------------------------------------------------------------- 1 | #!/bin/sh /etc/rc.common 2 | 3 | START=99 4 | STOP=15 5 | 6 | CONFIG=passwall2 7 | APP_FILE=/usr/share/${CONFIG}/app.sh 8 | LOCK_FILE_DIR=/var/lock 9 | LOCK_FILE=${LOCK_FILE_DIR}/${CONFIG}.lock 10 | 11 | set_lock() { 12 | [ ! -d "$LOCK_FILE_DIR" ] && mkdir -p $LOCK_FILE_DIR 13 | exec 999>"$LOCK_FILE" 14 | flock -xn 999 15 | } 16 | 17 | unset_lock() { 18 | flock -u 999 19 | rm -rf "$LOCK_FILE" 20 | } 21 | 22 | unlock() { 23 | failcount=1 24 | while [ "$failcount" -le 10 ]; do 25 | if [ -f "$LOCK_FILE" ]; then 26 | let "failcount++" 27 | sleep 1s 28 | [ "$failcount" -ge 10 ] && unset_lock 29 | else 30 | break 31 | fi 32 | done 33 | } 34 | 35 | boot_func() { 36 | local delay=$(uci -q get ${CONFIG}.@global_delay[0].start_delay || echo 1) 37 | if [ "$delay" -gt 0 ]; then 38 | $APP_FILE echolog "执行启动延时 $delay 秒后再启动!" 39 | sleep $delay 40 | fi 41 | restart 42 | touch ${LOCK_FILE_DIR}/${CONFIG}_ready.lock 43 | } 44 | 45 | boot() { 46 | boot_func >/dev/null 2>&1 & 47 | } 48 | 49 | start() { 50 | set_lock 51 | [ $? == 1 ] && $APP_FILE echolog "脚本已经在运行,不重复运行,退出." && exit 0 52 | $APP_FILE start 53 | unset_lock 54 | } 55 | 56 | stop() { 57 | unlock 58 | set_lock 59 | [ $? == 1 ] && $APP_FILE echolog "停止脚本等待超时,不重复运行,退出." && exit 0 60 | $APP_FILE stop 61 | unset_lock 62 | } 63 | 64 | restart() { 65 | set_lock 66 | [ $? == 1 ] && $APP_FILE echolog "脚本已经在运行,不重复运行,退出." && exit 0 67 | $APP_FILE stop 68 | $APP_FILE start 69 | unset_lock 70 | } 71 | -------------------------------------------------------------------------------- /luci-app-passwall2/root/etc/init.d/passwall2_server: -------------------------------------------------------------------------------- 1 | #!/bin/sh /etc/rc.common 2 | 3 | START=99 4 | 5 | start() { 6 | lua /usr/lib/lua/luci/passwall2/server_app.lua start 7 | } 8 | 9 | stop() { 10 | lua /usr/lib/lua/luci/passwall2/server_app.lua stop 11 | } 12 | 13 | restart() { 14 | stop 15 | start 16 | } -------------------------------------------------------------------------------- /luci-app-passwall2/root/etc/uci-defaults/luci-passwall2: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | uci -q batch <<-EOF >/dev/null 4 | set dhcp.@dnsmasq[0].localuse=1 5 | commit dhcp 6 | [ -e "/etc/config/ucitrack" ] && { 7 | delete ucitrack.@passwall2[-1] 8 | add ucitrack passwall2 9 | set ucitrack.@passwall2[-1].init=passwall2 10 | commit ucitrack 11 | } 12 | delete firewall.passwall2 13 | set firewall.passwall2=include 14 | set firewall.passwall2.type=script 15 | set firewall.passwall2.path=/var/etc/passwall2.include 16 | set firewall.passwall2.reload=1 17 | commit firewall 18 | [ -e "/etc/config/ucitrack" ] && { 19 | delete ucitrack.@passwall2_server[-1] 20 | add ucitrack passwall2_server 21 | set ucitrack.@passwall2_server[-1].init=passwall2_server 22 | commit ucitrack 23 | } 24 | delete firewall.passwall2_server 25 | set firewall.passwall2_server=include 26 | set firewall.passwall2_server.type=script 27 | set firewall.passwall2_server.path=/var/etc/passwall2_server.include 28 | set firewall.passwall2_server.reload=1 29 | commit firewall 30 | set uhttpd.main.max_requests=50 31 | commit uhttpd 32 | EOF 33 | 34 | [ ! -s "/etc/config/passwall2" ] && cp -f /usr/share/passwall2/0_default_config /etc/config/passwall2 35 | 36 | chmod +x /usr/share/passwall2/*.sh 37 | 38 | [ -e "/etc/config/passwall2_show" ] && rm -rf /etc/config/passwall2_show 39 | 40 | [ "$(uci -q get passwall2.@global_xray[0].sniffing)" == "1" ] && [ "$(uci -q get passwall2.@global_xray[0].route_only)" != "1" ] && uci -q set passwall2.@global_xray[0].sniffing_override_dest=1 41 | uci -q delete passwall2.@global_xray[0].sniffing 42 | uci -q delete passwall2.@global_xray[0].route_only 43 | uci -q commit passwall2 44 | 45 | rm -f /tmp/luci-indexcache 46 | rm -rf /tmp/luci-modulecache/ 47 | killall -HUP rpcd 2>/dev/null 48 | 49 | exit 0 50 | -------------------------------------------------------------------------------- /luci-app-passwall2/root/usr/share/passwall2/0_default_config: -------------------------------------------------------------------------------- 1 | 2 | config global 3 | option enabled '0' 4 | option node_socks_port '1070' 5 | option localhost_proxy '1' 6 | option client_proxy '1' 7 | option socks_enabled '0' 8 | option acl_enable '0' 9 | option node 'myshunt' 10 | option direct_dns_protocol 'auto' 11 | option direct_dns_query_strategy 'UseIP' 12 | option remote_dns_protocol 'tcp' 13 | option remote_dns '1.1.1.1' 14 | option remote_dns_query_strategy 'UseIPv4' 15 | option dns_hosts 'cloudflare-dns.com 1.1.1.1 16 | dns.google.com 8.8.8.8' 17 | option log_node '1' 18 | option loglevel 'error' 19 | 20 | config global_haproxy 21 | option balancing_enable '0' 22 | 23 | config global_delay 24 | option start_daemon '1' 25 | option start_delay '60' 26 | 27 | config global_forwarding 28 | option tcp_no_redir_ports 'disable' 29 | option udp_no_redir_ports 'disable' 30 | option tcp_redir_ports '22,25,53,143,465,587,853,993,995,80,443' 31 | option udp_redir_ports '1:65535' 32 | option accept_icmp '0' 33 | option use_nft '0' 34 | option tcp_proxy_way 'redirect' 35 | option ipv6_tproxy '0' 36 | 37 | config global_xray 38 | option sniffing_override_dest '0' 39 | 40 | config global_other 41 | option auto_detection_time 'tcping' 42 | option show_node_info '0' 43 | 44 | config global_rules 45 | option auto_update '0' 46 | option geosite_update '1' 47 | option geoip_update '1' 48 | option v2ray_location_asset '/usr/share/v2ray/' 49 | option enable_geoview '1' 50 | 51 | config global_app 52 | option xray_file '/usr/bin/xray' 53 | option hysteria_file '/usr/bin/hysteria' 54 | option singbox_file '/usr/bin/sing-box' 55 | 56 | config global_subscribe 57 | option filter_keyword_mode '1' 58 | list filter_discard_list '距离下次重置剩余' 59 | list filter_discard_list '套餐到期' 60 | list filter_discard_list '过期时间' 61 | list filter_discard_list '剩余流量' 62 | list filter_discard_list 'QQ群' 63 | list filter_discard_list '官网' 64 | 65 | config global_singbox 66 | option sniff_override_destination '0' 67 | option geoip_path '/usr/share/singbox/geoip.db' 68 | option geoip_url 'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.db' 69 | option geosite_path '/usr/share/singbox/geosite.db' 70 | option geosite_url 'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.db' 71 | 72 | config nodes 'myshunt' 73 | option remarks '分流总节点' 74 | option type 'Xray' 75 | option protocol '_shunt' 76 | option DirectGame '_direct' 77 | option ProxyGame '_default' 78 | option Direct '_direct' 79 | option GooglePlay '_default' 80 | option Proxy '_default' 81 | option China '_direct' 82 | option QUIC '_blackhole' 83 | option default_node '_direct' 84 | option domainStrategy 'IPOnDemand' 85 | option domainMatcher 'hybrid' 86 | 87 | config shunt_rules 'DirectGame' 88 | option remarks 'DirectGame' 89 | option network 'tcp,udp' 90 | option domain_list '# steam直连域名获取国内CDN走国内线路下载 91 | cm.steampowered.com 92 | steamserver.net 93 | 94 | # steam国内CDN华为云 95 | steampipe.steamcontent.tnkjmec.com 96 | # steam国内CDN白山云 97 | st.dl.eccdnx.com 98 | st.dl.bscstorage.net 99 | st.dl.pinyuncloud.com 100 | # steam国内CDN新流云(原金山云)(支持ipv6) 101 | dl.steam.clngaa.com 102 | # steam国内CDN网宿 103 | cdn.mileweb.cs.steampowered.com.8686c.com 104 | cdn-ws.content.steamchina.com 105 | # steam国内CDN腾讯云 (蒸汽中国独占) 106 | cdn-qc.content.steamchina.com 107 | # steam国内CDN阿里云(支持ipv6) 108 | cdn-ali.content.steamchina.com 109 | xz.pphimalayanrt.com 110 | lv.queniujq.cn 111 | alibaba.cdn.steampipe.steamcontent.com 112 | 113 | # 国内游戏geosite域名 114 | geosite:category-games@cn 115 | ' 116 | 117 | option ip_list '# steam直连IP 118 | 45.121.184.0/24 119 | 103.10.124.0/23 120 | 103.28.54.0/24 121 | 146.66.152.0/24 122 | 146.66.155.0/24 123 | 153.254.86.0/24 124 | 155.133.224.0/22 125 | 155.133.230.0/24 126 | 155.133.232.0/23 127 | 155.133.234.0/24 128 | 155.133.236.0/22 129 | 155.133.240.0/23 130 | 155.133.244.0/23 131 | 155.133.246.0/24 132 | 155.133.248.0/21 133 | 162.254.192.0/21 134 | 185.25.182.0/23 135 | 190.217.32.0/22 136 | 192.69.96.0/22 137 | 205.196.6.0/24 138 | 208.64.200.0/22 139 | 208.78.164.0/22 140 | 205.185.194.0/24' 141 | 142 | config shunt_rules 'ProxyGame' 143 | option remarks 'ProxyGame' 144 | option domain_list '# steam 商店/客服/聊天/网页布局/API/二维码 代理URL 145 | steamcommunity.com 146 | www.steamcommunity.com 147 | store.steampowered.com 148 | checkout.steampowered.com 149 | api.steampowered.com 150 | help.steampowered.com 151 | login.steampowered.com 152 | store.akamai.steamstatic.com 153 | steambroadcast.akamaized.net 154 | steamvideo-a.akamaihd.net 155 | steamusercontent-a.akamaihd.net 156 | steamstore-a.akamaihd.net 157 | steamcommunity-a.akamaihd.net 158 | steamcdn-a.akamaihd.net 159 | steamuserimages-a.akamaihd.net 160 | community.akamai.steamstatic.com 161 | avatars.akamai.steamstatic.com 162 | community.steamstatic.com 163 | cdn.akamai.steamstatic.com 164 | avatars.steamstatic.com 165 | shared.akamai.steamstatic.com 166 | clan.akamai.steamstatic.com 167 | cdn.cloudflare.steamstatic.com 168 | community.cloudflare.steamstatic.com 169 | store.cloudflare.steamstatic.com 170 | avatars.cloudflare.steamstatic.com 171 | clan.cloudflare.steamstatic.com 172 | shared.cloudflare.steamstatic.com 173 | steam-chat.com 174 | steamcloud-ugc.storage.googleapis.com 175 | steamcloud-eu-ams.storage.googleapis.com 176 | steamcloud-eu-fra.storage.googleapis.com 177 | steamcloud-finland.storage.googleapis.com 178 | steamcloud-saopaulo.storage.googleapis.com 179 | steamcloud-singapore.storage.googleapis.com 180 | steamcloud-sydney.storage.googleapis.com 181 | steamcloud-taiwan.storage.googleapis.com 182 | steamcloud-eu.storage.googleapis.com 183 | 184 | geosite:category-games' 185 | 186 | config shunt_rules 'Direct' 187 | option network 'tcp,udp' 188 | option remarks 'Direct' 189 | option ip_list 'geoip:private 190 | 114.114.114.114 191 | 114.114.115.115 192 | 223.5.5.5 193 | 223.6.6.6 194 | 119.29.29.29 195 | 180.76.76.76 196 | ' 197 | option domain_list 'apple.com 198 | microsoft.com 199 | dyndns.com 200 | steamcontent.com 201 | dl.steam.clngaa.com 202 | dl.steam.ksyna.com 203 | st.dl.bscstorage.net 204 | st.dl.eccdnx.com 205 | st.dl.pinyuncloud.com 206 | cdn.mileweb.cs.steampowered.com.8686c.com 207 | cdn-ws.content.steamchina.com 208 | cdn-qc.content.steamchina.com 209 | cdn-ali.content.steamchina.com 210 | epicgames-download1-1251447533.file.myqcloud.com' 211 | 212 | config shunt_rules 'GooglePlay' 213 | option remarks 'GooglePlay' 214 | option network 'tcp,udp' 215 | option domain_list 'domain:googleapis.cn 216 | domain:googleapis.com 217 | domain:xn--ngstr-lra8j.com' 218 | 219 | config shunt_rules 'Netflix' 220 | option remarks 'Netflix' 221 | option network 'tcp,udp' 222 | option domain_list 'geosite:netflix' 223 | 224 | config shunt_rules 'OpenAI' 225 | option remarks 'OpenAI' 226 | option network 'tcp,udp' 227 | option domain_list 'geosite:openai' 228 | 229 | config shunt_rules 'Proxy' 230 | option network 'tcp,udp' 231 | option remarks 'Proxy' 232 | option domain_list 'geosite:geolocation-!cn' 233 | option ip_list '149.154.160.0/20 234 | 91.108.4.0/22 235 | 91.108.56.0/24 236 | 109.239.140.0/24 237 | 67.198.55.0/24 238 | 8.8.4.4 239 | 8.8.8.8 240 | 208.67.222.222 241 | 208.67.220.220 242 | 1.1.1.1 243 | 1.1.1.2 244 | 1.0.0.1 245 | 9.9.9.9 246 | 149.112.112.112 247 | 2001:67c:4e8::/48 248 | 2001:b28:f23c::/48 249 | 2001:b28:f23d::/48 250 | 2001:b28:f23f::/48 251 | 2001:b28:f242::/48 252 | 2001:4860:4860::8888 253 | 2001:4860:4860::8844 254 | 2606:4700:4700::1111 255 | 2606:4700:4700::1001 256 | ' 257 | 258 | config shunt_rules 'China' 259 | option remarks 'China' 260 | option network 'tcp,udp' 261 | option ip_list 'geoip:cn' 262 | option domain_list 'geosite:cn' 263 | 264 | config shunt_rules 'QUIC' 265 | option remarks 'QUIC' 266 | option port '443' 267 | option network 'udp' 268 | 269 | config shunt_rules 'UDP' 270 | option remarks 'UDP' 271 | option network 'udp' 272 | -------------------------------------------------------------------------------- /luci-app-passwall2/root/usr/share/passwall2/domains_excluded: -------------------------------------------------------------------------------- 1 | courier.push.apple.com 2 | rbsxbxp-mim.vivox.com 3 | rbsxbxp.www.vivox.com 4 | rbsxbxp-ws.vivox.com 5 | rbspsxp.www.vivox.com 6 | rbspsxp-mim.vivox.com 7 | rbspsxp-ws.vivox.com 8 | rbswxp.www.vivox.com 9 | rbswxp-mim.vivox.com 10 | disp-rbspsp-5-1.vivox.com 11 | disp-rbsxbp-5-1.vivox.com 12 | proxy.rbsxbp.vivox.com 13 | proxy.rbspsp.vivox.com 14 | proxy.rbswp.vivox.com 15 | rbswp.vivox.com 16 | rbsxbp.vivox.com 17 | rbspsp.vivox.com 18 | rbspsp.www.vivox.com 19 | rbswp.www.vivox.com 20 | rbsxbp.www.vivox.com 21 | rbsxbxp.vivox.com 22 | rbspsxp.vivox.com 23 | rbswxp.vivox.com 24 | Mijia Cloud 25 | dlg.io.mi.com 26 | marscdn.c2c.wechat.com 27 | -------------------------------------------------------------------------------- /luci-app-passwall2/root/usr/share/passwall2/haproxy.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | local api = require ("luci.passwall2.api") 4 | local appname = "passwall2" 5 | local fs = api.fs 6 | local jsonc = api.jsonc 7 | local uci = api.uci 8 | local sys = api.sys 9 | 10 | local log = function(...) 11 | api.log(...) 12 | end 13 | 14 | function get_ip_port_from(str) 15 | local result_port = sys.exec("echo -n " .. str .. " | sed -n 's/^.*[:#]\\([0-9]*\\)$/\\1/p'") 16 | local result_ip = sys.exec(string.format("__host=%s;__varport=%s;", str, result_port) .. "echo -n ${__host%%${__varport:+[:#]${__varport}*}}") 17 | return result_ip, result_port 18 | end 19 | 20 | local new_port 21 | local function get_new_port() 22 | if new_port then 23 | new_port = tonumber(sys.exec(string.format("echo -n $(/usr/share/%s/app.sh get_new_port %s tcp)", appname, new_port + 1))) 24 | else 25 | new_port = tonumber(sys.exec(string.format("echo -n $(/usr/share/%s/app.sh get_new_port auto tcp)", appname))) 26 | end 27 | return new_port 28 | end 29 | 30 | local var = api.get_args(arg) 31 | local haproxy_path = var["-path"] 32 | local haproxy_conf = var["-conf"] 33 | local haproxy_dns = var["-dns"] or "119.29.29.29:53,223.5.5.5:53" 34 | 35 | local cpu_thread = sys.exec('echo -n $(cat /proc/cpuinfo | grep "processor" | wc -l)') or "1" 36 | local health_check_type = uci:get(appname, "@global_haproxy[0]", "health_check_type") or "tcp" 37 | local health_check_inter = uci:get(appname, "@global_haproxy[0]", "health_check_inter") or "10" 38 | local console_port = uci:get(appname, "@global_haproxy[0]", "console_port") 39 | local bind_local = uci:get(appname, "@global_haproxy[0]", "bind_local") or "0" 40 | local bind_address = "0.0.0.0" 41 | if bind_local == "1" then bind_address = "127.0.0.1" end 42 | 43 | log("HAPROXY 负载均衡:") 44 | log(string.format(" * 控制台端口:%s", console_port)) 45 | fs.mkdir(haproxy_path) 46 | local haproxy_file = haproxy_path .. "/" .. haproxy_conf 47 | 48 | local f_out = io.open(haproxy_file, "a") 49 | 50 | local haproxy_config = [[ 51 | global 52 | daemon 53 | log 127.0.0.1 local2 54 | maxconn 60000 55 | stats socket {{path}}/haproxy.sock 56 | nbthread {{nbthread}} 57 | external-check 58 | insecure-fork-wanted 59 | 60 | defaults 61 | mode tcp 62 | log global 63 | option tcplog 64 | option dontlognull 65 | option http-server-close 66 | #option forwardfor except 127.0.0.0/8 67 | option redispatch 68 | retries 2 69 | timeout http-request 10s 70 | timeout queue 1m 71 | timeout connect 10s 72 | timeout client 1m 73 | timeout server 1m 74 | timeout http-keep-alive 10s 75 | timeout check 10s 76 | maxconn 3000 77 | 78 | resolvers mydns 79 | resolve_retries 1 80 | timeout resolve 5s 81 | hold valid 600s 82 | {{dns}} 83 | ]] 84 | 85 | haproxy_config = haproxy_config:gsub("{{path}}", haproxy_path) 86 | haproxy_config = haproxy_config:gsub("{{nbthread}}", cpu_thread) 87 | 88 | local mydns = "" 89 | local index = 0 90 | string.gsub(haproxy_dns, '[^' .. "," .. ']+', function(w) 91 | index = index + 1 92 | local s = w:gsub("#", ":") 93 | if not s:find(":") then 94 | s = s .. ":53" 95 | end 96 | mydns = mydns .. (index > 1 and "\n" or "") .. " " .. string.format("nameserver dns%s %s", index, s) 97 | end) 98 | haproxy_config = haproxy_config:gsub("{{dns}}", mydns) 99 | 100 | f_out:write(haproxy_config) 101 | 102 | local listens = {} 103 | 104 | uci:foreach(appname, "haproxy_config", function(t) 105 | if t.enabled == "1" then 106 | local server_remark 107 | local server_address 108 | local server_port 109 | local lbss = t.lbss 110 | local listen_port = tonumber(t.haproxy_port) or 0 111 | local server_node = uci:get_all(appname, lbss) 112 | if server_node and server_node.address and server_node.port then 113 | server_remark = server_node.address .. ":" .. server_node.port 114 | server_address = server_node.address 115 | server_port = server_node.port 116 | t.origin_address = server_address 117 | t.origin_port = server_port 118 | if health_check_type == "passwall_logic" then 119 | if server_node.type ~= "Socks" then 120 | local relay_port = server_node.port 121 | new_port = get_new_port() 122 | local config_file = string.format("haproxy_%s_%s.json", t[".name"], new_port) 123 | sys.call(string.format('/usr/share/%s/app.sh run_socks "%s"> /dev/null', 124 | appname, 125 | string.format("flag=%s node=%s bind=%s socks_port=%s config_file=%s", 126 | new_port, --flag 127 | server_node[".name"], --node 128 | "127.0.0.1", --bind 129 | new_port, --socks port 130 | config_file --config file 131 | ) 132 | ) 133 | ) 134 | server_address = "127.0.0.1" 135 | server_port = new_port 136 | end 137 | end 138 | else 139 | server_address, server_port = get_ip_port_from(lbss) 140 | server_remark = server_address .. ":" .. server_port 141 | t.origin_address = server_address 142 | t.origin_port = server_port 143 | end 144 | if server_address and server_port and listen_port > 0 then 145 | if not listens[listen_port] then 146 | listens[listen_port] = {} 147 | end 148 | t.server_remark = server_remark 149 | t.server_address = server_address 150 | t.server_port = server_port 151 | table.insert(listens[listen_port], t) 152 | else 153 | log(" - 丢弃1个明显无效的节点") 154 | end 155 | end 156 | end) 157 | 158 | local sortTable = {} 159 | for i in pairs(listens) do 160 | if i ~= nil then 161 | table.insert(sortTable, i) 162 | end 163 | end 164 | table.sort(sortTable, function(a,b) return (a < b) end) 165 | 166 | for i, port in pairs(sortTable) do 167 | log(" + 入口 %s:%s" % {bind_address, port}) 168 | 169 | f_out:write("\n" .. string.format([[ 170 | listen %s 171 | bind %s:%s 172 | mode tcp 173 | balance roundrobin 174 | ]], port, bind_address, port)) 175 | 176 | if health_check_type == "passwall_logic" then 177 | f_out:write(string.format([[ 178 | option external-check 179 | external-check command "/usr/share/passwall2/haproxy_check.sh" 180 | ]], port, port)) 181 | end 182 | 183 | local count_M, count_B = 1, 1 184 | for i, o in ipairs(listens[port]) do 185 | local remark = o.server_remark or "" 186 | -- 防止重名导致无法运行 187 | if tostring(o.backup) ~= "1" then 188 | remark = "M" .. count_M .. "-" .. remark 189 | count_M = count_M + 1 190 | else 191 | remark = "B" .. count_B .. "-" .. remark 192 | count_B = count_B + 1 193 | end 194 | local server = o.server_address .. ":" .. o.server_port 195 | local server_conf = "server {{remark}} {{server}} weight {{weight}} {{resolvers}} check inter {{inter}} rise 1 fall 3 {{backup}}" 196 | server_conf = server_conf:gsub("{{remark}}", remark) 197 | server_conf = server_conf:gsub("{{server}}", server) 198 | server_conf = server_conf:gsub("{{weight}}", o.lbweight) 199 | local resolvers = "resolvers mydns" 200 | if api.is_ip(o.server_address) then 201 | resolvers = "" 202 | end 203 | server_conf = server_conf:gsub("{{resolvers}}", resolvers) 204 | server_conf = server_conf:gsub("{{inter}}", tonumber(health_check_inter) .. "s") 205 | server_conf = server_conf:gsub("{{backup}}", tostring(o.backup) == "1" and "backup" or "") 206 | 207 | f_out:write(" " .. server_conf .. "\n") 208 | 209 | if o.export ~= "0" then 210 | sys.call(string.format("/usr/share/passwall2/app.sh add_ip2route %s %s", o.origin_address, o.export)) 211 | end 212 | 213 | log(string.format(" | - 出口节点:%s:%s,权重:%s", o.origin_address, o.origin_port, o.lbweight)) 214 | end 215 | end 216 | 217 | --控制台配置 218 | local console_user = uci:get(appname, "@global_haproxy[0]", "console_user") 219 | local console_password = uci:get(appname, "@global_haproxy[0]", "console_password") 220 | local str = [[ 221 | listen console 222 | bind 0.0.0.0:%s 223 | mode http 224 | stats refresh 30s 225 | stats uri / 226 | stats admin if TRUE 227 | %s 228 | ]] 229 | f_out:write("\n" .. string.format(str, console_port, (console_user and console_user ~= "" and console_password and console_password ~= "") and "stats auth " .. console_user .. ":" .. console_password or "")) 230 | 231 | f_out:close() 232 | 233 | --内置健康检查URL 234 | if health_check_type == "passwall_logic" then 235 | local probeUrl = uci:get(appname, "@global_haproxy[0]", "health_probe_url") or "https://www.google.com/generate_204" 236 | local f_url = io.open(haproxy_path .. "/Probe_URL", "w") 237 | f_url:write(probeUrl) 238 | f_url:close() 239 | end 240 | -------------------------------------------------------------------------------- /luci-app-passwall2/root/usr/share/passwall2/haproxy_check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export PATH=/usr/sbin:/usr/bin:/sbin:/bin:/root/bin 4 | 5 | listen_address=$1 6 | listen_port=$2 7 | server_address=$3 8 | server_port=$4 9 | 10 | probe_file="/tmp/etc/passwall2/haproxy/Probe_URL" 11 | probeUrl="https://www.google.com/generate_204" 12 | if [ -f "$probe_file" ]; then 13 | firstLine=$(head -n 1 "$probe_file" | tr -d ' \t\n') 14 | [ -n "$firstLine" ] && probeUrl="$firstLine" 15 | fi 16 | 17 | extra_params="-x socks5h://${server_address}:${server_port}" 18 | if /usr/bin/curl --help all | grep -q "\-\-retry-all-errors"; then 19 | extra_params="${extra_params} --retry-all-errors" 20 | fi 21 | 22 | status=$(/usr/bin/curl -I -o /dev/null -skL ${extra_params} --connect-timeout 3 --retry 1 --max-time 10 -w "%{http_code}" "${probeUrl}") 23 | 24 | case "$status" in 25 | 200|204) 26 | exit 0 27 | ;; 28 | *) 29 | exit 1 30 | ;; 31 | esac 32 | -------------------------------------------------------------------------------- /luci-app-passwall2/root/usr/share/passwall2/helper_dnsmasq.lua: -------------------------------------------------------------------------------- 1 | local api = require "luci.passwall2.api" 2 | local appname = "passwall2" 3 | local uci = api.uci 4 | local sys = api.sys 5 | local fs = api.fs 6 | local datatypes = api.datatypes 7 | local TMP = {} 8 | 9 | local function tinsert(table_name, val) 10 | if table_name and type(table_name) == "table" then 11 | if not TMP[table_name] then 12 | TMP[table_name] = {} 13 | end 14 | if TMP[table_name][val] then 15 | return false 16 | end 17 | table.insert(table_name, val) 18 | TMP[table_name][val] = true 19 | return true 20 | end 21 | return false 22 | end 23 | 24 | local function backup_servers() 25 | local DNSMASQ_DNS = uci:get("dhcp", "@dnsmasq[0]", "server") 26 | if DNSMASQ_DNS and #DNSMASQ_DNS > 0 then 27 | uci:set(appname, "@global[0]", "dnsmasq_servers", DNSMASQ_DNS) 28 | api.uci_save(uci, appname, true) 29 | end 30 | end 31 | 32 | local function restore_servers() 33 | local dns_table = {} 34 | local DNSMASQ_DNS = uci:get("dhcp", "@dnsmasq[0]", "server") 35 | if DNSMASQ_DNS and #DNSMASQ_DNS > 0 then 36 | for k, v in ipairs(DNSMASQ_DNS) do 37 | tinsert(dns_table, v) 38 | end 39 | end 40 | local OLD_SERVER = uci:get(appname, "@global[0]", "dnsmasq_servers") 41 | if OLD_SERVER and #OLD_SERVER > 0 then 42 | for k, v in ipairs(OLD_SERVER) do 43 | tinsert(dns_table, v) 44 | end 45 | uci:delete(appname, "@global[0]", "dnsmasq_servers") 46 | api.uci_save(uci, appname, true) 47 | end 48 | if dns_table and #dns_table > 0 then 49 | uci:set_list("dhcp", "@dnsmasq[0]", "server", dns_table) 50 | api.uci_save(uci, "dhcp", true) 51 | end 52 | end 53 | 54 | function stretch() 55 | local dnsmasq_server = uci:get("dhcp", "@dnsmasq[0]", "server") 56 | local dnsmasq_noresolv = uci:get("dhcp", "@dnsmasq[0]", "noresolv") 57 | local _flag 58 | if dnsmasq_server and #dnsmasq_server > 0 then 59 | for k, v in ipairs(dnsmasq_server) do 60 | if not v:find("/") then 61 | _flag = true 62 | end 63 | end 64 | end 65 | if not _flag and dnsmasq_noresolv == "1" then 66 | uci:delete("dhcp", "@dnsmasq[0]", "noresolv") 67 | local RESOLVFILE = "/tmp/resolv.conf.d/resolv.conf.auto" 68 | local file = io.open(RESOLVFILE, "r") 69 | if not file then 70 | RESOLVFILE = "/tmp/resolv.conf.auto" 71 | else 72 | local size = file:seek("end") 73 | file:close() 74 | if size == 0 then 75 | RESOLVFILE = "/tmp/resolv.conf.auto" 76 | end 77 | end 78 | uci:set("dhcp", "@dnsmasq[0]", "resolvfile", RESOLVFILE) 79 | api.uci_save(uci, "dhcp", true) 80 | end 81 | end 82 | 83 | function restart(var) 84 | local LOG = var["-LOG"] 85 | sys.call("/etc/init.d/dnsmasq restart >/dev/null 2>&1") 86 | if LOG == "1" then 87 | api.log("重启 dnsmasq 服务") 88 | end 89 | end 90 | 91 | function logic_restart(var) 92 | local LOG = var["-LOG"] 93 | local DEFAULT_DNS = api.get_cache_var("DEFAULT_DNS") 94 | if DEFAULT_DNS then 95 | backup_servers() 96 | --sys.call("sed -i '/list server/d' /etc/config/dhcp >/dev/null 2>&1") 97 | local dns_table = {} 98 | local dnsmasq_server = uci:get("dhcp", "@dnsmasq[0]", "server") 99 | if dnsmasq_server and #dnsmasq_server > 0 then 100 | for k, v in ipairs(dnsmasq_server) do 101 | if v:find("/") then 102 | tinsert(dns_table, v) 103 | end 104 | end 105 | uci:set_list("dhcp", "@dnsmasq[0]", "server", dns_table) 106 | api.uci_save(uci, "dhcp", true) 107 | end 108 | sys.call("/etc/init.d/dnsmasq restart >/dev/null 2>&1") 109 | restore_servers() 110 | else 111 | sys.call("/etc/init.d/dnsmasq restart >/dev/null 2>&1") 112 | end 113 | if LOG == "1" then 114 | api.log("重启 dnsmasq 服务") 115 | end 116 | end 117 | 118 | function copy_instance(var) 119 | local LISTEN_PORT = var["-LISTEN_PORT"] 120 | local TMP_DNSMASQ_PATH = var["-TMP_DNSMASQ_PATH"] 121 | local conf_lines = {} 122 | local DEFAULT_DNSMASQ_CFGID = sys.exec("echo -n $(uci -q show dhcp.@dnsmasq[0] | awk 'NR==1 {split($0, conf, /[.=]/); print conf[2]}')") 123 | for line in io.lines("/tmp/etc/dnsmasq.conf." .. DEFAULT_DNSMASQ_CFGID) do 124 | local filter 125 | if line:find("passwall2") then filter = true end 126 | if line:find("ubus") then filter = true end 127 | if line:find("dhcp") then filter = true end 128 | if line:find("server=") == 1 then filter = true end 129 | if line:find("port=") == 1 then filter = true end 130 | if line:find("conf%-dir=") == 1 then 131 | filter = true 132 | if TMP_DNSMASQ_PATH then 133 | local tmp_path = line:sub(1 + #"conf-dir=") 134 | sys.call(string.format("cp -r %s/* %s/ 2>/dev/null", tmp_path, TMP_DNSMASQ_PATH)) 135 | end 136 | end 137 | if line:find("address=") == 1 or (line:find("server=") == 1 and line:find("/")) then filter = nil end 138 | if not filter then 139 | tinsert(conf_lines, line) 140 | end 141 | end 142 | tinsert(conf_lines, "port=" .. LISTEN_PORT) 143 | if TMP_DNSMASQ_PATH then 144 | sys.call("rm -rf " .. TMP_DNSMASQ_PATH .. "/*passwall*") 145 | end 146 | if var["-return"] == "1" then 147 | return conf_lines 148 | end 149 | if #conf_lines > 0 then 150 | local DNSMASQ_CONF = var["-DNSMASQ_CONF"] 151 | local conf_out = io.open(DNSMASQ_CONF, "a") 152 | conf_out:write(table.concat(conf_lines, "\n")) 153 | conf_out:write("\n") 154 | conf_out:close() 155 | end 156 | end 157 | 158 | function add_rule(var) 159 | local FLAG = var["-FLAG"] 160 | local TMP_DNSMASQ_PATH = var["-TMP_DNSMASQ_PATH"] 161 | local DNSMASQ_CONF_FILE = var["-DNSMASQ_CONF_FILE"] 162 | local LISTEN_PORT = var["-LISTEN_PORT"] 163 | local DEFAULT_DNS = var["-DEFAULT_DNS"] 164 | local LOCAL_DNS = var["-LOCAL_DNS"] 165 | local TUN_DNS = var["-TUN_DNS"] 166 | local NO_LOGIC_LOG = var["-NO_LOGIC_LOG"] 167 | local NFTFLAG = var["-NFTFLAG"] 168 | local CACHE_PATH = api.CACHE_PATH 169 | local CACHE_FLAG = "dnsmasq_" .. FLAG 170 | local CACHE_DNS_PATH = CACHE_PATH .. "/" .. CACHE_FLAG 171 | local CACHE_TEXT_FILE = CACHE_DNS_PATH .. ".txt" 172 | 173 | local list1 = {} 174 | local excluded_domain = {} 175 | local excluded_domain_str = "!" 176 | 177 | local function check_dns(domain, dns) 178 | if domain == "" or domain:find("#") then 179 | return false 180 | end 181 | if not dns then 182 | return 183 | end 184 | for k,v in ipairs(list1[domain].dns) do 185 | if dns == v then 186 | return true 187 | end 188 | end 189 | return false 190 | end 191 | 192 | local function check_ipset(domain, ipset) 193 | if domain == "" or domain:find("#") then 194 | return false 195 | end 196 | if not ipset then 197 | return 198 | end 199 | for k,v in ipairs(list1[domain].ipsets) do 200 | if ipset == v then 201 | return true 202 | end 203 | end 204 | return false 205 | end 206 | 207 | local function set_domain_dns(domain, dns) 208 | if domain == "" or domain:find("#") then 209 | return 210 | end 211 | if not dns then 212 | return 213 | end 214 | if not list1[domain] then 215 | list1[domain] = { 216 | dns = {}, 217 | ipsets = {} 218 | } 219 | end 220 | for line in string.gmatch(dns, '[^' .. "," .. ']+') do 221 | if not check_dns(domain, line) then 222 | table.insert(list1[domain].dns, line) 223 | end 224 | end 225 | end 226 | 227 | local function set_domain_ipset(domain, ipset) 228 | if domain == "" or domain:find("#") then 229 | return 230 | end 231 | if not ipset then 232 | return 233 | end 234 | if not list1[domain] then 235 | list1[domain] = { 236 | dns = {}, 237 | ipsets = {} 238 | } 239 | end 240 | for line in string.gmatch(ipset, '[^' .. "," .. ']+') do 241 | if not check_ipset(domain, line) then 242 | table.insert(list1[domain].ipsets, line) 243 | end 244 | end 245 | end 246 | 247 | local cache_text = "" 248 | local nodes_address_md5 = sys.exec("echo -n $(uci show passwall2 | grep '\\.address') | md5sum") 249 | local new_text = TMP_DNSMASQ_PATH .. DNSMASQ_CONF_FILE .. DEFAULT_DNS .. LOCAL_DNS .. TUN_DNS .. nodes_address_md5 .. NFTFLAG 250 | if fs.access(CACHE_TEXT_FILE) then 251 | for line in io.lines(CACHE_TEXT_FILE) do 252 | cache_text = line 253 | end 254 | end 255 | 256 | if cache_text ~= new_text then 257 | api.remove(CACHE_DNS_PATH .. "*") 258 | end 259 | 260 | local dnsmasq_default_dns = TUN_DNS 261 | 262 | local setflag_4= (NFTFLAG == "1") and "4#inet#passwall2#" or "" 263 | local setflag_6= (NFTFLAG == "1") and "6#inet#passwall2#" or "" 264 | 265 | if not fs.access(CACHE_DNS_PATH) then 266 | fs.mkdir(CACHE_DNS_PATH) 267 | 268 | local fwd_dns 269 | 270 | --始终用国内DNS解析节点域名 271 | if true then 272 | fwd_dns = LOCAL_DNS 273 | uci:foreach(appname, "nodes", function(t) 274 | local function process_address(address) 275 | if address == "engage.cloudflareclient.com" then return end 276 | if datatypes.hostname(address) then 277 | set_domain_dns(address, fwd_dns) 278 | set_domain_ipset(address, setflag_4 .. "passwall2_vps," .. setflag_6 .. "passwall2_vps6") 279 | end 280 | end 281 | process_address(t.address) 282 | process_address(t.download_address) 283 | end) 284 | end 285 | 286 | if list1 and next(list1) then 287 | local server_out = io.open(CACHE_DNS_PATH .. "/001-server.conf", "a") 288 | local ipset_out = io.open(CACHE_DNS_PATH .. "/ipset.conf", "a") 289 | local set_name = "ipset" 290 | if NFTFLAG == "1" then 291 | set_name = "nftset" 292 | end 293 | for key, value in pairs(list1) do 294 | if value.dns and #value.dns > 0 then 295 | for i, dns in ipairs(value.dns) do 296 | server_out:write(string.format("server=/.%s/%s", key, dns) .. "\n") 297 | end 298 | end 299 | if value.ipsets and #value.ipsets > 0 then 300 | local ipsets_str = "" 301 | for i, ipset in ipairs(value.ipsets) do 302 | ipsets_str = ipsets_str .. ipset .. "," 303 | end 304 | ipsets_str = ipsets_str:sub(1, #ipsets_str - 1) 305 | ipset_out:write(string.format("%s=/.%s/%s", set_name, key, ipsets_str) .. "\n") 306 | end 307 | end 308 | server_out:close() 309 | ipset_out:close() 310 | end 311 | 312 | local f_out = io.open(CACHE_TEXT_FILE, "a") 313 | f_out:write(new_text) 314 | f_out:close() 315 | end 316 | 317 | api.remove(TMP_DNSMASQ_PATH) 318 | fs.symlink(CACHE_DNS_PATH, TMP_DNSMASQ_PATH) 319 | 320 | if DNSMASQ_CONF_FILE ~= "nil" then 321 | local conf_lines = {} 322 | if LISTEN_PORT then 323 | --Copy dnsmasq instance 324 | conf_lines = copy_instance({["-LISTEN_PORT"] = LISTEN_PORT, ["-TMP_DNSMASQ_PATH"] = TMP_DNSMASQ_PATH, ["-return"] = "1"}) 325 | else 326 | --Modify the default dnsmasq service 327 | end 328 | tinsert(conf_lines, string.format("conf-dir=%s", TMP_DNSMASQ_PATH)) 329 | if dnsmasq_default_dns then 330 | for s in string.gmatch(dnsmasq_default_dns, '[^' .. "," .. ']+') do 331 | tinsert(conf_lines, string.format("server=%s", s)) 332 | end 333 | tinsert(conf_lines, "all-servers") 334 | tinsert(conf_lines, "no-poll") 335 | tinsert(conf_lines, "no-resolv") 336 | 337 | if FLAG == "default" then 338 | api.set_cache_var("DEFAULT_DNS", DEFAULT_DNS) 339 | end 340 | end 341 | if #conf_lines > 0 then 342 | local conf_out = io.open(DNSMASQ_CONF_FILE, "a") 343 | conf_out:write(table.concat(conf_lines, "\n")) 344 | conf_out:write("\n") 345 | conf_out:close() 346 | end 347 | end 348 | end 349 | 350 | _G.stretch = stretch 351 | _G.restart = restart 352 | _G.logic_restart = logic_restart 353 | _G.copy_instance = copy_instance 354 | _G.add_rule = add_rule 355 | 356 | if arg[1] then 357 | local func =_G[arg[1]] 358 | if func then 359 | func(api.get_function_args(arg)) 360 | end 361 | end 362 | -------------------------------------------------------------------------------- /luci-app-passwall2/root/usr/share/passwall2/monitor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | CONFIG=passwall2 4 | TMP_PATH=/tmp/etc/$CONFIG 5 | TMP_SCRIPT_FUNC_PATH=$TMP_PATH/script_func 6 | LOCK_FILE_DIR=/tmp/lock 7 | LOCK_FILE=${LOCK_FILE_DIR}/${CONFIG}_script.lock 8 | 9 | config_n_get() { 10 | local ret=$(uci -q get $CONFIG.$1.$2 2>/dev/null) 11 | echo ${ret:=$3} 12 | } 13 | 14 | config_t_get() { 15 | local index=0 16 | [ -n "$4" ] && index=$4 17 | local ret=$(uci -q get $CONFIG.@$1[$index].$2 2>/dev/null) 18 | echo ${ret:=$3} 19 | } 20 | 21 | ENABLED=$(config_t_get global enabled 0) 22 | [ "$ENABLED" != 1 ] && return 1 23 | ENABLED=$(config_t_get global_delay start_daemon 0) 24 | [ "$ENABLED" != 1 ] && return 1 25 | sleep 58s 26 | while [ "$ENABLED" -eq 1 ]; do 27 | [ -f "$LOCK_FILE" ] && { 28 | sleep 6s 29 | continue 30 | } 31 | touch $LOCK_FILE 32 | [ -d ${TMP_SCRIPT_FUNC_PATH} ] && { 33 | for filename in $(ls ${TMP_SCRIPT_FUNC_PATH} | grep -v "^_"); do 34 | cmd=$(cat ${TMP_SCRIPT_FUNC_PATH}/${filename}) 35 | cmd_check=$(echo $cmd | awk -F '>' '{print $1}') 36 | [ -n "$(echo $cmd_check | grep "dns2socks")" ] && cmd_check=$(echo $cmd_check | sed "s#:# #g") 37 | icount=$(pgrep -f "$(echo $cmd_check)" | wc -l) 38 | if [ $icount = 0 ]; then 39 | #echo "${cmd} 进程挂掉,重启" >> /tmp/log/passwall2.log 40 | eval $(echo "nohup ${cmd} 2>&1 &") >/dev/null 2>&1 & 41 | fi 42 | done 43 | } 44 | 45 | rm -f $LOCK_FILE 46 | sleep 58s 47 | done 48 | -------------------------------------------------------------------------------- /luci-app-passwall2/root/usr/share/passwall2/rule_update.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | local api = require "luci.passwall2.api" 4 | local name = api.appname 5 | local fs = api.fs 6 | local sys = api.sys 7 | local uci = api.uci 8 | local jsonc = api.jsonc 9 | 10 | local arg1 = arg[1] 11 | local arg2 = arg[2] 12 | local arg3 = arg[3] 13 | 14 | local reboot = 0 15 | local geoip_update = 0 16 | local geosite_update = 0 17 | local asset_location = uci:get_first(name, 'global_rules', "v2ray_location_asset", "/usr/share/v2ray/") 18 | 19 | -- Custom geo file 20 | local geoip_api = uci:get_first(name, 'global_rules', "geoip_url", "https://api.github.com/repos/Loyalsoldier/v2ray-rules-dat/releases/latest") 21 | local geosite_api = uci:get_first(name, 'global_rules', "geosite_url", "https://api.github.com/repos/Loyalsoldier/v2ray-rules-dat/releases/latest") 22 | -- 23 | local use_nft = uci:get(name, "@global_forwarding[0]", "use_nft") or "0" 24 | 25 | if arg3 == "cron" then 26 | arg2 = nil 27 | end 28 | 29 | local log = function(...) 30 | if arg1 then 31 | if arg1 == "log" then 32 | api.log(...) 33 | elseif arg1 == "print" then 34 | local result = os.date("%Y-%m-%d %H:%M:%S: ") .. table.concat({...}, " ") 35 | print(result) 36 | end 37 | end 38 | end 39 | 40 | -- curl 41 | local function curl(url, file) 42 | local args = { 43 | "-skL", "-w %{http_code}", "--retry 3", "--connect-timeout 3" 44 | } 45 | if file then 46 | args[#args + 1] = "-o " .. file 47 | end 48 | local return_code, result = api.curl_logic(url, nil, args) 49 | return tonumber(result) 50 | end 51 | 52 | --获取geoip 53 | local function fetch_geoip() 54 | --请求geoip 55 | xpcall(function() 56 | local return_code, content = api.curl_logic(geoip_api) 57 | local json = jsonc.parse(content) 58 | if json.tag_name and json.assets then 59 | for _, v in ipairs(json.assets) do 60 | if v.name and v.name == "geoip.dat.sha256sum" then 61 | local sret = curl(v.browser_download_url, "/tmp/geoip.dat.sha256sum") 62 | if sret == 200 then 63 | local f = io.open("/tmp/geoip.dat.sha256sum", "r") 64 | local content = f:read() 65 | f:close() 66 | f = io.open("/tmp/geoip.dat.sha256sum", "w") 67 | f:write(content:gsub("geoip.dat", "/tmp/geoip.dat"), "") 68 | f:close() 69 | 70 | if fs.access(asset_location .. "geoip.dat") then 71 | sys.call(string.format("cp -f %s %s", asset_location .. "geoip.dat", "/tmp/geoip.dat")) 72 | if sys.call('sha256sum -c /tmp/geoip.dat.sha256sum > /dev/null 2>&1') == 0 then 73 | log("geoip 版本一致,无需更新。") 74 | return 1 75 | end 76 | end 77 | for _2, v2 in ipairs(json.assets) do 78 | if v2.name and v2.name == "geoip.dat" then 79 | sret = curl(v2.browser_download_url, "/tmp/geoip.dat") 80 | if sys.call('sha256sum -c /tmp/geoip.dat.sha256sum > /dev/null 2>&1') == 0 then 81 | sys.call(string.format("mkdir -p %s && cp -f %s %s", asset_location, "/tmp/geoip.dat", asset_location .. "geoip.dat")) 82 | reboot = 1 83 | log("geoip 更新成功。") 84 | return 1 85 | else 86 | log("geoip 更新失败,请稍后再试。") 87 | end 88 | break 89 | end 90 | end 91 | end 92 | break 93 | end 94 | end 95 | end 96 | if json.message then 97 | log(json.message) 98 | end 99 | end, 100 | function(e) 101 | end) 102 | 103 | return 0 104 | end 105 | 106 | --获取geosite 107 | local function fetch_geosite() 108 | --请求geosite 109 | xpcall(function() 110 | local return_code, content = api.curl_logic(geosite_api) 111 | local json = jsonc.parse(content) 112 | if json.tag_name and json.assets then 113 | for _, v in ipairs(json.assets) do 114 | if v.name and (v.name == "geosite.dat.sha256sum" or v.name == "dlc.dat.sha256sum") then 115 | local sret = curl(v.browser_download_url, "/tmp/geosite.dat.sha256sum") 116 | if sret == 200 then 117 | local f = io.open("/tmp/geosite.dat.sha256sum", "r") 118 | local content = f:read() 119 | f:close() 120 | f = io.open("/tmp/geosite.dat.sha256sum", "w") 121 | f:write(content:gsub("[^%s]+.dat", "/tmp/geosite.dat"), "") 122 | f:close() 123 | 124 | if fs.access(asset_location .. "geosite.dat") then 125 | sys.call(string.format("cp -f %s %s", asset_location .. "geosite.dat", "/tmp/geosite.dat")) 126 | if sys.call('sha256sum -c /tmp/geosite.dat.sha256sum > /dev/null 2>&1') == 0 then 127 | log("geosite 版本一致,无需更新。") 128 | return 1 129 | end 130 | end 131 | for _2, v2 in ipairs(json.assets) do 132 | if v2.name and (v2.name == "geosite.dat" or v2.name == "dlc.dat") then 133 | sret = curl(v2.browser_download_url, "/tmp/geosite.dat") 134 | if sys.call('sha256sum -c /tmp/geosite.dat.sha256sum > /dev/null 2>&1') == 0 then 135 | sys.call(string.format("mkdir -p %s && cp -f %s %s", asset_location, "/tmp/geosite.dat", asset_location .. "geosite.dat")) 136 | reboot = 1 137 | log("geosite 更新成功。") 138 | return 1 139 | else 140 | log("geosite 更新失败,请稍后再试。") 141 | end 142 | break 143 | end 144 | end 145 | end 146 | break 147 | end 148 | end 149 | end 150 | if json.message then 151 | log(json.message) 152 | end 153 | end, 154 | function(e) 155 | end) 156 | 157 | return 0 158 | end 159 | 160 | if arg2 then 161 | string.gsub(arg2, '[^' .. "," .. ']+', function(w) 162 | if w == "geoip" then 163 | geoip_update = 1 164 | end 165 | if w == "geosite" then 166 | geosite_update = 1 167 | end 168 | end) 169 | else 170 | geoip_update = uci:get_first(name, 'global_rules', "geoip_update", 1) 171 | geosite_update = uci:get_first(name, 'global_rules', "geosite_update", 1) 172 | end 173 | if geoip_update == 0 and geosite_update == 0 then 174 | os.exit(0) 175 | end 176 | 177 | log("开始更新规则...") 178 | 179 | if tonumber(geoip_update) == 1 then 180 | log("geoip 开始更新...") 181 | local status = fetch_geoip() 182 | os.remove("/tmp/geoip.dat") 183 | os.remove("/tmp/geoip.dat.sha256sum") 184 | end 185 | 186 | if tonumber(geosite_update) == 1 then 187 | log("geosite 开始更新...") 188 | local status = fetch_geosite() 189 | os.remove("/tmp/geosite.dat") 190 | os.remove("/tmp/geosite.dat.sha256sum") 191 | end 192 | 193 | uci:set(name, uci:get_first(name, 'global_rules'), "geoip_update", geoip_update) 194 | uci:set(name, uci:get_first(name, 'global_rules'), "geosite_update", geosite_update) 195 | api.uci_save(uci, name, true) 196 | 197 | if reboot == 1 then 198 | if arg3 == "cron" then 199 | if not fs.access("/var/lock/" .. name .. ".lock") then 200 | sys.call("touch /tmp/lock/" .. name .. "_cron.lock") 201 | end 202 | end 203 | 204 | log("重启服务,应用新的规则。") 205 | uci:set(name, "@global[0]", "flush_set", "1") 206 | api.uci_save(uci, name, true, true) 207 | end 208 | log("规则更新完毕...") 209 | -------------------------------------------------------------------------------- /luci-app-passwall2/root/usr/share/passwall2/socks_auto_switch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | CONFIG=passwall2 4 | LOG_FILE=/tmp/log/$CONFIG.log 5 | LOCK_FILE_DIR=/tmp/lock 6 | 7 | flag=0 8 | 9 | echolog() { 10 | local d="$(date "+%Y-%m-%d %H:%M:%S")" 11 | #echo -e "$d: $1" 12 | echo -e "$d: $1" >> $LOG_FILE 13 | } 14 | 15 | config_n_get() { 16 | local ret=$(uci -q get "${CONFIG}.${1}.${2}" 2>/dev/null) 17 | echo "${ret:=$3}" 18 | } 19 | 20 | test_url() { 21 | local url=$1 22 | local try=1 23 | [ -n "$2" ] && try=$2 24 | local timeout=2 25 | [ -n "$3" ] && timeout=$3 26 | local extra_params=$4 27 | if /usr/bin/curl --help all | grep -q "\-\-retry-all-errors"; then 28 | extra_params="--retry-all-errors ${extra_params}" 29 | fi 30 | status=$(/usr/bin/curl -I -o /dev/null -skL ${extra_params} --connect-timeout ${timeout} --retry ${try} -w %{http_code} "$url") 31 | case "$status" in 32 | 204) 33 | status=200 34 | ;; 35 | esac 36 | echo $status 37 | } 38 | 39 | test_proxy() { 40 | result=0 41 | status=$(test_url "${probe_url}" ${retry_num} ${connect_timeout} "-x socks5h://127.0.0.1:${socks_port}") 42 | if [ "$status" = "200" ]; then 43 | result=0 44 | else 45 | status2=$(test_url "https://www.baidu.com" ${retry_num} ${connect_timeout}) 46 | if [ "$status2" = "200" ]; then 47 | result=1 48 | else 49 | result=2 50 | ping -c 3 -W 1 223.5.5.5 > /dev/null 2>&1 51 | [ $? -eq 0 ] && { 52 | result=1 53 | } 54 | fi 55 | fi 56 | echo $result 57 | } 58 | 59 | test_node() { 60 | local node_id=$1 61 | local _type=$(echo $(config_n_get ${node_id} type) | tr 'A-Z' 'a-z') 62 | [ -n "${_type}" ] && { 63 | local _tmp_port=$(/usr/share/${CONFIG}/app.sh get_new_port 61080 tcp,udp) 64 | /usr/share/${CONFIG}/app.sh run_socks flag="test_node_${node_id}" node=${node_id} bind=127.0.0.1 socks_port=${_tmp_port} config_file=test_node_${node_id}.json 65 | local curlx="socks5h://127.0.0.1:${_tmp_port}" 66 | sleep 1s 67 | _proxy_status=$(test_url "${probe_url}" ${retry_num} ${connect_timeout} "-x $curlx") 68 | pgrep -af "test_node_${node_id}" | awk '! /socks_auto_switch\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1 69 | rm -rf "/tmp/etc/${CONFIG}/test_node_${node_id}.json" 70 | if [ "${_proxy_status}" -eq 200 ]; then 71 | return 0 72 | fi 73 | } 74 | return 1 75 | } 76 | 77 | test_auto_switch() { 78 | flag=$(expr $flag + 1) 79 | local b_nodes=$1 80 | local now_node=$2 81 | [ -z "$now_node" ] && { 82 | if [ -n "$(/usr/share/${CONFIG}/app.sh get_cache_var "socks_${id}")" ]; then 83 | now_node=$(/usr/share/${CONFIG}/app.sh get_cache_var "socks_${id}") 84 | else 85 | #echolog "自动切换检测:未知错误" 86 | return 1 87 | fi 88 | } 89 | 90 | [ $flag -le 1 ] && { 91 | main_node=$now_node 92 | } 93 | 94 | status=$(test_proxy) 95 | if [ "$status" == 2 ]; then 96 | echolog "自动切换检测:无法连接到网络,请检查网络是否正常!" 97 | return 2 98 | fi 99 | 100 | #检测主节点是否能使用 101 | if [ "$restore_switch" == "1" ] && [ -n "$main_node" ] && [ "$now_node" != "$main_node" ]; then 102 | test_node ${main_node} 103 | [ $? -eq 0 ] && { 104 | #主节点正常,切换到主节点 105 | echolog "自动切换检测:${id}主节点【$(config_n_get $main_node type):[$(config_n_get $main_node remarks)]】正常,切换到主节点!" 106 | /usr/share/${CONFIG}/app.sh socks_node_switch flag=${id} new_node=${main_node} 107 | [ $? -eq 0 ] && { 108 | echolog "自动切换检测:${id}节点切换完毕!" 109 | } 110 | return 0 111 | } 112 | fi 113 | 114 | if [ "$status" == 0 ]; then 115 | #echolog "自动切换检测:${id}【$(config_n_get $now_node type):[$(config_n_get $now_node remarks)]】正常。" 116 | return 0 117 | elif [ "$status" == 1 ]; then 118 | echolog "自动切换检测:${id}【$(config_n_get $now_node type):[$(config_n_get $now_node remarks)]】异常,切换到下一个备用节点检测!" 119 | local new_node 120 | in_backup_nodes=$(echo $b_nodes | grep $now_node) 121 | # 判断当前节点是否存在于备用节点列表里 122 | if [ -z "$in_backup_nodes" ]; then 123 | # 如果不存在,设置第一个节点为新的节点 124 | new_node=$(echo $b_nodes | awk -F ' ' '{print $1}') 125 | else 126 | # 如果存在,设置下一个备用节点为新的节点 127 | #local count=$(expr $(echo $b_nodes | grep -o ' ' | wc -l) + 1) 128 | local next_node=$(echo $b_nodes | awk -F "$now_node" '{print $2}' | awk -F " " '{print $1}') 129 | if [ -z "$next_node" ]; then 130 | new_node=$(echo $b_nodes | awk -F ' ' '{print $1}') 131 | else 132 | new_node=$next_node 133 | fi 134 | fi 135 | test_node ${new_node} 136 | if [ $? -eq 0 ]; then 137 | [ "$restore_switch" == "0" ] && { 138 | uci set $CONFIG.${id}.node=$new_node 139 | [ -z "$(echo $b_nodes | grep $main_node)" ] && uci add_list $CONFIG.${id}.autoswitch_backup_node=$main_node 140 | uci commit $CONFIG 141 | } 142 | echolog "自动切换检测:${id}【$(config_n_get $new_node type):[$(config_n_get $new_node remarks)]】正常,切换到此节点!" 143 | /usr/share/${CONFIG}/app.sh socks_node_switch flag=${id} new_node=${new_node} 144 | [ $? -eq 0 ] && { 145 | echolog "自动切换检测:${id}节点切换完毕!" 146 | } 147 | return 0 148 | else 149 | test_auto_switch "${b_nodes}" ${new_node} 150 | fi 151 | fi 152 | } 153 | 154 | start() { 155 | id=$1 156 | LOCK_FILE=${LOCK_FILE_DIR}/${CONFIG}_socks_auto_switch_${id}.lock 157 | main_node=$(config_n_get $id node) 158 | socks_port=$(config_n_get $id port 0) 159 | delay=$(config_n_get $id autoswitch_testing_time 30) 160 | sleep 5s 161 | connect_timeout=$(config_n_get $id autoswitch_connect_timeout 3) 162 | retry_num=$(config_n_get $id autoswitch_retry_num 1) 163 | restore_switch=$(config_n_get $id autoswitch_restore_switch 0) 164 | probe_url=$(config_n_get $id autoswitch_probe_url "https://www.google.com/generate_204") 165 | backup_node=$(config_n_get $id autoswitch_backup_node) 166 | while [ -n "$backup_node" ]; do 167 | [ -f "$LOCK_FILE" ] && { 168 | sleep 6s 169 | continue 170 | } 171 | touch $LOCK_FILE 172 | backup_node=$(echo $backup_node | tr -s ' ' '\n' | uniq | tr -s '\n' ' ') 173 | test_auto_switch "$backup_node" 174 | rm -f $LOCK_FILE 175 | sleep ${delay} 176 | done 177 | } 178 | 179 | start $@ 180 | 181 | -------------------------------------------------------------------------------- /luci-app-passwall2/root/usr/share/passwall2/tasks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ## 循环更新脚本 4 | 5 | CONFIG=passwall2 6 | APP_PATH=/usr/share/$CONFIG 7 | TMP_PATH=/tmp/etc/$CONFIG 8 | LOCK_FILE=/tmp/lock/${CONFIG}_tasks.lock 9 | CFG_UPDATE_INT=0 10 | 11 | config_n_get() { 12 | local ret=$(uci -q get "${CONFIG}.${1}.${2}" 2>/dev/null) 13 | echo "${ret:=$3}" 14 | } 15 | 16 | config_t_get() { 17 | local index=${4:-0} 18 | local ret=$(uci -q get "${CONFIG}.@${1}[${index}].${2}" 2>/dev/null) 19 | echo "${ret:=${3}}" 20 | } 21 | 22 | exec 99>"$LOCK_FILE" 23 | flock -n 99 24 | if [ "$?" != 0 ]; then 25 | exit 0 26 | fi 27 | 28 | while true 29 | do 30 | 31 | if [ "$CFG_UPDATE_INT" -ne 0 ]; then 32 | 33 | stop_week_mode=$(config_t_get global_delay stop_week_mode) 34 | stop_interval_mode=$(config_t_get global_delay stop_interval_mode) 35 | stop_interval_mode=$(expr "$stop_interval_mode" \* 60) 36 | if [ -n "$stop_week_mode" ]; then 37 | [ "$stop_week_mode" = "8" ] && { 38 | [ "$(expr "$CFG_UPDATE_INT" % "$stop_interval_mode")" -eq 0 ] && /etc/init.d/$CONFIG stop > /dev/null 2>&1 & 39 | } 40 | fi 41 | 42 | start_week_mode=$(config_t_get global_delay start_week_mode) 43 | start_interval_mode=$(config_t_get global_delay start_interval_mode) 44 | start_interval_mode=$(expr "$start_interval_mode" \* 60) 45 | if [ -n "$start_week_mode" ]; then 46 | [ "$start_week_mode" = "8" ] && { 47 | [ "$(expr "$CFG_UPDATE_INT" % "$start_interval_mode")" -eq 0 ] && /etc/init.d/$CONFIG start > /dev/null 2>&1 & 48 | } 49 | fi 50 | 51 | restart_week_mode=$(config_t_get global_delay restart_week_mode) 52 | restart_interval_mode=$(config_t_get global_delay restart_interval_mode) 53 | restart_interval_mode=$(expr "$restart_interval_mode" \* 60) 54 | if [ -n "$restart_week_mode" ]; then 55 | [ "$restart_week_mode" = "8" ] && { 56 | [ "$(expr "$CFG_UPDATE_INT" % "$restart_interval_mode")" -eq 0 ] && /etc/init.d/$CONFIG restart > /dev/null 2>&1 & 57 | } 58 | fi 59 | 60 | autoupdate=$(config_t_get global_rules auto_update) 61 | weekupdate=$(config_t_get global_rules week_update) 62 | hourupdate=$(config_t_get global_rules interval_update) 63 | hourupdate=$(expr "$hourupdate" \* 60) 64 | if [ "$autoupdate" = "1" ]; then 65 | [ "$weekupdate" = "8" ] && { 66 | [ "$(expr "$CFG_UPDATE_INT" % "$hourupdate")" -eq 0 ] && lua $APP_PATH/rule_update.lua log all cron > /dev/null 2>&1 & 67 | } 68 | fi 69 | 70 | TMP_SUB_PATH=$TMP_PATH/sub_tasks 71 | mkdir -p $TMP_SUB_PATH 72 | for item in $(uci show ${CONFIG} | grep "=subscribe_list" | cut -d '.' -sf 2 | cut -d '=' -sf 1); do 73 | if [ "$(config_n_get $item auto_update 0)" = "1" ]; then 74 | cfgid=$(uci show ${CONFIG}.$item | head -n 1 | cut -d '.' -sf 2 | cut -d '=' -sf 1) 75 | remark=$(config_n_get $item remark) 76 | week_update=$(config_n_get $item week_update) 77 | hour_update=$(config_n_get $item interval_update) 78 | echo "$cfgid" >> $TMP_SUB_PATH/${week_update}_${hour_update} 79 | fi 80 | done 81 | 82 | [ -d "${TMP_SUB_PATH}" ] && { 83 | for name in $(ls ${TMP_SUB_PATH}); do 84 | week_update=$(echo $name | awk -F '_' '{print $1}') 85 | hour_update=$(echo $name | awk -F '_' '{print $2}') 86 | hour_update=$(expr "$hour_update" \* 60) 87 | cfgids=$(echo -n $(cat ${TMP_SUB_PATH}/${name}) | sed 's# #,#g') 88 | [ "$week_update" = "8" ] && { 89 | [ "$(expr "$CFG_UPDATE_INT" % "$hour_update")" -eq 0 ] && lua $APP_PATH/subscribe.lua start $cfgids cron > /dev/null 2>&1 & 90 | } 91 | 92 | done 93 | rm -rf $TMP_SUB_PATH 94 | } 95 | 96 | fi 97 | 98 | CFG_UPDATE_INT=$(expr "$CFG_UPDATE_INT" + 10) 99 | 100 | sleep 600 101 | 102 | done 2>/dev/null 103 | -------------------------------------------------------------------------------- /luci-app-passwall2/root/usr/share/passwall2/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | CONFIG=passwall2 4 | LOG_FILE=/tmp/log/$CONFIG.log 5 | 6 | echolog() { 7 | local d="$(date "+%Y-%m-%d %H:%M:%S")" 8 | #echo -e "$d: $1" 9 | echo -e "$d: $1" >> $LOG_FILE 10 | } 11 | 12 | config_n_get() { 13 | local ret=$(uci -q get "${CONFIG}.${1}.${2}" 2>/dev/null) 14 | echo "${ret:=$3}" 15 | } 16 | 17 | config_t_get() { 18 | local index=0 19 | [ -n "$4" ] && index=$4 20 | local ret=$(uci -q get $CONFIG.@$1[$index].$2 2>/dev/null) 21 | echo ${ret:=$3} 22 | } 23 | 24 | test_url() { 25 | local url=$1 26 | local try=1 27 | [ -n "$2" ] && try=$2 28 | local timeout=2 29 | [ -n "$3" ] && timeout=$3 30 | local extra_params=$4 31 | curl --help all | grep "\-\-retry-all-errors" > /dev/null 32 | [ $? == 0 ] && extra_params="--retry-all-errors ${extra_params}" 33 | status=$(/usr/bin/curl -I -o /dev/null -skL $extra_params --connect-timeout ${timeout} --retry ${try} -w %{http_code} "$url") 34 | case "$status" in 35 | 204|\ 36 | 200) 37 | status=200 38 | ;; 39 | esac 40 | echo $status 41 | } 42 | 43 | test_proxy() { 44 | result=0 45 | status=$(test_url "https://www.google.com/generate_204" ${retry_num} ${connect_timeout}) 46 | if [ "$status" = "200" ]; then 47 | result=0 48 | else 49 | status2=$(test_url "https://www.baidu.com" ${retry_num} ${connect_timeout}) 50 | if [ "$status2" = "200" ]; then 51 | result=1 52 | else 53 | result=2 54 | ping -c 3 -W 1 223.5.5.5 > /dev/null 2>&1 55 | [ $? -eq 0 ] && { 56 | result=1 57 | } 58 | fi 59 | fi 60 | echo $result 61 | } 62 | 63 | url_test_node() { 64 | result=0 65 | local node_id=$1 66 | local _type=$(echo $(config_n_get ${node_id} type) | tr 'A-Z' 'a-z') 67 | [ -n "${_type}" ] && { 68 | local _tmp_port=$(/usr/share/${CONFIG}/app.sh get_new_port 61080 tcp,udp) 69 | /usr/share/${CONFIG}/app.sh run_socks flag="url_test_${node_id}" node=${node_id} bind=127.0.0.1 socks_port=${_tmp_port} config_file=url_test_${node_id}.json 70 | local curlx="socks5h://127.0.0.1:${_tmp_port}" 71 | sleep 1s 72 | result=$(curl --connect-timeout 3 -o /dev/null -I -skL -w "%{http_code}:%{time_starttransfer}" -x $curlx "https://www.google.com/generate_204") 73 | pgrep -af "url_test_${node_id}" | awk '! /test\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1 74 | rm -rf /tmp/etc/${CONFIG}/*url_test_${node_id}*.json 75 | } 76 | echo $result 77 | } 78 | 79 | arg1=$1 80 | shift 81 | case $arg1 in 82 | test_url) 83 | test_url $@ 84 | ;; 85 | url_test_node) 86 | url_test_node $@ 87 | ;; 88 | esac 89 | -------------------------------------------------------------------------------- /luci-app-passwall2/root/usr/share/rpcd/acl.d/luci-app-passwall2.json: -------------------------------------------------------------------------------- 1 | { 2 | "luci-app-passwall2": { 3 | "description": "Grant UCI access for luci-app-passwall2", 4 | "read": { 5 | "uci": [ "passwall2", "passwall2_server" ] 6 | }, 7 | "write": { 8 | "uci": [ "passwall2", "passwall2_server" ] 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /luci-app-passwall2/root/usr/share/ucitrack/luci-app-passwall2-server.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": "passwall2_server", 3 | "init": "passwall2_server" 4 | } 5 | -------------------------------------------------------------------------------- /luci-app-passwall2/root/usr/share/ucitrack/luci-app-passwall2.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": "passwall2", 3 | "init": "passwall2" 4 | } 5 | --------------------------------------------------------------------------------