]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lib/nsrep: flag nameservers not supporting TCP
authorMarek Vavruša <mvavrusa@cloudflare.com>
Wed, 15 Aug 2018 22:12:37 +0000 (15:12 -0700)
committerMarek Vavruša <mvavrusa@cloudflare.com>
Fri, 7 Sep 2018 17:45:21 +0000 (10:45 -0700)
The previous behavior was to flag nameserver that doesn't respond
over TCP as dead, but this doesn't work in case when nameserver
only supports UDP (e.g. duckdns.org).

The new behavior flags nameservers that don't support TCP separately
in reputation cache. The effect of that is that when UDP times out,
another nameserver is elected instead of retrying over TCP and blacklisting
the nameserver.

daemon/worker.c
lib/layer/iterate.c
lib/nsrep.c
lib/nsrep.h
lib/resolve.c

index 5ab936d5683f5099478902a53ce13b3757f3aa50..b013c27ffbc7dd2febf242d121a6ca1497cb0e79 100644 (file)
@@ -1062,6 +1062,20 @@ static int session_next_waiting_send(struct session *session)
        return ret;
 }
 
+static struct kr_query *session_current_query(struct session *session)
+{
+       if (session->waiting.len == 0) {
+               return NULL;
+       }
+
+       struct qr_task *task = session->waiting.at[0];
+       if (task->ctx->req.rplan.pending.len == 0) {
+               return NULL;
+       }
+
+       return array_tail(task->ctx->req.rplan.pending);
+}
+
 static int session_tls_hs_cb(struct session *session, int status)
 {
        struct worker_ctx *worker = get_worker();
@@ -1069,10 +1083,14 @@ static int session_tls_hs_cb(struct session *session, int status)
        int deletion_res = worker_del_tcp_waiting(worker, &peer->ip);
        int ret = kr_ok();
 
-       if (status) {
-               kr_nsrep_update_rtt(NULL, &peer->ip, KR_NS_DEAD,
-                                   worker->engine->resolver.cache_rtt,
-                                   KR_NS_UPDATE_NORESET);
+       struct kr_query *qry = session_current_query(session);
+       if (status != 0) {
+               struct kr_context *ctx = &worker->engine->resolver;
+               /* Flag TCP as unsupported status */
+               qry->ns.reputation |= KR_NS_NOTCP;
+               kr_nsrep_update_rep(&qry->ns, qry->ns.reputation, ctx->cache_rep);
+               /* Penalize servers unresponsive over TCP */
+               kr_nsrep_update_rtt(&qry->ns, &peer->ip, KR_NS_PENALTY, ctx->cache_rtt, KR_NS_ADD);
                return ret;
        }
 
@@ -1139,20 +1157,6 @@ static int session_tls_hs_cb(struct session *session, int status)
        return kr_ok();
 }
 
-static struct kr_query *session_current_query(struct session *session)
-{
-       if (session->waiting.len == 0) {
-               return NULL;
-       }
-
-       struct qr_task *task = session->waiting.at[0];
-       if (task->ctx->req.rplan.pending.len == 0) {
-               return NULL;
-       }
-
-       return array_tail(task->ctx->req.rplan.pending);
-}
-
 static void on_connect(uv_connect_t *req, int status)
 {
        struct worker_ctx *worker = get_worker();
@@ -1176,7 +1180,18 @@ static void on_connect(uv_connect_t *req, int status)
 
        uv_timer_stop(&session->timeout);
 
+       struct kr_query *qry = session_current_query(session);
        if (status != 0) {
+               struct kr_context *ctx = &worker->engine->resolver;
+               WITH_VERBOSE (qry) {
+                       char addr_str[INET6_ADDRSTRLEN];
+                       inet_ntop(session->peer.ip.sa_family, kr_inaddr(&session->peer.ip),
+                                 addr_str, sizeof(addr_str));
+                       VERBOSE_MSG(qry, "=> failed to connect to '%s'\n", addr_str);
+               }
+               /* Flag TCP as unsupported status */
+               qry->ns.reputation |= KR_NS_NOTCP;
+               kr_nsrep_update_rep(&qry->ns, qry->ns.reputation, ctx->cache_rep);
                worker_del_tcp_waiting(worker, &peer->ip);
                while (session->waiting.len > 0) {
                        struct qr_task *task = session->waiting.at[0];
@@ -1214,7 +1229,6 @@ static void on_connect(uv_connect_t *req, int status)
                }
        }
 
-       struct kr_query *qry = session_current_query(session);
        WITH_VERBOSE (qry) {
                char addr_str[INET6_ADDRSTRLEN];
                inet_ntop(session->peer.ip.sa_family, kr_inaddr(&session->peer.ip),
@@ -1225,6 +1239,13 @@ static void on_connect(uv_connect_t *req, int status)
        session->connected = true;
        session->handle = (uv_handle_t *)handle;
 
+       /* Flag TCP as supported */
+       if (qry->ns.reputation & KR_NS_NOTCP) {
+               struct kr_context *ctx = &worker->engine->resolver;
+               qry->ns.reputation &= ~KR_NS_NOTCP;
+               kr_nsrep_update_rep(&qry->ns, qry->ns.reputation, ctx->cache_rep);
+       }
+
        int ret = kr_ok();
        if (session->has_tls) {
                ret = tls_client_connect_start(session->tls_client_ctx,
index 02b17855aef9ef933cc916ddf4c1ca3de8d179a7..06c4012f8f246cfa330bc7cb3c3f024ff59efe1f 100644 (file)
@@ -1068,8 +1068,8 @@ static int resolve(kr_layer_t *ctx, knot_pkt_t *pkt)
        } else if (knot_wire_get_tc(pkt->wire)) {
                VERBOSE_MSG("<= truncated response, failover to TCP\n");
                if (query) {
-                       /* Fail if already on TCP. */
-                       if (query->flags.TCP) {
+                       /* Fail if already on TCP or doesn't support TCP. */
+                       if (query->flags.TCP || (query->ns.reputation & KR_NS_NOTCP)) {
                                VERBOSE_MSG("<= TC=1 with TCP, bailing out\n");
                                return resolve_error(pkt, req);
                        }
index 20e70bd561af126b12a2920cff9ad8df8ba57748..4153f8b77e72a5452b89a063a676c3b49cba83d1 100644 (file)
@@ -345,7 +345,8 @@ int kr_nsrep_elect(struct kr_query *qry, struct kr_context *ctx)
        }
        trie_it_free(it);
 
-       if (qry->ns.score <= KR_NS_MAX_SCORE && qry->ns.score >= KR_NS_LONG) {
+       if (qry->ns.score <= KR_NS_MAX_SCORE && qry->ns.score >= KR_NS_LONG
+               && !(qry->ns.reputation & KR_NS_NOTCP)) {
                /* This is a low-reliability probe,
                 * go with TCP to get ICMP reachability check. */
                qry->flags.TCP = true;
index 6ea9f167e69ab88b8160971537ddf6acc2ef04f7..67cff4ad7d691e2b10723fe79c759ebcb7d6e702 100644 (file)
@@ -54,7 +54,8 @@ enum kr_ns_score {
 enum kr_ns_rep {
        KR_NS_NOIP4  = 1 << 0, /**< NS has no IPv4 */
        KR_NS_NOIP6  = 1 << 1, /**< NS has no IPv6 */
-       KR_NS_NOEDNS = 1 << 2  /**< NS has no EDNS support */
+       KR_NS_NOEDNS = 1 << 2, /**< NS has no EDNS support */
+       KR_NS_NOTCP  = 1 << 3  /**< NS doesn't support TCP */
 };
 
 /**
index 4b4f9522dbd1758937560940b340283d8df3b425..26cc73067dab7aef5b991cdb3813674ed520b72e 100644 (file)
@@ -921,7 +921,7 @@ int kr_resolve_consume(struct kr_request *request, const struct sockaddr *src, k
        }
        bool tried_tcp = (qry->flags.TCP);
        if (!packet || packet->size == 0) {
-               if (tried_tcp) {
+               if (tried_tcp || (qry->ns.reputation & KR_NS_NOTCP)) {
                        request->state = KR_STATE_FAIL;
                } else {
                        qry->flags.TCP = true;