From: Stan Grishin Date: Fri, 17 Jul 2026 18:24:22 +0000 (+0000) Subject: https-dns-proxy: update to 2026.05.06-1 X-Git-Url: http://git.99rst.org/?a=commitdiff_plain;h=f38b17190ad7c3b6cbbb4a5969b225f7c8f7d368;p=openwrt-packages.git https-dns-proxy: update to 2026.05.06-1 Maintainer: me Compile tested: x86_64, Dell EMC Edge620, OpenWrt 25.12.4 Run tested: x86_64, Dell EMC Edge620, OpenWrt 25.12.4 Description: Update to 2026.05.06 and add IP family option - Update PKG_VERSION to 2026.05.06 - Update PKG_RELEASE to 1 - Add PKG_CPE_ID for CVE tracking - Update PKG_MIRROR_HASH and PKG_SOURCE_VERSION files/etc/config/https-dns-proxy: - Add comments for default options - Add new 'force_ip_family' option to control IP family for resolvers - Update default 'bootstrap_dns' to include IPv6 addresses files/etc/init.d/https-dns-proxy: - Add DEFAULT_BOOTSTRAP4 and DEFAULT_BOOTSTRAP6 for family fallbacks - Change 'global_force_ipv6' to 'global_force_ip_family' for new option - Modify append_boot to filter bootstrap DNS by 'force_ip_family' - Implement fallback bootstrap DNS for forced IP families with no compatible configured servers - Change 'start_instance' to use 'force_ip_family' instead of 'force_ipv6' - Ensure 'service_started' and 'service_stopped' always return 0 to prevent incorrect failure reports for `start`/`reload`/`restart` commands files/etc/uci-defaults/50-https-dns-proxy-migrate-options.sh: - Migrate 'force_ipv6_resolvers' option to 'force_ip_family' option - Remove old 'force_ipv6_resolvers' option after migration Signed-off-by: Stan Grishin --- diff --git a/net/https-dns-proxy/Makefile b/net/https-dns-proxy/Makefile index 11db37807..3695d469c 100644 --- a/net/https-dns-proxy/Makefile +++ b/net/https-dns-proxy/Makefile @@ -2,13 +2,14 @@ include $(TOPDIR)/rules.mk PKG_NAME:=https-dns-proxy -PKG_VERSION:=2026.03.18 -PKG_RELEASE:=4 +PKG_VERSION:=2026.05.06 +PKG_RELEASE:=1 +PKG_CPE_ID:=cpe:/a:mossdef:https-dns-proxy PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://github.com/aarond10/https_dns_proxy/ -PKG_MIRROR_HASH:=4c356c19b62fc7bdef3a67fd678e48f3659d709da10517c2eadef76e3409f5ce -PKG_SOURCE_VERSION:=801881210ba8215dc9cd577222d8c10372423360 +PKG_MIRROR_HASH:=345f14e0a05e032754cea440f5e8798c6896cf5bc7cc12b3c7ed1c8d34e4daad +PKG_SOURCE_VERSION:=0ba0525fa7c10115cc7c92e99621bfe484ae96e7 PKG_MAINTAINER:=Stan Grishin PKG_LICENSE:=MIT diff --git a/net/https-dns-proxy/files/etc/config/https-dns-proxy b/net/https-dns-proxy/files/etc/config/https-dns-proxy index 62555f887..2198b5af1 100644 --- a/net/https-dns-proxy/files/etc/config/https-dns-proxy +++ b/net/https-dns-proxy/files/etc/config/https-dns-proxy @@ -1,9 +1,15 @@ config main 'config' +# Answer iCloud Private Relay canary domains so DNS-based filtering keeps working option canary_domains_icloud '1' +# Answer Mozilla canary domain so Firefox does not auto-enable its own DoH option canary_domains_mozilla '1' +# dnsmasq instances to point at the proxy ('*' = all, or space-separated indexes/names) option dnsmasq_config_update '*' +# Force LAN clients onto the proxy by redirecting/rejecting their outbound DNS option force_dns '1' +# Add nftables notrack rules for the proxy's loopback DNS traffic option notrack_dns '1' +# Destination ports intercepted by force_dns (53 = DNS, 853 = DoT) list force_dns_port '53' list force_dns_port '853' # ports listed below are used by some @@ -14,21 +20,37 @@ config main 'config' # list force_dns_port '4434' # list force_dns_port '5443' # list force_dns_port '8443' +# Interface(s) whose outbound DNS is forced to the proxy list force_dns_src_interface 'lan' +# Also (re)start instances on wan6 interface events option procd_trigger_wan6 '0' +# Domain resolved to confirm the resolver works after start ('-' disables the check) option heartbeat_domain 'heartbeat.mossdef.org' +# Seconds to wait before running the heartbeat check option heartbeat_sleep_timeout '10' +# Seconds to wait for the heartbeat domain to resolve option heartbeat_wait_timeout '10' +# User the proxy drops privileges to option user 'nobody' +# Group the proxy drops privileges to option group 'nogroup' +# Default local address instances bind to option listen_addr '127.0.0.1' +# IP family for resolvers: 'auto' (dual-stack, default), 'ipv4' (IPv4 only + -4), 'ipv6' (IPv6 only) + option force_ip_family 'auto' config https-dns-proxy - option bootstrap_dns '1.1.1.1,1.0.0.1' +# Plain DNS servers used to resolve the DoH resolver hostname at startup + option bootstrap_dns '1.1.1.1,1.0.0.1,2606:4700:4700::1111,2606:4700:4700::1001' +# DoH resolver endpoint (RFC 8484) option resolver_url 'https://cloudflare-dns.com/dns-query' +# Local port this instance listens on option listen_port '5053' config https-dns-proxy - option bootstrap_dns '8.8.8.8,8.8.4.4' +# Plain DNS servers used to resolve the DoH resolver hostname at startup + option bootstrap_dns '8.8.8.8,8.8.4.4,2001:4860:4860::8888,2001:4860:4860::8844' +# DoH resolver endpoint (RFC 8484) option resolver_url 'https://dns.google/dns-query' +# Local port this instance listens on option listen_port '5054' diff --git a/net/https-dns-proxy/files/etc/init.d/https-dns-proxy b/net/https-dns-proxy/files/etc/init.d/https-dns-proxy index c81de1daf..5803318f5 100755 --- a/net/https-dns-proxy/files/etc/init.d/https-dns-proxy +++ b/net/https-dns-proxy/files/etc/init.d/https-dns-proxy @@ -25,6 +25,10 @@ readonly PROG=/usr/sbin/https-dns-proxy readonly BOOTSTRAP_CF='1.1.1.1,1.0.0.1,2606:4700:4700::1111,2606:4700:4700::1001' readonly BOOTSTRAP_GOOGLE='8.8.8.8,8.8.4.4,2001:4860:4860::8888,2001:4860:4860::8844' readonly DEFAULT_BOOTSTRAP="${BOOTSTRAP_CF},${BOOTSTRAP_GOOGLE}" +# Cloudflare/Google servers by family, used as bootstrap fallback when a forced +# IP family has no compatible server in the instance's configured bootstrap_dns. +readonly DEFAULT_BOOTSTRAP4='1.1.1.1,1.0.0.1,8.8.8.8,8.8.4.4' +readonly DEFAULT_BOOTSTRAP6='2606:4700:4700::1111,2606:4700:4700::1001,2001:4860:4860::8888,2001:4860:4860::8844' readonly canaryDomainsMozilla='use-application-dns.net' readonly canaryDomainsiCloud='mask.icloud.com mask-h2.icloud.com' readonly NOTRACK_NFT_FILE='/usr/share/nftables.d/ruleset-post/20-https-dns-proxy-notrack.nft' @@ -57,7 +61,7 @@ global_polling_interval= global_proxy_server= global_force_http1= global_force_http3= -global_force_ipv6= +global_force_ip_family= global_max_idle_time= global_conn_loss_time= global_ca_certs_file= @@ -223,14 +227,24 @@ append_boot() { [ -z "$_loctmp" ] && return 0 IFS=" ," for i in $_loctmp; do - if { [ -z "$force_ipv6" ] && is_ipv4 "$i"; } || \ - { [ -n "$force_ipv6" ] && is_ipv6 "$i"; }; then - [ -z "$_newtmp" ] && _newtmp="$i" || _newtmp="${_newtmp},${i}" - fi + case "$force_ip_family" in + ipv4) is_ipv4 "$i" || continue;; + ipv6) is_ipv6 "$i" || continue;; + esac + [ -z "$_newtmp" ] && _newtmp="$i" || _newtmp="${_newtmp},${i}" done IFS="$_old_ifs" + # A forced family with no compatible bootstrap server configured would leave the + # proxy unable to resolve the resolver host; fall back to the Cloudflare/Google + # servers of the forced family so bootstrap still works. + if [ -z "$_newtmp" ]; then + case "$force_ip_family" in + ipv4) _newtmp="$DEFAULT_BOOTSTRAP4";; + ipv6) _newtmp="$DEFAULT_BOOTSTRAP6";; + esac + fi [ -n "$_newtmp" ] && xappend "$switch $_newtmp" - [ -z "$force_ipv6" ] && xappend '-4' + [ "$force_ip_family" = 'ipv4' ] && xappend '-4' } boot() { @@ -248,7 +262,7 @@ load_package_config() { config_get_bool procd_trigger_wan6 'config' 'procd_trigger_wan6' '0' config_get_bool global_force_http1 'config' 'force_http1' '0' config_get_bool global_force_http3 'config' 'force_http3' '0' - config_get_bool global_force_ipv6 'config' 'force_ipv6_resolvers' '0' + config_get global_force_ip_family 'config' 'force_ip_family' 'auto' config_get dnsmasq_config_update 'config' 'dnsmasq_config_update' '*' config_get force_dns_port 'config' 'force_dns_port' '53 853' config_get force_dns_src_interface 'config' 'force_dns_src_interface' 'lan' @@ -276,13 +290,12 @@ load_package_config() { start_instance() { local cfg="$1" param="$2" local PROG_param - local listen_addr listen_port force_ipv6 p url iface + local listen_addr listen_port force_ip_family p url iface config_get url "$cfg" 'resolver_url' config_get listen_addr "$cfg" 'listen_addr' "$global_listen_addr" config_get listen_port "$cfg" 'listen_port' "$port" - config_get_bool force_ipv6 "$cfg" 'force_ipv6_resolvers' "$global_force_ipv6" - [ "$force_ipv6" = '1' ] || unset force_ipv6 + config_get force_ip_family "$cfg" 'force_ip_family' "$global_force_ip_family" append_parm "$cfg" 'resolver_url' '-r' append_parm "$cfg" 'listen_addr' '-a' "$global_listen_addr" '127.0.0.1' @@ -460,8 +473,12 @@ service_triggers() { fi } -service_started() { { [ -n "$force_dns" ] || [ -n "$notrack_dns" ]; } && procd_set_config_changed firewall; } -service_stopped() { { [ -n "$force_dns" ] || [ -n "$notrack_dns" ]; } && procd_set_config_changed firewall; } +# These run for their side effect only (flag the firewall for reconfig). Force a +# 0 return so a falsy test doesn't become the exit code of start/reload/restart — +# force_dns/notrack_dns are empty in this scope, so without this restart exits 1 +# even on a successful start. See issue #11. +service_started() { { [ -n "$force_dns" ] || [ -n "$notrack_dns" ]; } && procd_set_config_changed firewall; return 0; } +service_stopped() { { [ -n "$force_dns" ] || [ -n "$notrack_dns" ]; } && procd_set_config_changed firewall; return 0; } restart() { reload "$@"; } dnsmasq_instance_append_force_dns_port() { diff --git a/net/https-dns-proxy/files/etc/uci-defaults/50-https-dns-proxy-migrate-options.sh b/net/https-dns-proxy/files/etc/uci-defaults/50-https-dns-proxy-migrate-options.sh index 6716bd710..b46bab373 100644 --- a/net/https-dns-proxy/files/etc/uci-defaults/50-https-dns-proxy-migrate-options.sh +++ b/net/https-dns-proxy/files/etc/uci-defaults/50-https-dns-proxy-migrate-options.sh @@ -6,3 +6,5 @@ sed -i "s|wan6_trigger|procd_trigger_wan6|" "/etc/config/https-dns-proxy" sed -i "s|procd_fw_src_interfaces|force_dns_src_interface|" "/etc/config/https-dns-proxy" sed -i "s|use_http1|force_http1|" "/etc/config/https-dns-proxy" sed -i "s|use_ipv6_resolvers_only|force_ipv6_resolvers|" "/etc/config/https-dns-proxy" +sed -i "s|option force_ipv6_resolvers '1'|option force_ip_family 'ipv6'|" "/etc/config/https-dns-proxy" +sed -i "/option force_ipv6_resolvers/d" "/etc/config/https-dns-proxy" diff --git a/net/https-dns-proxy/tests/run_tests.sh b/net/https-dns-proxy/tests/run_tests.sh index 207407160..9bf4bee5b 100644 --- a/net/https-dns-proxy/tests/run_tests.sh +++ b/net/https-dns-proxy/tests/run_tests.sh @@ -529,6 +529,44 @@ assert_eq "xappend adds parameter" " -r https://dns.google/dns-query" "$PROG_par xappend "-p 5053" assert_eq "xappend appends parameter" " -r https://dns.google/dns-query -p 5053" "$PROG_param" +printf "\n##\n## 07b: append_boot (force_ip_family)\n##\n\n" + +rm -f "$__uci_store"/* +__cfg_package="https-dns-proxy" +uci_set "https-dns-proxy" "inst" "bootstrap_dns" "1.1.1.1,2606:4700:4700::1111" + +# auto: keep both families, no -4 +PROG_param=""; force_ip_family="auto" +append_boot "inst" 'bootstrap_dns' '-b' "$DEFAULT_BOOTSTRAP" +assert_eq "append_boot auto keeps both families, no -4" " -b 1.1.1.1,2606:4700:4700::1111" "$PROG_param" + +# ipv4: v4 only + -4 +PROG_param=""; force_ip_family="ipv4" +append_boot "inst" 'bootstrap_dns' '-b' "$DEFAULT_BOOTSTRAP" +assert_eq "append_boot ipv4 keeps v4 and adds -4" " -b 1.1.1.1 -4" "$PROG_param" + +# ipv6: v6 only, no -4 +PROG_param=""; force_ip_family="ipv6" +append_boot "inst" 'bootstrap_dns' '-b' "$DEFAULT_BOOTSTRAP" +assert_eq "append_boot ipv6 keeps v6, no -4" " -b 2606:4700:4700::1111" "$PROG_param" + +# forced ipv6 but config bootstrap has only v4 → fall back to CF/Google v6 +uci_set "https-dns-proxy" "v4only" "bootstrap_dns" "1.1.1.1,8.8.8.8" +PROG_param=""; force_ip_family="ipv6" +append_boot "v4only" 'bootstrap_dns' '-b' "$DEFAULT_BOOTSTRAP" +assert_eq "append_boot ipv6 falls back to CF/Google v6" \ + " -b 2606:4700:4700::1111,2606:4700:4700::1001,2001:4860:4860::8888,2001:4860:4860::8844" "$PROG_param" + +# forced ipv4 but config bootstrap has only v6 → fall back to CF/Google v4 + -4 +uci_set "https-dns-proxy" "v6only" "bootstrap_dns" "2606:4700:4700::1111" +PROG_param=""; force_ip_family="ipv4" +append_boot "v6only" 'bootstrap_dns' '-b' "$DEFAULT_BOOTSTRAP" +assert_eq "append_boot ipv4 falls back to CF/Google v4 and adds -4" \ + " -b 1.1.1.1,1.0.0.1,8.8.8.8,8.8.4.4 -4" "$PROG_param" + +unset force_ip_family +rm -f "$__uci_store"/* + printf "\n##\n## 08: UCI migration script\n##\n\n" MIGRATION_SCRIPT="./files/etc/uci-defaults/50-https-dns-proxy-migrate-options.sh" @@ -541,7 +579,10 @@ config main 'config' option wan6_trigger '0' option procd_fw_src_interfaces 'lan' option use_http1 '0' - option use_ipv6_resolvers_only '0' + option use_ipv6_resolvers_only '1' + +config https-dns-proxy 'disabled_family' + option force_ipv6_resolvers '0' CONF # Run the migration sed commands against our test file @@ -550,6 +591,8 @@ CONF sed -i "s|procd_fw_src_interfaces|force_dns_src_interface|" "$MIGRATE_CONF" sed -i "s|use_http1|force_http1|" "$MIGRATE_CONF" sed -i "s|use_ipv6_resolvers_only|force_ipv6_resolvers|" "$MIGRATE_CONF" + sed -i "s|option force_ipv6_resolvers '1'|option force_ip_family 'ipv6'|" "$MIGRATE_CONF" + sed -i "/option force_ipv6_resolvers/d" "$MIGRATE_CONF" grep -q "dnsmasq_config_update" "$MIGRATE_CONF" assert_rc "migration: update_dnsmasq_config → dnsmasq_config_update" 0 $? @@ -563,8 +606,17 @@ CONF grep -q "force_http1" "$MIGRATE_CONF" assert_rc "migration: use_http1 → force_http1" 0 $? + grep -q "option force_ip_family 'ipv6'" "$MIGRATE_CONF" + assert_rc "migration: enabled force_ipv6_resolvers → force_ip_family=ipv6" 0 $? + grep -q "force_ipv6_resolvers" "$MIGRATE_CONF" - assert_rc "migration: use_ipv6_resolvers_only → force_ipv6_resolvers" 0 $? + assert_rc "migration: force_ipv6_resolvers option retired" 1 $? + + grep -qw "use_ipv6_resolvers_only" "$MIGRATE_CONF" + assert_rc "migration: old name use_ipv6_resolvers_only removed" 1 $? + + [ "$(grep -c 'option force_ip_family' "$MIGRATE_CONF")" = "1" ] + assert_rc "migration: disabled force_ipv6_resolvers dropped (defaults to auto)" 0 $? # Verify old names are gone grep -q "update_dnsmasq_config" "$MIGRATE_CONF" @@ -595,7 +647,7 @@ uci_set "https-dns-proxy" "config" "force_dns" "1" uci_set "https-dns-proxy" "config" "procd_trigger_wan6" "0" uci_set "https-dns-proxy" "config" "force_http1" "0" uci_set "https-dns-proxy" "config" "force_http3" "0" -uci_set "https-dns-proxy" "config" "force_ipv6_resolvers" "0" +uci_set "https-dns-proxy" "config" "force_ip_family" "auto" # Reset globals before load canary_domains_icloud="" @@ -611,6 +663,7 @@ assert_eq "load_package_config: force_dns=1" "1" "$force_dns" assert_eq "load_package_config: global_user defaults to nobody" "nobody" "$global_user" assert_eq "load_package_config: global_group defaults to nogroup" "nogroup" "$global_group" assert_eq "load_package_config: global_listen_addr defaults to 127.0.0.1" "127.0.0.1" "$global_listen_addr" +assert_eq "load_package_config: global_force_ip_family=auto" "auto" "$global_force_ip_family" # Canary domains should be populated echo "$canaryDomains" | grep -q "mask.icloud.com" @@ -626,7 +679,7 @@ uci_set "https-dns-proxy" "config" "force_dns" "0" uci_set "https-dns-proxy" "config" "procd_trigger_wan6" "0" uci_set "https-dns-proxy" "config" "force_http1" "0" uci_set "https-dns-proxy" "config" "force_http3" "0" -uci_set "https-dns-proxy" "config" "force_ipv6_resolvers" "0" +uci_set "https-dns-proxy" "config" "force_ip_family" "auto" canaryDomains="" load_package_config @@ -634,6 +687,18 @@ load_package_config assert_eq "load_package_config: canary disabled → canaryDomains empty" "" "$canaryDomains" assert_eq "load_package_config: force_dns=0 → unset" "" "$force_dns" +printf "\n##\n## 9b: service_started/service_stopped exit code (issue #11)\n##\n\n" + +# These run only for the procd_set_config_changed side effect. When force_dns and +# notrack_dns are both empty the guard test is false; without an explicit +# 'return 0' the function's exit status (1) becomes the exit code of +# start/reload/restart, so a successful restart wrongly reports failure. +force_dns='' notrack_dns='' +service_started; assert_rc "service_started returns 0 when force_dns/notrack_dns empty" 0 $? +service_stopped; assert_rc "service_stopped returns 0 when force_dns/notrack_dns empty" 0 $? +force_dns='1' notrack_dns='' +service_started; assert_rc "service_started returns 0 when force_dns set" 0 $? + printf "\n##\n## 10: notrack_nft (regression: missing nftables.d/ruleset-post dir)\n##\n\n" # Reset state — ensure parent dir does NOT exist (this is the apk-install