diff --git a/lib/dns/dispatch.c b/lib/dns/dispatch.c index 341ac86308..888ab1f315 100644 --- a/lib/dns/dispatch.c +++ b/lib/dns/dispatch.c @@ -60,7 +60,6 @@ struct dns_dispatchmgr { unsigned int state; ISC_LIST(dns_dispatch_t) list; - /* locked by buffer_lock */ dns_qid_t *qid; in_port_t *v4ports; /*%< available ports for IPv4 */ @@ -79,6 +78,7 @@ struct dns_dispentry { isc_nmhandle_t *handle; /*%< netmgr handle for UDP connection */ unsigned int bucket; unsigned int timeout; + unsigned int retries; isc_sockaddr_t local; isc_sockaddr_t peer; in_port_t port; @@ -91,6 +91,7 @@ struct dns_dispentry { ISC_LINK(dns_dispentry_t) link; ISC_LINK(dns_dispentry_t) alink; ISC_LINK(dns_dispentry_t) plink; + ISC_LINK(dns_dispentry_t) rlink; }; /*% @@ -125,8 +126,8 @@ struct dns_dispatch { isc_refcount_t references; unsigned int shutdown_out : 1; - ISC_LIST(dns_dispentry_t) pending; - ISC_LIST(dns_dispentry_t) active; + dns_displist_t pending; + dns_displist_t active; unsigned int nsockets; unsigned int requests; /*%< how many requests we have */ @@ -301,6 +302,10 @@ setup_socket(dns_dispatch_t *disp, dns_dispentry_t *resp, in_port_t *ports = NULL; in_port_t port; + if (resp->retries++ > 5) { + return (ISC_R_FAILURE); + } + if (isc_sockaddr_pf(&disp->local) == AF_INET) { nports = mgr->nv4ports; ports = mgr->v4ports; @@ -336,10 +341,6 @@ deactivate_dispentry(dns_dispatch_t *disp, dns_dispentry_t *resp) { ISC_LIST_UNLINK(disp->active, resp, alink); } - if (ISC_LINK_LINKED(resp, plink)) { - ISC_LIST_UNLINK(disp->pending, resp, plink); - } - if (resp->handle != NULL) { INSIST(disp->socktype == isc_socktype_udp); @@ -392,6 +393,13 @@ dispentry_destroy(dns_dispentry_t *resp) { resp->magic = 0; + if (ISC_LINK_LINKED(resp, plink)) { + ISC_LIST_UNLINK(disp->pending, resp, plink); + } + + INSIST(!ISC_LINK_LINKED(resp, alink)); + INSIST(!ISC_LINK_LINKED(resp, rlink)); + if (resp->handle != NULL) { isc_nmhandle_detach(&resp->handle); } @@ -433,11 +441,6 @@ dispentry_detach(dns_dispentry_t **respp) { * if event queue is not empty, queue. else, send. * restart. */ - -/* FIXME: If we read invalid packet, we never receive next that could be valid - * and we also don't notify the read callback - */ - static void udp_recv(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region, void *arg) { @@ -459,38 +462,32 @@ udp_recv(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region, LOCK(&disp->lock); - if (isc_log_wouldlog(dns_lctx, LVL(90))) { - dispatch_log(disp, LVL(90), "got UDP packet: requests %d", - disp->requests); + dispatch_log(disp, LVL(90), "UDP response %p:%s:requests %d", resp, + isc_result_totext(eresult), disp->requests); + + /* + * The resp may have been deactivated by shutdown; if + * so, we can skip the response callback. + */ + if (ISC_LINK_LINKED(resp, alink)) { + response = resp->response; } - if (eresult == ISC_R_CANCELED) { - /* - * This dispatcher is shutting down. - */ - goto sendevent; - } - - if (!ISC_LINK_LINKED(resp, alink)) { - goto unlock; - } - - id = resp->id; - - peer = isc_nmhandle_peeraddr(handle); - isc_netaddr_fromsockaddr(&netaddr, &peer); - if (eresult != ISC_R_SUCCESS) { /* * This is most likely a network error on a connected - * socket, or a timeout on a timer that has not been - * reset. It makes no sense to check the address or - * parse the packet, but it will help to return the - * error to the caller. + * socket, a timeout, or the query has been canceled. + * It makes no sense to check the address or parse the + * packet, but we can return the error to the caller. */ - goto sendevent; + goto done; } + INSIST(ISC_LINK_LINKED(resp, alink)); + + peer = isc_nmhandle_peeraddr(handle); + isc_netaddr_fromsockaddr(&netaddr, &peer); + /* * If this is from a blackholed address, drop it. */ @@ -506,18 +503,19 @@ udp_recv(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region, dispatch_log(disp, LVL(10), "blackholed packet from %s", netaddrstr); } - goto unlock; + goto next; } /* * Peek into the buffer to see what we can see. */ + id = resp->id; isc_buffer_init(&source, region->base, region->length); isc_buffer_add(&source, region->length); dres = dns_message_peekheader(&source, &id, &flags); if (dres != ISC_R_SUCCESS) { dispatch_log(disp, LVL(10), "got garbage packet"); - goto unlock; + goto next; } dispatch_log(disp, LVL(92), @@ -525,12 +523,10 @@ udp_recv(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region, (((flags & DNS_MESSAGEFLAG_QR) != 0) ? '1' : '0'), id); /* - * Look at flags. If query, drop it. If response, - * look to see where it goes. + * Look at the message flags. If it's a query, ignore it. */ if ((flags & DNS_MESSAGEFLAG_QR) == 0) { - /* query */ - goto unlock; + goto next; } /* @@ -539,19 +535,23 @@ udp_recv(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region, if (resp->id != id || !isc_sockaddr_equal(&peer, &resp->peer)) { dispatch_log(disp, LVL(90), "response doesn't match"); inc_stats(disp->mgr, dns_resstatscounter_mismatch); - goto unlock; + goto next; } -sendevent: /* - * At this point, rev contains the event we want to fill in, and - * resp contains the information on the place to send it to. - * Send the event off. + * We have the right resp, so call the caller back. */ + goto done; - response = resp->response; +next: + /* + * This is the wrong response. Don't call the caller back + * but keep listening. + */ + response = NULL; + dispatch_getnext(disp, resp, resp->timeout); -unlock: +done: UNLOCK(&disp->lock); if (response != NULL) { @@ -580,7 +580,7 @@ static void tcp_recv(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region, void *arg) { dns_dispatch_t *disp = (dns_dispatch_t *)arg; - dns_dispentry_t *resp = NULL; + dns_dispentry_t *resp = NULL, *next = NULL; dns_messageid_t id; isc_result_t dres; unsigned int flags; @@ -590,6 +590,7 @@ tcp_recv(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region, char buf[ISC_SOCKADDR_FORMATSIZE]; isc_buffer_t source; isc_sockaddr_t peer; + dns_displist_t resps; REQUIRE(VALID_DISPATCH(disp)); @@ -597,35 +598,49 @@ tcp_recv(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region, LOCK(&disp->lock); - dispatch_log(disp, LVL(90), "got TCP packet: requests %d, buffers %d", - disp->requests, disp->tcpbuffers); + dispatch_log(disp, LVL(90), "TCP read:%s:requests %d, buffers %d", + isc_result_totext(eresult), disp->requests, + disp->tcpbuffers); peer = isc_nmhandle_peeraddr(handle); + ISC_LIST_INIT(resps); switch (eresult) { case ISC_R_SUCCESS: /* got our answer */ break; - case ISC_R_CANCELED: - dispatch_log(disp, LVL(90), "shutting down on cancel"); - goto unlock; + case ISC_R_SHUTTINGDOWN: + case ISC_R_CANCELED: case ISC_R_EOF: - dispatch_log(disp, LVL(90), "shutting down on EOF"); - goto unlock; + dispatch_log(disp, LVL(90), "shutting down: %s", + isc_result_totext(eresult)); + /* + * If there are any active responses, shut them all down. + */ + for (resp = ISC_LIST_HEAD(disp->active); resp != NULL; + resp = next) { + next = ISC_LIST_NEXT(resp, alink); + dispentry_attach(resp, &(dns_dispentry_t *){ NULL }); + ISC_LIST_UNLINK(disp->active, resp, alink); + ISC_LIST_APPEND(resps, resp, rlink); + } + goto done; case ISC_R_TIMEDOUT: /* - * Time out the first active response for which - * no event has already been sent. + * Time out the oldest response in the active queue, + * and move it to the end. (We don't remove it from the + * active queue immediately, though, because the callback + * might decide to keep waiting and leave it active.) */ resp = ISC_LIST_HEAD(disp->active); - INSIST(resp != NULL); - - ISC_LIST_UNLINK(disp->active, resp, alink); - ISC_LIST_APPEND(disp->active, resp, alink); - - goto unlock; + if (resp != NULL) { + dispentry_attach(resp, &(dns_dispentry_t *){ NULL }); + ISC_LIST_UNLINK(disp->active, resp, alink); + ISC_LIST_APPEND(disp->active, resp, alink); + } + goto done; default: if (eresult == ISC_R_CONNECTIONRESET) { @@ -639,11 +654,11 @@ tcp_recv(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region, "shutting down due to TCP " "receive error: %s: %s", buf, isc_result_totext(eresult)); - goto unlock; + goto done; } - dispatch_log(disp, LVL(90), "result %d, length == %d, addr = %p", - eresult, region->length, region->base); + dispatch_log(disp, LVL(90), "success, length == %d, addr = %p", + region->length, region->base); /* * Peek into the buffer to see what we can see. @@ -661,13 +676,8 @@ tcp_recv(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region, (((flags & DNS_MESSAGEFLAG_QR) != 0) ? '1' : '0'), id); /* - * Allocate an event to send to the query or response client, and - * allocate a new buffer for our use. - */ - - /* - * Look at flags. If query, drop it. If response, - * look to see where it goes. + * Look at the message flags. If it's a query, ignore it + * and keep reading. */ if ((flags & DNS_MESSAGEFLAG_QR) == 0) { /* @@ -677,24 +687,37 @@ tcp_recv(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region, } /* - * We have a response; find the associated dispentry. + * We have a valid response; find the associated dispentry object + * and call the caller back. */ bucket = dns_hash(qid, &peer, id, disp->localport); LOCK(&qid->lock); resp = entry_search(qid, &peer, id, disp->localport, bucket); + if (resp != NULL) { + dispentry_attach(resp, &(dns_dispentry_t *){ NULL }); + } dispatch_log(disp, LVL(90), "search for response in bucket %d: %s", bucket, (resp == NULL ? "not found" : "found")); UNLOCK(&qid->lock); next: - /* Restart the reading from the TCP socket */ - dispatch_getnext(disp, resp, -1); + dispatch_getnext(disp, NULL, -1); -unlock: +done: UNLOCK(&disp->lock); if (resp != NULL) { + /* We got a matching response, or timed out */ resp->response(eresult, region, resp->arg); + dispentry_detach(&resp); + } else { + /* We're being shut down; cancel all outstanding resps */ + for (resp = ISC_LIST_HEAD(resps); resp != NULL; resp = next) { + next = ISC_LIST_NEXT(resp, rlink); + ISC_LIST_UNLINK(resps, resp, rlink); + resp->response(ISC_R_SHUTTINGDOWN, region, resp->arg); + dispentry_detach(&resp); + } } dns_dispatch_detach(&disp); @@ -1021,11 +1044,8 @@ dns_dispatch_createtcp(dns_dispatchmgr_t *mgr, const isc_sockaddr_t *localaddr, ISC_LIST_APPEND(mgr->list, disp, link); UNLOCK(&mgr->lock); - if (isc_log_wouldlog(dns_lctx, 90)) { - mgr_log(mgr, LVL(90), - "dns_dispatch_createtcp: created TCP dispatch %p", - disp); - } + mgr_log(mgr, LVL(90), "dns_dispatch_createtcp: created TCP dispatch %p", + disp); *dispp = disp; return (ISC_R_SUCCESS); @@ -1035,19 +1055,18 @@ isc_result_t dns_dispatch_gettcp(dns_dispatchmgr_t *mgr, const isc_sockaddr_t *destaddr, const isc_sockaddr_t *localaddr, bool *connected, dns_dispatch_t **dispp) { - dns_dispatch_t *disp = NULL; + dns_dispatch_t *disp_connected = NULL; + dns_dispatch_t *disp_fallback = NULL; + isc_result_t result = ISC_R_NOTFOUND; REQUIRE(VALID_DISPATCHMGR(mgr)); REQUIRE(destaddr != NULL); REQUIRE(connected != NULL); REQUIRE(dispp != NULL && *dispp == NULL); - /* First pass, look for connected TCP dispatches */ - *connected = true; - LOCK(&mgr->lock); -again: - for (disp = ISC_LIST_HEAD(mgr->list); disp != NULL && *dispp == NULL; + + for (dns_dispatch_t *disp = ISC_LIST_HEAD(mgr->list); disp != NULL; disp = ISC_LIST_NEXT(disp, link)) { isc_sockaddr_t sockname; @@ -1063,56 +1082,57 @@ again: peeraddr = disp->peer; } - if (*connected == true && - atomic_load(&disp->state) != DNS_DISPATCHSTATE_CONNECTED) - { - goto unlock; - } - - /* We don't reuse UDP sockets */ - if (disp->socktype != isc_socktype_tcp) { - goto unlock; - } - - /* Different destination address */ - if (!isc_sockaddr_equal(destaddr, &peeraddr)) { - goto unlock; - } - - /* Different local addr */ - if (localaddr != NULL) { - /* FIXME: This is weird as sockname == disp-local */ - if (!isc_sockaddr_eqaddr(localaddr, &disp->local) || - !isc_sockaddr_eqaddr(localaddr, &sockname)) - { - goto unlock; - } - } - /* * The conditions match: * 1. socktype is TCP * 2. destination address is same * 3. local address is either NULL or same */ - dns_dispatch_attach(disp, dispp); - unlock: + if (disp->socktype == isc_socktype_tcp && + isc_sockaddr_equal(destaddr, &peeraddr) && + (localaddr == NULL || + isc_sockaddr_eqaddr(localaddr, &sockname))) + { + if (atomic_load(&disp->state) == + DNS_DISPATCHSTATE_CONNECTED) { + /* We found connected dispatch */ + disp_connected = disp; + UNLOCK(&disp->lock); + break; + } + + /* We found "a" dispatch, store it for later */ + if (disp_fallback == NULL) { + disp_fallback = disp; + } + + UNLOCK(&disp->lock); + continue; + } + UNLOCK(&disp->lock); } - if (*dispp != NULL) { - UNLOCK(&mgr->lock); - return (ISC_R_SUCCESS); - } + if (disp_connected != NULL) { + /* We found connected dispatch */ + INSIST(disp_connected->handle != NULL); - if (*connected) { - /* Second pass, look also for not-yet-connected dispatch */ + *connected = true; + dns_dispatch_attach(disp_connected, dispp); + + result = ISC_R_SUCCESS; + } else if (disp_fallback != NULL) { + /* We found matching dispatch */ *connected = false; - goto again; + + dns_dispatch_attach(disp_fallback, dispp); + + result = ISC_R_SUCCESS; } UNLOCK(&mgr->lock); - return (ISC_R_NOTFOUND); + + return (result); } isc_result_t @@ -1305,6 +1325,7 @@ dns_dispatch_addresponse(dns_dispatch_t *disp, unsigned int options, ISC_LINK_INIT(res, link); ISC_LINK_INIT(res, alink); ISC_LINK_INIT(res, plink); + ISC_LINK_INIT(res, rlink); if (disp->socktype == isc_socktype_udp) { isc_result_t result = setup_socket(disp, res, dest, &localport); @@ -1390,21 +1411,20 @@ dispatch_getnext(dns_dispatch_t *disp, dns_dispentry_t *resp, int32_t timeout) { switch (disp->socktype) { case isc_socktype_udp: dispentry_attach(resp, &(dns_dispentry_t *){ NULL }); - if (timeout > 0) { isc_nmhandle_settimeout(resp->handle, timeout); } isc_nm_read(resp->handle, udp_recv, resp); - break; + case isc_socktype_tcp: dns_dispatch_attach(disp, &(dns_dispatch_t *){ NULL }); - if (timeout > 0) { isc_nmhandle_settimeout(disp->handle, timeout); } isc_nm_read(disp->handle, tcp_recv, disp); break; + default: INSIST(0); ISC_UNREACHABLE(); @@ -1418,14 +1438,12 @@ dns_dispatch_getnext(dns_dispentry_t *resp) { REQUIRE(VALID_RESPONSE(resp)); disp = resp->disp; + REQUIRE(VALID_DISPATCH(disp)); LOCK(&disp->lock); - dispatch_getnext(disp, resp, resp->timeout); - UNLOCK(&disp->lock); - return (ISC_R_SUCCESS); } @@ -1485,12 +1503,15 @@ startrecv(isc_nmhandle_t *handle, dns_dispatch_t *disp, dns_dispentry_t *resp) { break; case isc_socktype_tcp: - REQUIRE(resp != NULL && resp->handle == NULL); - REQUIRE(disp != NULL && disp->handle == NULL); + REQUIRE(disp != NULL); + LOCK(&disp->lock); + REQUIRE(disp->handle == NULL); isc_nmhandle_attach(handle, &disp->handle); dns_dispatch_attach(disp, &(dns_dispatch_t *){ NULL }); isc_nm_read(disp->handle, tcp_recv, disp); + UNLOCK(&disp->lock); + break; default: @@ -1499,51 +1520,88 @@ startrecv(isc_nmhandle_t *handle, dns_dispatch_t *disp, dns_dispentry_t *resp) { } } -/* - * FIXME: Split into tcp_connected() and udp_connected() - */ - static void -disp_connected(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) { - dns_dispentry_t *resp = (dns_dispentry_t *)arg; - dns_dispentry_t *pending = NULL, *next = NULL; - dns_dispatch_t *disp = resp->disp; +tcp_connected(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) { + dns_dispatch_t *disp = (dns_dispatch_t *)arg; + dns_dispentry_t *resp = NULL, *next = NULL; + dns_displist_t resps; - if (eresult == ISC_R_SUCCESS) { - if (resp->canceled) { - dispentry_detach(&resp); - return; - } + dispatch_log(disp, LVL(90), "TCP connected (%p): %s", disp, + isc_result_totext(eresult)); - if (disp->socktype == isc_socktype_tcp) { - REQUIRE(atomic_compare_exchange_strong( - &disp->state, - &(uint_fast32_t){ DNS_DISPATCHSTATE_CONNECTING }, - DNS_DISPATCHSTATE_CONNECTED)); - } - - startrecv(handle, disp, resp); - } + ISC_LIST_INIT(resps); if (MGR_IS_SHUTTINGDOWN(disp->mgr)) { eresult = ISC_R_SHUTTINGDOWN; } + if (eresult == ISC_R_SUCCESS) { + REQUIRE(atomic_compare_exchange_strong( + &disp->state, + &(uint_fast32_t){ DNS_DISPATCHSTATE_CONNECTING }, + DNS_DISPATCHSTATE_CONNECTED)); + startrecv(handle, disp, NULL); + } + + /* + * If there are pending responses, call the connect + * callbacks for all of them. + */ + LOCK(&disp->lock); + for (resp = ISC_LIST_HEAD(disp->pending); resp != NULL; resp = next) { + next = ISC_LIST_NEXT(resp, plink); + ISC_LIST_UNLINK(disp->pending, resp, plink); + ISC_LIST_APPEND(resps, resp, plink); + } + UNLOCK(&disp->lock); + + for (resp = ISC_LIST_HEAD(resps); resp != NULL; resp = next) { + next = ISC_LIST_NEXT(resp, plink); + ISC_LIST_UNLINK(resps, resp, plink); + + if (resp->connected != NULL) { + resp->connected(eresult, NULL, resp->arg); + } + dispentry_detach(&resp); + } + + dns_dispatch_detach(&disp); +} + +static void +udp_connected(isc_nmhandle_t *handle, isc_result_t eresult, void *arg) { + dns_dispentry_t *resp = (dns_dispentry_t *)arg; + dns_dispatch_t *disp = resp->disp; + + dispatch_log(disp, LVL(90), "UDP connected (%p): %s", resp, + isc_result_totext(eresult)); + + if (MGR_IS_SHUTTINGDOWN(disp->mgr)) { + eresult = ISC_R_SHUTTINGDOWN; + } + + if (eresult == ISC_R_SUCCESS && resp->canceled) { + eresult = ISC_R_CANCELED; + } else if (eresult == ISC_R_SUCCESS) { + startrecv(handle, disp, resp); + } else if (eresult == ISC_R_ADDRINUSE) { + in_port_t localport = 0; + isc_result_t result; + + /* probably a port collision; try a different one */ + disp->nsockets--; + result = setup_socket(disp, resp, &resp->peer, &localport); + if (result == ISC_R_SUCCESS) { + dns_dispatch_connect(resp); + goto detach; + } + } + if (resp->connected != NULL) { resp->connected(eresult, NULL, resp->arg); } - for (pending = ISC_LIST_HEAD(disp->pending); pending != NULL; - pending = next) { - next = ISC_LIST_NEXT(pending, plink); - ISC_LIST_UNLINK(disp->pending, pending, plink); - - if (pending->connected != NULL) { - pending->connected(eresult, NULL, pending->arg); - } - dispentry_detach(&pending); - } - +detach: dispentry_detach(&resp); } @@ -1556,46 +1614,58 @@ dns_dispatch_connect(dns_dispentry_t *resp) { disp = resp->disp; - /* This will be detached in disp_connected() */ + /* This will be detached once we've connected. */ dispentry_attach(resp, &(dns_dispentry_t *){ NULL }); switch (disp->socktype) { case isc_socktype_tcp: /* - * Check whether the dispatch was already connecting. - * If so, add resp to the pending responses. + * Check whether the dispatch is already connecting + * or connected. */ atomic_compare_exchange_strong(&disp->state, (uint_fast32_t *)&state, DNS_DISPATCHSTATE_CONNECTING); - switch (state) { case DNS_DISPATCHSTATE_NONE: /* First connection, continue with connecting */ - INSIST(disp->handle == NULL); + LOCK(&disp->lock); + INSIST(ISC_LIST_EMPTY(disp->pending)); + ISC_LIST_APPEND(disp->pending, resp, plink); + UNLOCK(&disp->lock); + dns_dispatch_attach(disp, &(dns_dispatch_t *){ NULL }); isc_nm_tcpdnsconnect(disp->mgr->nm, &disp->local, - &disp->peer, disp_connected, resp, + &disp->peer, tcp_connected, disp, resp->timeout, 0); break; + case DNS_DISPATCHSTATE_CONNECTING: + /* Connection pending; add resp to the list */ + LOCK(&disp->lock); ISC_LIST_APPEND(disp->pending, resp, plink); - return (ISC_R_SUCCESS); + UNLOCK(&disp->lock); + break; + case DNS_DISPATCHSTATE_CONNECTED: - /* We are already connected, call the connected cb */ + /* We are already connected; call the connected cb */ if (resp->connected != NULL) { resp->connected(ISC_R_SUCCESS, NULL, resp->arg); } - return (ISC_R_SUCCESS); + dispentry_detach(&resp); + break; + default: INSIST(0); ISC_UNREACHABLE(); } break; + case isc_socktype_udp: isc_nm_udpconnect(disp->mgr->nm, &resp->local, &resp->peer, - disp_connected, resp, resp->timeout, 0); + udp_connected, resp, resp->timeout, 0); break; + default: return (ISC_R_NOTIMPLEMENTED); } @@ -1621,6 +1691,7 @@ send_done(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { void dns_dispatch_resume(dns_dispentry_t *resp, uint16_t timeout) { dns_dispatch_t *disp = NULL; + REQUIRE(VALID_RESPONSE(resp)); disp = resp->disp; @@ -1668,22 +1739,29 @@ dns_dispatch_cancel(dns_dispentry_t *resp) { resp->canceled = true; - /* UDP case. */ + /* Connected UDP */ if (resp->handle != NULL) { isc_nm_cancelread(resp->handle); return; } - /* - * TCP case. We only want to cancel if this is the last resp - * listening on this TCP connection. - */ + /* TCP pending connection. */ if (ISC_LINK_LINKED(resp, plink)) { ISC_LIST_UNLINK(resp->disp->pending, resp, plink); if (resp->connected != NULL) { resp->connected(ISC_R_CANCELED, NULL, resp->arg); + dispentry_detach(&resp); } - } else if (ISC_LINK_LINKED(resp, alink)) { + return; + } + + /* + * Connected TCP, or unconnected UDP. + * + * If TCP, we don't want to cancel the dispatch + * unless this is the last resp waiting. + */ + if (ISC_LINK_LINKED(resp, alink)) { ISC_LIST_UNLINK(resp->disp->active, resp, alink); if (ISC_LIST_EMPTY(resp->disp->active) && resp->disp->handle != NULL) { diff --git a/lib/dns/request.c b/lib/dns/request.c index b2b569ddf1..9214225e04 100644 --- a/lib/dns/request.c +++ b/lib/dns/request.c @@ -590,15 +590,15 @@ again: req_send(request); req_detach(&rclone); } else { + request->flags |= DNS_REQUEST_F_CONNECTING; + if (tcp) { + request->flags |= DNS_REQUEST_F_TCP; + } + result = dns_dispatch_connect(request->dispentry); if (result != ISC_R_SUCCESS) { goto unlink; } - request->flags |= DNS_REQUEST_F_CONNECTING; - - if (tcp) { - request->flags |= DNS_REQUEST_F_TCP; - } } req_log(ISC_LOG_DEBUG(3), "dns_request_createraw: request %p", request); @@ -763,14 +763,15 @@ use_tcp: req_send(request); req_detach(&rclone); } else { - result = dns_dispatch_connect(request->dispentry); - if (result != ISC_R_SUCCESS) { - goto unlink; - } request->flags |= DNS_REQUEST_F_CONNECTING; if (tcp) { request->flags |= DNS_REQUEST_F_TCP; } + + result = dns_dispatch_connect(request->dispentry); + if (result != ISC_R_SUCCESS) { + goto unlink; + } } req_log(ISC_LOG_DEBUG(3), "dns_request_createvia: request %p", request); @@ -908,7 +909,8 @@ send_if_done(dns_request_t *request, isc_result_t result) { void request_cancel(dns_request_t *request) { if (!DNS_REQUEST_CANCELED(request)) { - req_log(ISC_LOG_DEBUG(3), "do_cancel: request %p", request); + req_log(ISC_LOG_DEBUG(3), "request_cancel: request %p", + request); request->flags |= DNS_REQUEST_F_CANCELED; request->flags &= ~DNS_REQUEST_F_CONNECTING; @@ -1006,9 +1008,6 @@ dns_request_destroy(dns_request_t **requestp) { req_detach(&request); } -/*** - *** Private: request. - ***/ static void req_connected(isc_result_t eresult, isc_region_t *region, void *arg) { dns_request_t *request = (dns_request_t *)arg; @@ -1018,13 +1017,9 @@ req_connected(isc_result_t eresult, isc_region_t *region, void *arg) { req_log(ISC_LOG_DEBUG(3), "req_connected: request %p: %s", request, isc_result_totext(eresult)); - if (eresult == ISC_R_CANCELED) { - req_detach(&request); - return; - } - REQUIRE(VALID_REQUEST(request)); - REQUIRE(DNS_REQUEST_CONNECTING(request)); + REQUIRE(DNS_REQUEST_CONNECTING(request) || + DNS_REQUEST_CANCELED(request)); LOCK(&request->requestmgr->locks[request->hash]); request->flags &= ~DNS_REQUEST_F_CONNECTING; @@ -1078,13 +1073,14 @@ static void req_response(isc_result_t result, isc_region_t *region, void *arg) { dns_request_t *request = (dns_request_t *)arg; - if (result == ISC_R_CANCELED) { + req_log(ISC_LOG_DEBUG(3), "req_response: request %p: %s", request, + dns_result_totext(result)); + + if (result == ISC_R_CANCELED || result == ISC_R_EOF) { return; } if (result == ISC_R_TIMEDOUT) { - req_log(ISC_LOG_DEBUG(3), "req_timeout: request %p", request); - LOCK(&request->requestmgr->locks[request->hash]); if (--request->udpcount != 0) { dns_dispatch_resume(request->dispentry, @@ -1102,9 +1098,6 @@ req_response(isc_result_t result, isc_region_t *region, void *arg) { REQUIRE(VALID_REQUEST(request)); - req_log(ISC_LOG_DEBUG(3), "req_response: request %p: %s", request, - dns_result_totext(result)); - LOCK(&request->requestmgr->locks[request->hash]); if (result != ISC_R_SUCCESS) { @@ -1119,11 +1112,14 @@ req_response(isc_result_t result, isc_region_t *region, void *arg) { if (result != ISC_R_SUCCESS) { isc_buffer_free(&request->answer); } + done: /* * Cleanup. */ - dns_dispatch_removeresponse(&request->dispentry); + if (request->dispentry != NULL) { + dns_dispatch_removeresponse(&request->dispentry); + } request_cancel(request); /* diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 1643b3c0d6..ba9b9f01b5 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -1388,8 +1388,8 @@ fctx_cancelquery(resquery_t *query, isc_time_t *finish, bool no_response, } /* - * Check for any outstanding dispatch responses. If they exist, - * cancel them and let their callbacks finish the cleanup. + * Check for any outstanding dispatch responses and if they + * exist, cancel them. */ if (query->dispentry != NULL) { dns_dispatch_cancel(query->dispentry); @@ -1756,6 +1756,7 @@ resquery_senddone(isc_result_t eresult, isc_region_t *region, void *arg) { switch (eresult) { case ISC_R_SUCCESS: + case ISC_R_SHUTTINGDOWN: goto detach; case ISC_R_HOSTUNREACH: @@ -1777,8 +1778,10 @@ resquery_senddone(isc_result_t eresult, isc_region_t *region, void *arg) { FCTX_ATTR_CLR(fctx, FCTX_ATTR_ADDRWAIT); fctx_try(fctx, true, false); break; + case ISC_R_CANCELED: break; + default: FCTXTRACE3("query canceled in resquery_senddone() " "due to unexpected result; responding", @@ -2822,6 +2825,13 @@ resquery_connected(isc_result_t eresult, isc_region_t *region, void *arg) { } goto detach; + case ISC_R_SHUTTINGDOWN: + FCTXTRACE3("shutdown in resquery_connected(): no response", + eresult); + fctx_cancelquery(query, NULL, true, false); + fctx_done(fctx, eresult, __LINE__); + break; + case ISC_R_NETUNREACH: case ISC_R_HOSTUNREACH: case ISC_R_CONNREFUSED: @@ -7193,7 +7203,7 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) { fetchctx_t *fctx = NULL; respctx_t rctx; - if (eresult == ISC_R_CANCELED) { + if (eresult == ISC_R_CANCELED || eresult == ISC_R_EOF) { return; } @@ -7697,7 +7707,8 @@ rctx_dispfail(respctx_t *rctx) { if (rctx->result == ISC_R_HOSTUNREACH || rctx->result == ISC_R_NETUNREACH || rctx->result == ISC_R_CONNREFUSED || - rctx->result == ISC_R_CANCELED) + rctx->result == ISC_R_CANCELED || + rctx->result == ISC_R_SHUTTINGDOWN) { rctx->broken_server = rctx->result; rctx->broken_type = badns_unreachable; @@ -10827,8 +10838,7 @@ dns_resolver_disable_algorithm(dns_resolver_t *resolver, const dns_name_t *name, memmove(tmp, algorithms, *algorithms); } tmp[len - 1] |= mask; - /* 'tmp[0]' should contain the length of 'tmp'. - */ + /* tmp[0] should contain the length of 'tmp'. */ *tmp = len; node->data = tmp; /* Free the older bitfield. */ diff --git a/lib/dns/tests/Makefile.am b/lib/dns/tests/Makefile.am index bc7250600f..a90f97c79e 100644 --- a/lib/dns/tests/Makefile.am +++ b/lib/dns/tests/Makefile.am @@ -3,6 +3,7 @@ include $(top_srcdir)/Makefile.top AM_CPPFLAGS += \ $(LIBISC_CFLAGS) \ $(LIBDNS_CFLAGS) \ + $(LIBUV_CFLAGS) \ $(KRB5_CFLAGS) \ -DSRCDIR=\"$(abs_srcdir)\" \ -DBUILDDIR=\"$(abs_builddir)\" @@ -10,6 +11,7 @@ AM_CPPFLAGS += \ LDADD += \ libdnstest.la \ $(LIBISC_LIBS) \ + $(LIBUV_LIBS) \ $(LIBDNS_LIBS) check_LTLIBRARIES = libdnstest.la diff --git a/lib/dns/tests/dispatch_test.c b/lib/dns/tests/dispatch_test.c index b8d083b687..8b64c82ce0 100644 --- a/lib/dns/tests/dispatch_test.c +++ b/lib/dns/tests/dispatch_test.c @@ -25,7 +25,6 @@ #define UNIT_TESTING #include -#include #include #include #include @@ -39,18 +38,31 @@ #include "dnstest.h" +uv_sem_t sem; + /* Timeouts in miliseconds */ -#define T_INIT 120 * 1000 -#define T_IDLE 120 * 1000 -#define T_KEEPALIVE 120 * 1000 -#define T_ADVERTISED 120 * 1000 -#define T_CONNECT 30 * 1000 +#define T_SERVER_INIT 5000 +#define T_SERVER_IDLE 5000 +#define T_SERVER_KEEPALIVE 5000 +#define T_SERVER_ADVERTISED 5000 + +#define T_CLIENT_INIT 2000 +#define T_CLIENT_IDLE 2000 +#define T_CLIENT_KEEPALIVE 2000 +#define T_CLIENT_ADVERTISED 2000 + +#define T_CLIENT_CONNECT 1000 dns_dispatchmgr_t *dispatchmgr = NULL; dns_dispatchset_t *dset = NULL; isc_nm_t *connect_nm = NULL; -static isc_sockaddr_t server_addr; -static isc_sockaddr_t connect_addr; +static isc_sockaddr_t udp_server_addr; +static isc_sockaddr_t udp_connect_addr; +static isc_sockaddr_t tcp_server_addr; +static isc_sockaddr_t tcp_connect_addr; + +const struct in6_addr in6addr_blackhole = { { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1 } } }; static int setup_ephemeral_port(isc_sockaddr_t *addr, sa_family_t family) { @@ -96,21 +108,35 @@ setup_ephemeral_port(isc_sockaddr_t *addr, sa_family_t family) { return (fd); } +static void +reset_testdata(void); + static int _setup(void **state) { isc_result_t result; uv_os_sock_t sock = -1; + int r; UNUSED(state); result = dns_test_begin(NULL, true); assert_int_equal(result, ISC_R_SUCCESS); - connect_addr = (isc_sockaddr_t){ .length = 0 }; - isc_sockaddr_fromin6(&connect_addr, &in6addr_loopback, 0); + udp_connect_addr = (isc_sockaddr_t){ .length = 0 }; + isc_sockaddr_fromin6(&udp_connect_addr, &in6addr_loopback, 0); - server_addr = (isc_sockaddr_t){ .length = 0 }; - sock = setup_ephemeral_port(&server_addr, SOCK_DGRAM); + tcp_connect_addr = (isc_sockaddr_t){ .length = 0 }; + isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0); + + udp_server_addr = (isc_sockaddr_t){ .length = 0 }; + sock = setup_ephemeral_port(&udp_server_addr, SOCK_DGRAM); + if (sock < 0) { + return (-1); + } + close(sock); + + tcp_server_addr = (isc_sockaddr_t){ .length = 0 }; + sock = setup_ephemeral_port(&tcp_server_addr, SOCK_STREAM); if (sock < 0) { return (-1); } @@ -120,9 +146,20 @@ _setup(void **state) { isc_managers_create(dt_mctx, ncpus, 0, 0, &connect_nm, NULL, NULL, NULL); - isc_nm_settimeouts(netmgr, T_INIT, T_IDLE, T_KEEPALIVE, T_ADVERTISED); - isc_nm_settimeouts(connect_nm, T_INIT, T_IDLE, T_KEEPALIVE, - T_ADVERTISED); + isc_nm_settimeouts(netmgr, T_SERVER_INIT, T_SERVER_IDLE, + T_SERVER_KEEPALIVE, T_SERVER_ADVERTISED); + + /* + * Use shorter client-side timeouts, to ensure that clients + * time out before the server. + */ + isc_nm_settimeouts(connect_nm, T_CLIENT_INIT, T_CLIENT_IDLE, + T_CLIENT_KEEPALIVE, T_CLIENT_ADVERTISED); + + r = uv_sem_init(&sem, 0); + assert_int_equal(r, 0); + + reset_testdata(); return (0); } @@ -131,6 +168,8 @@ static int _teardown(void **state) { UNUSED(state); + uv_sem_destroy(&sem); + isc_managers_destroy(&connect_nm, NULL, NULL, NULL); assert_null(connect_nm); @@ -151,12 +190,12 @@ make_dispatchset(unsigned int ndisps) { } isc_sockaddr_any(&any); - result = dns_dispatch_createudp(dispatchmgr, taskmgr, &any, 0, &disp); + result = dns_dispatch_createudp(dispatchmgr, &any, &disp); if (result != ISC_R_SUCCESS) { return (result); } - result = dns_dispatchset_create(dt_mctx, taskmgr, disp, &dset, ndisps); + result = dns_dispatchset_create(dt_mctx, disp, &dset, ndisps); dns_dispatch_detach(&disp); return (result); @@ -231,20 +270,29 @@ dispatchset_get(void **state) { } struct { - isc_nmhandle_t *handle; atomic_uint_fast32_t responses; + atomic_uint_fast32_t result; } testdata; static dns_dispatch_t *dispatch = NULL; static dns_dispentry_t *dispentry = NULL; static atomic_bool first = ATOMIC_VAR_INIT(true); +static void +reset_testdata(void) { + atomic_init(&testdata.responses, 0); + atomic_init(&testdata.result, ISC_R_UNSET); +} + static void server_senddone(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) { UNUSED(handle); UNUSED(eresult); UNUSED(cbarg); + fprintf(stderr, "%s(..., %s, ...)\n", __func__, + isc_result_totext(eresult)); + return; } @@ -281,88 +329,137 @@ nameserver(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region, isc_nm_send(handle, &response, server_senddone, NULL); } -static void -response(isc_task_t *task, isc_event_t *event) { - dns_dispatchevent_t *devent = (dns_dispatchevent_t *)event; - bool exp_true = true; +static isc_result_t +accept_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) { + UNUSED(handle); + UNUSED(cbarg); - UNUSED(task); + return (eresult); +} + +static void +noop_nameserver(isc_nmhandle_t *handle, isc_result_t eresult, + isc_region_t *region, void *cbarg) { + UNUSED(handle); + UNUSED(eresult); + UNUSED(region); + UNUSED(cbarg); +} + +static void +response_getnext(isc_result_t result, isc_region_t *region, void *arg) { + UNUSED(region); + UNUSED(arg); atomic_fetch_add_relaxed(&testdata.responses, 1); - if (atomic_compare_exchange_strong(&first, &exp_true, false)) { - isc_result_t result = dns_dispatch_getnext(dispentry, &devent); + + if (atomic_compare_exchange_strong(&first, &(bool){ true }, false)) { + result = dns_dispatch_getnext(dispentry); assert_int_equal(result, ISC_R_SUCCESS); } else { - dns_dispatch_removeresponse(&dispentry, &devent); - isc_nmhandle_detach(&testdata.handle); - isc_app_shutdown(); + uv_sem_post(&sem); } } static void -connected(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) { +response(isc_result_t eresult, isc_region_t *region, void *arg) { + UNUSED(region); + UNUSED(arg); + + fprintf(stderr, "%s(..., %s, ...)\n", __func__, + isc_result_totext(eresult)); + + switch (eresult) { + case ISC_R_EOF: + case ISC_R_CANCELED: + case ISC_R_SHUTTINGDOWN: + break; + default: + atomic_fetch_add_relaxed(&testdata.responses, 1); + atomic_store_relaxed(&testdata.result, eresult); + } + + uv_sem_post(&sem); +} + +static void +response_timeout(isc_result_t eresult, isc_region_t *region, void *arg) { + UNUSED(region); + UNUSED(arg); + + fprintf(stderr, "%s(..., %s, ...)\n", __func__, + isc_result_totext(eresult)); + + atomic_store_relaxed(&testdata.result, eresult); + + uv_sem_post(&sem); +} + +static void +connected(isc_result_t eresult, isc_region_t *region, void *cbarg) { isc_region_t *r = (isc_region_t *)cbarg; UNUSED(eresult); + UNUSED(region); + + fprintf(stderr, "%s(..., %s, ...)\n", __func__, + isc_result_totext(eresult)); - isc_nmhandle_attach(handle, &testdata.handle); dns_dispatch_send(dispentry, r, -1); } static void -client_senddone(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) { - UNUSED(handle); +client_senddone(isc_result_t eresult, isc_region_t *region, void *cbarg) { UNUSED(eresult); + UNUSED(region); UNUSED(cbarg); + fprintf(stderr, "%s(..., %s, ...)\n", __func__, + isc_result_totext(eresult)); + return; } static void -startit(isc_task_t *task, isc_event_t *event) { - UNUSED(task); - dns_dispatch_connect(dispentry); - isc_event_free(&event); +timeout_connected(isc_result_t eresult, isc_region_t *region, void *cbarg) { + UNUSED(region); + UNUSED(cbarg); + + fprintf(stderr, "%s(..., %s, ...)\n", __func__, + isc_result_totext(eresult)); + + atomic_store_relaxed(&testdata.result, eresult); + + uv_sem_post(&sem); } -/* test dispatch getnext */ static void -dispatch_getnext(void **state) { +dispatch_timeout_tcp_connect(void **state) { isc_result_t result; isc_region_t region; - isc_nmsocket_t *sock = NULL; - isc_task_t *task = NULL; - unsigned char message[12]; - unsigned char rbuf[12]; + unsigned char rbuf[12] = { 0 }; + unsigned char message[12] = { 0 }; uint16_t id; UNUSED(state); - testdata.handle = NULL; - atomic_init(&testdata.responses, 0); - - result = isc_task_create(taskmgr, 0, &task); - assert_int_equal(result, ISC_R_SUCCESS); + tcp_connect_addr = (isc_sockaddr_t){ .length = 0 }; + isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_blackhole, 0); result = dns_dispatchmgr_create(dt_mctx, connect_nm, &dispatchmgr); assert_int_equal(result, ISC_R_SUCCESS); - result = dns_dispatch_createudp(dispatchmgr, taskmgr, &connect_addr, 0, - &dispatch); - assert_int_equal(result, ISC_R_SUCCESS); - - /* - * Create a local udp nameserver on the loopback. - */ - result = isc_nm_listenudp(netmgr, &server_addr, nameserver, NULL, 0, - &sock); + result = dns_dispatch_createtcp(dispatchmgr, &tcp_connect_addr, + &tcp_server_addr, -1, &dispatch); assert_int_equal(result, ISC_R_SUCCESS); region.base = rbuf; region.length = sizeof(rbuf); - result = dns_dispatch_addresponse( - dispatch, 0, 10000, &server_addr, task, connected, - client_senddone, response, NULL, ®ion, &id, &dispentry); + + result = dns_dispatch_addresponse(dispatch, 0, T_CLIENT_CONNECT, + &tcp_server_addr, timeout_connected, + client_senddone, response, ®ion, + &id, &dispentry); assert_int_equal(result, ISC_R_SUCCESS); memset(message, 0, sizeof(message)); @@ -372,26 +469,249 @@ dispatch_getnext(void **state) { region.base = message; region.length = sizeof(message); - result = isc_app_onrun(dt_mctx, task, startit, NULL); + dns_dispatch_connect(dispentry); + + uv_sem_wait(&sem); + + dns_dispatch_removeresponse(&dispentry); + + dns_dispatch_detach(&dispatch); + dns_dispatchmgr_detach(&dispatchmgr); + + /* Skip if the IPv6 is not available or not blackholed */ + + result = atomic_load_acquire(&testdata.result); + if (result == ISC_R_ADDRNOTAVAIL || result == ISC_R_CONNREFUSED) { + skip(); + return; + } + + assert_int_equal(result, ISC_R_TIMEDOUT); +} + +static void +dispatch_timeout_tcp_response(void **state __attribute__((unused))) { + isc_result_t result; + isc_region_t region; + unsigned char rbuf[12] = { 0 }; + unsigned char message[12] = { 0 }; + uint16_t id; + isc_nmsocket_t *sock = NULL; + + UNUSED(state); + + tcp_connect_addr = (isc_sockaddr_t){ .length = 0 }; + isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0); + + result = dns_dispatchmgr_create(dt_mctx, connect_nm, &dispatchmgr); assert_int_equal(result, ISC_R_SUCCESS); - result = isc_app_run(); + result = dns_dispatch_createtcp(dispatchmgr, &tcp_connect_addr, + &tcp_server_addr, -1, &dispatch); assert_int_equal(result, ISC_R_SUCCESS); - assert_int_equal(atomic_load_acquire(&testdata.responses), 2); + result = isc_nm_listentcpdns(netmgr, &tcp_server_addr, noop_nameserver, + NULL, accept_cb, NULL, 0, 0, NULL, &sock); + assert_int_equal(result, ISC_R_SUCCESS); + + region.base = rbuf; + region.length = sizeof(rbuf); + + result = dns_dispatch_addresponse( + dispatch, 0, T_CLIENT_CONNECT, &tcp_server_addr, connected, + client_senddone, response_timeout, ®ion, &id, &dispentry); + assert_int_equal(result, ISC_R_SUCCESS); + + memset(message, 0, sizeof(message)); + message[0] = (id >> 8) & 0xff; + message[1] = id & 0xff; + + region.base = message; + region.length = sizeof(message); + + dns_dispatch_connect(dispentry); + + uv_sem_wait(&sem); + + assert_int_equal(atomic_load_acquire(&testdata.result), ISC_R_TIMEDOUT); isc_nm_stoplistening(sock); isc_nmsocket_close(&sock); assert_null(sock); - /* - * Shutdown nameserver. - */ - isc_task_detach(&task); + dns_dispatch_removeresponse(&dispentry); + + dns_dispatch_detach(&dispatch); + dns_dispatchmgr_detach(&dispatchmgr); +} + +static void +dispatch_tcp_response(void **state __attribute__((unused))) { + isc_result_t result; + isc_region_t region; + unsigned char rbuf[12] = { 0 }; + unsigned char message[12] = { 0 }; + uint16_t id; + isc_nmsocket_t *sock = NULL; + + UNUSED(state); + + tcp_connect_addr = (isc_sockaddr_t){ .length = 0 }; + isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0); + + result = dns_dispatchmgr_create(dt_mctx, connect_nm, &dispatchmgr); + assert_int_equal(result, ISC_R_SUCCESS); + + result = dns_dispatch_createtcp(dispatchmgr, &tcp_connect_addr, + &tcp_server_addr, -1, &dispatch); + assert_int_equal(result, ISC_R_SUCCESS); + + result = isc_nm_listentcpdns(netmgr, &tcp_server_addr, nameserver, NULL, + accept_cb, NULL, 0, 0, NULL, &sock); + assert_int_equal(result, ISC_R_SUCCESS); + + region.base = rbuf; + region.length = sizeof(rbuf); + + result = dns_dispatch_addresponse( + dispatch, 0, T_CLIENT_CONNECT, &tcp_server_addr, connected, + client_senddone, response, ®ion, &id, &dispentry); + assert_int_equal(result, ISC_R_SUCCESS); + + memset(message, 0, sizeof(message)); + message[0] = (id >> 8) & 0xff; + message[1] = id & 0xff; + + region.base = message; + region.length = sizeof(message); + + dns_dispatch_connect(dispentry); + + uv_sem_wait(&sem); + + assert_in_range(atomic_load_acquire(&testdata.responses), 1, 2); + assert_int_equal(atomic_load_acquire(&testdata.result), ISC_R_SUCCESS); + + /* Cleanup */ + + isc_nm_stoplistening(sock); + isc_nmsocket_close(&sock); + assert_null(sock); + + dns_dispatch_removeresponse(&dispentry); + + dns_dispatch_detach(&dispatch); + dns_dispatchmgr_detach(&dispatchmgr); +} + +static void +dispatch_timeout_udp_response(void **state __attribute__((unused))) { + isc_result_t result; + isc_region_t region; + unsigned char rbuf[12] = { 0 }; + unsigned char message[12] = { 0 }; + uint16_t id; + isc_nmsocket_t *sock = NULL; + + UNUSED(state); + + udp_connect_addr = (isc_sockaddr_t){ .length = 0 }; + isc_sockaddr_fromin6(&udp_connect_addr, &in6addr_loopback, 0); + + result = dns_dispatchmgr_create(dt_mctx, connect_nm, &dispatchmgr); + assert_int_equal(result, ISC_R_SUCCESS); + + result = dns_dispatch_createudp(dispatchmgr, &tcp_connect_addr, + &dispatch); + assert_int_equal(result, ISC_R_SUCCESS); + + result = isc_nm_listenudp(netmgr, &udp_server_addr, noop_nameserver, + NULL, 0, &sock); + assert_int_equal(result, ISC_R_SUCCESS); + + region.base = rbuf; + region.length = sizeof(rbuf); + + result = dns_dispatch_addresponse( + dispatch, 0, T_CLIENT_CONNECT, &udp_server_addr, connected, + client_senddone, response_timeout, ®ion, &id, &dispentry); + assert_int_equal(result, ISC_R_SUCCESS); + + memset(message, 0, sizeof(message)); + message[0] = (id >> 8) & 0xff; + message[1] = id & 0xff; + + region.base = message; + region.length = sizeof(message); + + dns_dispatch_connect(dispentry); + + uv_sem_wait(&sem); + + assert_int_equal(atomic_load_acquire(&testdata.result), ISC_R_TIMEDOUT); + + isc_nm_stoplistening(sock); + isc_nmsocket_close(&sock); + assert_null(sock); + + dns_dispatch_removeresponse(&dispentry); + + dns_dispatch_detach(&dispatch); + dns_dispatchmgr_detach(&dispatchmgr); +} + +/* test dispatch getnext */ +static void +dispatch_getnext(void **state) { + isc_result_t result; + isc_region_t region; + isc_nmsocket_t *sock = NULL; + unsigned char message[12] = { 0 }; + unsigned char rbuf[12] = { 0 }; + uint16_t id; + + UNUSED(state); + + result = dns_dispatchmgr_create(dt_mctx, connect_nm, &dispatchmgr); + assert_int_equal(result, ISC_R_SUCCESS); + + result = dns_dispatch_createudp(dispatchmgr, &udp_connect_addr, + &dispatch); + assert_int_equal(result, ISC_R_SUCCESS); /* - * Shutdown the dispatch. + * Create a local udp nameserver on the loopback. */ + result = isc_nm_listenudp(netmgr, &udp_server_addr, nameserver, NULL, 0, + &sock); + assert_int_equal(result, ISC_R_SUCCESS); + + region.base = rbuf; + region.length = sizeof(rbuf); + result = dns_dispatch_addresponse( + dispatch, 0, T_CLIENT_CONNECT, &udp_server_addr, connected, + client_senddone, response_getnext, ®ion, &id, &dispentry); + assert_int_equal(result, ISC_R_SUCCESS); + + memset(message, 0, sizeof(message)); + message[0] = (id >> 8) & 0xff; + message[1] = id & 0xff; + + region.base = message; + region.length = sizeof(message); + + dns_dispatch_connect(dispentry); + + uv_sem_wait(&sem); + + assert_int_equal(atomic_load_acquire(&testdata.responses), 2); + + /* Cleanup */ + isc_nm_stoplistening(sock); + isc_nmsocket_close(&sock); + assert_null(sock); + + dns_dispatch_removeresponse(&dispentry); dns_dispatch_detach(&dispatch); dns_dispatchmgr_detach(&dispatchmgr); } @@ -399,6 +719,14 @@ dispatch_getnext(void **state) { int main(void) { const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(dispatch_timeout_tcp_connect, + _setup, _teardown), + cmocka_unit_test_setup_teardown(dispatch_timeout_tcp_response, + _setup, _teardown), + cmocka_unit_test_setup_teardown(dispatch_tcp_response, _setup, + _teardown), + cmocka_unit_test_setup_teardown(dispatch_timeout_udp_response, + _setup, _teardown), cmocka_unit_test_setup_teardown(dispatchset_create, _setup, _teardown), cmocka_unit_test_setup_teardown(dispatchset_get, _setup, diff --git a/lib/dns/tests/dnstest.c b/lib/dns/tests/dnstest.c index fac3f25725..990bc8ceb9 100644 --- a/lib/dns/tests/dnstest.c +++ b/lib/dns/tests/dnstest.c @@ -69,7 +69,6 @@ isc_nm_t *netmgr = NULL; isc_taskmgr_t *taskmgr = NULL; isc_task_t *maintask = NULL; isc_timermgr_t *timermgr = NULL; -isc_socketmgr_t *socketmgr = NULL; dns_zonemgr_t *zonemgr = NULL; bool app_running = false; int ncpus; @@ -100,8 +99,7 @@ cleanup_managers(void) { isc_managers_destroy(netmgr == NULL ? NULL : &netmgr, taskmgr == NULL ? NULL : &taskmgr, - timermgr == NULL ? NULL : &timermgr, - socketmgr == NULL ? NULL : &socketmgr); + timermgr == NULL ? NULL : &timermgr, NULL); if (app_running) { isc_app_finish(); @@ -114,7 +112,7 @@ create_managers(void) { ncpus = isc_os_ncpus(); isc_managers_create(dt_mctx, ncpus, 0, 0, &netmgr, &taskmgr, &timermgr, - &socketmgr); + NULL); CHECK(isc_task_create(taskmgr, 0, &maintask)); return (ISC_R_SUCCESS); @@ -296,8 +294,7 @@ dns_test_setupzonemgr(void) { isc_result_t result; REQUIRE(zonemgr == NULL); - result = dns_zonemgr_create(dt_mctx, taskmgr, timermgr, socketmgr, NULL, - &zonemgr); + result = dns_zonemgr_create(dt_mctx, taskmgr, timermgr, NULL, &zonemgr); return (result); } diff --git a/lib/dns/tests/dnstest.h b/lib/dns/tests/dnstest.h index a2c32cc146..adc88913c2 100644 --- a/lib/dns/tests/dnstest.h +++ b/lib/dns/tests/dnstest.h @@ -45,7 +45,6 @@ extern isc_log_t *lctx; extern isc_taskmgr_t *taskmgr; extern isc_task_t *maintask; extern isc_timermgr_t *timermgr; -extern isc_socketmgr_t *socketmgr; extern isc_nm_t *netmgr; extern dns_zonemgr_t *zonemgr; extern bool app_running; diff --git a/lib/dns/tests/resolver_test.c b/lib/dns/tests/resolver_test.c index 2c8b6f4e9a..866498e35e 100644 --- a/lib/dns/tests/resolver_test.c +++ b/lib/dns/tests/resolver_test.c @@ -57,8 +57,7 @@ _setup(void **state) { assert_int_equal(result, ISC_R_SUCCESS); isc_sockaddr_any(&local); - result = dns_dispatch_createudp(dispatchmgr, taskmgr, &local, 0, - &dispatch); + result = dns_dispatch_createudp(dispatchmgr, &local, &dispatch); assert_int_equal(result, ISC_R_SUCCESS); return (0); diff --git a/lib/dns/tests/zonemgr_test.c b/lib/dns/tests/zonemgr_test.c index ca7559e181..694401e871 100644 --- a/lib/dns/tests/zonemgr_test.c +++ b/lib/dns/tests/zonemgr_test.c @@ -62,7 +62,7 @@ zonemgr_create(void **state) { UNUSED(state); - result = dns_zonemgr_create(dt_mctx, taskmgr, timermgr, socketmgr, NULL, + result = dns_zonemgr_create(dt_mctx, taskmgr, timermgr, NULL, &myzonemgr); assert_int_equal(result, ISC_R_SUCCESS); @@ -80,7 +80,7 @@ zonemgr_managezone(void **state) { UNUSED(state); - result = dns_zonemgr_create(dt_mctx, taskmgr, timermgr, socketmgr, NULL, + result = dns_zonemgr_create(dt_mctx, taskmgr, timermgr, NULL, &myzonemgr); assert_int_equal(result, ISC_R_SUCCESS); @@ -121,7 +121,7 @@ zonemgr_createzone(void **state) { UNUSED(state); - result = dns_zonemgr_create(dt_mctx, taskmgr, timermgr, socketmgr, NULL, + result = dns_zonemgr_create(dt_mctx, taskmgr, timermgr, NULL, &myzonemgr); assert_int_equal(result, ISC_R_SUCCESS); @@ -160,7 +160,7 @@ zonemgr_unreachable(void **state) { TIME_NOW(&now); - result = dns_zonemgr_create(dt_mctx, taskmgr, timermgr, socketmgr, NULL, + result = dns_zonemgr_create(dt_mctx, taskmgr, timermgr, NULL, &myzonemgr); assert_int_equal(result, ISC_R_SUCCESS); diff --git a/lib/ns/tests/nstest.c b/lib/ns/tests/nstest.c index 8f49e53047..1df32364e1 100644 --- a/lib/ns/tests/nstest.c +++ b/lib/ns/tests/nstest.c @@ -442,8 +442,7 @@ ns_test_setupzonemgr(void) { isc_result_t result; REQUIRE(zonemgr == NULL); - result = dns_zonemgr_create(mctx, taskmgr, timermgr, socketmgr, NULL, - &zonemgr); + result = dns_zonemgr_create(mctx, taskmgr, timermgr, NULL, &zonemgr); return (result); }