From bacadd86617581ac152eeb7548c8388028132cfb Mon Sep 17 00:00:00 2001 From: ShanStone <31815718+ShanStone@users.noreply.github.com> Date: Mon, 24 Jul 2023 02:44:48 +0800 Subject: [PATCH] luci: initial tuic-client support --- luci-app-passwall2/Makefile | 2 +- .../cbi/passwall2/client/node_config.lua | 102 ++++++++++++++++++ .../luasrc/passwall2/util_tuic.lua | 57 ++++++++++ luci-app-passwall2/po/zh-cn/passwall2.po | 48 +++++++++ .../root/usr/share/passwall2/app.sh | 5 + 5 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 luci-app-passwall2/luasrc/passwall2/util_tuic.lua diff --git a/luci-app-passwall2/Makefile b/luci-app-passwall2/Makefile index 4486ac269..6ede0e2f7 100644 --- a/luci-app-passwall2/Makefile +++ b/luci-app-passwall2/Makefile @@ -5,7 +5,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-passwall2 -PKG_VERSION:=1.16-8 +PKG_VERSION:=1.17-1 PKG_RELEASE:= PKG_CONFIG_DEPENDS:= \ diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_config.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_config.lua index 226ae562c..b3a2b0338 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_config.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_config.lua @@ -98,6 +98,9 @@ end if api.is_finded("hysteria") then type:value("Hysteria", translate("Hysteria")) end +if api.is_finded("tuic-client") then + type:value("TUIC", translate("TUIC")) +end protocol = s:option(ListValue, "protocol", translate("Protocol")) protocol:value("vmess", translate("Vmess")) @@ -279,6 +282,7 @@ address:depends("type", "SSR") address:depends("type", "Brook") address:depends("type", "Naiveproxy") address:depends("type", "Hysteria") +address:depends("type", "TUIC") address:depends({ type = "V2ray", protocol = "vmess" }) address:depends({ type = "V2ray", protocol = "vless" }) address:depends({ type = "V2ray", protocol = "http" }) @@ -324,6 +328,7 @@ port:depends("type", "SSR") port:depends("type", "Brook") port:depends("type", "Naiveproxy") port:depends("type", "Hysteria") +port:depends("type", "TUIC") port:depends({ type = "V2ray", protocol = "vmess" }) port:depends({ type = "V2ray", protocol = "vless" }) port:depends({ type = "V2ray", protocol = "http" }) @@ -535,6 +540,7 @@ uuid:depends({ type = "V2ray", protocol = "vmess" }) uuid:depends({ type = "V2ray", protocol = "vless" }) uuid:depends({ type = "Xray", protocol = "vmess" }) uuid:depends({ type = "Xray", protocol = "vless" }) +uuid:depends({ type = "TUIC"}) tls = s:option(Flag, "tls", translate("TLS")) tls.default = 0 @@ -893,6 +899,102 @@ hysteria_disable_mtu_discovery:depends("type", "Hysteria") hysteria_lazy_start = s:option(Flag, "hysteria_lazy_start", translate("Lazy Start")) hysteria_lazy_start:depends("type", "Hysteria") +--[[ +-- Tuic username for local socks connect +tuic_passwd = s:option(Value, "tuic_socks_username", translate("TUIC UserName For Local Socks")) +tuic_passwd.rmempty = true +tuic_passwd.default = "" +tuic_passwd:depends("type", "TUIC") +-- Tuic Password for local socks connect +tuic_passwd = s:option(Value, "tuic_socks_password", translate("TUIC Password For Local Socks")) +tuic_passwd.password = true +tuic_passwd.rmempty = true +tuic_passwd.default = "" +tuic_passwd:depends("type", "TUIC") +--]] + +tuic_ip = s:option(Value, "tuic_ip", translate("Set the TUIC proxy server ip address")) +tuic_ip:depends("type", "TUIC") +tuic_ip.datatype = "ipaddr" +tuic_ip.rmempty = true + +tuic_udp_relay_mode = s:option(ListValue, "tuic_udp_relay_mode", translate("UDP relay mode")) +tuic_udp_relay_mode:depends("type", "TUIC") +tuic_udp_relay_mode:value("native", translate("native")) +tuic_udp_relay_mode:value("quic", translate("QUIC")) +tuic_udp_relay_mode.default = "native" +tuic_udp_relay_mode.rmempty = true + +tuic_congestion_control = s:option(ListValue, "tuic_congestion_control", translate("Congestion control algorithm")) +tuic_congestion_control:depends("type", "TUIC") +tuic_congestion_control:value("bbr", translate("BBR")) +tuic_congestion_control:value("cubic", translate("CUBIC")) +tuic_congestion_control:value("new_reno", translate("New Reno")) +tuic_congestion_control.default = "cubic" +tuic_congestion_control.rmempty = true + +tuic_heartbeat = s:option(Value, "tuic_heartbeat", translate("Heartbeat interval(second)")) +tuic_heartbeat:depends("type", "TUIC") +tuic_heartbeat.datatype = "uinteger" +tuic_heartbeat.default = "3" +tuic_heartbeat.rmempty = true + +tuic_timeout = s:option(Value, "tuic_timeout", translate("Timeout for establishing a connection to server(second)")) +tuic_timeout:depends("type", "TUIC") +tuic_timeout.datatype = "uinteger" +tuic_timeout.default = "8" +tuic_timeout.rmempty = true + +tuic_gc_interval = s:option(Value, "tuic_gc_interval", translate("Garbage collection interval(second)")) +tuic_gc_interval:depends("type", "TUIC") +tuic_gc_interval.datatype = "uinteger" +tuic_gc_interval.default = "3" +tuic_gc_interval.rmempty = true + +tuic_gc_lifetime = s:option(Value, "tuic_gc_lifetime", translate("Garbage collection lifetime(second)")) +tuic_gc_lifetime:depends("type", "TUIC") +tuic_gc_lifetime.datatype = "uinteger" +tuic_gc_lifetime.default = "15" +tuic_gc_lifetime.rmempty = true + +tuic_send_window = s:option(Value, "tuic_send_window", translate("TUIC send window")) +tuic_send_window.datatype = "uinteger" +tuic_send_window:depends("type", "TUIC") +tuic_send_window.default = 20971520 +tuic_send_window.rmempty = true + +tuic_receive_window = s:option(Value, "tuic_receive_window", translate("TUIC receive window")) +tuic_receive_window.datatype = "uinteger" +tuic_receive_window:depends("type", "TUIC") +tuic_receive_window.default = 10485760 +tuic_receive_window.rmempty = true + +tuic_max_package_size = s:option(Value, "tuic_max_package_size", translate("TUIC Maximum packet size the socks5 server can receive from external, in bytes")) +tuic_max_package_size.datatype = "uinteger" +tuic_max_package_size:depends("type", "TUIC") +tuic_max_package_size.default = 1500 +tuic_max_package_size.rmempty = true + +--Tuic settings for the local inbound socks5 server +tuic_dual_stack = s:option(Flag, "tuic_dual_stack", translate("Set if the listening socket should be dual-stack")) +tuic_dual_stack:depends("type", "TUIC") +tuic_dual_stack.default = 0 +tuic_dual_stack.rmempty = true + +tuic_disable_sni = s:option(Flag, "tuic_disable_sni", translate("Disable SNI")) +tuic_disable_sni:depends("type", "TUIC") +tuic_disable_sni.default = 0 +tuic_disable_sni.rmempty = true + +tuic_zero_rtt_handshake = s:option(Flag, "tuic_zero_rtt_handshake", translate("Enable 0-RTT QUIC handshake")) +tuic_zero_rtt_handshake:depends("type", "TUIC") +tuic_zero_rtt_handshake.default = 0 +tuic_zero_rtt_handshake.rmempty = true + +tuic_tls_alpn = s:option(DynamicList, "tuic_tls_alpn", translate("TLS ALPN")) +tuic_tls_alpn:depends({ type = "TUIC"}) +tuic_tls_alpn.rmempty = true + protocol.validate = function(self, value) if value == "_shunt" or value == "_balancing" then address.rmempty = true diff --git a/luci-app-passwall2/luasrc/passwall2/util_tuic.lua b/luci-app-passwall2/luasrc/passwall2/util_tuic.lua new file mode 100644 index 000000000..b37027c4f --- /dev/null +++ b/luci-app-passwall2/luasrc/passwall2/util_tuic.lua @@ -0,0 +1,57 @@ +module("luci.passwall2.util_tuic", package.seeall) +local api = require "luci.passwall2.api" +local uci = api.uci +local json = api.jsonc + +function gen_config(var) + local node_id = var["-node"] + if not node_id then + print("-node 不能为空") + return + end + local node = uci:get_all("passwall2", node_id) + local local_addr = var["-local_addr"] + local local_port = var["-local_port"] + local server_host = var["-server_host"] or node.address + local server_port = var["-server_port"] or node.port + local loglevel = var["-loglevel"] or "warn" + + local tuic= { + relay = { + server = server_host .. ":" .. server_port, + ip = node.tuic_ip, + uuid = node.uuid, + password = node.tuic_password, + -- certificates = node.tuic_certificate and { node.tuic_certpath } or nil, + udp_relay_mode = node.tuic_udp_relay_mode, + congestion_control = node.tuic_congestion_control, + heartbeat = node.tuic_heartbeat .. "s", + timeout = node.tuic_timeout .. "s", + gc_interval = node.tuic_gc_interval .. "s", + gc_lifetime = node.tuic_gc_lifetime .. "s", + alpn = node.tuic_tls_alpn, + disable_sni = (node.tuic_disable_sni == "1"), + zero_rtt_handshake = (node.tuic_zero_rtt_handshake == "1"), + send_window = tonumber(node.tuic_send_window), + receive_window = tonumber(node.tuic_receive_window) + }, + ["local"] = { + server = "[::]:" .. local_port, + username = node.tuic_socks_username, + password = node.tuic_socks_password, + dual_stack = (node.tuic_dual_stack == "1") and true or false, + max_packet_size = tonumber(node.tuic_max_package_size) + }, + log_level = loglevel + } + return json.stringify(tuic, 1) +end + +_G.gen_config = gen_config + +if arg[1] then + local func =_G[arg[1]] + if func then + print(func(api.get_function_args(arg))) + end +end diff --git a/luci-app-passwall2/po/zh-cn/passwall2.po b/luci-app-passwall2/po/zh-cn/passwall2.po index d7ff4d0bf..1f38d6047 100644 --- a/luci-app-passwall2/po/zh-cn/passwall2.po +++ b/luci-app-passwall2/po/zh-cn/passwall2.po @@ -1063,6 +1063,54 @@ msgstr "SS AEAD节点使用类型" msgid "Trojan Node Use Type" msgstr "Trojan节点使用类型" +msgid "Set the TUIC proxy server ip address" +msgstr "指定远程TUIC服务器IP" + +msgid "TUIC User Password For Connect Remote Server" +msgstr "用于远程TUIC服务器连接的密码" + +msgid "TUIC UserName For Local Socks" +msgstr "用于本地Socks服务器连接的用户名" + +msgid "TUIC Password For Local Socks" +msgstr "用于本地Socks服务器连接的密码" + +msgid "UDP relay mode" +msgstr "UDP中继模式" + +msgid "Congestion control algorithm" +msgstr "拥塞控制算法" + +msgid "Heartbeat interval(second)" +msgstr "保活心跳包发送间隔(单位:秒)" + +msgid "Timeout for establishing a connection to server(second)" +msgstr "连接超时时间(单位:秒)" + +msgid "Garbage collection interval(second)" +msgstr "UDP数据包片残片清理间隔(单位:秒)" + +msgid "Garbage collection lifetime(second)" +msgstr "UDP数据包残片在服务器的保留时间(单位:秒)" + +msgid "Disable SNI" +msgstr "关闭SNI服务器名称指示" + +msgid "Enable 0-RTT QUIC handshake" +msgstr "客户端启用 0-RTT QUIC 连接握手" + +msgid "TUIC send window" +msgstr "发送窗口(无需确认即可发送的最大字节数:默认8Mb*2)" + +msgid "TUIC receive window" +msgstr "接收窗口(无需确认即可接收的最大字节数:默认8Mb)" + +msgid "TUIC Maximum packet size the socks5 server can receive from external, in bytes" +msgstr "TUIC socks5 服务器可以从外部接收的最大数据包大小(以字节为单位)" + +msgid "Set if the listening socket should be dual-stack" +msgstr "设置监听套接字为双栈" + msgid "
none: default, no masquerade, data sent is packets with no characteristics.
srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).
utp: packets disguised as uTP will be recognized as bittorrent downloaded data.
wechat-video: packets disguised as WeChat video calls.
dtls: disguised as DTLS 1.2 packet.
wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)" msgstr "
none:默认值,不进行伪装,发送的数据是没有特征的数据包。
srtp:伪装成 SRTP 数据包,会被识别为视频通话数据(如 FaceTime)。
utp:伪装成 uTP 数据包,会被识别为 BT 下载数据。
wechat-video:伪装成微信视频通话的数据包。
dtls:伪装成 DTLS 1.2 数据包。
wireguard:伪装成 WireGuard 数据包。(并不是真正的 WireGuard 协议)" diff --git a/luci-app-passwall2/root/usr/share/passwall2/app.sh b/luci-app-passwall2/root/usr/share/passwall2/app.sh index 5fc71ab8d..7f23bbf63 100755 --- a/luci-app-passwall2/root/usr/share/passwall2/app.sh +++ b/luci-app-passwall2/root/usr/share/passwall2/app.sh @@ -31,6 +31,7 @@ UTIL_SS=$LUA_UTIL_PATH/util_shadowsocks.lua UTIL_XRAY=$LUA_UTIL_PATH/util_xray.lua UTIL_NAIVE=$LUA_UTIL_PATH/util_naiveproxy.lua UTIL_HYSTERIA=$LUA_UTIL_PATH/util_hysteria.lua +UTIL_TUIC=$LUA_UTIL_PATH/util_tuic.lua V2RAY_ARGS="" V2RAY_CONFIG="" @@ -529,6 +530,10 @@ run_socks() { lua $UTIL_HYSTERIA gen_config -node $node -local_socks_port $socks_port -server_host $server_host -server_port $port ${_extra_param} > $config_file ln_run "$(first_type $(config_t_get global_app hysteria_file))" "hysteria" $log_file -c "$config_file" client ;; + tuic) + lua $UTIL_TUIC gen_config -node $node -local_addr $bind -local_port $socks_port -server_host $server_host -server_port $port > $config_file + ln_run "$(first_type tuic-client)" "tuic-client" $log_file -c "$config_file" + ;; esac # http to socks