mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-09-04 00:25:29 +00:00
Refactor tcp_recv()
The tcp_recv() function used lot of gotos that made the function hard to read. Refactor the function by splitting it into smaller logical chunks.
This commit is contained in:
@@ -595,6 +595,117 @@ done:
|
|||||||
dispentry_detach(&resp);
|
dispentry_detach(&resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static isc_result_t
|
||||||
|
tcp_recv_success(dns_dispatch_t *disp, isc_region_t *region, dns_qid_t *qid,
|
||||||
|
isc_sockaddr_t *peer, dns_dispentry_t **respp) {
|
||||||
|
isc_buffer_t source;
|
||||||
|
dns_messageid_t id;
|
||||||
|
unsigned int flags;
|
||||||
|
unsigned int bucket;
|
||||||
|
isc_result_t result = ISC_R_SUCCESS;
|
||||||
|
dns_dispentry_t *resp = NULL;
|
||||||
|
|
||||||
|
dispatch_log(disp, LVL(90), "success, length == %d, addr = %p",
|
||||||
|
region->length, region->base);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Peek into the buffer to see what we can see.
|
||||||
|
*/
|
||||||
|
isc_buffer_init(&source, region->base, region->length);
|
||||||
|
isc_buffer_add(&source, region->length);
|
||||||
|
result = dns_message_peekheader(&source, &id, &flags);
|
||||||
|
if (result != ISC_R_SUCCESS) {
|
||||||
|
dispatch_log(disp, LVL(10), "got garbage packet");
|
||||||
|
result = ISC_R_UNEXPECTED;
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch_log(disp, LVL(92),
|
||||||
|
"got valid DNS message header, /QR %c, id %u",
|
||||||
|
(((flags & DNS_MESSAGEFLAG_QR) != 0) ? '1' : '0'), id);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look at the message flags. If it's a query, ignore it and keep
|
||||||
|
* reading.
|
||||||
|
*/
|
||||||
|
if ((flags & DNS_MESSAGEFLAG_QR) == 0) {
|
||||||
|
dispatch_log(disp, LVL(10), "got DNS query instead of answer");
|
||||||
|
result = ISC_R_UNEXPECTED;
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 });
|
||||||
|
*respp = resp;
|
||||||
|
} else {
|
||||||
|
result = ISC_R_NOTFOUND;
|
||||||
|
}
|
||||||
|
dispatch_log(disp, LVL(90), "search for response in bucket %d: %s",
|
||||||
|
bucket, isc_result_totext(result));
|
||||||
|
UNLOCK(&qid->lock);
|
||||||
|
|
||||||
|
next:
|
||||||
|
dispatch_getnext(disp, NULL, -1);
|
||||||
|
|
||||||
|
return (result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static isc_result_t
|
||||||
|
tcp_recv_timeout(dns_dispatch_t *disp, dns_dispentry_t **respp) {
|
||||||
|
dns_dispentry_t *resp = ISC_LIST_HEAD(disp->active);
|
||||||
|
if (resp != NULL) {
|
||||||
|
dispentry_attach(resp, &(dns_dispentry_t *){ NULL });
|
||||||
|
ISC_LIST_UNLINK(disp->active, resp, alink);
|
||||||
|
ISC_LIST_APPEND(disp->active, resp, alink);
|
||||||
|
|
||||||
|
*respp = resp;
|
||||||
|
return (ISC_R_TIMEDOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ISC_R_NOTFOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
tcp_recv_shutdown(dns_dispatch_t *disp, dns_displist_t *resps) {
|
||||||
|
dns_dispentry_t *resp = NULL, *next = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
tcp_recv_done(dns_dispentry_t *resp, isc_result_t eresult,
|
||||||
|
isc_region_t *region) {
|
||||||
|
resp->response(eresult, region, resp->arg);
|
||||||
|
dispentry_detach(&resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
tcp_recv_cancelall(dns_displist_t *resps, isc_region_t *region) {
|
||||||
|
dns_dispentry_t *resp = NULL, *next = NULL;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* General flow:
|
* General flow:
|
||||||
*
|
*
|
||||||
@@ -611,18 +722,12 @@ done:
|
|||||||
* restart.
|
* restart.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
tcp_recv(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
|
tcp_recv(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region,
|
||||||
void *arg) {
|
void *arg) {
|
||||||
dns_dispatch_t *disp = (dns_dispatch_t *)arg;
|
dns_dispatch_t *disp = (dns_dispatch_t *)arg;
|
||||||
dns_dispentry_t *resp = NULL, *next = NULL;
|
dns_dispentry_t *resp = NULL;
|
||||||
dns_messageid_t id;
|
|
||||||
isc_result_t dres;
|
|
||||||
unsigned int flags;
|
|
||||||
unsigned int bucket;
|
|
||||||
dns_qid_t *qid = NULL;
|
dns_qid_t *qid = NULL;
|
||||||
int level;
|
|
||||||
char buf[ISC_SOCKADDR_FORMATSIZE];
|
char buf[ISC_SOCKADDR_FORMATSIZE];
|
||||||
isc_buffer_t source;
|
|
||||||
isc_sockaddr_t peer;
|
isc_sockaddr_t peer;
|
||||||
dns_displist_t resps;
|
dns_displist_t resps;
|
||||||
|
|
||||||
@@ -630,29 +735,26 @@ tcp_recv(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
|
|||||||
|
|
||||||
qid = disp->mgr->qid;
|
qid = disp->mgr->qid;
|
||||||
|
|
||||||
|
ISC_LIST_INIT(resps);
|
||||||
|
|
||||||
LOCK(&disp->lock);
|
LOCK(&disp->lock);
|
||||||
|
|
||||||
dispatch_log(disp, LVL(90), "TCP read:%s:requests %d, buffers %d",
|
dispatch_log(disp, LVL(90), "TCP read:%s:requests %d, buffers %d",
|
||||||
isc_result_totext(eresult), disp->requests,
|
isc_result_totext(result), disp->requests,
|
||||||
disp->tcpbuffers);
|
disp->tcpbuffers);
|
||||||
|
|
||||||
peer = isc_nmhandle_peeraddr(handle);
|
peer = isc_nmhandle_peeraddr(handle);
|
||||||
ISC_LIST_INIT(resps);
|
|
||||||
|
|
||||||
switch (eresult) {
|
|
||||||
case ISC_R_SUCCESS:
|
|
||||||
/* got our answer */
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
switch (result) {
|
||||||
case ISC_R_SHUTTINGDOWN:
|
case ISC_R_SHUTTINGDOWN:
|
||||||
case ISC_R_CANCELED:
|
case ISC_R_CANCELED:
|
||||||
case ISC_R_EOF:
|
case ISC_R_EOF:
|
||||||
dispatch_log(disp, LVL(90), "shutting down: %s",
|
case ISC_R_CONNECTIONRESET:
|
||||||
isc_result_totext(eresult));
|
isc_sockaddr_format(&peer, buf, sizeof(buf));
|
||||||
/*
|
dispatch_log(disp, LVL(90), "shutting down TCP: %s: %s", buf,
|
||||||
* If there are any active responses, shut them all down.
|
isc_result_totext(result));
|
||||||
*/
|
tcp_recv_shutdown(disp, &resps);
|
||||||
goto shutdown;
|
break;
|
||||||
|
|
||||||
case ISC_R_TIMEDOUT:
|
case ISC_R_TIMEDOUT:
|
||||||
/*
|
/*
|
||||||
@@ -661,108 +763,53 @@ tcp_recv(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
|
|||||||
* active queue immediately, though, because the callback
|
* active queue immediately, though, because the callback
|
||||||
* might decide to keep waiting and leave it active.)
|
* might decide to keep waiting and leave it active.)
|
||||||
*/
|
*/
|
||||||
resp = ISC_LIST_HEAD(disp->active);
|
result = tcp_recv_timeout(disp, &resp);
|
||||||
if (resp != NULL) {
|
break;
|
||||||
dispentry_attach(resp, &(dns_dispentry_t *){ NULL });
|
|
||||||
ISC_LIST_UNLINK(disp->active, resp, alink);
|
|
||||||
ISC_LIST_APPEND(disp->active, resp, alink);
|
|
||||||
}
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
|
case ISC_R_SUCCESS:
|
||||||
|
/* We got an answer */
|
||||||
|
result = tcp_recv_success(disp, region, qid, &peer, &resp);
|
||||||
|
if (result == ISC_R_SUCCESS || result == ISC_R_NOTFOUND) {
|
||||||
|
/*
|
||||||
|
* It's a valid DNS response, which may or may not
|
||||||
|
* have matched an outstanding query.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Got an invalid DNS response, terminate the connection */
|
||||||
|
/* FALLTHROUGH */
|
||||||
default:
|
default:
|
||||||
if (eresult == ISC_R_CONNECTIONRESET) {
|
|
||||||
level = ISC_LOG_INFO;
|
|
||||||
} else {
|
|
||||||
level = ISC_LOG_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
isc_sockaddr_format(&peer, buf, sizeof(buf));
|
isc_sockaddr_format(&peer, buf, sizeof(buf));
|
||||||
dispatch_log(disp, level,
|
dispatch_log(disp, ISC_LOG_ERROR,
|
||||||
"shutting down due to TCP "
|
"shutting down due to TCP "
|
||||||
"receive error: %s: %s",
|
"receive error: %s: %s",
|
||||||
buf, isc_result_totext(eresult));
|
buf, isc_result_totext(result));
|
||||||
/*
|
tcp_recv_shutdown(disp, &resps);
|
||||||
* If there are any active responses, shut them all down.
|
break;
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch_log(disp, LVL(90), "success, length == %d, addr = %p",
|
|
||||||
region->length, region->base);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Peek into the buffer to see what we can see.
|
|
||||||
*/
|
|
||||||
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");
|
|
||||||
eresult = ISC_R_UNEXPECTED;
|
|
||||||
goto shutdown;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch_log(disp, LVL(92),
|
|
||||||
"got valid DNS message header, /QR %c, id %u",
|
|
||||||
(((flags & DNS_MESSAGEFLAG_QR) != 0) ? '1' : '0'), id);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Look at the message flags. If it's a query, stop reading.
|
|
||||||
*/
|
|
||||||
if ((flags & DNS_MESSAGEFLAG_QR) == 0) {
|
|
||||||
dispatch_log(disp, LVL(10), "got DNS query instead of answer");
|
|
||||||
eresult = ISC_R_UNEXPECTED;
|
|
||||||
goto shutdown;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
dispatch_getnext(disp, NULL, -1);
|
|
||||||
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
shutdown:
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
|
||||||
UNLOCK(&disp->lock);
|
UNLOCK(&disp->lock);
|
||||||
|
|
||||||
if (resp != NULL) {
|
switch (result) {
|
||||||
/* We got a matching response, or timed out */
|
case ISC_R_SUCCESS:
|
||||||
resp->response(eresult, region, resp->arg);
|
case ISC_R_TIMEDOUT:
|
||||||
dispentry_detach(&resp);
|
/*
|
||||||
} else {
|
* Either we found a matching response, or we timed out
|
||||||
/* We're being shut down; cancel all outstanding resps */
|
* and canceled the oldest resp.
|
||||||
for (resp = ISC_LIST_HEAD(resps); resp != NULL; resp = next) {
|
*/
|
||||||
next = ISC_LIST_NEXT(resp, rlink);
|
INSIST(resp != NULL);
|
||||||
ISC_LIST_UNLINK(resps, resp, rlink);
|
tcp_recv_done(resp, result, region);
|
||||||
resp->response(ISC_R_SHUTTINGDOWN, region, resp->arg);
|
break;
|
||||||
dispentry_detach(&resp);
|
case ISC_R_NOTFOUND:
|
||||||
}
|
/*
|
||||||
|
* Either we got a response that didn't match any active
|
||||||
|
* resps, or we timed out but there *were* no active resps.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* We're being shut down; cancel all outstanding resps. */
|
||||||
|
tcp_recv_cancelall(&resps, region);
|
||||||
}
|
}
|
||||||
|
|
||||||
dns_dispatch_detach(&disp);
|
dns_dispatch_detach(&disp);
|
||||||
|
Reference in New Issue
Block a user