From: Štěpán Balážik Date: Thu, 18 Feb 2021 11:10:26 +0000 (+0100) Subject: lib/selection{,_iter}.c: allow switching back to UDP X-Git-Tag: v5.3.0~3^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f032e5ec056c332458a3cf766a0875c3918b725d;p=thirdparty%2Fknot-resolver.git lib/selection{,_iter}.c: allow switching back to UDP Switching to TCP instead of querying very slow servers over UDP has had unwanted side effect – we would sometimes get stuck with a server permanently switched to TCP. And if the server happens to not reply over TCP we were in trouble. Therefore after we TCP connect fails or timeouts we provide one last chance for the server over UDP. This will not prevent the next request to try TCP again on this server again, but we don't care because DNS MUST ******* work over TCP. --- diff --git a/lib/selection.c b/lib/selection.c index 1f47e159b..5a829bb18 100644 --- a/lib/selection.c +++ b/lib/selection.c @@ -230,7 +230,7 @@ static void check_tls_capable(struct address_state *address_state, /* TODO: uncomment these once we actually use the information it collects. */ /** * Check if there is a existing TCP connection to this address. - * + * * @p req has to have the selection_context properly initiazed. */ void check_tcp_connections(struct address_state *address_state, struct kr_request *req, struct sockaddr *address) { @@ -567,6 +567,11 @@ void error(struct kr_query *qry, struct address_state *addr_state, qry->flags.NO_MINIMIZE = true; } break; + case KR_SELECTION_TCP_CONNECT_FAILED: + case KR_SELECTION_TCP_CONNECT_TIMEOUT: + qry->server_selection.local_state->force_udp = true; + qry->flags.NO_0X20 = false; + break; case KR_SELECTION_NOTIMPL: case KR_SELECTION_OTHER_RCODE: case KR_SELECTION_DNSSEC_ERROR: @@ -576,8 +581,6 @@ void error(struct kr_query *qry, struct address_state *addr_state, addr_state->broken = true; break; case KR_SELECTION_TLS_HANDSHAKE_FAILED: - case KR_SELECTION_TCP_CONNECT_FAILED: - case KR_SELECTION_TCP_CONNECT_TIMEOUT: /* These might get resolved by retrying. */ break; default: @@ -587,7 +590,7 @@ void error(struct kr_query *qry, struct address_state *addr_state, addr_state->error_count++; addr_state->errors[sel_error]++; - + WITH_VERBOSE(qry) { KR_DNAME_GET_STR(ns_name, transport->ns_name); diff --git a/lib/selection.h b/lib/selection.h index 59e478bca..5b7bf15f9 100644 --- a/lib/selection.h +++ b/lib/selection.h @@ -81,6 +81,8 @@ struct local_state { /** Force resolution of a new NS name (if possible) * Done by selection.c:error in some cases. */ bool force_resolve; + /** Used to work around auths with broken TCP. */ + bool force_udp; void *private; /**< Inner state of the implementation.*/ }; diff --git a/lib/selection_iter.c b/lib/selection_iter.c index 61e1080df..97feead56 100644 --- a/lib/selection_iter.c +++ b/lib/selection_iter.c @@ -99,7 +99,7 @@ static void unpack_state_from_zonecut(struct iter_local_state *local_state, name_state->a_state = RECORD_UNKNOWN; name_state->aaaa_state = RECORD_UNKNOWN; } - + /* Iterate over all addresses of this NS (if any). */ for (uint8_t *obj = pack_head(*addresses); obj != pack_tail(*addresses); obj = pack_obj_next(obj)) { @@ -284,6 +284,14 @@ void iter_choose_transport(struct kr_query *qry, struct kr_transport **transport assert(0); break; } + + if (*transport && + (*transport)->protocol==KR_TRANSPORT_TCP && + !qry->server_selection.local_state->truncated && + qry->server_selection.local_state->force_udp) { + // Last chance on broken TCP. + (*transport)->protocol = KR_TRANSPORT_UDP; + } } if (*transport == NULL && local_state->last_error == KR_SELECTION_DNSSEC_ERROR) {