diff --git a/UnblockNeteaseMusic/Makefile b/UnblockNeteaseMusic/Makefile
index a05bbf16d..f5fba0a56 100644
--- a/UnblockNeteaseMusic/Makefile
+++ b/UnblockNeteaseMusic/Makefile
@@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=UnblockNeteaseMusic
PKG_BASE_VERSION:=0.27.4-patch.1
-PKG_RELEASE:=$(AUTORELEASE)
+PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/UnblockNeteaseMusic/server.git
diff --git a/brook/Makefile b/brook/Makefile
index 469defe43..4e2e15ab5 100644
--- a/brook/Makefile
+++ b/brook/Makefile
@@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=brook
PKG_VERSION:=20240214
-PKG_RELEASE:=$(AUTORELEASE)
+PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/txthinking/brook/tar.gz/v$(PKG_VERSION)?
diff --git a/luci-app-passwall2/Makefile b/luci-app-passwall2/Makefile
index 428b2245b..f473ed261 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.25-5
+PKG_VERSION:=1.26-1
PKG_RELEASE:=
PKG_CONFIG_DEPENDS:= \
diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua
index 275380518..0c03cf3db 100644
--- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua
+++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua
@@ -141,9 +141,25 @@ if has_xray then
s_xray.anonymous = true
s_xray.addremove = false
+ 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."))
+ o.default = 0
+
+ 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."))
+ o.default = "tlshello"
+ o:value("1-3", "1-3")
+ o:value("tlshello", "tlshello")
+ o:depends("fragment", true)
+
+ o = s_xray:option(Value, "fragment_length", translate("Fragment Length"), translate("Fragmented packet length (byte)"))
+ o.default = "10-20"
+ o:depends("fragment", true)
+
+ o = s_xray:option(Value, "fragment_interval", translate("Fragment Interval"), translate("Fragmentation interval (ms)"))
+ o.default = "10-20"
+ o:depends("fragment", true)
+
o = s_xray:option(Flag, "sniffing", translate("Sniffing"), translate("When using the shunt, must be enabled, otherwise the shunt will invalid."))
o.default = 1
- o.rmempty = false
o = s_xray:option(Flag, "route_only", translate("Sniffing Route Only"))
o.default = 0
diff --git a/luci-app-passwall2/luasrc/passwall2/util_xray.lua b/luci-app-passwall2/luasrc/passwall2/util_xray.lua
index fcbfb4452..676271308 100644
--- a/luci-app-passwall2/luasrc/passwall2/util_xray.lua
+++ b/luci-app-passwall2/luasrc/passwall2/util_xray.lua
@@ -43,9 +43,11 @@ function gen_outbound(flag, node, tag, proxy_table)
local proxy = 0
local proxy_tag = "nil"
+ local fragment = nil
if proxy_table ~= nil and type(proxy_table) == "table" then
proxy = proxy_table.proxy or 0
proxy_tag = proxy_table.tag or "nil"
+ fragment = proxy_table.fragment or nil
end
if node.type == "Xray" then
@@ -130,6 +132,7 @@ function gen_outbound(flag, node, tag, proxy_table)
mark = 255,
tcpMptcp = (node.tcpMptcp == "1") and true or nil,
tcpNoDelay = (node.tcpNoDelay == "1") and true or nil,
+ dialerProxy = fragment and "fragment" or nil
},
network = node.transport,
security = node.stream_security,
@@ -639,7 +642,7 @@ function gen_config(var)
end
if is_new_blc_node then
local blc_node = uci:get_all(appname, blc_node_id)
- local outbound = gen_outbound(flag, blc_node, blc_node_tag)
+ local outbound = gen_outbound(flag, blc_node, blc_node_tag, { fragment = xray_settings.fragment == "1" or nil })
if outbound then
table.insert(outbounds, outbound)
valid_nodes[#valid_nodes + 1] = blc_node_tag
@@ -717,7 +720,7 @@ function gen_config(var)
preproxy_enabled = false
end
elseif preproxy_node and api.is_normal_node(preproxy_node) then
- local preproxy_outbound = gen_outbound(flag, preproxy_node, preproxy_tag)
+ local preproxy_outbound = gen_outbound(flag, preproxy_node, preproxy_tag, { fragment = xray_settings.fragment == "1" or nil })
if preproxy_outbound then
table.insert(outbounds, preproxy_outbound)
else
@@ -819,7 +822,14 @@ function gen_config(var)
})
end
end
- local _outbound = gen_outbound(flag, _node, rule_name, { proxy = proxy and 1 or 0, tag = proxy and preproxy_tag or nil })
+ local proxy_table = {
+ proxy = proxy and 1 or 0,
+ tag = proxy and preproxy_tag or nil
+ }
+ if xray_settings.fragment == "1" and not proxy_table.tag then
+ proxy_table.fragment = true
+ end
+ local _outbound = gen_outbound(flag, _node, rule_name, proxy_table)
if _outbound then
table.insert(outbounds, _outbound)
if proxy then preproxy_used = true end
@@ -998,7 +1008,7 @@ function gen_config(var)
sys.call("touch /tmp/etc/passwall2/iface/" .. node.iface)
end
else
- outbound = gen_outbound(flag, node)
+ outbound = gen_outbound(flag, node, nil, { fragment = xray_settings.fragment == "1" or nil })
end
if outbound then table.insert(outbounds, outbound) end
routing = {
@@ -1340,6 +1350,28 @@ function gen_config(var)
-- }
}
}
+
+ if xray_settings.fragment == "1" then
+ table.insert(outbounds, {
+ protocol = "freedom",
+ tag = "fragment",
+ settings = {
+ domainStrategy = (direct_dns_query_strategy and direct_dns_query_strategy ~= "") and direct_dns_query_strategy or "UseIP",
+ fragments = {
+ packets = (xray_settings.fragment_packets and xray_settings.fragment_packets ~= "") and xray_settings.fragment_packets,
+ length = (xray_settings.fragment_length and xray_settings.fragment_length ~= "") and xray_settings.fragment_length,
+ interval = (xray_settings.fragment_interval and xray_settings.fragment_interval ~= "") and xray_settings.fragment_interval
+ }
+ },
+ streamSettings = {
+ sockopt = {
+ mark = 255,
+ tcpNoDelay = true
+ }
+ }
+ })
+ end
+
table.insert(outbounds, {
protocol = "freedom",
tag = "direct",
diff --git a/luci-app-passwall2/po/zh-cn/passwall2.po b/luci-app-passwall2/po/zh-cn/passwall2.po
index 869b0dfb0..2211a3163 100644
--- a/luci-app-passwall2/po/zh-cn/passwall2.po
+++ b/luci-app-passwall2/po/zh-cn/passwall2.po
@@ -1449,3 +1449,27 @@ msgstr "禁用 TLS 记录的自适应大小调整"
msgid "Enable Multipath TCP, need to be enabled in both server and client configuration."
msgstr "启用 Multipath TCP,需在服务端和客户端配置中同时启用。"
+
+msgid "Fragment"
+msgstr "分片"
+
+msgid "TCP fragments, which can deceive the censorship system in some cases, such as bypassing SNI blacklists."
+msgstr "TCP 分片,在某些情况下可以欺骗审查系统,比如绕过 SNI 黑名单。"
+
+msgid "Fragment Packets"
+msgstr "分片方式"
+
+msgid " \"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."
+msgstr " \"1-3\" 是 TCP 的流切片,应用于客户端第 1 至第 3 次写数据。\"tlshello\" 是 TLS 握手包切片。"
+
+msgid "Fragment Length"
+msgstr "分片包长"
+
+msgid "Fragmented packet length (byte)"
+msgstr "分片包长 (byte)"
+
+msgid "Fragment Interval"
+msgstr "分片间隔"
+
+msgid "Fragmentation interval (ms)"
+msgstr "分片间隔(ms)"
diff --git a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua
index 3bdb464cc..eb716f062 100644
--- a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua
+++ b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua
@@ -323,33 +323,39 @@ o = s:option(Value, "hy2_auth", translate("Users Authentication"))
o:depends("type", "hysteria")
o.rmempty = false
-o = s:option(ListValue, "transport_protocol", translate("Protocol"))
-o:depends("type", "hysteria")
-o:value("udp", translate("udp"))
-o.default = "udp"
-o.rmempty = true
-
-o = s:option(Flag, "port_hopping", translate("Enable Port Hopping"))
+o = s:option(Flag, "flag_port_hopping", translate("Enable Port Hopping"))
o:depends("type", "hysteria")
o.rmempty = true
o.default = "0"
-o = s:option(Value, "hopinterval", translate("Port Hopping Interval(Unit:Second)"))
-o:depends({type = "hysteria", port_hopping = true})
+o = s:option(Value, "port_range", translate("Port Range"))
+o:depends({type = "hysteria", flag_port_hopping = true})
+o.datatype = "portrange"
+o.rmempty = true
+
+o = s:option(Flag, "flag_transport", translate("Enable Transport Protocol Settings"))
+o:depends("type", "hysteria")
+o.rmempty = true
+o.default = "0"
+
+o = s:option(ListValue, "transport_protocol", translate("Transport Protocol"))
+o:depends({type = "hysteria", flag_transport = true})
+o:value("udp", translate("UDP"))
+o.default = "udp"
+o.rmempty = true
+
+o = s:option(Value, "hopinterval", translate("Hop Interval(Unit:Second)"))
+o:depends({type = "hysteria", flag_transport = true, flag_port_hopping = true})
o.datatype = "uinteger"
o.rmempty = true
o.default = "30"
-o = s:option(Value, "port_range", translate("Port Range"))
-o:depends({type = "hysteria", port_hopping = true})
-o.rmempty = true
-
-o = s:option(Flag, "lazy_mode", translate("Enable Lazy Mode"))
+o = s:option(Flag, "flag_obfs", translate("Enable Obfuscation"))
o:depends("type", "hysteria")
o.rmempty = true
o.default = "0"
-o = s:option(Flag, "flag_obfs", translate("Enable Obfuscation"))
+o = s:option(Flag, "lazy_mode", translate("Lazy Mode"))
o:depends("type", "hysteria")
o.rmempty = true
o.default = "0"
@@ -369,6 +375,11 @@ o:depends("type", "hysteria")
o.rmempty = true
o.default = "0"
+o = s:option(Flag, "disablepathmtudiscovery", translate("Disable QUIC path MTU discovery."))
+o:depends({type = "hysteria",flag_quicparam = "1"})
+o.rmempty = true
+o.default = false
+
--[[Hysteria2 QUIC parameters setting]]
o = s:option(Value, "initstreamreceivewindow", translate("QUIC initStreamReceiveWindow"))
o:depends({type = "hysteria", flag_quicparam = "1"})
@@ -406,11 +417,6 @@ o.rmempty = true
o.datatype = "uinteger"
o.default = "10"
-o = s:option(Flag, "disablepathmtudiscovery", translate("Disable Path MTU discovery"))
-o:depends({type = "hysteria", flag_quicparam = "1"})
-o.rmempty = true
-o.default = false
-
--[[ Shadow-TLS Options ]]
o = s:option(ListValue, "shadowtls_protocol", translate("shadowTLS protocol Version"))
@@ -902,9 +908,7 @@ o:depends("reality", true)
o.rmempty = true
o = s:option(DynamicList, "tls_alpn", translate("TLS ALPN"))
-o:depends("tls", true)
-o:depends("type", "tuic")
-o:depends("type", "hysteria")
+o:depends({type = "tuic", tls = true})
o.rmempty = true
-- [[ allowInsecure ]]--
@@ -1187,7 +1191,7 @@ if is_finded("kcptun-client") then
o:depends("type", "ss")
o = s:option(Value, "kcp_port", translate("KcpTun Port"))
- o.datatype = "port"
+ o.datatype = "portrange"
o.default = 4000
o:depends("type", "ssr")
o:depends("type", "ss")
diff --git a/luci-app-ssr-plus/po/zh-cn/ssr-plus.po b/luci-app-ssr-plus/po/zh-cn/ssr-plus.po
index 8d3bdd7ea..6e70883ee 100644
--- a/luci-app-ssr-plus/po/zh-cn/ssr-plus.po
+++ b/luci-app-ssr-plus/po/zh-cn/ssr-plus.po
@@ -181,6 +181,9 @@ msgstr "注意: 如果服务器使用 userpass 验证,格式必须是 userna
msgid "Enable Port Hopping"
msgstr "启用端口跃迁"
+msgid "Enable Transport Protocol Settings"
+msgstr "启用传输协议设置"
+
msgid "Port Range"
msgstr "端口范围值"
diff --git a/luci-app-ssr-plus/root/etc/init.d/shadowsocksr b/luci-app-ssr-plus/root/etc/init.d/shadowsocksr
index a9a7e57a8..9f2690243 100755
--- a/luci-app-ssr-plus/root/etc/init.d/shadowsocksr
+++ b/luci-app-ssr-plus/root/etc/init.d/shadowsocksr
@@ -433,7 +433,7 @@ start_udp() {
hysteria)
gen_config_file $UDP_RELAY_SERVER $type 2 $tmp_udp_port
ln_start_bin $(first_type hysteria) hysteria client --config $udp_config_file
- echolog "UDP TPROXY Relay:$($(first_type "hysteria") version | awk '{print "Hhysteria2: " $2}' | head -9 | tail +9) Started!"
+ echolog "UDP TPROXY Relay:$($(first_type "hysteria") version | grep Version | awk '{print "Hysteria2:" $2}') Started!"
;;
tuic)
# FIXME: ipt2socks cannot handle udp reply from tuic
@@ -564,7 +564,7 @@ start_shunt() {
fi
ln_start_bin $(first_type hysteria) hysteria client --config $shunt_config_file
shunt_dns_command
- echolog "shunt:$($(first_type hysteria) version | awk '{print "Hhysteria2: " $2}' | head -9 | tail +9) Started!"
+ echolog "shunt:$($(first_type hysteria) version | grep Version | awk '{print "Hysteria2:" $2})' Started!"
;;
tuic)
local chain_shunt_port="30${tmp_shunt_port}"
@@ -663,7 +663,7 @@ start_local() {
if [ "$_local" == "2" ]; then
gen_config_file $LOCAL_SERVER $type 4 0 $local_port
ln_start_bin $(first_type hysteria) hysteria client --config $local_config_file
- echolog "Global_Socks5:$($(first_type hysteria) version | awk '{print "Hhysteria2: " $2}' | head -9 | tail +9) Started!"
+ echolog "Global_Socks5:$($(first_type hysteria) version | grep Version | awk '{print "Hysteria2:" $2}') Started!"
fi
;;
tuic)
@@ -758,7 +758,7 @@ Start_Run() {
hysteria)
gen_config_file $GLOBAL_SERVER $type 1 $tcp_port $socks_port
ln_start_bin $(first_type hysteria) hysteria client --config $tcp_config_file
- echolog "Main node:$($(first_type hysteria) version | awk '{print "Hhysteria2: " $2}' | head -9 | tail +9) Started!"
+ echolog "Main node:$($(first_type hysteria) version | grep Version | awk '{print "Hysteria2:" $2}') Started!"
;;
tuic)
local PARAM
diff --git a/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua
index 02603c3cf..91498b0bf 100755
--- a/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua
+++ b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua
@@ -392,7 +392,7 @@ local ss = {
reuse_port = true
}
local hysteria = {
- server = (server.port_range and (server.server .. ":" .. server.port_range)) or (server.server_port and (server.server .. ":" .. server.server_port)),
+ server = (server.server_port and (server.port_range and (server.server .. ":" .. server.server_port .. "," .. server.port_range) or server.server .. ":" .. server.server_port) or (server.port_range and server.server .. ":" .. server.port_range or server.server .. ":443")),
bandwidth = {
up = tonumber(server.uplink_capacity) and tonumber(server.uplink_capacity) .. " mbps" or nil,
down = tonumber(server.downlink_capacity) and tonumber(server.downlink_capacity) .. " mbps" or nil
@@ -401,12 +401,13 @@ local hysteria = {
listen = "0.0.0.0:" .. tonumber(socks_port),
disable_udp = false
} or nil,
- transport = {
- type = server.transport_protocol,
- udp = {
- hopInterval = tonumber(server.hopinterval) and tonumber(server.hopinterval) .. "s" or "30s"
- }
- },
+ transport = (server.transport_protocol) and {
+ type = (server.transport_protocol) or udp,
+ udp = (server.port_range and (server.hopinterval) and {
+ hopInterval = (server.port_range and (tonumber(server.hopinterval) .. "s") or nil)
+ } or nil)
+ } or nil,
+
--[[
tcpTProxy = (proto:find("tcp") and local_port ~= "0") and {
listen = "0.0.0.0:" .. tonumber(local_port)
diff --git a/luci-app-xray/README.md b/luci-app-xray/README.md
index 1456259dc..2ea2ccf78 100644
--- a/luci-app-xray/README.md
+++ b/luci-app-xray/README.md
@@ -44,6 +44,7 @@ Fork this repository and:
* 2024-02-18 chore: optimize code style; bump version
* 2024-02-19 fix: several DNS related validation
+* 2024-02-20 fix: domain match priority; stricter resolve options; socks / http auth
## Changelog since 3.3.0
diff --git a/luci-app-xray/core/root/usr/share/xray/feature/dns.mjs b/luci-app-xray/core/root/usr/share/xray/feature/dns.mjs
index 9f6bb4080..7375a0a19 100644
--- a/luci-app-xray/core/root/usr/share/xray/feature/dns.mjs
+++ b/luci-app-xray/core/root/usr/share/xray/feature/dns.mjs
@@ -9,18 +9,42 @@ const fallback_secure_dns = "8.8.8.8:53";
const fallback_default_dns = "1.1.1.1:53";
const geosite_existence = access("/usr/share/xray/geosite.dat") || false;
-function split_ipv4_host_port(val, port_default) {
- const result = match(val, /^([0-9\.]+):([0-9]+)$/);
- if (result == null) {
+function parse_ip_port(val, port_default) {
+ const split_dot = split(val, ".");
+ if (length(split_dot) > 1) {
+ const split_ipv4 = split(val, ":");
return {
- address: val,
- port: int(port_default)
+ ip: split_ipv4[0],
+ port: int(split_ipv4[1])
+ };
+ }
+ const split_ipv6_port = split(val, "]:");
+ if (length(split_ipv6_port) == 2) {
+ return {
+ ip: ltrim(split_ipv6_port[0], "["),
+ port: int(split_ipv6_port[1]),
};
}
-
return {
- address: result[1],
- port: int(result[2])
+ ip: val,
+ port: port_default
+ };
+}
+
+function format_dns(method, val) {
+ const parsed = parse_ip_port(val, 53);
+ if (method == "udp") {
+ return {
+ address: parsed["ip"],
+ port: parsed["port"]
+ };
+ }
+ let url_suffix = "";
+ if (substr(method, 0, 5) == "https") {
+ url_suffix = "/dns-query";
+ }
+ return {
+ address: `${method}://${val}${url_suffix}`
};
}
@@ -52,7 +76,7 @@ export function dns_server_inbounds(proxy) {
let result = [];
const dns_port = int(proxy["dns_port"] || 5300);
const dns_count = int(proxy["dns_count"] || 3);
- const default_dns = split_ipv4_host_port(proxy["default_dns"] || fallback_default_dns, 53);
+ const default_dns = format_dns("udp", proxy["default_dns"] || fallback_default_dns);
for (let i = dns_port; i <= dns_port + dns_count; i++) {
push(result, {
port: i,
@@ -126,8 +150,8 @@ export function dns_server_outbounds(proxy) {
};
export function dns_conf(proxy, config, manual_tproxy, fakedns) {
- const fast_dns_object = split_ipv4_host_port(proxy["fast_dns"] || fallback_fast_dns, 53);
- const default_dns_object = split_ipv4_host_port(proxy["default_dns"] || fallback_default_dns, 53);
+ const fast_dns_object = format_dns("udp", proxy["fast_dns"] || fallback_fast_dns);
+ const default_dns_object = format_dns("udp", proxy["default_dns"] || fallback_default_dns);
let domain_names_set = {};
let domain_extra_options = {};
@@ -137,7 +161,7 @@ export function dns_conf(proxy, config, manual_tproxy, fakedns) {
continue;
}
if (server["domain_resolve_dns"]) {
- domain_extra_options[server["server"]] = server["domain_resolve_dns"];
+ domain_extra_options[server["server"]] = `${server["domain_resolve_dns_method"] || "udp"};${server["domain_resolve_dns"]}`;
} else {
domain_names_set[`domain:${server["server"]}`] = true;
}
@@ -147,17 +171,21 @@ export function dns_conf(proxy, config, manual_tproxy, fakedns) {
for (let k in keys(domain_extra_options)) {
const v = domain_extra_options[k];
let original = resolve_merged[v] || [];
- push(original, k);
+ push(original, `domain:${k}`);
resolve_merged[v] = original;
}
let servers = [
...fake_dns_domains(fakedns),
...map(keys(resolve_merged), function (k) {
- let i = split_ipv4_host_port(k);
- i["domains"] = uniq(resolve_merged[k]);
- i["skipFallback"] = true;
- return i;
+ const dns_split = split(k, ";");
+ const resolve_dns_object = format_dns(dns_split[0], dns_split[1]);
+ return {
+ address: resolve_dns_object["address"],
+ port: resolve_dns_object["port"],
+ domains: uniq(resolve_merged[k]),
+ skipFallback: true,
+ };
}),
default_dns_object,
{
@@ -169,7 +197,7 @@ export function dns_conf(proxy, config, manual_tproxy, fakedns) {
];
if (length(secure_domain_rules(proxy)) > 0) {
- const secure_dns_object = split_ipv4_host_port(proxy["secure_dns"] || fallback_secure_dns, 53);
+ const secure_dns_object = format_dns("udp", proxy["secure_dns"] || fallback_secure_dns);
push(servers, {
address: secure_dns_object["address"],
port: secure_dns_object["port"],
@@ -199,3 +227,18 @@ export function dns_conf(proxy, config, manual_tproxy, fakedns) {
queryStrategy: "UseIP"
};
};
+
+export function dns_direct_servers(config) {
+ let result = [];
+ for (let server in filter(values(config), i => i[".type"] == "servers")) {
+ if (iptoarr(server["server"])) {
+ continue;
+ }
+ if (server["domain_resolve_dns"]) {
+ if (index(server["domain_resolve_dns_method"], "local") > 1) {
+ push(result, parse_ip_port(server["domain_resolve_dns"])["ip"]);
+ }
+ }
+ }
+ return result;
+};
diff --git a/luci-app-xray/core/root/usr/share/xray/firewall_include.ut b/luci-app-xray/core/root/usr/share/xray/firewall_include.ut
index 8361a540f..a03d40576 100644
--- a/luci-app-xray/core/root/usr/share/xray/firewall_include.ut
+++ b/luci-app-xray/core/root/usr/share/xray/firewall_include.ut
@@ -3,6 +3,7 @@
"use strict";
import { stat } from "fs";
import { load_config } from "./common/config.mjs";
+ import { dns_direct_servers } from "./feature/dns.mjs";
const ignore_tp_spec_def_gw = stat("/usr/share/xray/ignore_tp_spec_def_gw");
const config = load_config();
const general = config[filter(keys(config), k => config[k][".type"] == "general")[0]];
@@ -16,6 +17,9 @@
let wan_bp_ips_no_dns = general.wan_bp_ips || [];
let wan_fw_ips_no_dns = general.wan_fw_ips || [];
push(wan_bp_ips_no_dns, split(general.fast_dns || "223.5.5.5:53", ":")[0]);
+ for (let i in dns_direct_servers(config)) {
+ push(wan_bp_ips_no_dns, i);
+ }
push(wan_fw_ips_no_dns, split(general.secure_dns || "8.8.8.8:53", ":")[0]);
const wan_bp_ips_v4 = filter(uniq(wan_bp_ips_no_dns), v => index(v, ":") == -1);
const wan_bp_ips_v6 = filter(uniq(wan_bp_ips_no_dns), v => index(v, ":") != -1);
diff --git a/luci-app-xray/core/root/usr/share/xray/protocol/http.mjs b/luci-app-xray/core/root/usr/share/xray/protocol/http.mjs
index 8c80c65c7..b340243ec 100644
--- a/luci-app-xray/core/root/usr/share/xray/protocol/http.mjs
+++ b/luci-app-xray/core/root/usr/share/xray/protocol/http.mjs
@@ -6,6 +6,15 @@ export function http_outbound(server, tag) {
const stream_settings_object = stream_settings(server, "http", tag);
const stream_settings_result = stream_settings_object["stream_settings"];
const dialer_proxy = stream_settings_object["dialer_proxy"];
+ let users = null;
+ if (server["username"] && server["password"]) {
+ users = [
+ {
+ user: server["username"],
+ pass: server["password"],
+ }
+ ];
+ }
return {
outbound: {
protocol: "http",
@@ -15,12 +24,7 @@ export function http_outbound(server, tag) {
{
address: server["server"],
port: int(server["server_port"]),
- users: [
- {
- user: server["username"],
- pass: server["password"],
- }
- ]
+ users: users
}
]
},
diff --git a/luci-app-xray/core/root/usr/share/xray/protocol/socks.mjs b/luci-app-xray/core/root/usr/share/xray/protocol/socks.mjs
index 95c8619c1..4d11a696c 100644
--- a/luci-app-xray/core/root/usr/share/xray/protocol/socks.mjs
+++ b/luci-app-xray/core/root/usr/share/xray/protocol/socks.mjs
@@ -6,6 +6,15 @@ export function socks_outbound(server, tag) {
const stream_settings_object = stream_settings(server, "socks", tag);
const stream_settings_result = stream_settings_object["stream_settings"];
const dialer_proxy = stream_settings_object["dialer_proxy"];
+ let users = null;
+ if (server["username"] && server["password"]) {
+ users = [
+ {
+ user: server["username"],
+ pass: server["password"],
+ }
+ ];
+ }
return {
outbound: {
protocol: "socks",
@@ -15,12 +24,7 @@ export function socks_outbound(server, tag) {
{
address: server["server"],
port: int(server["server_port"]),
- users: [
- {
- user: server["username"],
- pass: server["password"],
- }
- ]
+ users: users
}
]
},
diff --git a/luci-app-xray/core/root/www/luci-static/resources/view/xray/core.js b/luci-app-xray/core/root/www/luci-static/resources/view/xray/core.js
index 9b0b408bb..59ecde180 100644
--- a/luci-app-xray/core/root/www/luci-static/resources/view/xray/core.js
+++ b/luci-app-xray/core/root/www/luci-static/resources/view/xray/core.js
@@ -42,14 +42,15 @@ function list_folded_format(config_data, k, noun, max_chars, mapping, empty) {
};
}
-function destination_format(config_data, k, max_chars, null_itatic) {
- const null_placeholder = function () {
- if (null_itatic) {
- return `${_("direct")}`;
+function destination_format(config_data, k, e, max_chars) {
+ return function (s) {
+ if (e) {
+ if (!uci.get(config_data, s, e)) {
+ return `${_("use global settings")}`;
+ }
}
- return _("direct");
- }();
- return list_folded_format(config_data, k, "outbounds", max_chars, v => uci.get(config_data, v, "alias"), null_placeholder);
+ return list_folded_format(config_data, k, "outbounds", max_chars, v => uci.get(config_data, v, "alias"), `${_("direct")}`)(s);
+ };
}
function extra_outbound_format(config_data, s, select_item) {
@@ -191,16 +192,6 @@ return view.extend({
o.datatype = 'host';
o.rmempty = false;
- o = ss.taboption('general', form.ListValue, 'domain_strategy', _('Domain Strategy'), _("Whether to use IPv4 or IPv6 address if Server Hostname is a domain."));
- o.value("UseIP");
- o.value("UseIPv4");
- o.value("UseIPv6");
- o.default = "UseIP";
- o.modalonly = true;
-
- o = ss.taboption('general', form.Value, 'domain_resolve_dns', _('Resolve Domain via DNS'), _("Specify a DNS to resolve server hostname. Be careful of possible recursion."));
- o.modalonly = true;
-
o = ss.taboption('general', form.Value, 'server_port', _('Server Port'));
o.datatype = 'port';
o.rmempty = false;
@@ -212,6 +203,29 @@ return view.extend({
o.modalonly = true;
o.rmempty = false;
+ ss.tab('resolving', _("Server Hostname Resolving"));
+
+ o = ss.taboption('resolving', form.ListValue, 'domain_strategy', _('Domain Strategy'), _("Whether to use IPv4 or IPv6 address if Server Hostname is a domain."));
+ o.value("UseIP");
+ o.value("UseIPv4");
+ o.value("UseIPv6");
+ o.default = "UseIP";
+ o.modalonly = true;
+
+ o = ss.taboption('resolving', form.Value, 'domain_resolve_dns', _('Resolve Domain via DNS'), _("Specify a DNS to resolve server hostname. Be careful of possible recursion."));
+ o.datatype = "or(ipaddr, ipaddrport(1))";
+ o.modalonly = true;
+
+ o = ss.taboption('resolving', form.ListValue, 'domain_resolve_dns_method', _('Resolve Domain DNS Method'), _("Effective when DNS above is set. Direct methods will bypass Xray completely so it may get blocked."));
+ o.value("udp", _("UDP"));
+ o.value("quic+local", _("DNS over QUIC (direct)"));
+ o.value("tcp", _("TCP"));
+ o.value("tcp+local", _("TCP (direct)"));
+ o.value("https", _("DNS over HTTPS"));
+ o.value("https+local", _("DNS over HTTPS (direct)"));
+ o.default = "UseIP";
+ o.modalonly = true;
+
ss.tab('protocol', _('Protocol Settings'));
o = ss.taboption('protocol', form.ListValue, "protocol", _("Protocol"));
@@ -299,7 +313,7 @@ return view.extend({
let destination = extra_inbounds.option(form.MultiValue, 'destination', _('Destination'), _("Select multiple outbounds for load balancing. If none selected, requests will be sent via direct outbound."));
destination.depends("specify_outbound", "1");
destination.datatype = "uciname";
- destination.textvalue = destination_format(config_data, "destination", 60, true);
+ destination.textvalue = destination_format(config_data, "destination", "specify_outbound", 60);
let balancer_strategy = extra_inbounds.option(form.Value, 'balancer_strategy', _('Balancer Strategy'), _('Strategy leastPing
requires observatory (see "Extra Options" tab) to be enabled.'));
balancer_strategy.depends("specify_outbound", "1");
@@ -499,11 +513,11 @@ return view.extend({
let fake_dns_forward_server_tcp = fs.option(form.MultiValue, 'fake_dns_forward_server_tcp', _('Force Forward server (TCP)'));
fake_dns_forward_server_tcp.datatype = "uciname";
- fake_dns_forward_server_tcp.textvalue = destination_format(config_data, "fake_dns_forward_server_tcp", 40, true);
+ fake_dns_forward_server_tcp.textvalue = destination_format(config_data, "fake_dns_forward_server_tcp", null, 40);
let fake_dns_forward_server_udp = fs.option(form.MultiValue, 'fake_dns_forward_server_udp', _('Force Forward server (UDP)'));
fake_dns_forward_server_udp.datatype = "uciname";
- fake_dns_forward_server_udp.textvalue = destination_format(config_data, "fake_dns_forward_server_udp", 40, true);
+ fake_dns_forward_server_udp.textvalue = destination_format(config_data, "fake_dns_forward_server_udp", null, 40);
let fake_dns_balancer_strategy = fs.option(form.Value, 'fake_dns_balancer_strategy', _('Balancer Strategy'), _('Strategy leastPing
requires observatory (see "Extra Options" tab) to be enabled.'));
fake_dns_balancer_strategy.value("random");