Handle UDP send errors when sending DNS message larger than MTU

When the fragmentation is disabled on UDP sockets, the uv_udp_send()
call can fail with UV_EMSGSIZE for messages larger than path MTU.
Previously, this error would end with just discarding the response.  In
this commit, a proper handling of such case is added and on such error,
a new DNS response with truncated bit set is generated and sent to the
client.

This change allows us to disable the fragmentation on the UDP
sockets again.
This commit is contained in:
Evan Hunt
2021-06-22 17:01:35 +02:00
committed by Ondřej Surý
parent e7e48414e0
commit a3ba95116e
2 changed files with 36 additions and 5 deletions

View File

@@ -85,6 +85,8 @@ isc___nm_uverr2result(int uverr, bool dolog, const char *file,
return (ISC_R_CANCELED);
case UV_EOF:
return (ISC_R_EOF);
case UV_EMSGSIZE:
return (ISC_R_MAXSIZE);
default:
if (dolog) {
UNEXPECTED_ERROR(

View File

@@ -257,13 +257,33 @@ client_senddone(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
REQUIRE(client->sendhandle == handle);
CTRACE("senddone");
/*
* Set sendhandle to NULL, but don't detach it immediately, in
* case we need to retry the send. If we do resend, then
* sendhandle will be reattached. Whether or not we resend,
* we will then detach the handle from *this* send by detaching
* 'handle' directly below.
*/
client->sendhandle = NULL;
if (result != ISC_R_SUCCESS) {
ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
"send failed: %s", isc_result_totext(result));
if (!TCP_CLIENT(client) && result == ISC_R_MAXSIZE) {
ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
"send exceeded maximum size: truncating");
client->query.attributes &= ~NS_QUERYATTR_ANSWERED;
client->rcode_override = dns_rcode_noerror;
ns_client_error(client, ISC_R_MAXSIZE);
} else {
ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
"send failed: %s",
isc_result_totext(result));
}
}
isc_nmhandle_detach(&client->sendhandle);
isc_nmhandle_detach(&handle);
}
static void
@@ -699,8 +719,9 @@ ns_client_dropport(in_port_t port) {
void
ns_client_error(ns_client_t *client, isc_result_t result) {
dns_message_t *message = NULL;
dns_rcode_t rcode;
dns_message_t *message;
bool trunc = false;
REQUIRE(NS_CLIENT_VALID(client));
@@ -714,6 +735,10 @@ ns_client_error(ns_client_t *client, isc_result_t result) {
rcode = (dns_rcode_t)(client->rcode_override & 0xfff);
}
if (result == ISC_R_MAXSIZE) {
trunc = true;
}
#if NS_CLIENT_DROPPORT
/*
* Don't send FORMERR to ports on the drop port list.
@@ -810,7 +835,11 @@ ns_client_error(ns_client_t *client, isc_result_t result) {
return;
}
}
message->rcode = rcode;
if (trunc) {
message->flags |= DNS_MESSAGEFLAG_TC;
}
if (rcode == dns_rcode_formerr) {
/*