diff --git a/lib/dns/zone.c b/lib/dns/zone.c index b8cd90b129..db94e130ac 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -614,6 +614,7 @@ struct dns_stub { dns_zone_t *zone; dns_db_t *db; dns_dbversion_t *version; + atomic_uint_fast32_t pending_requests; }; /*% @@ -937,6 +938,22 @@ struct ssevent { uint32_t serial; }; +struct stub_cb_args { + dns_stub_t *stub; + dns_tsigkey_t *tsig_key; + isc_dscp_t dscp; + uint16_t udpsize; + int timeout; + bool reqnsid; +}; + +struct stub_glue_request { + dns_request_t *request; + dns_name_t name; + struct stub_cb_args *args; + bool ipv4; +}; + /*% * Increment resolver-related statistics counters. Zone must be locked. */ @@ -12491,9 +12508,395 @@ cleanup1: /*** *** Private ***/ +static inline isc_result_t +create_query(dns_zone_t *zone, dns_rdatatype_t rdtype, dns_name_t *name, + dns_message_t **messagep) { + dns_message_t *message = NULL; + dns_name_t *qname = NULL; + dns_rdataset_t *qrdataset = NULL; + isc_result_t result; + + dns_message_create(zone->mctx, DNS_MESSAGE_INTENTRENDER, &message); + + message->opcode = dns_opcode_query; + message->rdclass = zone->rdclass; + + result = dns_message_gettempname(message, &qname); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = dns_message_gettemprdataset(message, &qrdataset); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + /* + * Make question. + */ + dns_name_init(qname, NULL); + dns_name_clone(name, qname); + dns_rdataset_makequestion(qrdataset, zone->rdclass, rdtype); + ISC_LIST_APPEND(qname->list, qrdataset, link); + dns_message_addname(message, qname, DNS_SECTION_QUESTION); + + *messagep = message; + return (ISC_R_SUCCESS); + +cleanup: + if (qname != NULL) { + dns_message_puttempname(message, &qname); + } + if (qrdataset != NULL) { + dns_message_puttemprdataset(message, &qrdataset); + } + dns_message_detach(&message); + return (result); +} + +static isc_result_t +add_opt(dns_message_t *message, uint16_t udpsize, bool reqnsid, + bool reqexpire) { + isc_result_t result; + dns_rdataset_t *rdataset = NULL; + dns_ednsopt_t ednsopts[DNS_EDNSOPTIONS]; + int count = 0; + + /* Set EDNS options if applicable. */ + if (reqnsid) { + INSIST(count < DNS_EDNSOPTIONS); + ednsopts[count].code = DNS_OPT_NSID; + ednsopts[count].length = 0; + ednsopts[count].value = NULL; + count++; + } + if (reqexpire) { + INSIST(count < DNS_EDNSOPTIONS); + ednsopts[count].code = DNS_OPT_EXPIRE; + ednsopts[count].length = 0; + ednsopts[count].value = NULL; + count++; + } + result = dns_message_buildopt(message, &rdataset, 0, udpsize, 0, + ednsopts, count); + if (result != ISC_R_SUCCESS) { + return (result); + } + + return (dns_message_setopt(message, rdataset)); +} + +/* + * Called when stub zone update is finished. + * Update zone refresh, retry, expire values accordingly with + * SOA received from master, sync database to file, restart + * zone management timer. + */ +static void +stub_finish_zone_update(dns_stub_t *stub, isc_time_t now) { + uint32_t refresh, retry, expire; + isc_result_t result; + isc_interval_t i; + unsigned int soacount; + dns_zone_t *zone = stub->zone; + + /* + * Tidy up. + */ + dns_db_closeversion(stub->db, &stub->version, true); + ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_write); + if (zone->db == NULL) { + zone_attachdb(zone, stub->db); + } + result = zone_get_from_db(zone, zone->db, NULL, &soacount, NULL, + &refresh, &retry, &expire, NULL, NULL); + if (result == ISC_R_SUCCESS && soacount > 0U) { + zone->refresh = RANGE(refresh, zone->minrefresh, + zone->maxrefresh); + zone->retry = RANGE(retry, zone->minretry, zone->maxretry); + zone->expire = RANGE(expire, zone->refresh + zone->retry, + DNS_MAX_EXPIRE); + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_HAVETIMERS); + } + ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write); + dns_db_detach(&stub->db); + + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_REFRESH); + DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED); + DNS_ZONE_JITTER_ADD(&now, zone->refresh, &zone->refreshtime); + isc_interval_set(&i, zone->expire, 0); + DNS_ZONE_TIME_ADD(&now, zone->expire, &zone->expiretime); + + if (zone->masterfile != NULL) { + zone_needdump(zone, 0); + } + + zone_settimer(zone, &now); +} + +/* + * Process answers for A and AAAA queries when + * resolving nameserver addresses for which glue + * was missing in a previous answer for a NS query. + */ +static void +stub_glue_response_cb(isc_task_t *task, isc_event_t *event) { + const char me[] = "stub_glue_response_cb"; + dns_requestevent_t *revent = (dns_requestevent_t *)event; + dns_stub_t *stub = NULL; + dns_message_t *msg = NULL; + dns_zone_t *zone = NULL; + char master[ISC_SOCKADDR_FORMATSIZE]; + char source[ISC_SOCKADDR_FORMATSIZE]; + uint32_t addr_count, cnamecnt; + isc_result_t result; + isc_time_t now; + struct stub_glue_request *request; + struct stub_cb_args *cb_args; + dns_rdataset_t *addr_rdataset = NULL; + dns_dbnode_t *node = NULL; + + UNUSED(task); + + request = revent->ev_arg; + cb_args = request->args; + stub = cb_args->stub; + INSIST(DNS_STUB_VALID(stub)); + + zone = stub->zone; + + ENTER; + + TIME_NOW(&now); + + LOCK_ZONE(zone); + + if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING)) { + zone_debuglog(zone, me, 1, "exiting"); + goto cleanup; + } + + isc_sockaddr_format(&zone->masteraddr, master, sizeof(master)); + isc_sockaddr_format(&zone->sourceaddr, source, sizeof(source)); + + if (revent->result != ISC_R_SUCCESS) { + dns_zonemgr_unreachableadd(zone->zmgr, &zone->masteraddr, + &zone->sourceaddr, &now); + dns_zone_log(zone, ISC_LOG_INFO, + "could not refresh stub from master %s" + " (source %s): %s", + master, source, dns_result_totext(revent->result)); + goto cleanup; + } + + dns_message_create(zone->mctx, DNS_MESSAGE_INTENTPARSE, &msg); + result = dns_request_getresponse(revent->request, msg, 0); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_INFO, + "refreshing stub: unable to parse response (%s)", + isc_result_totext(result)); + goto cleanup; + } + + /* + * Unexpected rcode. + */ + if (msg->rcode != dns_rcode_noerror) { + char rcode[128]; + isc_buffer_t rb; + + isc_buffer_init(&rb, rcode, sizeof(rcode)); + (void)dns_rcode_totext(msg->rcode, &rb); + + dns_zone_log(zone, ISC_LOG_INFO, + "refreshing stub: " + "unexpected rcode (%.*s) from %s (source %s)", + (int)rb.used, rcode, master, source); + goto cleanup; + } + + /* + * We need complete messages. + */ + if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) { + if (dns_request_usedtcp(revent->request)) { + dns_zone_log(zone, ISC_LOG_INFO, + "refreshing stub: truncated TCP " + "response from master %s (source %s)", + master, source); + } + goto cleanup; + } + + /* + * If non-auth log. + */ + if ((msg->flags & DNS_MESSAGEFLAG_AA) == 0) { + dns_zone_log(zone, ISC_LOG_INFO, + "refreshing stub: " + "non-authoritative answer from " + "master %s (source %s)", + master, source); + goto cleanup; + } + + /* + * Sanity checks. + */ + cnamecnt = message_count(msg, DNS_SECTION_ANSWER, dns_rdatatype_cname); + addr_count = message_count(msg, DNS_SECTION_ANSWER, + request->ipv4 ? dns_rdatatype_a + : dns_rdatatype_aaaa); + + if (cnamecnt != 0) { + dns_zone_log(zone, ISC_LOG_INFO, + "refreshing stub: unexpected CNAME response " + "from master %s (source %s)", + master, source); + goto cleanup; + } + + if (addr_count == 0) { + dns_zone_log(zone, ISC_LOG_INFO, + "refreshing stub: no %s records in response " + "from master %s (source %s)", + request->ipv4 ? "A" : "AAAA", master, source); + goto cleanup; + } + /* + * Extract A or AAAA RRset from message. + */ + result = dns_message_findname(msg, DNS_SECTION_ANSWER, &request->name, + request->ipv4 ? dns_rdatatype_a + : dns_rdatatype_aaaa, + dns_rdatatype_none, NULL, &addr_rdataset); + if (result != ISC_R_SUCCESS) { + if (result != DNS_R_NXDOMAIN && result != DNS_R_NXRRSET) { + char namebuf[DNS_NAME_FORMATSIZE]; + dns_name_format(&request->name, namebuf, + sizeof(namebuf)); + dns_zone_log( + zone, ISC_LOG_INFO, + "refreshing stub: dns_message_findname(%s/%s) " + "failed (%s)", + namebuf, request->ipv4 ? "A" : "AAAA", + isc_result_totext(result)); + } + goto cleanup; + } + + result = dns_db_findnode(stub->db, &request->name, true, &node); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_INFO, + "refreshing stub: " + "dns_db_findnode() failed: %s", + dns_result_totext(result)); + goto cleanup; + } + + result = dns_db_addrdataset(stub->db, node, stub->version, 0, + addr_rdataset, 0, NULL); + if (result != ISC_R_SUCCESS) { + dns_zone_log(zone, ISC_LOG_INFO, + "refreshing stub: " + "dns_db_addrdataset() failed: %s", + dns_result_totext(result)); + } + dns_db_detachnode(stub->db, &node); + +cleanup: + if (msg != NULL) { + dns_message_detach(&msg); + } + isc_event_free(&event); + dns_name_free(&request->name, zone->mctx); + dns_request_destroy(&request->request); + isc_mem_put(zone->mctx, request, sizeof(*request)); + + /* If last request, release all related resources */ + if (atomic_fetch_sub_release(&stub->pending_requests, 1) == 1) { + isc_mem_put(zone->mctx, cb_args, sizeof(*cb_args)); + stub_finish_zone_update(stub, now); + UNLOCK_ZONE(zone); + stub->magic = 0; + dns_zone_idetach(&stub->zone); + INSIST(stub->db == NULL); + INSIST(stub->version == NULL); + isc_mem_put(stub->mctx, stub, sizeof(*stub)); + } else { + UNLOCK_ZONE(zone); + } +} + +/* + * Create and send an A or AAAA query to the master + * server of the stub zone given. + */ +static isc_result_t +stub_request_nameserver_address(struct stub_cb_args *args, bool ipv4, + const dns_name_t *name) { + dns_message_t *message = NULL; + dns_zone_t *zone; + isc_result_t result; + struct stub_glue_request *request; + + zone = args->stub->zone; + request = isc_mem_get(zone->mctx, sizeof(*request)); + request->request = NULL; + request->args = args; + request->name = (dns_name_t)DNS_NAME_INITEMPTY; + request->ipv4 = ipv4; + dns_name_dup(name, zone->mctx, &request->name); + + result = create_query(zone, ipv4 ? dns_rdatatype_a : dns_rdatatype_aaaa, + &request->name, &message); + INSIST(result == ISC_R_SUCCESS); + + if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_NOEDNS)) { + result = add_opt(message, args->udpsize, args->reqnsid, false); + if (result != ISC_R_SUCCESS) { + zone_debuglog(zone, "stub_send_query", 1, + "unable to add opt record: %s", + dns_result_totext(result)); + goto fail; + } + } + + atomic_fetch_add_release(&args->stub->pending_requests, 1); + + result = dns_request_createvia( + zone->view->requestmgr, message, &zone->sourceaddr, + &zone->masteraddr, args->dscp, DNS_REQUESTOPT_TCP, + args->tsig_key, args->timeout * 3, args->timeout, 0, zone->task, + stub_glue_response_cb, request, &request->request); + + if (result != ISC_R_SUCCESS) { + INSIST(atomic_fetch_sub_release(&args->stub->pending_requests, + 1) > 1); + zone_debuglog(zone, "stub_send_query", 1, + "dns_request_createvia() failed: %s", + dns_result_totext(result)); + goto fail; + } + + dns_message_detach(&message); + + return (ISC_R_SUCCESS); + +fail: + dns_name_free(&request->name, zone->mctx); + isc_mem_put(zone->mctx, request, sizeof(*request)); + + if (message != NULL) { + dns_message_detach(&message); + } + + return (result); +} static inline isc_result_t -save_nsrrset(dns_message_t *message, dns_name_t *name, dns_db_t *db, +save_nsrrset(dns_message_t *message, dns_name_t *name, + struct stub_cb_args *cb_args, dns_db_t *db, dns_dbversion_t *version) { dns_rdataset_t *nsrdataset = NULL; dns_rdataset_t *rdataset = NULL; @@ -12501,6 +12904,14 @@ save_nsrrset(dns_message_t *message, dns_name_t *name, dns_db_t *db, dns_rdata_ns_t ns; isc_result_t result; dns_rdata_t rdata = DNS_RDATA_INIT; + bool has_glue = false; + dns_name_t *ns_name; + /* + * List of NS entries in answer, keep names that will be used + * to resolve missing A/AAAA glue for each entry. + */ + dns_namelist_t ns_list; + ISC_LIST_INIT(ns_list); /* * Extract NS RRset from message. @@ -12509,7 +12920,7 @@ save_nsrrset(dns_message_t *message, dns_name_t *name, dns_db_t *db, dns_rdatatype_ns, dns_rdatatype_none, NULL, &nsrdataset); if (result != ISC_R_SUCCESS) { - goto fail; + goto done; } /* @@ -12517,12 +12928,12 @@ save_nsrrset(dns_message_t *message, dns_name_t *name, dns_db_t *db, */ result = dns_db_findnode(db, name, true, &node); if (result != ISC_R_SUCCESS) { - goto fail; + goto done; } result = dns_db_addrdataset(db, node, version, 0, nsrdataset, 0, NULL); dns_db_detachnode(db, &node); if (result != ISC_R_SUCCESS) { - goto fail; + goto done; } /* * Add glue rdatasets. @@ -12534,6 +12945,7 @@ save_nsrrset(dns_message_t *message, dns_name_t *name, dns_db_t *db, result = dns_rdata_tostruct(&rdata, &ns, NULL); RUNTIME_CHECK(result == ISC_R_SUCCESS); dns_rdata_reset(&rdata); + if (!dns_name_issubdomain(&ns.name, name)) { continue; } @@ -12543,41 +12955,92 @@ save_nsrrset(dns_message_t *message, dns_name_t *name, dns_db_t *db, dns_rdatatype_none, NULL, &rdataset); if (result == ISC_R_SUCCESS) { + has_glue = true; result = dns_db_findnode(db, &ns.name, true, &node); if (result != ISC_R_SUCCESS) { - goto fail; + goto done; } result = dns_db_addrdataset(db, node, version, 0, rdataset, 0, NULL); dns_db_detachnode(db, &node); if (result != ISC_R_SUCCESS) { - goto fail; + goto done; } } + rdataset = NULL; result = dns_message_findname( message, DNS_SECTION_ADDITIONAL, &ns.name, dns_rdatatype_a, dns_rdatatype_none, NULL, &rdataset); if (result == ISC_R_SUCCESS) { + has_glue = true; result = dns_db_findnode(db, &ns.name, true, &node); if (result != ISC_R_SUCCESS) { - goto fail; + goto done; } result = dns_db_addrdataset(db, node, version, 0, rdataset, 0, NULL); dns_db_detachnode(db, &node); if (result != ISC_R_SUCCESS) { - goto fail; + goto done; + } + } + + /* + * If no glue is found so far, we add the name to the list to + * resolve the A/AAAA glue later. If any glue is found in any + * iteration step, this list will be discarded and only the glue + * provided in this message will be used. + */ + if (!has_glue && dns_name_issubdomain(&ns.name, name)) { + dns_name_t *tmp_name; + tmp_name = isc_mem_get(cb_args->stub->mctx, + sizeof(*tmp_name)); + dns_name_init(tmp_name, NULL); + dns_name_dup(&ns.name, cb_args->stub->mctx, tmp_name); + ISC_LIST_APPEND(ns_list, tmp_name, link); + } + } + + if (result != ISC_R_NOMORE) { + goto done; + } + + /* + * If no glue records were found, we attempt to resolve A/AAAA + * for each NS entry found in the answer. + */ + if (!has_glue) { + for (ns_name = ISC_LIST_HEAD(ns_list); ns_name != NULL; + ns_name = ISC_LIST_NEXT(ns_name, link)) + { + /* + * Resolve NS IPv4 address/A. + */ + result = stub_request_nameserver_address(cb_args, true, + ns_name); + if (result != ISC_R_SUCCESS) { + goto done; + } + /* + * Resolve NS IPv6 address/AAAA. + */ + result = stub_request_nameserver_address(cb_args, false, + ns_name); + if (result != ISC_R_SUCCESS) { + goto done; } } } - if (result != ISC_R_NOMORE) { - goto fail; + + result = ISC_R_SUCCESS; + +done: + while ((ns_name = ISC_LIST_HEAD(ns_list)) != NULL) { + ISC_LIST_UNLINK(ns_list, ns_name, link); + dns_name_free(ns_name, cb_args->stub->mctx); + isc_mem_put(cb_args->stub->mctx, ns_name, sizeof(*ns_name)); } - - return (ISC_R_SUCCESS); - -fail: return (result); } @@ -12590,14 +13053,15 @@ stub_callback(isc_task_t *task, isc_event_t *event) { dns_zone_t *zone = NULL; char master[ISC_SOCKADDR_FORMATSIZE]; char source[ISC_SOCKADDR_FORMATSIZE]; - uint32_t nscnt, cnamecnt, refresh, retry, expire; + uint32_t nscnt, cnamecnt; isc_result_t result; isc_time_t now; bool exiting = false; - isc_interval_t i; - unsigned int j, soacount; + unsigned int j; + struct stub_cb_args *cb_args; - stub = revent->ev_arg; + cb_args = revent->ev_arg; + stub = cb_args->stub; INSIST(DNS_STUB_VALID(stub)); UNUSED(task); @@ -12725,10 +13189,13 @@ stub_callback(isc_task_t *task, isc_event_t *event) { goto next_master; } + atomic_fetch_add(&stub->pending_requests, 1); + /* * Save answer. */ - result = save_nsrrset(msg, &zone->origin, stub->db, stub->version); + result = save_nsrrset(msg, &zone->origin, cb_args, stub->db, + stub->version); if (result != ISC_R_SUCCESS) { dns_zone_log(zone, ISC_LOG_INFO, "refreshing stub: unable to save NS records " @@ -12737,45 +13204,25 @@ stub_callback(isc_task_t *task, isc_event_t *event) { goto next_master; } - /* - * Tidy up. - */ - dns_db_closeversion(stub->db, &stub->version, true); - ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_write); - if (zone->db == NULL) { - zone_attachdb(zone, stub->db); - } - result = zone_get_from_db(zone, zone->db, NULL, &soacount, NULL, - &refresh, &retry, &expire, NULL, NULL); - if (result == ISC_R_SUCCESS && soacount > 0U) { - zone->refresh = RANGE(refresh, zone->minrefresh, - zone->maxrefresh); - zone->retry = RANGE(retry, zone->minretry, zone->maxretry); - zone->expire = RANGE(expire, zone->refresh + zone->retry, - DNS_MAX_EXPIRE); - DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_HAVETIMERS); - } - ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write); - dns_db_detach(&stub->db); - dns_message_detach(&msg); isc_event_free(&event); dns_request_destroy(&zone->request); - DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_REFRESH); - DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_LOADED); - DNS_ZONE_JITTER_ADD(&now, zone->refresh, &zone->refreshtime); - isc_interval_set(&i, zone->expire, 0); - DNS_ZONE_TIME_ADD(&now, zone->expire, &zone->expiretime); - - if (zone->masterfile != NULL) { - zone_needdump(zone, 0); + /* + * Check to see if there are no outstanding requests and + * finish off if that is so. + */ + if (atomic_fetch_sub(&stub->pending_requests, 1) == 1) { + isc_mem_put(zone->mctx, cb_args, sizeof(*cb_args)); + stub_finish_zone_update(stub, now); + goto free_stub; } - zone_settimer(zone, &now); - goto free_stub; + UNLOCK_ZONE(zone); + return; next_master: + isc_mem_put(zone->mctx, cb_args, sizeof(*cb_args)); if (stub->version != NULL) { dns_db_closeversion(stub->db, &stub->version, false); } @@ -12836,6 +13283,7 @@ next_master: goto free_stub; same_master: + isc_mem_put(zone->mctx, cb_args, sizeof(*cb_args)); if (msg != NULL) { dns_message_detach(&msg); } @@ -13416,84 +13864,6 @@ queue_soa_query(dns_zone_t *zone) { } } -static inline isc_result_t -create_query(dns_zone_t *zone, dns_rdatatype_t rdtype, - dns_message_t **messagep) { - dns_message_t *message = NULL; - dns_name_t *qname = NULL; - dns_rdataset_t *qrdataset = NULL; - isc_result_t result; - - dns_message_create(zone->mctx, DNS_MESSAGE_INTENTRENDER, &message); - - message->opcode = dns_opcode_query; - message->rdclass = zone->rdclass; - - result = dns_message_gettempname(message, &qname); - if (result != ISC_R_SUCCESS) { - goto cleanup; - } - - result = dns_message_gettemprdataset(message, &qrdataset); - if (result != ISC_R_SUCCESS) { - goto cleanup; - } - - /* - * Make question. - */ - dns_name_init(qname, NULL); - dns_name_clone(&zone->origin, qname); - dns_rdataset_makequestion(qrdataset, zone->rdclass, rdtype); - ISC_LIST_APPEND(qname->list, qrdataset, link); - dns_message_addname(message, qname, DNS_SECTION_QUESTION); - - *messagep = message; - return (ISC_R_SUCCESS); - -cleanup: - if (qname != NULL) { - dns_message_puttempname(message, &qname); - } - if (qrdataset != NULL) { - dns_message_puttemprdataset(message, &qrdataset); - } - dns_message_detach(&message); - return (result); -} - -static isc_result_t -add_opt(dns_message_t *message, uint16_t udpsize, bool reqnsid, - bool reqexpire) { - isc_result_t result; - dns_rdataset_t *rdataset = NULL; - dns_ednsopt_t ednsopts[DNS_EDNSOPTIONS]; - int count = 0; - - /* Set EDNS options if applicable */ - if (reqnsid) { - INSIST(count < DNS_EDNSOPTIONS); - ednsopts[count].code = DNS_OPT_NSID; - ednsopts[count].length = 0; - ednsopts[count].value = NULL; - count++; - } - if (reqexpire) { - INSIST(count < DNS_EDNSOPTIONS); - ednsopts[count].code = DNS_OPT_EXPIRE; - ednsopts[count].length = 0; - ednsopts[count].value = NULL; - count++; - } - result = dns_message_buildopt(message, &rdataset, 0, udpsize, 0, - ednsopts, count); - if (result != ISC_R_SUCCESS) { - return (result); - } - - return (dns_message_setopt(message, rdataset)); -} - static void soa_query(isc_task_t *task, isc_event_t *event) { const char me[] = "soa_query"; @@ -13528,7 +13898,7 @@ soa_query(isc_task_t *task, isc_event_t *event) { } again: - result = create_query(zone, dns_rdatatype_soa, &message); + result = create_query(zone, dns_rdatatype_soa, &zone->origin, &message); if (result != ISC_R_SUCCESS) { goto cleanup; } @@ -13729,6 +14099,7 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) { bool reqnsid; uint16_t udpsize = SEND_BUFFER_SIZE; isc_dscp_t dscp = -1; + struct stub_cb_args *cb_args; REQUIRE(DNS_ZONE_VALID(zone)); REQUIRE(LOCKED_ZONE(zone)); @@ -13745,6 +14116,7 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) { stub->zone = NULL; stub->db = NULL; stub->version = NULL; + atomic_init(&stub->pending_requests, 0); /* * Attach so that the zone won't disappear from under us. @@ -13815,7 +14187,7 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) { /* * XXX Optimisation: Create message when zone is setup and reuse. */ - result = create_query(zone, dns_rdatatype_ns, &message); + result = create_query(zone, dns_rdatatype_ns, &zone->origin, &message); INSIST(result == ISC_R_SUCCESS); INSIST(zone->masterscnt > 0); @@ -13920,10 +14292,21 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) { if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALREFRESH)) { timeout = 30; } + + /* Save request parameters so we can reuse them later on + for resolving missing glue A/AAAA records. */ + cb_args = isc_mem_get(zone->mctx, sizeof(*cb_args)); + cb_args->stub = stub; + cb_args->tsig_key = key; + cb_args->dscp = dscp; + cb_args->udpsize = udpsize; + cb_args->timeout = timeout; + cb_args->reqnsid = reqnsid; + result = dns_request_createvia( zone->view->requestmgr, message, &zone->sourceaddr, &zone->masteraddr, dscp, DNS_REQUESTOPT_TCP, key, timeout * 3, - timeout, 0, zone->task, stub_callback, stub, &zone->request); + timeout, 0, zone->task, stub_callback, cb_args, &zone->request); if (result != ISC_R_SUCCESS) { zone_debuglog(zone, me, 1, "dns_request_createvia() failed: %s", dns_result_totext(result));