feat: Add support for DoH URLs with paths and UDP port specification

This commit is contained in:
Andrey Petelin
2025-09-14 17:51:20 +05:00
parent 4a17cf66a3
commit 27719f90ee
4 changed files with 77 additions and 84 deletions

View File

@@ -50,20 +50,10 @@ function createAdditionalSection(mainSection, network) {
return _('DNS server address cannot be empty'); return _('DNS server address cannot be empty');
} }
const ipRegex = /^(\d{1,3}\.){3}\d{1,3}$/; const ipRegex = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}(:[0-9]{1,5})?$/;
if (ipRegex.test(value)) { const domainRegex = /^(?:https:\/\/)?([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\.)+[a-zA-Z]{2,63}(:[0-9]{1,5})?(\/[^?#\s]*)?$/;
const parts = value.split('.');
for (const part of parts) {
const num = parseInt(part);
if (num < 0 || num > 255) {
return _('IP address parts must be between 0 and 255');
}
}
return true;
}
const domainRegex = /^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.[a-zA-Z]{2,}(\/[^\s]*)?$/; if (!ipRegex.test(value) && !domainRegex.test(value)) {
if (!domainRegex.test(value)) {
return _('Invalid DNS server format. Examples: 8.8.8.8 or dns.example.com or dns.example.com/nicedns for DoH'); return _('Invalid DNS server format. Examples: 8.8.8.8 or dns.example.com or dns.example.com/nicedns for DoH');
} }
@@ -97,20 +87,10 @@ function createAdditionalSection(mainSection, network) {
return _('DNS server address cannot be empty'); return _('DNS server address cannot be empty');
} }
const ipRegex = /^(\d{1,3}\.){3}\d{1,3}$/; const ipRegex = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}(:[0-9]{1,5})?$/;
if (ipRegex.test(value)) { const domainRegex = /^(?:https:\/\/)?([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\.)+[a-zA-Z]{2,63}(:[0-9]{1,5})?(\/[^?#\s]*)?$/;
const parts = value.split('.');
for (const part of parts) {
const num = parseInt(part);
if (num < 0 || num > 255) {
return _('IP address parts must be between 0 and 255');
}
}
return true;
}
const domainRegex = /^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.[a-zA-Z]{2,}(\/[^\s]*)?$/; if (!ipRegex.test(value) && !domainRegex.test(value)) {
if (!domainRegex.test(value)) {
return _('Invalid DNS server format. Examples: 8.8.8.8 or dns.example.com or dns.example.com/nicedns for DoH'); return _('Invalid DNS server format. Examples: 8.8.8.8 or dns.example.com or dns.example.com/nicedns for DoH');
} }

View File

@@ -687,14 +687,16 @@ sing_box_configure_dns() {
fi fi
config=$(sing_box_cm_configure_dns "$config" "$final_dns_server" "ipv4_only" true) config=$(sing_box_cm_configure_dns "$config" "$final_dns_server" "ipv4_only" true)
local dns_type dns_server split_dns_type split_dns_server local dns_type dns_server split_dns_type split_dns_server dns_server_address split_dns_server_address
config_get dns_type "main" "dns_type" "doh" config_get dns_type "main" "dns_type" "doh"
config_get dns_server "main" "dns_server" "1.1.1.1" config_get dns_server "main" "dns_server" "1.1.1.1"
config_get split_dns_type "main" "split_dns_type" "udp" config_get split_dns_type "main" "split_dns_type" "udp"
config_get split_dns_server "main" "split_dns_server" "1.1.1.1" config_get split_dns_server "main" "split_dns_server" "1.1.1.1"
dns_server_address=$(url_get_host "$dns_server")
split_dns_server_address=$(url_get_host "$split_dns_server")
local need_dns_domain_resolver=0 local need_dns_domain_resolver=0
if ! is_ipv4 "$dns_server" || ! is_ipv4 "$split_dns_server"; then if ! is_ipv4 "$dns_server_address" || ! is_ipv4 "$split_dns_server_address"; then
need_dns_domain_resolver=1 need_dns_domain_resolver=1
fi fi
@@ -715,15 +717,12 @@ sing_box_configure_dns() {
dns_domain_resolver="$SB_DNS_DOMAIN_RESOLVER_TAG" dns_domain_resolver="$SB_DNS_DOMAIN_RESOLVER_TAG"
fi fi
config=$( config=$(sing_box_cf_add_dns_server "$config" "$dns_type" "$SB_DNS_SERVER_TAG" "$dns_server" "$dns_domain_resolver")
sing_box_cf_add_dns_server "$config" "$dns_type" "$SB_DNS_SERVER_TAG" "$dns_server" "" "" \
"$dns_domain_resolver"
)
if [ "$split_dns_enabled" -eq 1 ]; then if [ "$split_dns_enabled" -eq 1 ]; then
config=$( config=$(
sing_box_cf_add_dns_server "$config" "$split_dns_type" "$SB_SPLIT_DNS_SERVER_TAG" "$split_dns_server" \ sing_box_cf_add_dns_server "$config" "$split_dns_type" "$SB_SPLIT_DNS_SERVER_TAG" "$split_dns_server" \
"" "" "$dns_domain_resolver" "$SB_MAIN_OUTBOUND_TAG" "$dns_domain_resolver" "$SB_MAIN_OUTBOUND_TAG"
) )
fi fi

View File

@@ -113,19 +113,25 @@ url_decode() {
# Extracts the userinfo (username[:password]) part from a URL # Extracts the userinfo (username[:password]) part from a URL
url_get_userinfo() { url_get_userinfo() {
local url="$1" local url="$1"
echo "$url" | sed -n 's#^[^:]*://\([^@]*\)@.*#\1#p' echo "$url" | sed -n -e 's#^[^:/?]*://##' -e '/@/!d' -e 's/@.*//p'
} }
# Extracts the host part from a URL # Extracts the host part from a URL
url_get_host() { url_get_host() {
local url="$1" local url="$1"
echo "$url" | sed -n 's#^[^:]*://[^@]*@\([^:/?#]*\).*#\1#p' echo "$url" | sed -n -e 's#^[^:/?]*://##' -e 's#^[^/]*@##' -e 's#\([:/].*\|$\)##p'
} }
# Extracts the port number from a URL # Extracts the port number from a URL
url_get_port() { url_get_port() {
local url="$1" local url="$1"
echo "$url" | sed -n 's#^[^:]*://[^@]*@[^:/?#]*:\([0-9]*\).*#\1#p' echo "$url" | sed -n -e 's#^[^:/?]*://##' -e 's#^[^/]*@##' -e 's#^[^/]*:\([0-9][0-9]*\).*#\1#p'
}
# Extracts the path from a URL (without query or fragment; returns "/" if empty)
url_get_path() {
local url="$1"
echo "$url" | sed -n -e 's#^[^:/?]*://##' -e 's#^[^/]*##' -e 's#\([^?]*\).*#\1#p'
} }
# Extracts the value of a specific query parameter from a URL # Extracts the value of a specific query parameter from a URL

View File

@@ -6,24 +6,32 @@ sing_box_cf_add_dns_server() {
local config="$1" local config="$1"
local type="$2" local type="$2"
local tag="$3" local tag="$3"
local server_address="$4" local server="$4"
local path="$5" local domain_resolver="$5"
local headers="$6" local detour="$6"
local domain_resolver="$7"
local detour="$8" local server_address server_port
server_address=$(url_get_host "$server")
server_port=$(url_get_port "$server")
case "$type" in case "$type" in
udp) udp)
config=$(sing_box_cm_add_udp_dns_server "$config" "$tag" "$server_address" 53 "$domain_resolver" "$detour") [ -z "$server_port" ] && server_port=53
config=$(sing_box_cm_add_udp_dns_server "$config" "$tag" "$server_address" "$server_port" "$domain_resolver" \
"$detour")
;; ;;
dot) dot)
config=$(sing_box_cm_add_tls_dns_server "$config" "$tag" "$server_address" 853 "$domain_resolver" "$detour") [ -z "$server_port" ] && server_port=853
config=$(sing_box_cm_add_tls_dns_server "$config" "$tag" "$server_address" "$server_port" "$domain_resolver" \
"$detour")
;; ;;
doh) doh)
config=$( [ -z "$server_port" ] && server_port=443
sing_box_cm_add_https_dns_server "$config" "$tag" "$server_address" 443 "$path" "$headers" \ local path headers
"$domain_resolver" "$detour" path=$(url_get_path "$server")
) headers="" # TODO(ampetelin): implement it if necessary
config=$(sing_box_cm_add_https_dns_server "$config" "$tag" "$server_address" "$server_port" "$path" "$headers" \
"$domain_resolver" "$detour")
;; ;;
*) *)
log "Unsupported DNS server type: $type" log "Unsupported DNS server type: $type"