dispatch: Clean up connect and recv callbacks

- disp_connected() has been split into two functions,
  udp_connected() (which takes 'resp' as an argument) and
  tcp_connected() (which takes 'disp', and calls the connect callbacks
  for all pending resps).

- In dns_dispatch_connect(), if a connection is already open, we need to
  detach the dispentry immediately because we won't be running
  tcp_connected().

- dns_disptach_cancel() also now calls the connect callbacks for pending
  TCP responses, and the response callbacks for open TCP connections
  waiting on read.

- If udp_connected() runs after dns_dispatch_cancel() has been called,
  ensure that the caller's connect callback is run.

- If a UDP connection fails with EADDRINUSE, we try again up to five
  times with a different local port number before giving up.

- If a TCP connection is canceled while still pending connection, the
  connect timeout may still fire. we attach the dispatch before
  connecting to ensure that it won't be detached too soon in this case.

- The dispentry is no longer removed from the pending list when
  deactivating, so that the connect callback can still be run if
  dns_dispatch_removeresponse() was run while the connecting was
  pending.

- Rewrote dns_dispatch_gettcp() to avoid a data race.

- startrecv() and dispatch_getnext() can be called with a NULL resp when
  using TCP.

- Refactored udp_recv() and tcp_recv() and added result logging.

- EOF is now treated the same as CANCELED in response callbacks.

- ISC_R_SHUTTINGDOWN is sent to the reponse callbacks for all resps if
  tcp_recv() is triggered by a netmgr shutdown.  (response callbacks
  are *not* sent by udp_recv() in this case.)
This commit is contained in:
Evan Hunt
2021-08-04 13:14:11 -07:00
parent 8551ad026f
commit 6ea7d59ad2
10 changed files with 705 additions and 297 deletions

View File

@@ -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) {

View File

@@ -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);
/*

View File

@@ -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. */

View File

@@ -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

View File

@@ -25,7 +25,6 @@
#define UNIT_TESTING
#include <cmocka.h>
#include <isc/app.h>
#include <isc/buffer.h>
#include <isc/managers.h>
#include <isc/refcount.h>
@@ -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, &region, &id, &dispentry);
result = dns_dispatch_addresponse(dispatch, 0, T_CLIENT_CONNECT,
&tcp_server_addr, timeout_connected,
client_senddone, response, &region,
&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, &region, &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, &region, &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, &region, &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, &region, &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,

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);
}