summaryrefslogtreecommitdiff
path: root/src/resolve/resolved-dns-transaction.c
diff options
context:
space:
mode:
authorTom Gundersen <teg@jklm.no>2015-07-06 08:15:25 +0200
committerTom Gundersen <teg@jklm.no>2015-11-27 01:35:33 +0100
commitbe808ea083fa07271116b4519c3c27fd20c5f077 (patch)
treeda9ed99807850c11ee7c6414c3041aa117586679 /src/resolve/resolved-dns-transaction.c
parent90c739259fc35feb773b953d61f75790cacf6b15 (diff)
resolved: fallback to TCP if UDP fails
This is inspired by the logic in BIND [0], follow-up patches will implement the reset of that scheme. If we get a server error back, or if after several attempts we don't get a reply at all, we switch from UDP to TCP for the given server for the current and all subsequent requests. However, if we ever successfully received a reply over UDP, we never fall back to TCP, and once a grace-period has passed, we try to upgrade again to using UDP. The grace-period starts off at five minutes after the current feature level was verified and then grows exponentially to six hours. This is to mitigate problems due to temporary lack of network connectivity, but at the same time avoid flooding the network with retries when the feature attempted feature level genuinely does not work. Note that UDP is likely much more commonly supported than TCP, but depending on the path between the client and the server, we may have more luck with TCP in case something is wrong. We really do prefer UDP though, as that is much more lightweight, that is why TCP is only the last resort. [0]: <https://kb.isc.org/article/AA-01219/0/Refinements-to-EDNS-fallback-behavior-can-cause-different-outcomes-in-Recursive-Servers.html>
Diffstat (limited to 'src/resolve/resolved-dns-transaction.c')
-rw-r--r--src/resolve/resolved-dns-transaction.c34
1 files changed, 25 insertions, 9 deletions
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 8c4f23a4da..0e09a339aa 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -418,7 +418,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
case DNS_PROTOCOL_DNS:
assert(t->server);
- dns_server_packet_received(t->server, ts - t->start_usec);
+ dns_server_packet_received(t->server, t->current_features, ts - t->start_usec);
break;
case DNS_PROTOCOL_LLMNR:
@@ -534,6 +534,9 @@ static int dns_transaction_emit(DnsTransaction *t) {
if (r < 0)
return r;
+ if (t->server)
+ t->current_features = t->server->possible_features;
+
return 0;
}
@@ -544,15 +547,26 @@ static int on_transaction_timeout(sd_event_source *s, usec_t usec, void *userdat
assert(s);
assert(t);
- /* Timeout reached? Try again, with a new server */
- dns_transaction_next_dns_server(t);
+ /* Timeout reached? Increase the timeout for the server used */
+ switch (t->scope->protocol) {
+ case DNS_PROTOCOL_DNS:
+ assert(t->server);
- /* ... and possibly increased timeout */
- if (t->server)
- dns_server_packet_lost(t->server, usec - t->start_usec);
- else
+ dns_server_packet_lost(t->server, t->current_features, usec - t->start_usec);
+
+ break;
+ case DNS_PROTOCOL_LLMNR:
+ case DNS_PROTOCOL_MDNS:
dns_scope_packet_lost(t->scope, usec - t->start_usec);
+ break;
+ default:
+ assert_not_reached("Invalid DNS protocol.");
+ }
+
+ /* ...and try again with a new server */
+ dns_transaction_next_dns_server(t);
+
r = dns_transaction_go(t);
if (r < 0)
dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
@@ -734,11 +748,13 @@ int dns_transaction_go(DnsTransaction *t) {
* always be made via TCP on LLMNR */
r = dns_transaction_open_tcp(t);
} else {
- /* Try via UDP, and if that fails due to large size try via TCP */
+ /* Try via UDP, and if that fails due to large size or lack of
+ * support try via TCP */
r = dns_transaction_emit(t);
- if (r == -EMSGSIZE)
+ if (r == -EMSGSIZE || r == -EAGAIN)
r = dns_transaction_open_tcp(t);
}
+
if (r == -ESRCH) {
/* No servers to send this to? */
dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);