unbound: Add option to force using TCP for upstream servers

Message ID 20191001113259.17455-1-michael.tremer@ipfire.org
State Superseded
Headers
Series unbound: Add option to force using TCP for upstream servers |

Commit Message

Michael Tremer Oct. 1, 2019, 11:32 a.m. UTC
  Some users have problems to reach DNS servers. This change adds an option
which allows to force using TCP for upstream name servers.

This is a good workaround for users behind a broken Fritz!Box in modem
mode which does not allow resolving any records of the root zone.

The name server tests in the script will also only use TCP.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
---
 src/initscripts/system/unbound | 40 ++++++++++++++++++++++++++++++----------
 1 file changed, 30 insertions(+), 10 deletions(-)
  

Comments

Michael Tremer Oct. 1, 2019, 11:37 a.m. UTC | #1
Please ignore this patch.

Version 2 has been submitted which fixes an incorrect variable name.

-Michael

> On 1 Oct 2019, at 12:32, Michael Tremer <michael.tremer@ipfire.org> wrote:
> 
> Some users have problems to reach DNS servers. This change adds an option
> which allows to force using TCP for upstream name servers.
> 
> This is a good workaround for users behind a broken Fritz!Box in modem
> mode which does not allow resolving any records of the root zone.
> 
> The name server tests in the script will also only use TCP.
> 
> Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
> ---
> src/initscripts/system/unbound | 40 ++++++++++++++++++++++++++++++----------
> 1 file changed, 30 insertions(+), 10 deletions(-)
> 
> diff --git a/src/initscripts/system/unbound b/src/initscripts/system/unbound
> index dbcfc951f..bec2ab6ef 100644
> --- a/src/initscripts/system/unbound
> +++ b/src/initscripts/system/unbound
> @@ -15,6 +15,7 @@ TEST_DOMAIN_FAIL="dnssec-failed.org"
> INSECURE_ZONES=
> USE_FORWARDERS=1
> ENABLE_SAFE_SEARCH=off
> +FORCE_TCP=off
> 
> # Cache any local zones for 60 seconds
> LOCAL_TTL=60
> @@ -25,6 +26,12 @@ EDNS_DEFAULT_BUFFER_SIZE=4096
> # Load optional configuration
> [ -e "/etc/sysconfig/unbound" ] && . /etc/sysconfig/unbound
> 
> +DIG_ARGS=()
> +
> +if [ "${FORCE_TCP}" = "on" ]; then
> +	DIG_ARGS+=( "+tcp" )
> +fi
> +
> ip_address_revptr() {
> 	local addr=${1}
> 
> @@ -199,6 +206,14 @@ write_forward_conf() {
> 	(
> 		config_header
> 
> +		# Force using TCP for upstream servers only
> +		if [ "${FORCE_TCP}" = "on" ]; then
> +			echo "# Force using TCP for upstream servers only"
> +			echo "server:"
> +			echo "	tcp-upstream: yes"
> +			echo
> +		fi
> +
> 		local insecure_zones="${INSECURE_ZONES}"
> 
> 		local enabled zone server servers remark disable_dnssec rest
> @@ -391,7 +406,7 @@ ns_is_online() {
> 	local ns=${1}
> 	shift
> 
> -	dig @${ns} +nodnssec A ${TEST_DOMAIN} $@ >/dev/null
> +	dig "${DIG_ARGS[@]}" @${ns} +nodnssec A ${TEST_DOMAIN} $@ >/dev/null
> }
> 
> # Resolving ${TEST_DOMAIN_FAIL} will fail if the nameserver is validating
> @@ -399,11 +414,11 @@ ns_is_validating() {
> 	local ns=${1}
> 	shift
> 
> -	if ! dig @${ns} A ${TEST_DOMAIN_FAIL} $@ | grep -q SERVFAIL; then
> +	if ! dig "${DIG_ARGS[@]}" @${ns} A ${TEST_DOMAIN_FAIL} $@ | grep -q SERVFAIL; then
> 		return 1
> 	else
> 		# Determine if NS replies with "ad" data flag if DNSSEC enabled
> -		dig @${ns} +dnssec SOA ${TEST_DOMAIN} $@ | awk -F: '/\;\;\ flags\:/ { s=1; if (/\ ad/) s=0; exit s }'
> +		dig "${DIG_ARGS[@]}" @${ns} +dnssec SOA ${TEST_DOMAIN} $@ | awk -F: '/\;\;\ flags\:/ { s=1; if (/\ ad/) s=0; exit s }'
> 	fi
> }
> 
> @@ -413,28 +428,33 @@ ns_forwards_DNSKEY() {
> 	local ns=${1}
> 	shift
> 
> -	dig @${ns} DNSKEY ${TEST_DOMAIN} $@ | grep -qv SOA
> +	dig "${DIG_ARGS[@]}" @${ns} DNSKEY ${TEST_DOMAIN} $@ | grep -qv SOA
> }
> 
> ns_forwards_DS() {
> 	local ns=${1}
> 	shift
> 
> -	dig @${ns} DS ${TEST_DOMAIN} $@ | grep -qv SOA
> +	dig "${DIG_ARGS[@]}" @${ns} DS ${TEST_DOMAIN} $@ | grep -qv SOA
> }
> 
> ns_forwards_RRSIG() {
> 	local ns=${1}
> 	shift
> 
> -	dig @${ns} +dnssec A ${TEST_DOMAIN} $@ | grep -q RRSIG
> +	dig "${DIG_ARGS[@]}" @${ns} +dnssec A ${TEST_DOMAIN} $@ | grep -q RRSIG
> }
> 
> ns_supports_tcp() {
> 	local ns=${1}
> 	shift
> 
> -	dig @${ns} +tcp A ${TEST_DOMAIN} $@ >/dev/null || return 1
> +	# If TCP is forced we know by now if the server responds to it
> +	if [ "${TCP_TUNNEL}" = "on" ]; then
> +		return 0
> +	fi
> +
> +	dig "${DIG_ARGS[@]}" @${ns} +tcp A ${TEST_DOMAIN} $@ >/dev/null || return 1
> }
> 
> ns_determine_edns_buffer_size() {
> @@ -443,7 +463,7 @@ ns_determine_edns_buffer_size() {
> 
> 	local b
> 	for b in 4096 2048 1500 1480 1464 1400 1280 512; do
> -		if dig @${ns} +dnssec +bufsize=${b} A ${TEST_DOMAIN} $@ >/dev/null; then
> +		if dig "${DIG_ARGS[@]}" @${ns} +dnssec +bufsize=${b} A ${TEST_DOMAIN} $@ >/dev/null; then
> 			echo "${b}"
> 			return 0
> 		fi
> @@ -464,7 +484,7 @@ get_root_nameservers() {
> can_resolve_root() {
> 	local ns
> 	for ns in $(get_root_nameservers); do
> -		if dig @${ns} +dnssec SOA . $@ >/dev/null; then
> +		if dig "${DIG_ARGS[@]}" @${ns} +dnssec SOA . $@ >/dev/null; then
> 			return 0
> 		fi
> 	done
> @@ -514,7 +534,7 @@ resolve() {
> 	local ns
> 	for ns in $(read_name_servers); do
> 		local answer
> -		for answer in $(dig +short "@${ns}" A "${hostname}"); do
> +		for answer in $(dig "${DIG_ARGS[@]}" +short "@${ns}" A "${hostname}"); do
> 			found=1
> 
> 			# Filter out non-IP addresses
> -- 
> 2.12.2
>
  

Patch

diff --git a/src/initscripts/system/unbound b/src/initscripts/system/unbound
index dbcfc951f..bec2ab6ef 100644
--- a/src/initscripts/system/unbound
+++ b/src/initscripts/system/unbound
@@ -15,6 +15,7 @@  TEST_DOMAIN_FAIL="dnssec-failed.org"
 INSECURE_ZONES=
 USE_FORWARDERS=1
 ENABLE_SAFE_SEARCH=off
+FORCE_TCP=off
 
 # Cache any local zones for 60 seconds
 LOCAL_TTL=60
@@ -25,6 +26,12 @@  EDNS_DEFAULT_BUFFER_SIZE=4096
 # Load optional configuration
 [ -e "/etc/sysconfig/unbound" ] && . /etc/sysconfig/unbound
 
+DIG_ARGS=()
+
+if [ "${FORCE_TCP}" = "on" ]; then
+	DIG_ARGS+=( "+tcp" )
+fi
+
 ip_address_revptr() {
 	local addr=${1}
 
@@ -199,6 +206,14 @@  write_forward_conf() {
 	(
 		config_header
 
+		# Force using TCP for upstream servers only
+		if [ "${FORCE_TCP}" = "on" ]; then
+			echo "# Force using TCP for upstream servers only"
+			echo "server:"
+			echo "	tcp-upstream: yes"
+			echo
+		fi
+
 		local insecure_zones="${INSECURE_ZONES}"
 
 		local enabled zone server servers remark disable_dnssec rest
@@ -391,7 +406,7 @@  ns_is_online() {
 	local ns=${1}
 	shift
 
-	dig @${ns} +nodnssec A ${TEST_DOMAIN} $@ >/dev/null
+	dig "${DIG_ARGS[@]}" @${ns} +nodnssec A ${TEST_DOMAIN} $@ >/dev/null
 }
 
 # Resolving ${TEST_DOMAIN_FAIL} will fail if the nameserver is validating
@@ -399,11 +414,11 @@  ns_is_validating() {
 	local ns=${1}
 	shift
 
-	if ! dig @${ns} A ${TEST_DOMAIN_FAIL} $@ | grep -q SERVFAIL; then
+	if ! dig "${DIG_ARGS[@]}" @${ns} A ${TEST_DOMAIN_FAIL} $@ | grep -q SERVFAIL; then
 		return 1
 	else
 		# Determine if NS replies with "ad" data flag if DNSSEC enabled
-		dig @${ns} +dnssec SOA ${TEST_DOMAIN} $@ | awk -F: '/\;\;\ flags\:/ { s=1; if (/\ ad/) s=0; exit s }'
+		dig "${DIG_ARGS[@]}" @${ns} +dnssec SOA ${TEST_DOMAIN} $@ | awk -F: '/\;\;\ flags\:/ { s=1; if (/\ ad/) s=0; exit s }'
 	fi
 }
 
@@ -413,28 +428,33 @@  ns_forwards_DNSKEY() {
 	local ns=${1}
 	shift
 
-	dig @${ns} DNSKEY ${TEST_DOMAIN} $@ | grep -qv SOA
+	dig "${DIG_ARGS[@]}" @${ns} DNSKEY ${TEST_DOMAIN} $@ | grep -qv SOA
 }
 
 ns_forwards_DS() {
 	local ns=${1}
 	shift
 
-	dig @${ns} DS ${TEST_DOMAIN} $@ | grep -qv SOA
+	dig "${DIG_ARGS[@]}" @${ns} DS ${TEST_DOMAIN} $@ | grep -qv SOA
 }
 
 ns_forwards_RRSIG() {
 	local ns=${1}
 	shift
 
-	dig @${ns} +dnssec A ${TEST_DOMAIN} $@ | grep -q RRSIG
+	dig "${DIG_ARGS[@]}" @${ns} +dnssec A ${TEST_DOMAIN} $@ | grep -q RRSIG
 }
 
 ns_supports_tcp() {
 	local ns=${1}
 	shift
 
-	dig @${ns} +tcp A ${TEST_DOMAIN} $@ >/dev/null || return 1
+	# If TCP is forced we know by now if the server responds to it
+	if [ "${TCP_TUNNEL}" = "on" ]; then
+		return 0
+	fi
+
+	dig "${DIG_ARGS[@]}" @${ns} +tcp A ${TEST_DOMAIN} $@ >/dev/null || return 1
 }
 
 ns_determine_edns_buffer_size() {
@@ -443,7 +463,7 @@  ns_determine_edns_buffer_size() {
 
 	local b
 	for b in 4096 2048 1500 1480 1464 1400 1280 512; do
-		if dig @${ns} +dnssec +bufsize=${b} A ${TEST_DOMAIN} $@ >/dev/null; then
+		if dig "${DIG_ARGS[@]}" @${ns} +dnssec +bufsize=${b} A ${TEST_DOMAIN} $@ >/dev/null; then
 			echo "${b}"
 			return 0
 		fi
@@ -464,7 +484,7 @@  get_root_nameservers() {
 can_resolve_root() {
 	local ns
 	for ns in $(get_root_nameservers); do
-		if dig @${ns} +dnssec SOA . $@ >/dev/null; then
+		if dig "${DIG_ARGS[@]}" @${ns} +dnssec SOA . $@ >/dev/null; then
 			return 0
 		fi
 	done
@@ -514,7 +534,7 @@  resolve() {
 	local ns
 	for ns in $(read_name_servers); do
 		local answer
-		for answer in $(dig +short "@${ns}" A "${hostname}"); do
+		for answer in $(dig "${DIG_ARGS[@]}" +short "@${ns}" A "${hostname}"); do
 			found=1
 
 			# Filter out non-IP addresses