update 2026-01-14 04:30:07

This commit is contained in:
kenzok8
2026-01-14 04:30:07 +08:00
parent 8b3d7f032a
commit 6669ec34e0
18 changed files with 173 additions and 88 deletions

View File

@@ -334,10 +334,7 @@ function connect_status()
local proxy_mode = uci:get(appname, "@global[0]", "tcp_proxy_mode") or "proxy"
local localhost_proxy = uci:get(appname, "@global[0]", "localhost_proxy") or "1"
local socks_server = (localhost_proxy == "0") and api.get_cache_var("GLOBAL_TCP_SOCKS_server") or ""
-- 兼容 curl 8.6 time_starttransfer 错误
local curl_ver = api.get_bin_version_cache("/usr/bin/curl", "-V 2>/dev/null | head -n 1 | awk '{print $2}' | cut -d. -f1,2 | tr -d ' \n'") or "0"
url = (curl_ver == "8.6") and "-w %{http_code}:%{time_appconnect} https://" .. url
or "-w %{http_code}:%{time_starttransfer} http://" .. url
url = "-w %{http_code}:%{time_pretransfer} " .. url
if socks_server and socks_server ~= "" then
if (chn_list == "proxy" and gfw_list == "0" and proxy_mode ~= "proxy" and baidu ~= nil) or (chn_list == "0" and gfw_list == "0" and proxy_mode == "proxy") then
-- 中国列表+百度 or 全局

View File

@@ -682,13 +682,36 @@ o.default = "proxy"
o = s:taboption("Proxy", DummyValue, "switch_mode", " ")
o.template = appname .. "/global/proxy"
o = s:taboption("Proxy", Flag, "localhost_proxy", translate("Localhost Proxy"), translate("When selected, localhost can transparent proxy."))
o.default = "1"
o.rmempty = false
---- Check the transparent proxy component
local handle = io.popen("lsmod")
local mods = ""
if handle then
mods = handle:read("*a") or ""
handle:close()
end
o = s:taboption("Proxy", Flag, "client_proxy", translate("Client Proxy"), translate("When selected, devices in LAN can transparent proxy. Otherwise, it will not be proxy. But you can still use access control to allow the designated device to proxy."))
o.default = "1"
o.rmempty = false
if (mods:find("REDIRECT") and mods:find("TPROXY")) or (mods:find("nft_redir") and mods:find("nft_tproxy")) then
o = s:taboption("Proxy", Flag, "localhost_proxy", translate("Localhost Proxy"), translate("When selected, localhost can transparent proxy."))
o.default = "1"
o.rmempty = false
o = s:taboption("Proxy", Flag, "client_proxy", translate("Client Proxy"), translate("When selected, devices in LAN can transparent proxy. Otherwise, it will not be proxy. But you can still use access control to allow the designated device to proxy."))
o.default = "1"
o.rmempty = false
else
local html = string.format([[<div class="cbi-checkbox"><input class="cbi-input-checkbox" type="checkbox" disabled></div><div class="cbi-value-description"><font color="red">%s</font></div>]], translate("Missing components, transparent proxy is unavailable."))
o = s:taboption("Proxy", DummyValue, "localhost_proxy", translate("Localhost Proxy"))
o.rawhtml = true
function o.cfgvalue(self, section)
return html
end
o = s:taboption("Proxy", DummyValue, "client_proxy", translate("Client Proxy"))
o.rawhtml = true
function o.cfgvalue(self, section)
return html
end
end
o = s:taboption("Proxy", DummyValue, "_proxy_tips", " ")
o.rawhtml = true

View File

@@ -124,7 +124,15 @@ if has_fw4 then
o:value("1", "NFtables")
end
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
---- Check the transparent proxy component
local handle = io.popen("lsmod")
local mods = ""
if handle then
mods = handle:read("*a") or ""
handle:close()
end
if (mods:find("REDIRECT") and mods:find("TPROXY")) or (mods:find("nft_redir") and mods:find("nft_tproxy")) then
o = s:option(ListValue, "tcp_proxy_way", translate("TCP Proxy Way"))
o.default = "redirect"
o:value("redirect", "REDIRECT")
@@ -142,7 +150,7 @@ if (os.execute("lsmod | grep -i REDIRECT >/dev/null") == 0 and os.execute("lsmod
self.map:set(section, "tcp_proxy_way", value)
end
if os.execute("lsmod | grep -i ip6table_mangle >/dev/null") == 0 or os.execute("lsmod | grep -i nft_tproxy >/dev/null") == 0 then
if mods:find("ip6table_mangle") or mods:find("nft_tproxy") then
---- IPv6 TProxy
o = s:option(Flag, "ipv6_tproxy", translate("IPv6 TProxy"),
"<font color='red'>" .. translate(

View File

@@ -138,7 +138,7 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
</div>
</div>
</div>
<div class="pure-u-1-4 check" onclick="check_connect('baidu', 'http://www.baidu.com')">
<div class="pure-u-1-4 check" onclick="check_connect('baidu', 'https://www.baidu.com')">
<div class="block pure-g">
<div class="pure-u-1-3">
<div class="img-con">
@@ -150,7 +150,7 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
</div>
</div>
</div>
<div class="pure-u-1-4 check" onclick="check_connect('google', 'http://www.google.com/generate_204')">
<div class="pure-u-1-4 check" onclick="check_connect('google', 'https://www.google.com/generate_204')">
<div class="block pure-g">
<div class="pure-u-1-3">
<div class="img-con">
@@ -162,7 +162,7 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
</div>
</div>
</div>
<div class="pure-u-1-4 check" onclick="check_connect('github', 'http://github.com')">
<div class="pure-u-1-4 check" onclick="check_connect('github', 'https://github.com')">
<div class="block pure-g">
<div class="pure-u-1-3">
<div class="img-con">
@@ -174,7 +174,7 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
</div>
</div>
</div>
<div class="pure-u-1-4 check" onclick="check_connect('instagram', 'http://www.instagram.com')">
<div class="pure-u-1-4 check" onclick="check_connect('instagram', 'https://www.instagram.com')">
<div class="block pure-g">
<div class="pure-u-1-3">
<div class="img-con">

View File

@@ -352,6 +352,9 @@ msgstr "客户端代理"
msgid "When selected, devices in LAN can transparent proxy. Otherwise, it will not be proxy. But you can still use access control to allow the designated device to proxy."
msgstr "当勾选时,局域网内的设备可以透明代理。否则,将不代理。但您仍然可以使用访问控制允许指定的设备代理。"
msgid "Missing components, transparent proxy is unavailable."
msgstr "缺少组件,透明代理不可用。"
msgid "Want different devices to use different proxy modes/ports/nodes? Please use access control."
msgstr "希望不同设备使用不同的代理模式/端口/节点?请使用访问控制。"

View File

@@ -74,7 +74,7 @@ url_test_node() {
fi
sleep 1s
local probeUrl=$(config_t_get global_other url_test_url https://www.google.com/generate_204)
result=$(curl --connect-timeout 3 --max-time 5 -o /dev/null -I -skL -w "%{http_code}:%{time_starttransfer}" -x ${curlx} "${probeUrl}")
result=$(curl --connect-timeout 3 --max-time 5 -o /dev/null -I -skL -w "%{http_code}:%{time_pretransfer}" -x ${curlx} "${probeUrl}")
# 结束 SS 插件进程
local pid_file="/tmp/etc/${CONFIG}/url_test_${node_id}_plugin.pid"
[ -s "$pid_file" ] && kill -9 "$(head -n 1 "$pid_file")" >/dev/null 2>&1

View File

@@ -38,7 +38,7 @@ Fork this repository and:
* Create a release by pushing a tag
* Wait until actions finish
* Use `opkg -i *` to install both ipks from Releases.
* Use `opkg -i *` to install all ipks from Releases.
## Enable preview app
@@ -56,6 +56,7 @@ Some features are deprecated / unstable so they are placed in preview app. To en
* 2025-07-27 fix: dynamic direct: only cover global servers; increase default timeout
* 2025-08-20 fix: dynamic direct connection tracking
* 2025-08-26 fix: dnsmasq global integration mode
* 2026-01-13 feat: feat: vless encryption; minor DNS tweaks; code cleanups
## Changelog since 3.5.0

View File

@@ -34,14 +34,14 @@ function parse_ip_port(val, port_default) {
function format_dns(method, val) {
const parsed = parse_ip_port(val, 53);
if (method == "udp") {
if (method === "udp") {
return {
address: parsed["ip"],
port: parsed["port"]
};
}
let url_suffix = "";
if (substr(method, 0, 5) == "https") {
if (substr(method, 0, 5) === "https") {
url_suffix = "/dns-query";
}
return {
@@ -50,11 +50,11 @@ function format_dns(method, val) {
}
function domain_rules(proxy, k) {
if (proxy[k] == null) {
if (proxy[k] === null) {
return [];
}
return filter(proxy[k], function (x) {
if (substr(x, 0, 8) == "geosite:") {
if (substr(x, 0, 8) === "geosite:") {
return geosite_existence;
}
return true;
@@ -96,11 +96,27 @@ export function dns_server_inbounds(proxy) {
export function dns_rules(proxy, tcp_hijack_inbound_tags, udp_hijack_inbound_tags) {
const dns_port = int(proxy["dns_port"] || 5300);
const dns_count = int(proxy["dns_count"] || 3);
const fast_dns = parse_ip_port(proxy["fast_dns"] || fallback_fast_dns, 53);
const secure_dns = parse_ip_port(proxy["secure_dns"] || fallback_secure_dns, 53);
let dns_server_tags = [];
for (let i = dns_port; i <= dns_port + dns_count; i++) {
push(dns_server_tags, sprintf("dns_server_inbound:%d", i));
}
let result = [
{
type: "field",
inboundTag: ["dns_conf_inbound"],
ip: [fast_dns["ip"]],
port: `${fast_dns["port"]}`,
outboundTag: "dynamic_direct"
},
{
type: "field",
inboundTag: ["dns_conf_inbound"],
ip: [secure_dns["ip"]],
port: `${secure_dns["port"]}`,
balancerTag: "udp_outbound_v4"
},
{
type: "field",
inboundTag: dns_server_tags,
@@ -157,7 +173,7 @@ export function dns_conf(proxy, config, manual_tproxy, fakedns) {
let domain_names_set = {};
let domain_extra_options = {};
for (let server in filter(values(config), i => i[".type"] == "servers")) {
for (let server in filter(values(config), i => i[".type"] === "servers")) {
if (iptoarr(server["server"])) {
continue;
}
@@ -206,6 +222,20 @@ export function dns_conf(proxy, config, manual_tproxy, fakedns) {
port: fast_dns_object["port"],
domains: [...keys(domain_names_set), ...fast_domain_rules(proxy)],
skipFallback: true,
expectIPs: function () {
let ips = [];
if (proxy["align_fast_dns_to_geoip_direct"] === "1") {
if (geoip_existence) {
const geoip_direct_code_list = map(proxy["geoip_direct_code_list"] || [], v => index(v, ":") > 0 ? v : `geoip:${v}`);
const geoip_direct_code_list_v6 = map(proxy["geoip_direct_code_list_v6"] || [], v => index(v, ":") > 0 ? v : `geoip:${v}`);
push(ips, ...geoip_direct_code_list, ...geoip_direct_code_list_v6);
}
}
if (length(ips) > 0) {
return uniq(ips);
}
return null;
}(),
},
];
@@ -228,7 +258,7 @@ export function dns_conf(proxy, config, manual_tproxy, fakedns) {
for (let v in manual_tproxy) {
if (v.domain_names != null) {
for (let d in v.domain_names) {
if (index(v.source_addr, ":") == -1) {
if (index(v.source_addr, ":") === -1) {
hosts[d] = [v.source_addr];
}
}
@@ -245,13 +275,13 @@ export function dns_conf(proxy, config, manual_tproxy, fakedns) {
export function dns_direct_servers(config) {
let result = [];
for (let server in filter(values(config), i => i[".type"] == "servers")) {
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"]);
push(result, parse_ip_port(server["domain_resolve_dns"]));
}
}
}

View File

@@ -7,7 +7,7 @@ export function fake_dns_domains(fakedns) {
for (let f in fakedns) {
push(domains, ...f["fake_dns_domain_names"]);
}
if (length(domains) == 0) {
if (length(domains) === 0) {
return [];
}
return [

View File

@@ -6,7 +6,7 @@ export function manual_tproxy_outbounds(config, manual_tproxy) {
let result = [];
for (let v in manual_tproxy) {
let tcp_tag = "direct";
if (v["force_forward_tcp"] == "1") {
if (v["force_forward_tcp"] === "1") {
if (v["force_forward_server_tcp"] != null) {
tcp_tag = `manual_tproxy:${v[".name"]}@tcp_outbound@force_forward:${v["force_forward_server_tcp"]}`;
const force_forward_server_tcp = config[v["force_forward_server_tcp"]];
@@ -26,7 +26,7 @@ export function manual_tproxy_outbounds(config, manual_tproxy) {
});
let udp_tag = "direct";
if (v["force_forward_udp"] == "1") {
if (v["force_forward_udp"] === "1") {
if (v["force_forward_server_udp"] != null) {
udp_tag = `manual_tproxy:${v[".name"]}@udp_outbound@force_forward:${v["force_forward_server_udp"]}`;
const force_forward_server_udp = config[v["force_forward_server_udp"]];
@@ -51,10 +51,10 @@ export function manual_tproxy_outbounds(config, manual_tproxy) {
export function manual_tproxy_outbound_tags(manual_tproxy) {
let result = [];
for (let v in manual_tproxy) {
if (v["force_forward_tcp"] == "1") {
if (v["force_forward_tcp"] === "1") {
push(result, `manual_tproxy:${v[".name"]}@tcp_outbound@force_forward:${v["force_forward_server_tcp"]}`);
}
if (v["force_forward_udp"] == "1") {
if (v["force_forward_udp"] === "1") {
push(result, `manual_tproxy:${v[".name"]}@udp_outbound@force_forward:${v["force_forward_server_udp"]}`);
}
}
@@ -66,14 +66,14 @@ export function manual_tproxy_rules(manual_tproxy) {
for (let v in manual_tproxy) {
splice(result, 0, 0, {
type: "field",
inboundTag: ["tproxy_tcp_inbound_v4", "socks_inbound", "https_inbound", "http_inbound"],
inboundTag: ["tproxy_tcp_inbound_v4", "tproxy_tcp_inbound_v6", "socks_inbound", "https_inbound", "http_inbound"],
ip: [v["source_addr"]],
port: v["source_port"],
outboundTag: sprintf("manual_tproxy:%s@tcp_outbound", v[".name"])
});
splice(result, 0, 0, {
type: "field",
inboundTag: ["tproxy_udp_inbound_v4"],
inboundTag: ["tproxy_udp_inbound_v4", "tproxy_udp_inbound_v6"],
ip: [v["source_addr"]],
port: v["source_port"],
outboundTag: sprintf("manual_tproxy:%s@udp_outbound", v[".name"])

View File

@@ -14,17 +14,15 @@
const udp6_enabled = length(general.udp_balancer_v6 || []) > 0;
const uids_direct = uniq(general.uids_direct || []);
const gids_direct = uniq(general.gids_direct || []);
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);
const wan_fw_ips_v4 = filter(uniq(wan_fw_ips_no_dns), v => index(v, ":") == -1);
const wan_fw_ips_v6 = filter(uniq(wan_fw_ips_no_dns), v => index(v, ":") != -1);
let wan_bp_ips = uniq(general.wan_bp_ips || []);
let wan_fw_ips = uniq(general.wan_fw_ips || []);
let wan_bp_dns = dns_direct_servers(config);
const wan_bp_ips_v4 = filter(wan_bp_ips, v => index(v, ":") == -1);
const wan_bp_ips_v6 = filter(wan_bp_ips, v => index(v, ":") != -1);
const wan_fw_ips_v4 = filter(wan_fw_ips, v => index(v, ":") == -1);
const wan_fw_ips_v6 = filter(wan_fw_ips, v => index(v, ":") != -1);
const wan_bp_dns_v4 = filter(wan_bp_dns, v => index(v.ip, ":") == -1);
const wan_bp_dns_v6 = filter(wan_bp_dns, v => index(v.ip, ":") != -1);
const transparent_default_port_policy = general.transparent_default_port_policy || "forwarded";
const wan_fw_tcp_ports = general.wan_fw_tcp_ports || [];
const wan_fw_udp_ports = general.wan_fw_udp_ports || [];
@@ -142,6 +140,28 @@
}();
%}
{% if (length(wan_bp_dns_v4) > 0): %}
set tp_spec_dp4_bp {
typeof ip daddr . udp dport
size {{ length(wan_bp_dns_v4) * 2 + 1 }}
flags interval
elements = {
{{ join(", ", uniq(map(wan_bp_dns_v4, v => `${v.ip} . ${v.port}`))) }}
}
}
{% endif %}
{% if (length(wan_bp_dns_v6) > 0): %}
set tp_spec_dp6_bp {
typeof ip6 daddr . udp dport
size {{ length(wan_bp_dns_v6) * 2 + 1 }}
flags interval
elements = {
{{ join(", ", uniq(map(wan_bp_dns_v6, v => `${v.ip} . ${v.port}`))) }}
}
}
{% endif %}
set tp_spec_dv4_sp {
type ipv4_addr
size 32
@@ -508,6 +528,14 @@
{% if (length(manual_tproxy_source_ips) > 0): %}
ip protocol tcp ip daddr @tp_spec_dv4_mt {{ counter }} goto tp_spec_lan_fw comment "Xray manual transparent proxy TCP"
ip protocol udp ip daddr @tp_spec_dv4_mt {{ counter }} goto tp_spec_lan_fw comment "Xray manual transparent proxy UDP"
{% endif %}
{% if (length(wan_bp_dns_v4) > 0): %}
ip daddr . tcp dport @tp_spec_dp4_bp {{ counter }} accept
ip daddr . udp dport @tp_spec_dp4_bp {{ counter }} accept
{% endif %}
{% if (length(wan_bp_dns_v6) > 0): %}
ip6 daddr . tcp dport @tp_spec_dp6_bp {{ counter }} accept
ip6 daddr . udp dport @tp_spec_dp6_bp {{ counter }} accept
{% endif %}
{{ counter }} return
}

View File

@@ -285,4 +285,4 @@ function gen_config() {
});
}
print(gen_config());
printf("%.4J", gen_config());

View File

@@ -38,7 +38,7 @@ export function vless_outbound(server, tag) {
email: server["username"],
id: server["password"],
flow: flow,
encryption: server["vless_encryption"]
encryption: server["vless_encryption"] || "none"
}
]
};
@@ -66,7 +66,7 @@ export function https_vless_inbound(proxy, config) {
tag: "https_inbound",
settings: {
clients: map(proxy["web_server_password"], k => vless_inbound_user(k, flow)),
decryption: "none",
decryption: proxy["vless_decryption"] || "none",
fallbacks: fallbacks(proxy, config)
},
streamSettings: {

View File

@@ -242,20 +242,17 @@ return view.extend({
transport.init(o, ss, 'transport');
o.rmempty = false;
o = ss.taboption('transport', form.ListValue, 'dialer_proxy', _('Dialer Proxy'), _('Similar to <a href="https://xtls.github.io/config/outbound.html#proxysettingsobject">ProxySettings.Tag</a>'));
o.datatype = "uciname";
o.value("disabled", _("Disabled"));
for (const v of uci.sections(config_data, "servers")) {
o.value(v[".name"], server_alias(v));
}
o.modalonly = true;
let dialer_proxy = ss.taboption('transport', form.ListValue, 'dialer_proxy', _('Dialer Proxy'), _('Similar to <a href="https://xtls.github.io/config/outbound.html#proxysettingsobject">ProxySettings.Tag</a>'));
dialer_proxy.datatype = "uciname";
dialer_proxy.value("disabled", _("Disabled"));
dialer_proxy.modalonly = true;
ss.tab('custom', _('Custom Options'));
o = ss.taboption('custom', form.TextValue, 'custom_config', _('Custom Configurations'), _(`Configurations here override settings in the previous tabs with the following rules: <ol><li>Object values will be replaced recursively so settings in previous tabs matter.</li><li>Arrays will be replaced entirely instead of being merged.</li><li>Tag <code>tag</code> and mark <code>streamSettings.sockopt.mark</code> are ignored. </li></ol>Aliases are not handled while merging configurations:<ol><li>Use <code>tcpSettings</code> instead of <code>rawSettings</code>.</li><li>Use <code>splithttpSettings</code> instead of <code>xhttpSettings</code>.</li></ol>Some transports like <code>splithttp</code> may use another <code>streamSettings.sockopt</code>:<ol><li><a href="https://github.com/yichya/luci-app-xray/issues/434">Read instructions here</a>, and use <code>${firewall_mark}</code> as <code>sockopt.mark</code> to avoid loopback traffic.</ol>Override rules here may be changed later. Use this only for experimental or pre-release features.`));
o = ss.taboption('custom', form.TextValue, 'custom_config', _('Custom Configurations'), _(`Configurations here override settings in the previous tabs with the following rules: <ul><li>Object values will be replaced recursively so settings in previous tabs matter.</li><li>Arrays will be replaced entirely instead of being merged.</li><li>Tag <code>tag</code> and mark <code>streamSettings.sockopt.mark</code> are ignored. </li></ul>Aliases are not handled while merging configurations:<ul><li>Use <code>tcpSettings</code> instead of <code>rawSettings</code>.</li><li>Use <code>splithttpSettings</code> instead of <code>xhttpSettings</code>.</li></ul>Some transports like <code>splithttp</code> may use another <code>streamSettings.sockopt</code>:<ul><li><a href="https://github.com/yichya/luci-app-xray/issues/434">Read instructions here</a>, and use <code>${firewall_mark}</code> as <code>sockopt.mark</code> to avoid loopback traffic.</ul>Override rules here may be changed later. Use this only for experimental or pre-release features.`));
o.modalonly = true;
o.monospace = true;
o.rows = 10;
o.rows = 12;
o.validate = shared.validate_object;
s.tab('inbounds', _('Inbounds'));
@@ -465,13 +462,6 @@ return view.extend({
o.datatype = 'range(0, 50)';
o.placeholder = 3;
o = s.taboption('dns', form.ListValue, 'routing_domain_strategy', _('Routing Domain Strategy'), _("Domain resolution strategy when matching domain against rules. (For tproxy, this is effective only when sniffing is enabled.)"));
o.value("AsIs", "AsIs");
o.value("IPIfNonMatch", "IPIfNonMatch");
o.value("IPOnDemand", "IPOnDemand");
o.default = "AsIs";
o.rmempty = false;
s.tab('fake_dns', _('FakeDNS'));
let tproxy_port_tcp_f4 = s.taboption('fake_dns', form.Value, 'tproxy_port_tcp_f4', _('Transparent proxy port (TCP4)'));
@@ -613,24 +603,18 @@ return view.extend({
o = ss.option(form.Flag, 'force_forward_tcp', _('Force Forward (TCP)'), _('This destination must be forwarded through an outbound server.'));
o.modalonly = true;
o = ss.option(form.ListValue, 'force_forward_server_tcp', _('Force Forward server (TCP)'));
o.depends("force_forward_tcp", "1");
o.datatype = "uciname";
for (const v of uci.sections(config_data, "servers")) {
o.value(v[".name"], server_alias(v));
}
o.modalonly = true;
let force_forward_server_tcp = ss.option(form.ListValue, 'force_forward_server_tcp', _('Force Forward server (TCP)'));
force_forward_server_tcp.depends("force_forward_tcp", "1");
force_forward_server_tcp.datatype = "uciname";
force_forward_server_tcp.modalonly = true;
o = ss.option(form.Flag, 'force_forward_udp', _('Force Forward (UDP)'), _('This destination must be forwarded through an outbound server.'));
o.modalonly = true;
o = ss.option(form.ListValue, 'force_forward_server_udp', _('Force Forward server (UDP)'));
o.depends("force_forward_udp", "1");
o.datatype = "uciname";
for (const v of uci.sections(config_data, "servers")) {
o.value(v[".name"], server_alias(v));
}
o.modalonly = true;
let force_forward_server_udp = ss.option(form.ListValue, 'force_forward_server_udp', _('Force Forward server (UDP)'));
force_forward_server_udp.depends("force_forward_udp", "1");
force_forward_server_udp.datatype = "uciname";
force_forward_server_udp.modalonly = true;
s.tab('xray_server', _('HTTPS Server'));
@@ -739,11 +723,8 @@ return view.extend({
ss.anonymous = true;
ss.addremove = true;
o = ss.option(form.ListValue, "upstream", _("Upstream"));
o.datatype = "uciname";
for (const v of uci.sections(config_data, "servers")) {
o.value(v[".name"], server_alias(v));
}
let bridge_upstream = ss.option(form.ListValue, "upstream", _("Upstream"));
bridge_upstream.datatype = "uciname";
o = ss.option(form.Value, "domain", _("Domain"));
o.rmempty = false;
@@ -759,7 +740,7 @@ return view.extend({
custom_configuration_hook.rows = 20;
const servers = uci.sections(config_data, "servers");
for (let selection of [destination, fake_dns_forward_server_tcp, fake_dns_forward_server_udp, tcp_balancer_v4, tcp_balancer_v6, udp_balancer_v4, udp_balancer_v6]) {
for (let selection of [destination, fake_dns_forward_server_tcp, fake_dns_forward_server_udp, tcp_balancer_v4, tcp_balancer_v6, udp_balancer_v4, udp_balancer_v6, bridge_upstream, force_forward_server_tcp, force_forward_server_udp, dialer_proxy]) {
if (servers.length == 0) {
selection.value("direct", _("No server configured"));
selection.readonly = true;

View File

@@ -17,6 +17,7 @@ return view.extend({
s.anonymous = true;
s.tab("dns_hijack", _("DNS Hijacking"));
s.taboption('dns_hijack', form.Flag, 'align_fast_dns_to_geoip_direct', _('Align Fast DNS & GeoIP Direct'), _("Return only IP adddresses from GeoIP Direct List for Fast DNS."));
let dnsmasq_integration_mode = s.taboption('dns_hijack', form.ListValue, 'dnsmasq_integration_mode', _('Dnsmasq Integration Mode'), _('Per Instance mode requires OpenWrt 24.10 or later versions.'));
dnsmasq_integration_mode.value("global", _("Global"));
@@ -72,6 +73,13 @@ return view.extend({
let direct_bittorrent = s.taboption('sniffing', form.Flag, 'direct_bittorrent', _('Bittorrent Direct'), _("If enabled, no bittorrent request will be forwarded through Xray."));
direct_bittorrent.depends("tproxy_sniffing", "1");
let routing_domain_strategy = s.taboption('sniffing', form.ListValue, 'routing_domain_strategy', _('Routing Domain Strategy'), _("Domain resolution strategy when matching domain against rules. (For tproxy, this is effective only when sniffing is enabled.)"));
routing_domain_strategy.value("AsIs", "AsIs");
routing_domain_strategy.value("IPIfNonMatch", "IPIfNonMatch");
routing_domain_strategy.value("IPOnDemand", "IPOnDemand");
routing_domain_strategy.depends("tproxy_sniffing", "1");
routing_domain_strategy.default = "AsIs";
s.tab('dynamic_direct', _('Dynamic Direct'));
s.taboption('dynamic_direct', form.Flag, 'dynamic_direct_tcp4', _('Enable for IPv4 TCP'), _("This should improve performance with large number of connections."));

View File

@@ -214,10 +214,10 @@ function vmess_client(protocol, sub_section, tab_name) {
function vless_client(protocol, sub_section, tab_name) {
protocol.value("vless", "VLESS");
let vless_encryption = sub_section.taboption(tab_name, form.ListValue, "vless_encryption", _("[vless] Encrypt Method"));
let vless_encryption = sub_section.taboption(tab_name, form.Value, "vless_encryption", _("[vless] Encrypt Method"));
vless_encryption.depends("protocol", "vless");
vless_encryption.value("none", "none");
vless_encryption.rmempty = false;
vless_encryption.placeholder = "none";
vless_encryption.rmempty = true;
vless_encryption.modalonly = true;
add_flow_and_stream_security_conf(sub_section, tab_name, "protocol", "vless", true, false);
@@ -235,6 +235,12 @@ function http_client(protocol, sub_section, tab_name) {
function vless_server(protocol, section, tab_name) {
protocol.value("vless", "VLESS");
let vless_decryption = section.taboption(tab_name, form.Value, "vless_decryption", _("[vless] Decrypt Method"));
vless_decryption.depends("web_server_protocol", "vless");
vless_decryption.placeholder = "none";
vless_decryption.rmempty = true;
add_flow_and_stream_security_conf(section, tab_name, "web_server_protocol", "vless", true, true);
}

View File

@@ -1,12 +1,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=xray-core
PKG_VERSION:=25.12.8
PKG_VERSION:=26.1.13
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/XTLS/Xray-core/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=d4519b2d9bb1871f4d7612aa7a8db1c451573b5a44ac824219bb44d63f404e61
PKG_HASH:=c814c9b2e6c92e08d3db929792c56e2863a1a0e252c774ec048095efea6b67a1
PKG_MAINTAINER:=Tianling Shen <cnsztl@immortalwrt.org>
PKG_LICENSE:=MPL-2.0