2023-02-16 23:37:45 +08:00
local api = require " luci.passwall2.api "
2022-03-11 18:29:27 +08:00
local appname = api.appname
local uci = api.uci
local datatypes = api.datatypes
local has_v2ray = api.is_finded ( " v2ray " )
local has_xray = api.is_finded ( " xray " )
m = Map ( appname )
local nodes_table = { }
for k , e in ipairs ( api.get_valid_nodes ( ) ) do
2023-04-09 23:35:07 +08:00
nodes_table [ # nodes_table + 1 ] = e
2022-03-11 18:29:27 +08:00
end
local doh_validate = function ( self , value , t )
2023-04-09 23:35:07 +08:00
if value ~= " " then
local flag = 0
local util = require " luci.util "
local val = util.split ( value , " , " )
local url = val [ 1 ]
val [ 1 ] = nil
for i = 1 , # val do
local v = val [ i ]
if v then
if not datatypes.ipmask4 ( v ) then
flag = 1
end
end
end
if flag == 0 then
return value
end
end
return nil , translate ( " DoH request address " ) .. " " .. translate ( " Format must be: " ) .. " URL,IP "
2022-03-11 18:29:27 +08:00
end
m : append ( Template ( appname .. " /global/status " ) )
s = m : section ( TypedSection , " global " )
s.anonymous = true
s.addremove = false
s : tab ( " Main " , translate ( " Main " ) )
-- [[ Global Settings ]]--
o = s : taboption ( " Main " , Flag , " enabled " , translate ( " Main switch " ) )
o.rmempty = false
2023-04-10 09:16:48 +08:00
local auto_switch_tip
2023-04-23 09:28:00 +08:00
local shunt_remark
2023-04-10 23:35:38 +08:00
local current_node = luci.sys . exec ( string.format ( " [ -f '/tmp/etc/%s/id/global' ] && echo -n $(cat /tmp/etc/%s/id/global) " , appname , appname ) )
2022-03-11 18:29:27 +08:00
if current_node and current_node ~= " " and current_node ~= " nil " then
2023-04-09 23:35:07 +08:00
local n = uci : get_all ( appname , current_node )
if n then
if tonumber ( m : get ( " @auto_switch[0] " , " enable " ) or 0 ) == 1 then
2023-04-10 09:16:48 +08:00
if n.protocol == " _shunt " then
local shunt_logic = tonumber ( m : get ( " @auto_switch[0] " , " shunt_logic " ) )
2023-04-23 09:28:00 +08:00
if shunt_logic == 1 or shunt_logic == 2 then
if shunt_logic == 1 then
shunt_remark = " default "
elseif shunt_logic == 2 then
shunt_remark = " main "
end
current_node = luci.sys . exec ( string.format ( " [ -f '/tmp/etc/%s/id/global_%s' ] && echo -n $(cat /tmp/etc/%s/id/global_%s) " , appname , shunt_remark , appname , shunt_remark ) )
if current_node and current_node ~= " " and current_node ~= " nil " then
n = uci : get_all ( appname , current_node )
end
2023-04-10 09:16:48 +08:00
end
end
2023-04-23 09:28:00 +08:00
if n then
local remarks = api.get_node_remarks ( n )
local url = api.url ( " node_config " , n [ " .name " ] )
auto_switch_tip = translatef ( " Current node: %s " , string.format ( ' <a href="%s">%s</a> ' , url , remarks ) ) .. " <br /> "
end
2023-04-09 23:35:07 +08:00
end
end
2022-03-11 18:29:27 +08:00
end
2023-04-10 09:16:48 +08:00
---- Node
node = s : taboption ( " Main " , ListValue , " node " , " <a style='color: red'> " .. translate ( " Node " ) .. " </a> " )
2022-03-11 18:29:27 +08:00
node : value ( " nil " , translate ( " Close " ) )
2023-04-23 09:28:00 +08:00
if not shunt_remark and auto_switch_tip then
2023-04-10 09:16:48 +08:00
node.description = auto_switch_tip
end
2022-03-11 18:29:27 +08:00
-- 分流
if ( has_v2ray or has_xray ) and # nodes_table > 0 then
2023-04-09 23:35:07 +08:00
local normal_list = { }
2023-05-25 23:35:42 +08:00
local balancing_list = { }
2023-04-09 23:35:07 +08:00
local shunt_list = { }
2023-08-12 09:06:45 +08:00
local iface_list = { }
2023-04-09 23:35:07 +08:00
for k , v in pairs ( nodes_table ) do
if v.node_type == " normal " then
normal_list [ # normal_list + 1 ] = v
end
2023-05-25 23:35:42 +08:00
if v.protocol and v.protocol == " _balancing " then
balancing_list [ # balancing_list + 1 ] = v
end
2023-04-09 23:35:07 +08:00
if v.protocol and v.protocol == " _shunt " then
shunt_list [ # shunt_list + 1 ] = v
end
2023-08-12 09:06:45 +08:00
if v.protocol and v.protocol == " _iface " then
iface_list [ # iface_list + 1 ] = v
end
2023-04-09 23:35:07 +08:00
end
2023-05-25 23:35:42 +08:00
local function get_cfgvalue ( shunt_node_id , option )
return function ( self , section )
return m : get ( shunt_node_id , option ) or " nil "
end
end
local function get_write ( shunt_node_id , option )
return function ( self , section , value )
m : set ( shunt_node_id , option , value )
end
end
if # normal_list > 0 then
for k , v in pairs ( shunt_list ) do
local vid = v.id
-- shunt node type, V2ray or Xray
local type = s : taboption ( " Main " , ListValue , vid .. " -type " , translate ( " Type " ) )
if has_v2ray then
type : value ( " V2ray " , translate ( " V2ray " ) )
end
if has_xray then
type : value ( " Xray " , translate ( " Xray " ) )
end
type.cfgvalue = get_cfgvalue ( v.id , " type " )
type.write = get_write ( v.id , " type " )
-- pre-proxy
o = s : taboption ( " Main " , Flag , vid .. " -preproxy_enabled " , translate ( " Preproxy " ) )
o : depends ( " node " , v.id )
o.rmempty = false
o.cfgvalue = get_cfgvalue ( v.id , " preproxy_enabled " )
o.write = get_write ( v.id , " preproxy_enabled " )
o = s : taboption ( " Main " , Value , vid .. " -main_node " , string.format ( ' <a style="color:red">%s</a> ' , translate ( " Preproxy Node " ) ) , translate ( " Set the node to be used as a pre-proxy. Each rule (including <code>Default</code>) has a separate switch that controls whether this rule uses the pre-proxy or not. " ) )
o : depends ( vid .. " -preproxy_enabled " , " 1 " )
for k1 , v1 in pairs ( balancing_list ) do
o : value ( v1.id , v1.remark )
end
2023-08-12 09:06:45 +08:00
for k1 , v1 in pairs ( iface_list ) do
o : value ( v1.id , v1.remark )
end
2023-05-25 23:35:42 +08:00
for k1 , v1 in pairs ( normal_list ) do
o : value ( v1.id , v1.remark )
end
if # o.keylist > 0 then
o.default = o.keylist [ 1 ]
end
o.cfgvalue = get_cfgvalue ( v.id , " main_node " )
o.write = get_write ( v.id , " main_node " )
if shunt_remark == " main " and auto_switch_tip then
o.description = auto_switch_tip
end
if ( has_v2ray and has_xray ) or ( v.type == " V2ray " and not has_v2ray ) or ( v.type == " Xray " and not has_xray ) then
type : depends ( " node " , v.id )
else
type : depends ( " node " , " hide " ) --不存在的依赖,即始终隐藏
end
uci : foreach ( appname , " shunt_rules " , function ( e )
local id = e [ " .name " ]
local node_option = vid .. " - " .. id .. " _node "
if id and e.remarks then
o = s : taboption ( " Main " , Value , node_option , string.format ( ' * <a href="%s" target="_blank">%s</a> ' , api.url ( " shunt_rules " , id ) , e.remarks ) )
o.cfgvalue = get_cfgvalue ( v.id , id )
o.write = get_write ( v.id , id )
o : depends ( " node " , v.id )
o.default = " nil "
o : value ( " nil " , translate ( " Close " ) )
o : value ( " _default " , translate ( " Default " ) )
o : value ( " _direct " , translate ( " Direct Connection " ) )
o : value ( " _blackhole " , translate ( " Blackhole " ) )
local pt = s : taboption ( " Main " , ListValue , vid .. " - " .. id .. " _proxy_tag " , string.format ( ' * <a style="color:red">%s</a> ' , e.remarks .. " " .. translate ( " Preproxy " ) ) )
pt.cfgvalue = get_cfgvalue ( v.id , id .. " _proxy_tag " )
pt.write = get_write ( v.id , id .. " _proxy_tag " )
pt : value ( " nil " , translate ( " Close " ) )
pt : value ( " main " , translate ( " Preproxy Node " ) )
pt.default = " nil "
for k1 , v1 in pairs ( balancing_list ) do
o : value ( v1.id , v1.remark )
end
2023-08-12 09:06:45 +08:00
for k1 , v1 in pairs ( iface_list ) do
o : value ( v1.id , v1.remark )
end
2023-05-25 23:35:42 +08:00
for k1 , v1 in pairs ( normal_list ) do
o : value ( v1.id , v1.remark )
pt : depends ( { [ node_option ] = v1.id , [ vid .. " -preproxy_enabled " ] = " 1 " } )
end
2023-04-09 23:35:07 +08:00
end
2023-05-25 23:35:42 +08:00
end )
local id = " default_node "
o = s : taboption ( " Main " , Value , vid .. " - " .. id , string.format ( ' * <a style="color:red">%s</a> ' , translate ( " Default " ) ) )
o.cfgvalue = get_cfgvalue ( v.id , id )
o.write = get_write ( v.id , id )
o : depends ( " node " , v.id )
o.default = " _direct "
o : value ( " _direct " , translate ( " Direct Connection " ) )
o : value ( " _blackhole " , translate ( " Blackhole " ) )
for k1 , v1 in pairs ( balancing_list ) do
o : value ( v1.id , v1.remark )
end
2023-08-12 09:06:45 +08:00
for k1 , v1 in pairs ( iface_list ) do
o : value ( v1.id , v1.remark )
end
2023-05-25 23:35:42 +08:00
for k1 , v1 in pairs ( normal_list ) do
o : value ( v1.id , v1.remark )
end
if shunt_remark == " default " and auto_switch_tip then
o.description = auto_switch_tip
end
local id = " default_proxy_tag "
o = s : taboption ( " Main " , ListValue , vid .. " - " .. id , string.format ( ' * <a style="color:red">%s</a> ' , translate ( " Default Preproxy " ) ) , translate ( " When using, localhost will connect this node first and then use this node to connect the default node. " ) )
o.cfgvalue = get_cfgvalue ( v.id , id )
o.write = get_write ( v.id , id )
o : value ( " nil " , translate ( " Close " ) )
o : value ( " main " , translate ( " Preproxy Node " ) )
for k1 , v1 in pairs ( normal_list ) do
if v1.protocol ~= " _balancing " then
o : depends ( { [ vid .. " -default_node " ] = v1.id , [ vid .. " -preproxy_enabled " ] = " 1 " } )
2023-04-09 23:35:07 +08:00
end
end
end
2023-05-25 23:35:42 +08:00
else
local tips = s : taboption ( " Main " , DummyValue , " tips " , " " )
tips.rawhtml = true
tips.cfgvalue = function ( t , n )
return string.format ( ' <a style="color: red">%s</a> ' , translate ( " There are no available nodes, please add or subscribe nodes first. " ) )
2023-04-09 23:35:07 +08:00
end
2023-05-25 23:35:42 +08:00
tips : depends ( { node = " nil " , [ " !reverse " ] = true } )
for k , v in pairs ( shunt_list ) do
tips : depends ( " node " , v.id )
2023-04-09 23:35:07 +08:00
end
2023-05-25 23:35:42 +08:00
for k , v in pairs ( balancing_list ) do
tips : depends ( " node " , v.id )
2023-04-10 09:16:48 +08:00
end
2023-04-09 23:35:07 +08:00
end
2022-03-11 18:29:27 +08:00
end
2022-04-19 07:44:18 +08:00
o = s : taboption ( " Main " , Flag , " localhost_proxy " , translate ( " Localhost Proxy " ) , translate ( " When selected, localhost can transparent proxy. " ) )
o.default = " 1 "
o.rmempty = false
2023-02-08 00:47:38 +08:00
node_socks_port = s : taboption ( " Main " , Value , " node_socks_port " , translate ( " Node " ) .. " Socks " .. translate ( " Listen Port " ) )
node_socks_port.default = 1070
node_socks_port.datatype = " port "
--[[
if has_v2ray or has_xray then
2023-04-09 23:35:07 +08:00
node_http_port = s : taboption ( " Main " , Value , " node_http_port " , translate ( " Node " ) .. " HTTP " .. translate ( " Listen Port " ) .. " " .. translate ( " 0 is not use " ) )
node_http_port.default = 0
node_http_port.datatype = " port "
2023-02-08 00:47:38 +08:00
end
] ] --
2022-03-11 18:29:27 +08:00
s : tab ( " DNS " , translate ( " DNS " ) )
2022-04-12 20:35:28 +08:00
o = s : taboption ( " DNS " , ListValue , " direct_dns_protocol " , translate ( " Direct DNS Protocol " ) )
o.default = " auto "
o : value ( " auto " , translate ( " Auto " ) )
2023-08-12 09:06:45 +08:00
--[[
2022-04-12 20:35:28 +08:00
o : value ( " udp " , " UDP " )
2022-03-11 18:29:27 +08:00
o : value ( " tcp " , " TCP " )
o : value ( " doh " , " DoH " )
2023-08-12 09:06:45 +08:00
] ] --
2022-04-12 20:35:28 +08:00
---- DNS Forward
o = s : taboption ( " DNS " , Value , " direct_dns " , translate ( " Direct DNS " ) )
o.datatype = " or(ipaddr,ipaddrport) "
o.default = " 119.29.29.29 "
o : value ( " 114.114.114.114 " , " 114.114.114.114 (114DNS) " )
o : value ( " 119.29.29.29 " , " 119.29.29.29 (DNSPod) " )
o : value ( " 223.5.5.5 " , " 223.5.5.5 (AliDNS) " )
o : depends ( " direct_dns_protocol " , " udp " )
o : depends ( " direct_dns_protocol " , " tcp " )
2022-03-11 18:29:27 +08:00
---- DoH
2022-04-12 20:35:28 +08:00
o = s : taboption ( " DNS " , Value , " direct_dns_doh " , translate ( " Direct DNS DoH " ) )
o.default = " https://223.5.5.5/dns-query "
o : value ( " https://1.12.12.12/dns-query " , " DNSPod 1 " )
o : value ( " https://120.53.53.53/dns-query " , " DNSPod 2 " )
o : value ( " https://223.5.5.5/dns-query " , " AliDNS " )
2022-03-11 18:29:27 +08:00
o.validate = doh_validate
2022-04-12 20:35:28 +08:00
o : depends ( " direct_dns_protocol " , " doh " )
2023-02-09 09:40:07 +08:00
o = s : taboption ( " DNS " , Value , " direct_dns_client_ip " , translate ( " Direct DNS EDNS Client Subnet " ) )
o.description = translate ( " Notify the DNS server when the DNS query is notified, the location of the client (cannot be a private IP address). " ) .. " <br /> " ..
2023-04-09 23:35:07 +08:00
translate ( " This feature requires the DNS server to support the Edns Client Subnet (RFC7871). " )
2023-02-09 09:40:07 +08:00
o.datatype = " ipaddr "
o : depends ( " direct_dns_protocol " , " tcp " )
o : depends ( " direct_dns_protocol " , " doh " )
o = s : taboption ( " DNS " , ListValue , " direct_dns_query_strategy " , translate ( " Direct Query Strategy " ) )
o.default = " UseIP "
o : value ( " UseIP " )
o : value ( " UseIPv4 " )
o : value ( " UseIPv6 " )
2022-04-12 20:35:28 +08:00
o = s : taboption ( " DNS " , ListValue , " remote_dns_protocol " , translate ( " Remote DNS Protocol " ) )
o : value ( " tcp " , " TCP " )
o : value ( " doh " , " DoH " )
2022-04-13 20:35:44 +08:00
o : value ( " udp " , " UDP " )
2022-03-11 18:29:27 +08:00
---- DNS Forward
2022-04-12 20:35:28 +08:00
o = s : taboption ( " DNS " , Value , " remote_dns " , translate ( " Remote DNS " ) )
2022-03-11 18:29:27 +08:00
o.datatype = " or(ipaddr,ipaddrport) "
o.default = " 1.1.1.1 "
2022-04-12 20:35:28 +08:00
o : value ( " 1.1.1.1 " , " 1.1.1.1 (CloudFlare) " )
o : value ( " 1.1.1.2 " , " 1.1.1.2 (CloudFlare-Security) " )
o : value ( " 8.8.4.4 " , " 8.8.4.4 (Google) " )
o : value ( " 8.8.8.8 " , " 8.8.8.8 (Google) " )
o : value ( " 9.9.9.9 " , " 9.9.9.9 (Quad9-Recommended) " )
o : value ( " 208.67.220.220 " , " 208.67.220.220 (OpenDNS) " )
o : value ( " 208.67.222.222 " , " 208.67.222.222 (OpenDNS) " )
o : depends ( " remote_dns_protocol " , " tcp " )
2022-04-13 20:35:44 +08:00
o : depends ( " remote_dns_protocol " , " udp " )
2022-04-12 20:35:28 +08:00
---- DoH
o = s : taboption ( " DNS " , Value , " remote_dns_doh " , translate ( " Remote DNS DoH " ) )
o.default = " https://1.1.1.1/dns-query "
o : value ( " https://1.1.1.1/dns-query " , " CloudFlare " )
o : value ( " https://1.1.1.2/dns-query " , " CloudFlare-Security " )
o : value ( " https://8.8.4.4/dns-query " , " Google 8844 " )
o : value ( " https://8.8.8.8/dns-query " , " Google 8888 " )
o : value ( " https://9.9.9.9/dns-query " , " Quad9-Recommended " )
o : value ( " https://208.67.222.222/dns-query " , " OpenDNS " )
o : value ( " https://dns.adguard.com/dns-query,176.103.130.130 " , " AdGuard " )
o : value ( " https://doh.libredns.gr/dns-query,116.202.176.26 " , " LibreDNS " )
o : value ( " https://doh.libredns.gr/ads,116.202.176.26 " , " LibreDNS (No Ads) " )
o.validate = doh_validate
o : depends ( " remote_dns_protocol " , " doh " )
o = s : taboption ( " DNS " , Value , " remote_dns_client_ip " , translate ( " Remote DNS EDNS Client Subnet " ) )
2022-03-11 18:29:27 +08:00
o.description = translate ( " Notify the DNS server when the DNS query is notified, the location of the client (cannot be a private IP address). " ) .. " <br /> " ..
2023-04-09 23:35:07 +08:00
translate ( " This feature requires the DNS server to support the Edns Client Subnet (RFC7871). " )
2022-03-11 18:29:27 +08:00
o.datatype = " ipaddr "
2022-04-12 20:35:28 +08:00
o : depends ( " remote_dns_protocol " , " tcp " )
o : depends ( " remote_dns_protocol " , " doh " )
2022-03-11 18:29:27 +08:00
2023-04-27 00:22:40 +08:00
o = s : taboption ( " DNS " , Flag , " remote_fakedns " , " FakeDNS " , translate ( " Use FakeDNS work in the shunt domain that proxy. " ) )
o.default = " 0 "
o.rmempty = false
2023-02-09 09:40:07 +08:00
o = s : taboption ( " DNS " , ListValue , " remote_dns_query_strategy " , translate ( " Remote Query Strategy " ) )
2022-04-13 20:35:44 +08:00
o.default = " UseIPv4 "
o : value ( " UseIP " )
o : value ( " UseIPv4 " )
2023-02-09 09:40:07 +08:00
o : value ( " UseIPv6 " )
2022-04-13 20:35:44 +08:00
hosts = s : taboption ( " DNS " , TextValue , " dns_hosts " , translate ( " Domain Override " ) )
hosts.rows = 5
hosts.wrap = " off "
2023-04-26 16:21:10 +08:00
o = s : taboption ( " DNS " , Button , " clear_ipset " , translate ( " Clear IPSET " ) , translate ( " Try this feature if the rule modification does not take effect. " ) )
o.inputstyle = " remove "
function o . write ( e , e )
2023-05-25 23:35:42 +08:00
luci.sys . call ( " [ -n \" $(nft list sets 2>/dev/null | grep \" passwall2_ \" ) \" ] && sh /usr/share/ " .. appname .. " /nftables.sh flush_nftset || sh /usr/share/ " .. appname .. " /iptables.sh flush_ipset > /dev/null 2>&1 & " )
2023-04-26 16:21:10 +08:00
luci.http . redirect ( api.url ( " log " ) )
end
2022-03-11 18:29:27 +08:00
s : tab ( " log " , translate ( " Log " ) )
o = s : taboption ( " log " , Flag , " close_log " , translate ( " Close Node Log " ) )
o.rmempty = false
loglevel = s : taboption ( " log " , ListValue , " loglevel " , translate ( " Log Level " ) )
loglevel.default = " warning "
loglevel : value ( " debug " )
loglevel : value ( " info " )
loglevel : value ( " warning " )
loglevel : value ( " error " )
s : tab ( " faq " , " FAQ " )
o = s : taboption ( " faq " , DummyValue , " " )
o.template = appname .. " /global/faq "
-- [[ Socks Server ]]--
o = s : taboption ( " Main " , Flag , " socks_enabled " , " Socks " .. translate ( " Main switch " ) )
o.rmempty = false
s = m : section ( TypedSection , " socks " , translate ( " Socks Config " ) )
s.anonymous = true
s.addremove = true
s.template = " cbi/tblsection "
function s . create ( e , t )
2023-05-07 23:35:16 +08:00
TypedSection.create ( e , api.gen_short_uuid ( ) )
2022-03-11 18:29:27 +08:00
end
o = s : option ( DummyValue , " status " , translate ( " Status " ) )
o.rawhtml = true
o.cfgvalue = function ( t , n )
2023-04-09 23:35:07 +08:00
return string.format ( ' <div class="_status" socks_id="%s"></div> ' , n )
2022-03-11 18:29:27 +08:00
end
---- Enable
o = s : option ( Flag , " enabled " , translate ( " Enable " ) )
o.default = 1
o.rmempty = false
socks_node = s : option ( ListValue , " node " , translate ( " Socks Node " ) )
2023-02-08 00:47:38 +08:00
local n = 1
2022-03-11 18:29:27 +08:00
uci : foreach ( appname , " socks " , function ( s )
2023-04-09 23:35:07 +08:00
if s [ " .name " ] == section then
return false
end
n = n + 1
2022-03-11 18:29:27 +08:00
end )
o = s : option ( Value , " port " , " Socks " .. translate ( " Listen Port " ) )
o.default = n + 1080
o.datatype = " port "
o.rmempty = false
if has_v2ray or has_xray then
2023-04-09 23:35:07 +08:00
o = s : option ( Value , " http_port " , " HTTP " .. translate ( " Listen Port " ) .. " " .. translate ( " 0 is not use " ) )
o.default = 0
o.datatype = " port "
2022-03-11 18:29:27 +08:00
end
for k , v in pairs ( nodes_table ) do
2023-04-09 23:35:07 +08:00
node : value ( v.id , v [ " remark " ] )
if v.type == " Socks " then
if has_v2ray or has_xray then
socks_node : value ( v.id , v [ " remark " ] )
end
else
socks_node : value ( v.id , v [ " remark " ] )
end
2022-03-11 18:29:27 +08:00
end
m : append ( Template ( appname .. " /global/footer " ) )
return m