diff --git a/README.md b/README.md
index 1456259..2ea2ccf 100644
--- a/README.md
+++ b/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/core/root/usr/share/xray/feature/dns.mjs b/core/root/usr/share/xray/feature/dns.mjs
index 9f6bb40..7375a0a 100644
--- a/core/root/usr/share/xray/feature/dns.mjs
+++ b/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 {
+ 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: result[1],
- port: int(result[2])
+ 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/core/root/usr/share/xray/firewall_include.ut b/core/root/usr/share/xray/firewall_include.ut
index 8361a54..a03d405 100644
--- a/core/root/usr/share/xray/firewall_include.ut
+++ b/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/core/root/usr/share/xray/protocol/http.mjs b/core/root/usr/share/xray/protocol/http.mjs
index 8c80c65..b340243 100644
--- a/core/root/usr/share/xray/protocol/http.mjs
+++ b/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/core/root/usr/share/xray/protocol/socks.mjs b/core/root/usr/share/xray/protocol/socks.mjs
index 95c8619..4d11a69 100644
--- a/core/root/usr/share/xray/protocol/socks.mjs
+++ b/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/core/root/www/luci-static/resources/view/xray/core.js b/core/root/www/luci-static/resources/view/xray/core.js
index 9b0b408..59ecde1 100644
--- a/core/root/www/luci-static/resources/view/xray/core.js
+++ b/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");