]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
dig: add reference counter to the dig_lookup_t object
authorOndřej Surý <ondrej@sury.org>
Thu, 5 Nov 2020 14:22:38 +0000 (15:22 +0100)
committerOndřej Surý <ondrej@sury.org>
Sat, 7 Nov 2020 20:11:42 +0000 (21:11 +0100)
Sometimes, the dig_lookup_t could be destroyed before the final
send_done() callback was be called, leading to dereferencing an
already freed dig_lookup_t object.  By making the dig_lookup_t
reference counted, we are ensuring that it won't be freed until
the last reference (from dig_query_t .lookup) is released.

bin/dig/dighost.c
bin/dig/dighost.h
bin/dig/nslookup.c

index 6972fbece6cf8b4266a629553e3021eee76d815a..081518cb17325078a0421d6f040b467981944ab6 100644 (file)
@@ -94,10 +94,12 @@ dig_lookuplist_t lookup_list;
 dig_serverlist_t server_list;
 dig_searchlistlist_t search_list;
 
+static atomic_bool cancel_now = ATOMIC_VAR_INIT(false);
+
 bool check_ra = false, have_ipv4 = false, have_ipv6 = false,
-     specified_source = false, free_now = false, cancel_now = false,
-     usesearch = false, showsearch = false, is_dst_up = false,
-     keep_open = false, verbose = false, yaml = false;
+     specified_source = false, free_now = false, usesearch = false,
+     showsearch = false, is_dst_up = false, keep_open = false, verbose = false,
+     yaml = false;
 in_port_t port = 53;
 unsigned int timeout = 0;
 unsigned int extrabytes;
@@ -699,6 +701,14 @@ make_empty_lookup(void) {
        ISC_LIST_INIT(looknew->q);
        ISC_LIST_INIT(looknew->connecting);
        ISC_LIST_INIT(looknew->my_server_list);
+
+       isc_refcount_init(&looknew->references, 1);
+
+       looknew->magic = DIG_LOOKUP_MAGIC;
+
+       debug("make_empty_lookup() = %p->references = %" PRIuFAST32, looknew,
+             isc_refcount_current(&looknew->references));
+
        return (looknew);
 }
 
@@ -750,7 +760,6 @@ clone_lookup(dig_lookup_t *lookold, bool servers) {
        INSIST(!free_now);
 
        looknew = make_empty_lookup();
-       INSIST(looknew != NULL);
        strlcpy(looknew->textname, lookold->textname, MXNAME);
        strlcpy(looknew->cmdline, lookold->cmdline, MXNAME);
        looknew->textname[MXNAME - 1] = 0;
@@ -842,6 +851,11 @@ clone_lookup(dig_lookup_t *lookold, bool servers) {
                clone_server_list(lookold->my_server_list,
                                  &looknew->my_server_list);
        }
+
+       isc_refcount_init(&looknew->references, 1);
+
+       looknew->magic = DIG_LOOKUP_MAGIC;
+
        return (looknew);
 }
 
@@ -855,7 +869,7 @@ clone_lookup(dig_lookup_t *lookold, bool servers) {
  */
 dig_lookup_t *
 requeue_lookup(dig_lookup_t *lookold, bool servers) {
-       dig_lookup_t *looknew;
+       dig_lookup_t *looknew = NULL;
 
        debug("requeue_lookup()");
 
@@ -1549,8 +1563,19 @@ add_question(dns_message_t *message, dns_name_t *name, dns_rdataclass_t rdclass,
  */
 static void
 check_if_done(void) {
+       dig_lookup_t *lookup = NULL;
+
        debug("check_if_done()");
        debug("list %s", ISC_LIST_EMPTY(lookup_list) ? "empty" : "full");
+
+       lookup = ISC_LIST_HEAD(lookup_list);
+       while (lookup != NULL) {
+               dig_lookup_t *next = NULL;
+               debug("pending lookup %p", lookup);
+               next = ISC_LIST_NEXT(lookup, link);
+               lookup = next;
+       }
+
        if (ISC_LIST_EMPTY(lookup_list) && current_lookup == NULL &&
            isc_refcount_current(&sendcount) == 0)
        {
@@ -1560,33 +1585,119 @@ check_if_done(void) {
        }
 }
 
-#define clear_query(q) _clear_query(q, __FILE__, __LINE__)
+static void
+_destroy_lookup(dig_lookup_t *lookup) {
+       dig_server_t *s;
+       void *ptr;
 
-/*%
- * Clear out a query when we're done with it.  WARNING: This routine
- * WILL invalidate the query pointer.
- */
+       debug("destroy_lookup");
+
+       isc_refcount_destroy(&lookup->references);
+
+       REQUIRE(ISC_LIST_EMPTY(lookup->q));
+       REQUIRE(ISC_LIST_EMPTY(lookup->connecting));
+
+       s = ISC_LIST_HEAD(lookup->my_server_list);
+       while (s != NULL) {
+               debug("freeing server %p belonging to %p", s, lookup);
+               ptr = s;
+               s = ISC_LIST_NEXT(s, link);
+               ISC_LIST_DEQUEUE(lookup->my_server_list, (dig_server_t *)ptr,
+                                link);
+               isc_mem_free(mctx, ptr);
+       }
+       if (lookup->sendmsg != NULL) {
+               dns_message_detach(&lookup->sendmsg);
+       }
+       if (lookup->querysig != NULL) {
+               debug("freeing buffer %p", lookup->querysig);
+               isc_buffer_free(&lookup->querysig);
+       }
+       if (lookup->sendspace != NULL) {
+               isc_mempool_put(commctx, lookup->sendspace);
+       }
+
+       if (lookup->tsigctx != NULL) {
+               dst_context_destroy(&lookup->tsigctx);
+       }
+
+       if (lookup->ecs_addr != NULL) {
+               isc_mem_free(mctx, lookup->ecs_addr);
+       }
+
+       if (lookup->ednsopts != NULL) {
+               size_t i;
+               for (i = 0; i < EDNSOPT_OPTIONS; i++) {
+                       if (lookup->ednsopts[i].value != NULL) {
+                               isc_mem_free(mctx, lookup->ednsopts[i].value);
+                       }
+               }
+               isc_mem_free(mctx, lookup->ednsopts);
+       }
+
+       isc_mem_free(mctx, lookup);
+}
+
+#define lookup_attach(s, t) _lookup_attach(s, t, __FILE__, __LINE__)
 static void
-_clear_query(dig_query_t *query, const char *file, unsigned int line) {
-       dig_lookup_t *lookup = NULL;
+_lookup_attach(dig_lookup_t *lookup, dig_lookup_t **lookupp, const char *file,
+              unsigned int line) {
+       REQUIRE(DIG_VALID_LOOKUP(lookup));
+       REQUIRE(lookupp != NULL && *lookupp == NULL);
 
-       REQUIRE(query != NULL);
+       debug("%s:%u:lookup_attach(%p) = %" PRIuFAST32, file, line, lookup,
+             isc_refcount_current(&lookup->references) + 1);
 
-       debug("%s:%u:clear_query(%p) = %" PRIuFAST32, file, line, query,
-             isc_refcount_current(&query->references));
+       (void)isc_refcount_increment(&lookup->references);
 
-       INSIST(query->recvspace != NULL);
+       *lookupp = lookup;
+}
 
-       isc_mempool_put(commctx, query->recvspace);
-       isc_mempool_put(commctx, query->tmpsendspace);
+#define lookup_detach(l) _lookup_detach(l, __FILE__, __LINE__)
+static void
+_lookup_detach(dig_lookup_t **lookupp, const char *file, unsigned int line) {
+       REQUIRE(DIG_VALID_LOOKUP(*lookupp));
+
+       dig_lookup_t *lookup = *lookupp;
+       *lookupp = NULL;
+
+       debug("%s:%u:lookup_detach(%p) = %" PRIuFAST32, file, line, lookup,
+             isc_refcount_current(&lookup->references) - 1);
+
+       if (isc_refcount_decrement(&lookup->references) == 1) {
+               _destroy_lookup(lookup);
+               if (lookup == current_lookup) {
+                       current_lookup = NULL;
+                       start_lookup();
+               }
+       }
 }
 
+void
+destroy_lookup(dig_lookup_t *lookup) {
+       REQUIRE(DIG_VALID_LOOKUP(lookup));
+
+       REQUIRE(isc_refcount_decrement(&lookup->references) == 1);
+       _destroy_lookup(lookup);
+}
+
+/*%
+ * Destroy a query when we're done with it.  WARNING: This routine
+ * WILL invalidate the query pointer.
+ */
 static void
-_destroy_query(dig_query_t *query, const char *file, unsigned int line) {
+destroy_query(dig_query_t *query, const char *file, unsigned int line) {
        debug("%s:%u:destroy_query(%p) = %" PRIuFAST32, file, line, query,
              isc_refcount_current(&query->references));
 
-       _clear_query(query, file, line);
+       isc_refcount_destroy(&query->references);
+
+       lookup_detach(&query->lookup);
+
+       INSIST(query->recvspace != NULL);
+
+       isc_mempool_put(commctx, query->recvspace);
+       isc_mempool_put(commctx, query->tmpsendspace);
 
        query->magic = 0;
        isc_mem_free(mctx, query);
@@ -1623,7 +1734,7 @@ _query_detach(dig_query_t **queryp, const char *file, unsigned int line) {
        lookup = query->lookup;
 
        if (lookup->current_query == query) {
-               lookup->current_query = NULL;
+               query_detach(&lookup->current_query);
        }
 
        if (ISC_LINK_LINKED(query, link)) {
@@ -1637,96 +1748,8 @@ _query_detach(dig_query_t **queryp, const char *file, unsigned int line) {
              isc_refcount_current(&query->references) - 1);
 
        if (isc_refcount_decrement(&query->references) == 1) {
-               _destroy_query(query, file, line);
-       }
-}
-
-/*%
- * Try and clear out a lookup if we're done with it.  Return true if
- * the lookup was successfully cleared.  If true is returned, the
- * lookup pointer has been invalidated.
- */
-static bool
-try_clear_lookup(dig_lookup_t *lookup) {
-       dig_query_t *q;
-
-       REQUIRE(lookup != NULL);
-
-       debug("try_clear_lookup(%p)", lookup);
-
-       if (ISC_LIST_HEAD(lookup->q) != NULL ||
-           ISC_LIST_HEAD(lookup->connecting) != NULL)
-       {
-               if (debugging) {
-                       q = ISC_LIST_HEAD(lookup->q);
-                       while (q != NULL) {
-                               debug("query to %s still pending", q->servname);
-                               q = ISC_LIST_NEXT(q, link);
-                       }
-
-                       q = ISC_LIST_HEAD(lookup->connecting);
-                       while (q != NULL) {
-                               debug("query to %s still connecting",
-                                     q->servname);
-                               q = ISC_LIST_NEXT(q, clink);
-                       }
-               }
-               return (false);
-       }
-
-       /*
-        * At this point, we know there are no queries on the lookup,
-        * so can make it go away also.
-        */
-       destroy_lookup(lookup);
-       return (true);
-}
-
-void
-destroy_lookup(dig_lookup_t *lookup) {
-       dig_server_t *s;
-       void *ptr;
-
-       debug("destroy_lookup");
-       s = ISC_LIST_HEAD(lookup->my_server_list);
-       while (s != NULL) {
-               debug("freeing server %p belonging to %p", s, lookup);
-               ptr = s;
-               s = ISC_LIST_NEXT(s, link);
-               ISC_LIST_DEQUEUE(lookup->my_server_list, (dig_server_t *)ptr,
-                                link);
-               isc_mem_free(mctx, ptr);
-       }
-       if (lookup->sendmsg != NULL) {
-               dns_message_detach(&lookup->sendmsg);
-       }
-       if (lookup->querysig != NULL) {
-               debug("freeing buffer %p", lookup->querysig);
-               isc_buffer_free(&lookup->querysig);
-       }
-       if (lookup->sendspace != NULL) {
-               isc_mempool_put(commctx, lookup->sendspace);
-       }
-
-       if (lookup->tsigctx != NULL) {
-               dst_context_destroy(&lookup->tsigctx);
-       }
-
-       if (lookup->ecs_addr != NULL) {
-               isc_mem_free(mctx, lookup->ecs_addr);
-       }
-
-       if (lookup->ednsopts != NULL) {
-               size_t i;
-               for (i = 0; i < EDNSOPT_OPTIONS; i++) {
-                       if (lookup->ednsopts[i].value != NULL) {
-                               isc_mem_free(mctx, lookup->ednsopts[i].value);
-                       }
-               }
-               isc_mem_free(mctx, lookup->ednsopts);
+               destroy_query(query, file, line);
        }
-
-       isc_mem_free(mctx, lookup);
 }
 
 /*%
@@ -1738,7 +1761,8 @@ destroy_lookup(dig_lookup_t *lookup) {
 void
 start_lookup(void) {
        debug("start_lookup()");
-       if (cancel_now) {
+
+       if (atomic_load(&cancel_now)) {
                return;
        }
 
@@ -1749,15 +1773,22 @@ start_lookup(void) {
        INSIST(current_lookup == NULL);
 
        current_lookup = ISC_LIST_HEAD(lookup_list);
+
        /*
         * Put the current lookup somewhere so cancel_all can find it
         */
        if (current_lookup != NULL) {
+               /*
+                * Formally, we should attach the lookup to the current_lookup
+                * and detach it from the lookup_list, but it would be one
+                * attach and one detach.
+                */
                ISC_LIST_DEQUEUE(lookup_list, current_lookup, link);
                if (setup_lookup(current_lookup)) {
                        do_lookup(current_lookup);
                } else if (next_origin(current_lookup)) {
-                       check_next_lookup(current_lookup);
+                       lookup_detach(&current_lookup);
+                       start_lookup();
                }
        } else {
                check_if_done();
@@ -1772,16 +1803,16 @@ static void
 check_next_lookup(dig_lookup_t *lookup) {
        INSIST(!free_now);
 
+       INSIST(lookup == current_lookup);
+
        debug("check_next_lookup(%p)", lookup);
 
        if (ISC_LIST_HEAD(lookup->q) != NULL) {
                debug("still have a worker");
                return;
        }
-       if (try_clear_lookup(lookup)) {
-               current_lookup = NULL;
-               start_lookup();
-       }
+
+       lookup_detach(&lookup);
 }
 
 /*%
@@ -2098,8 +2129,7 @@ _new_query(dig_lookup_t *lookup, char *servname, char *userarg,
 
        query = isc_mem_allocate(mctx, sizeof(dig_query_t));
        debug("create query %p linked to lookup %p", query, lookup);
-       *query = (dig_query_t){ .lookup = lookup,
-                               .sendbuf = lookup->renderbuf,
+       *query = (dig_query_t){ .sendbuf = lookup->renderbuf,
                                .servname = servname,
                                .userarg = userarg,
                                .first_pass = true,
@@ -2107,6 +2137,10 @@ _new_query(dig_lookup_t *lookup, char *servname, char *userarg,
                                .recvspace = isc_mempool_get(commctx),
                                .tmpsendspace = isc_mempool_get(commctx) };
 
+       lookup_attach(lookup, &query->lookup);
+
+       isc_refcount_init(&query->references, 1);
+
        debug("%s:%u:new_query(%p) = %" PRIuFAST32, file, line, query,
              isc_refcount_current(&query->references));
 
@@ -2643,34 +2677,42 @@ send_done(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) {
 
        INSIST(!free_now);
 
-       if (eresult == ISC_R_CANCELED) {
-               goto done;
-       } else if (eresult != ISC_R_SUCCESS) {
+       LOCK_LOOKUP;
+
+       isc_nmhandle_detach(&query->sendhandle);
+
+       if (eresult != ISC_R_SUCCESS) {
                if (eresult != ISC_R_CANCELED) {
                        debug("send failed: %s", isc_result_totext(eresult));
                }
-               goto done;
-       }
-
-       LOCK_LOOKUP;
+               query_detach(&query);
 
-       l = query->lookup;
+               UNLOCK_LOOKUP;
+               return;
+       }
 
+       lookup_attach(query->lookup, &l);
        if (l->ns_search_only && !l->trace_root && !l->tcp_mode) {
                debug("sending next, since searching");
                next = ISC_LIST_NEXT(query, link);
-               if (next != NULL) {
+
+               query_detach(&query);
+               lookup_detach(&l);
+
+               if (next == NULL) {
+                       check_next_lookup(current_lookup);
+               } else {
                        start_udp(next);
                }
-       }
 
-       UNLOCK_LOOKUP;
+               check_if_done();
+               UNLOCK_LOOKUP;
+               return;
+       }
 
-done:
-       isc_nmhandle_detach(&query->sendhandle);
        query_detach(&query);
+       lookup_detach(&l);
 
-       LOCK_LOOKUP;
        check_if_done();
        UNLOCK_LOOKUP;
 }
@@ -2708,13 +2750,11 @@ static void
 start_tcp(dig_query_t *query) {
        isc_result_t result;
        dig_query_t *next;
-       dig_lookup_t *l;
        REQUIRE(DIG_VALID_QUERY(query));
 
        debug("start_tcp(%p)", query);
 
-       l = query->lookup;
-       query->lookup->current_query = query;
+       query_attach(query, &query->lookup->current_query);
        result = get_address(query->servname, port, &query->sockaddr);
        if (result != ISC_R_SUCCESS) {
                /*
@@ -2726,7 +2766,8 @@ start_tcp(dig_query_t *query) {
                return;
        }
 
-       if (!l->mapped && isc_sockaddr_pf(&query->sockaddr) == AF_INET6 &&
+       if (!query->lookup->mapped &&
+           isc_sockaddr_pf(&query->sockaddr) == AF_INET6 &&
            IN6_IS_ADDR_V4MAPPED(&query->sockaddr.type.sin6.sin6_addr))
        {
                isc_netaddr_t netaddr;
@@ -2741,14 +2782,13 @@ start_tcp(dig_query_t *query) {
                } else {
                        next = NULL;
                }
-               l = query->lookup;
                query_detach(&query);
                if (next == NULL) {
                        dighost_warning("No acceptable nameservers");
-                       check_next_lookup(l);
-                       return;
+                       check_next_lookup(current_lookup);
+               } else {
+                       start_tcp(next);
                }
-               start_tcp(next);
                return;
        }
 
@@ -2792,15 +2832,15 @@ start_tcp(dig_query_t *query) {
         * If we're at the endgame of a nameserver search, we need to
         * immediately bring up all the queries.  Do it here.
         */
-       if (l->ns_search_only && !l->trace_root) {
+       if (query->lookup->ns_search_only && !query->lookup->trace_root) {
                debug("sending next, since searching");
                if (ISC_LINK_LINKED(query, link)) {
                        next = ISC_LIST_NEXT(query, link);
-                       ISC_LIST_DEQUEUE(l->q, query, link);
+                       ISC_LIST_DEQUEUE(query->lookup->q, query, link);
                } else {
                        next = NULL;
                }
-               ISC_LIST_ENQUEUE(l->connecting, query, clink);
+               ISC_LIST_ENQUEUE(query->lookup->connecting, query, clink);
                if (next != NULL) {
                        start_tcp(next);
                }
@@ -2891,7 +2931,6 @@ udp_ready(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) {
  */
 static void
 start_udp(dig_query_t *query) {
-       dig_lookup_t *l = NULL;
        isc_result_t result;
        dig_query_t *next = NULL;
        int i = 0;
@@ -2900,11 +2939,12 @@ start_udp(dig_query_t *query) {
 
        debug("start_udp(%p)", query);
 
-       l = query->lookup;
-       l->current_query = query;
+       query_attach(query, &query->lookup->current_query);
        debug("working on lookup %p, query %p", query->lookup, query);
+
        if (query->handle != NULL) {
-               send_udp(query);
+               launch_next_query(query);
+               query_detach(&query);
                return;
        }
 
@@ -2915,7 +2955,8 @@ start_udp(dig_query_t *query) {
                return;
        }
 
-       if (!l->mapped && isc_sockaddr_pf(&query->sockaddr) == AF_INET6 &&
+       if (!query->lookup->mapped &&
+           isc_sockaddr_pf(&query->sockaddr) == AF_INET6 &&
            IN6_IS_ADDR_V4MAPPED(&query->sockaddr.type.sin6.sin6_addr))
        {
                isc_netaddr_t netaddr;
@@ -2925,11 +2966,10 @@ start_udp(dig_query_t *query) {
                isc_netaddr_format(&netaddr, buf, sizeof(buf));
                dighost_warning("Skipping mapped address '%s'", buf);
                next = ISC_LIST_NEXT(query, link);
-               l = query->lookup;
                query_detach(&query);
                if (next == NULL) {
                        dighost_warning("No acceptable nameservers");
-                       check_next_lookup(l);
+                       check_next_lookup(current_lookup);
                } else {
                        start_udp(next);
                }
@@ -3003,16 +3043,17 @@ force_next(dig_query_t *query) {
        debug("force_next()");
 
        LOCK_LOOKUP;
-       l = query->lookup;
-
        INSIST(!free_now);
 
-       if (cancel_now) {
+       if (atomic_load(&cancel_now)) {
                UNLOCK_LOOKUP;
                return;
        }
 
+       lookup_attach(query->lookup, &l);
+
        if (try_next_server(l)) {
+               lookup_detach(&l);
                UNLOCK_LOOKUP;
                return;
        }
@@ -3021,9 +3062,10 @@ force_next(dig_query_t *query) {
                l->retries--;
                debug("making new TCP request, %d tries left", l->retries);
                requeue_lookup(l, true);
+               lookup_detach(&l);
                isc_refcount_decrement0(&recvcount);
                query_detach(&query);
-               check_next_lookup(l);
+               check_next_lookup(current_lookup);
                UNLOCK_LOOKUP;
                return;
        }
@@ -3052,7 +3094,8 @@ force_next(dig_query_t *query) {
 
        query_detach(&query);
        cancel_lookup(l);
-       check_next_lookup(l);
+       lookup_detach(&l);
+       check_next_lookup(current_lookup);
        UNLOCK_LOOKUP;
 }
 
@@ -3093,11 +3136,13 @@ launch_next_query(dig_query_t *query) {
 
        debug("launch_next_query()");
 
-       if (!query->lookup->pending) {
+       lookup_attach(query->lookup, &l);
+
+       if (!l->pending) {
                debug("ignoring launch_next_query because !pending");
-               l = query->lookup;
                query_detach(&query);
-               check_next_lookup(l);
+               lookup_detach(&l);
+               check_next_lookup(current_lookup);
                return;
        }
 
@@ -3131,16 +3176,16 @@ launch_next_query(dig_query_t *query) {
                      isc_refcount_current(&sendcount));
 
                /* XXX qrflag, print_query, etc... */
-               if (!ISC_LIST_EMPTY(query->lookup->q) && query->lookup->qr) {
+               if (!ISC_LIST_EMPTY(l->q) && l->qr) {
                        extrabytes = 0;
-                       dighost_printmessage(ISC_LIST_HEAD(query->lookup->q),
-                                            &query->lookup->renderbuf,
-                                            query->lookup->sendmsg, true);
-                       if (query->lookup->stats) {
+                       dighost_printmessage(ISC_LIST_HEAD(l->q), &l->renderbuf,
+                                            l->sendmsg, true);
+                       if (l->stats) {
                                print_query_size(query);
                        }
                }
        }
+       lookup_detach(&l);
        return;
 }
 
@@ -3163,13 +3208,14 @@ tcp_connected(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) {
        debug("tcp_connected()");
 
        LOCK_LOOKUP;
+       lookup_attach(query->lookup, &l);
 
        if (eresult == ISC_R_CANCELED) {
                debug("in cancel handler");
                isc_sockaddr_format(&query->sockaddr, sockstr, sizeof(sockstr));
-               l = query->lookup;
                query_detach(&query);
-               check_next_lookup(l);
+               lookup_detach(&l);
+               check_next_lookup(current_lookup);
                UNLOCK_LOOKUP;
                return;
        } else if (eresult != ISC_R_SUCCESS) {
@@ -3179,8 +3225,7 @@ tcp_connected(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) {
                if (eresult != ISC_R_CANCELED) {
                        dighost_warning("Connection to %s(%s) for %s failed: "
                                        "%s.",
-                                       sockstr, query->servname,
-                                       query->lookup->textname,
+                                       sockstr, query->servname, l->textname,
                                        isc_result_totext(eresult));
                }
 
@@ -3188,7 +3233,6 @@ tcp_connected(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) {
                if (exitcode < 9) {
                        exitcode = 9;
                }
-               l = query->lookup;
 
                if (l->retries > 1) {
                        debug("making new TCP request, %d tries left",
@@ -3205,15 +3249,15 @@ tcp_connected(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) {
                }
 
                query_detach(&query);
+               lookup_detach(&l);
 
                if (next != NULL) {
                        start_tcp(next);
                } else {
-                       check_next_lookup(l);
+                       check_next_lookup(current_lookup);
                }
 
                check_if_done();
-
                UNLOCK_LOOKUP;
                return;
        }
@@ -3233,6 +3277,7 @@ tcp_connected(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) {
        launch_next_query(query);
        query_detach(&query);
        isc_nmhandle_detach(&handle);
+       lookup_detach(&l);
        UNLOCK_LOOKUP;
 }
 
@@ -3243,15 +3288,15 @@ tcp_connected(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) {
  * false means more data is on the way, and the recv has been issued.
  */
 static bool
-check_for_more_data(dig_query_t *query, dns_message_t *msg,
-                   isc_sockaddr_t *peer, int len) {
+check_for_more_data(dig_lookup_t *lookup, dig_query_t *query,
+                   dns_message_t *msg, isc_sockaddr_t *peer, int len) {
        dns_rdataset_t *rdataset = NULL;
        dns_rdata_t rdata = DNS_RDATA_INIT;
        dns_rdata_soa_t soa;
-       uint32_t ixfr_serial = query->lookup->ixfr_serial, serial;
+       uint32_t ixfr_serial = lookup->ixfr_serial, serial;
        isc_result_t result;
-       bool ixfr = query->lookup->rdtype == dns_rdatatype_ixfr;
-       bool axfr = query->lookup->rdtype == dns_rdatatype_axfr;
+       bool ixfr = lookup->rdtype == dns_rdatatype_ixfr;
+       bool axfr = lookup->rdtype == dns_rdatatype_axfr;
 
        if (ixfr) {
                axfr = query->ixfr_axfr;
@@ -3487,7 +3532,8 @@ recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
        isc_buffer_t b;
        dns_message_t *msg = NULL;
        isc_result_t result;
-       dig_lookup_t *n, *l;
+       dig_lookup_t *n = NULL;
+       dig_lookup_t *l = NULL;
        bool docancel = false;
        bool match = true;
        bool done_process_opt = false;
@@ -3517,7 +3563,7 @@ recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
 
        TIME_NOW(&query->time_recv);
 
-       l = query->lookup;
+       lookup_attach(query->lookup, &l);
 
        if (eresult == ISC_R_TIMEDOUT && !l->tcp_mode && l->retries > 1) {
                dig_query_t *newq = NULL;
@@ -3525,26 +3571,26 @@ recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
                l->retries--;
                debug("resending UDP request to first server, %d tries left",
                      l->retries);
-               newq = new_query(query->lookup, query->servname,
-                                query->userarg);
+               newq = new_query(l, query->servname, query->userarg);
 
                ISC_LIST_PREPEND(l->q, newq, link);
 
                isc_nmhandle_detach(&query->readhandle);
                query_detach(&query);
-
                UNLOCK_LOOKUP;
 
                start_udp(ISC_LIST_HEAD(l->q));
+               lookup_detach(&l);
                return;
        }
 
-       if ((!l->pending && !l->ns_search_only) || cancel_now) {
+       if ((!l->pending && !l->ns_search_only) || atomic_load(&cancel_now)) {
                debug("no longer pending.  Got %s", isc_result_totext(eresult));
 
                isc_nmhandle_detach(&query->readhandle);
                query_detach(&query);
-               check_next_lookup(l);
+               lookup_detach(&l);
+               check_next_lookup(current_lookup);
                UNLOCK_LOOKUP;
                return;
        }
@@ -3583,7 +3629,8 @@ recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
                isc_nmhandle_detach(&query->readhandle);
                query_detach(&query);
                cancel_lookup(l);
-               check_next_lookup(l);
+               lookup_detach(&l);
+               check_next_lookup(current_lookup);
                UNLOCK_LOOKUP;
                return;
        }
@@ -3621,7 +3668,8 @@ recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
                                isc_nmhandle_detach(&query->readhandle);
                                query_detach(&query);
                                cancel_lookup(l);
-                               check_next_lookup(l);
+                               lookup_detach(&l);
+                               check_next_lookup(current_lookup);
                                UNLOCK_LOOKUP;
                                return;
                        }
@@ -3647,6 +3695,7 @@ recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
                 */
                isc_refcount_increment0(&recvcount);
                isc_nm_read(handle, recv_done, query);
+               lookup_detach(&l);
                UNLOCK_LOOKUP;
                return;
        }
@@ -3694,7 +3743,8 @@ recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
                isc_nmhandle_detach(&query->readhandle);
                query_detach(&query);
                cancel_lookup(l);
-               check_next_lookup(l);
+               lookup_detach(&l);
+               check_next_lookup(current_lookup);
                UNLOCK_LOOKUP;
                return;
        }
@@ -3712,16 +3762,10 @@ recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
 
                dighost_warning("Warning: Opcode mismatch: expected %s, got %s",
                                expect, got);
-               dns_message_detach(&msg);
-               if (l->tcp_mode) {
-                       isc_nmhandle_detach(&query->readhandle);
-                       query_detach(&query);
-                       cancel_lookup(l);
-                       check_next_lookup(l);
-               } else {
-                       isc_refcount_increment0(&recvcount);
-                       isc_nm_read(handle, recv_done, query);
-               }
+
+               isc_refcount_increment0(&recvcount);
+               isc_nm_read(handle, recv_done, query);
+               lookup_detach(&l);
                UNLOCK_LOOKUP;
                return;
        }
@@ -3772,11 +3816,14 @@ recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
                                isc_nmhandle_detach(&query->readhandle);
                                query_detach(&query);
                                cancel_lookup(l);
-                               check_next_lookup(l);
-                       } else {
-                               isc_refcount_increment0(&recvcount);
-                               isc_nm_read(handle, recv_done, query);
+                               lookup_detach(&l);
+                               check_next_lookup(current_lookup);
+                               UNLOCK_LOOKUP;
+                               return;
                        }
+
+                       isc_refcount_increment0(&recvcount);
+                       isc_nm_read(handle, recv_done, query);
                        UNLOCK_LOOKUP;
                        return;
                }
@@ -3799,7 +3846,8 @@ recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
                isc_nmhandle_detach(&query->readhandle);
                query_detach(&query);
                cancel_lookup(l);
-               check_next_lookup(l);
+               lookup_detach(&l);
+               check_next_lookup(current_lookup);
                UNLOCK_LOOKUP;
                return;
        }
@@ -3819,7 +3867,8 @@ recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
                isc_nmhandle_detach(&query->readhandle);
                query_detach(&query);
                cancel_lookup(l);
-               check_next_lookup(l);
+               lookup_detach(&l);
+               check_next_lookup(current_lookup);
                UNLOCK_LOOKUP;
                return;
        }
@@ -3844,7 +3893,8 @@ recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
                        isc_nmhandle_detach(&query->readhandle);
                        query_detach(&query);
                        cancel_lookup(l);
-                       check_next_lookup(l);
+                       lookup_detach(&l);
+                       check_next_lookup(current_lookup);
                        UNLOCK_LOOKUP;
                        return;
                }
@@ -3856,7 +3906,7 @@ recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
        {
                dig_query_t *next = ISC_LIST_NEXT(query, link);
                if (l->current_query == query) {
-                       l->current_query = NULL;
+                       query_detach(&l->current_query);
                }
                if (next != NULL) {
                        debug("sending query %p", next);
@@ -3881,11 +3931,12 @@ recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
                                                 ? "SERVFAIL reply"
                                                 : "recursion not available",
                                         query->servname);
+
                        isc_nmhandle_detach(&query->readhandle);
                        query_detach(&query);
-                       check_next_lookup(l);
+                       lookup_detach(&l);
+                       check_next_lookup(current_lookup);
                        dns_message_detach(&msg);
-                       isc_nmhandle_detach(&query->readhandle);
                        UNLOCK_LOOKUP;
                        return;
                }
@@ -3956,7 +4007,7 @@ recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
        if (!l->doing_xfr || l->xfr_q == query) {
                if (msg->rcode == dns_rcode_nxdomain &&
                    (l->origin != NULL || l->need_search)) {
-                       if (!next_origin(query->lookup) || showsearch) {
+                       if (!next_origin(l) || showsearch) {
                                dighost_printmessage(query, &b, msg, true);
                                dighost_received(isc_buffer_usedlength(&b),
                                                 &peer, query);
@@ -4020,18 +4071,22 @@ recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
                if (query != l->xfr_q) {
                        dns_message_detach(&msg);
                        query_detach(&query);
+                       lookup_detach(&l);
                        UNLOCK_LOOKUP;
                        return;
                }
                if (!docancel) {
-                       docancel = check_for_more_data(query, msg, &peer,
+                       docancel = check_for_more_data(l, query, msg, &peer,
                                                       region->length);
                }
                if (docancel) {
                        dns_message_detach(&msg);
                        query_detach(&query);
                        cancel_lookup(l);
-                       check_next_lookup(l);
+                       lookup_detach(&l);
+                       check_next_lookup(current_lookup);
+                       UNLOCK_LOOKUP;
+                       return;
                }
        } else {
                if (msg->rcode == dns_rcode_noerror || l->origin == NULL) {
@@ -4039,21 +4094,22 @@ recv_done(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
                                         query);
                }
 
-               if (!query->lookup->ns_search_only) {
-                       query->lookup->pending = false;
+               if (!l->ns_search_only) {
+                       l->pending = false;
                }
-               if (!query->lookup->ns_search_only ||
-                   query->lookup->trace_root || docancel) {
-                       dns_message_detach(&msg);
+               if (!l->ns_search_only || l->trace_root || docancel) {
                        cancel_lookup(l);
                }
-               query_detach(&query);
-               check_next_lookup(l);
-       }
-       if (msg != NULL) {
                dns_message_detach(&msg);
+               query_detach(&query);
+               lookup_detach(&l);
+               check_next_lookup(current_lookup);
+               UNLOCK_LOOKUP;
+               return;
        }
 
+       dns_message_detach(&msg);
+       lookup_detach(&l);
        UNLOCK_LOOKUP;
        return;
 }
@@ -4169,7 +4225,7 @@ cancel_all(void) {
                UNLOCK_LOOKUP;
                return;
        }
-       cancel_now = true;
+       atomic_store(&cancel_now, true);
        if (current_lookup != NULL) {
                for (q = ISC_LIST_HEAD(current_lookup->q); q != NULL; q = nq) {
                        nq = ISC_LIST_NEXT(q, link);
@@ -4189,17 +4245,15 @@ cancel_all(void) {
                              q, current_lookup);
                        query_detach(&q);
                }
-               try_clear_lookup(current_lookup);
-               current_lookup = NULL;
+               lookup_detach(&current_lookup);
        }
        l = ISC_LIST_HEAD(lookup_list);
        while (l != NULL) {
                n = ISC_LIST_NEXT(l, link);
                ISC_LIST_DEQUEUE(lookup_list, l, link);
-               try_clear_lookup(l);
+               lookup_detach(&l);
                l = n;
        }
-       current_lookup = NULL;
        UNLOCK_LOOKUP;
 }
 
index 4c0a81339221ecb18163467afae31d59b2642fad..6a30b5ef8546feb8f98dadb7e08b3c7e389c0f82 100644 (file)
@@ -89,12 +89,18 @@ typedef struct dig_server dig_server_t;
 typedef ISC_LIST(dig_server_t) dig_serverlist_t;
 typedef struct dig_searchlist dig_searchlist_t;
 
+#define DIG_LOOKUP_MAGIC ISC_MAGIC('D', 'i', 'g', 'l')
+
+#define DIG_VALID_LOOKUP(x) ISC_MAGIC_VALID((x), DIG_LOOKUP_MAGIC)
+
 #define DIG_QUERY_MAGIC ISC_MAGIC('D', 'i', 'g', 'q')
 
 #define DIG_VALID_QUERY(x) ISC_MAGIC_VALID((x), DIG_QUERY_MAGIC)
 
 /*% The dig_lookup structure */
 struct dig_lookup {
+       unsigned int magic;
+       isc_refcount_t references;
        bool pending, /*%< Pending a successful answer */
                waiting_connect, doing_xfr, ns_search_only, /*%< dig
                                                             * +nssearch,
index a2aad3bc9afdcfd26a57fc2c0e933a8c9cd9a6eb..c28a432b2a7aa6ff3374983f576c67b659a29440 100644 (file)
@@ -110,8 +110,6 @@ static const char *rtypetext[] = {
 
 #define N_KNOWN_RRTYPES (sizeof(rtypetext) / sizeof(rtypetext[0]))
 
-static void
-flush_lookup_list(void);
 static void
 getinput(isc_task_t *task, isc_event_t *event);
 
@@ -136,7 +134,6 @@ static void
 query_finished(void) {
        isc_event_t *event = global_event;
 
-       flush_lookup_list();
        debug("dighost_shutdown()");
 
        if (!in_use) {
@@ -622,6 +619,8 @@ static void
 setoption(char *opt) {
        size_t l = strlen(opt);
 
+       debugging = true;
+
 #define CHECKOPT(A, N) \
        ((l >= N) && (l < sizeof(A)) && (strncasecmp(opt, A, l) == 0))
 
@@ -904,42 +903,6 @@ parse_args(int argc, char **argv) {
        }
 }
 
-static void
-flush_lookup_list(void) {
-       dig_lookup_t *l, *lp;
-       dig_query_t *q, *qp;
-       dig_server_t *s, *sp;
-
-       lookup_counter = 0;
-       l = ISC_LIST_HEAD(lookup_list);
-       while (l != NULL) {
-               q = ISC_LIST_HEAD(l->q);
-               while (q != NULL) {
-                       if (q->readhandle != NULL) {
-                               isc_nmhandle_detach(&q->readhandle);
-                       }
-                       qp = q;
-                       q = ISC_LIST_NEXT(q, link);
-                       ISC_LIST_DEQUEUE(l->q, qp, link);
-                       isc_mem_free(mctx, qp);
-               }
-               s = ISC_LIST_HEAD(l->my_server_list);
-               while (s != NULL) {
-                       sp = s;
-                       s = ISC_LIST_NEXT(s, link);
-                       ISC_LIST_DEQUEUE(l->my_server_list, sp, link);
-                       isc_mem_free(mctx, sp);
-               }
-               if (l->sendmsg != NULL) {
-                       dns_message_detach(&l->sendmsg);
-               }
-               lp = l;
-               l = ISC_LIST_NEXT(l, link);
-               ISC_LIST_DEQUEUE(lookup_list, lp, link);
-               isc_mem_free(mctx, lp);
-       }
-}
-
 static void
 getinput(isc_task_t *task, isc_event_t *event) {
        UNUSED(task);