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');
}
const ipRegex = /^(\d{1,3}\.){3}\d{1,3}$/;
if (ipRegex.test(value)) {
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 ipRegex = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}(:[0-9]{1,5})?$/;
const domainRegex = /^(?:https:\/\/)?([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\.)+[a-zA-Z]{2,63}(:[0-9]{1,5})?(\/[^?#\s]*)?$/;
const domainRegex = /^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.[a-zA-Z]{2,}(\/[^\s]*)?$/;
if (!domainRegex.test(value)) {
if (!ipRegex.test(value) && !domainRegex.test(value)) {
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');
}
const ipRegex = /^(\d{1,3}\.){3}\d{1,3}$/;
if (ipRegex.test(value)) {
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 ipRegex = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}(:[0-9]{1,5})?$/;
const domainRegex = /^(?:https:\/\/)?([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\.)+[a-zA-Z]{2,63}(:[0-9]{1,5})?(\/[^?#\s]*)?$/;
const domainRegex = /^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.[a-zA-Z]{2,}(\/[^\s]*)?$/;
if (!domainRegex.test(value)) {
if (!ipRegex.test(value) && !domainRegex.test(value)) {
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
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_server "main" "dns_server" "1.1.1.1"
config_get split_dns_type "main" "split_dns_type" "udp"
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
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
fi
@@ -715,15 +717,12 @@ sing_box_configure_dns() {
dns_domain_resolver="$SB_DNS_DOMAIN_RESOLVER_TAG"
fi
config=$(
sing_box_cf_add_dns_server "$config" "$dns_type" "$SB_DNS_SERVER_TAG" "$dns_server" "" "" \
"$dns_domain_resolver"
)
config=$(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
config=$(
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

View File

@@ -26,7 +26,7 @@ is_domain() {
is_base64() {
local str="$1"
if echo "$str" | base64 -d >/dev/null 2>&1; then
if echo "$str" | base64 -d > /dev/null 2>&1; then
return 0
fi
return 1
@@ -113,19 +113,25 @@ url_decode() {
# Extracts the userinfo (username[:password]) part from a URL
url_get_userinfo() {
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
url_get_host() {
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
url_get_port() {
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
@@ -167,7 +173,7 @@ base64_decode() {
local str="$1"
local decoded_url
decoded_url="$(echo "$str" | base64 -d 2>/dev/null)"
decoded_url="$(echo "$str" | base64 -d 2 > /dev/null)"
echo "$decoded_url"
}

View File

@@ -6,24 +6,32 @@ sing_box_cf_add_dns_server() {
local config="$1"
local type="$2"
local tag="$3"
local server_address="$4"
local path="$5"
local headers="$6"
local domain_resolver="$7"
local detour="$8"
local server="$4"
local domain_resolver="$5"
local detour="$6"
local server_address server_port
server_address=$(url_get_host "$server")
server_port=$(url_get_port "$server")
case "$type" in
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)
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)
config=$(
sing_box_cm_add_https_dns_server "$config" "$tag" "$server_address" 443 "$path" "$headers" \
"$domain_resolver" "$detour"
)
[ -z "$server_port" ] && server_port=443
local path headers
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"