Skip to content

Commit

Permalink
fix: domain match priority; stricter resolve options; socks / http auth
Browse files Browse the repository at this point in the history
  • Loading branch information
yichya committed Feb 19, 2024
1 parent 64469f3 commit b5e4393
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 49 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
77 changes: 60 additions & 17 deletions core/root/usr/share/xray/feature/dns.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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}`
};
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 = {};
Expand All @@ -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;
}
Expand All @@ -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,
{
Expand All @@ -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"],
Expand Down Expand Up @@ -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;
};
4 changes: 4 additions & 0 deletions core/root/usr/share/xray/firewall_include.ut
Original file line number Diff line number Diff line change
Expand Up @@ -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]];
Expand All @@ -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);
Expand Down
16 changes: 10 additions & 6 deletions core/root/usr/share/xray/protocol/http.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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
}
]
},
Expand Down
16 changes: 10 additions & 6 deletions core/root/usr/share/xray/protocol/socks.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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
}
]
},
Expand Down
54 changes: 34 additions & 20 deletions core/root/www/luci-static/resources/view/xray/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<i>${_("direct")}</i>`;
function destination_format(config_data, k, e, max_chars) {
return function (s) {
if (e) {
if (!uci.get(config_data, s, e)) {
return `<i>${_("use global settings")}</i>`;
}
}
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"), `<i>${_("direct")}</i>`)(s);
};
}

function extra_outbound_format(config_data, s, select_item) {
Expand Down Expand Up @@ -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;
Expand All @@ -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"));
Expand Down Expand Up @@ -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 <code>leastPing</code> requires observatory (see "Extra Options" tab) to be enabled.'));
balancer_strategy.depends("specify_outbound", "1");
Expand Down Expand Up @@ -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 <code>leastPing</code> requires observatory (see "Extra Options" tab) to be enabled.'));
fake_dns_balancer_strategy.value("random");
Expand Down

0 comments on commit b5e4393

Please sign in to comment.