2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-30 22:15:20 +00:00

dispatch: Enforce original timeout when calling _getnext()

udp_recv() will call dispatch_getnext() if the message received is
invalid or doesn't match; we need to reduce the timeout each time this
happens so we can't be starved forever by someone sending garbage
packets.
This commit is contained in:
Evan Hunt
2021-10-01 12:53:31 -07:00
parent 6ea7d59ad2
commit f67f524405

View File

@@ -77,8 +77,9 @@ struct dns_dispentry {
dns_dispatch_t *disp;
isc_nmhandle_t *handle; /*%< netmgr handle for UDP connection */
unsigned int bucket;
unsigned int timeout;
unsigned int retries;
unsigned int timeout;
isc_time_t start;
isc_sockaddr_t local;
isc_sockaddr_t peer;
in_port_t port;
@@ -427,6 +428,23 @@ dispentry_detach(dns_dispentry_t **respp) {
}
}
/*
* How long in milliseconds has it been since this dispentry
* started reading? (Only used for UDP, to adjust the timeout
* downward when running getnext.)
*/
static unsigned int
dispentry_runtime(dns_dispentry_t *resp) {
isc_time_t now;
if (isc_time_isepoch(&resp->start)) {
return (0);
}
TIME_NOW(&now);
return (isc_time_microdiff(&now, &resp->start) / 1000);
}
/*
* General flow:
*
@@ -452,7 +470,7 @@ udp_recv(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
unsigned int flags;
isc_sockaddr_t peer;
isc_netaddr_t netaddr;
int match;
int match, timeout;
dispatch_cb_t response = NULL;
REQUIRE(VALID_RESPONSE(resp));
@@ -549,7 +567,13 @@ next:
* but keep listening.
*/
response = NULL;
dispatch_getnext(disp, resp, resp->timeout);
timeout = resp->timeout - dispentry_runtime(resp);
if (timeout <= 0) {
eresult = ISC_R_TIMEDOUT;
goto done;
}
dispatch_getnext(disp, resp, resp->timeout - dispentry_runtime(resp));
done:
UNLOCK(&disp->lock);
@@ -1402,12 +1426,6 @@ void
dispatch_getnext(dns_dispatch_t *disp, dns_dispentry_t *resp, int32_t timeout) {
REQUIRE(timeout <= UINT16_MAX);
/*
* FIXME: Since there's no global timeout now, any call to getnext will
* always restart the read timer, so it's possible to keep the client
* connecting until end of times by just feeding it with invalid
* packets.
*/
switch (disp->socktype) {
case isc_socktype_udp:
dispentry_attach(resp, &(dns_dispentry_t *){ NULL });
@@ -1434,6 +1452,7 @@ dispatch_getnext(dns_dispatch_t *disp, dns_dispentry_t *resp, int32_t timeout) {
isc_result_t
dns_dispatch_getnext(dns_dispentry_t *resp) {
dns_dispatch_t *disp = NULL;
int32_t timeout;
REQUIRE(VALID_RESPONSE(resp));
@@ -1441,8 +1460,17 @@ dns_dispatch_getnext(dns_dispentry_t *resp) {
REQUIRE(VALID_DISPATCH(disp));
if (disp->socktype == isc_socktype_udp) {
timeout = resp->timeout - dispentry_runtime(resp);
if (timeout <= 0) {
return (ISC_R_TIMEDOUT);
}
} else {
timeout = -1;
}
LOCK(&disp->lock);
dispatch_getnext(disp, resp, resp->timeout);
dispatch_getnext(disp, resp, timeout);
UNLOCK(&disp->lock);
return (ISC_R_SUCCESS);
}
@@ -1497,6 +1525,7 @@ startrecv(isc_nmhandle_t *handle, dns_dispatch_t *disp, dns_dispentry_t *resp) {
case isc_socktype_udp:
REQUIRE(resp != NULL && resp->handle == NULL);
TIME_NOW(&resp->start);
isc_nmhandle_attach(handle, &resp->handle);
dispentry_attach(resp, &(dns_dispentry_t *){ NULL });
isc_nm_read(resp->handle, udp_recv, resp);