From 9c5e12a4314e7192e834e1b855e5e80111e636a6 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Tue, 23 Jun 2015 23:06:09 +0200 Subject: [PATCH] resolved: implement minimal EDNS0 support This is a minimal implementation of RFC6891. Only default values are used, so in reality this will be a noop. EDNS0 support is dependent on the current server's feature level, so appending the OPT pseudo RR is done when the packet is emitted, rather than when it is assembled. To handle different feature levels on retransmission, we strip off the OPT RR again after sending the packet. Similarly, to how we fall back to TCP if UDP fails, we fall back to plain UDP if EDNS0 fails (but if EDNS0 ever succeeded we never fall back again, and after a timeout we will retry EDNS0). --- src/resolve/resolved-dns-packet.c | 2 +- src/resolve/resolved-dns-packet.h | 2 ++ src/resolve/resolved-dns-scope.c | 21 +++++++++++++++++++-- src/resolve/resolved-dns-scope.h | 2 +- src/resolve/resolved-dns-server.c | 1 + src/resolve/resolved-dns-server.h | 1 + src/resolve/resolved-dns-transaction.c | 2 +- 7 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 49c36e3..cb713f1f 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -267,7 +267,7 @@ static int dns_packet_extend(DnsPacket *p, size_t add, void **ret, size_t *start return 0; } -static void dns_packet_truncate(DnsPacket *p, size_t sz) { +void dns_packet_truncate(DnsPacket *p, size_t sz) { Iterator i; char *s; void *n; diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index ca94a20..385a8af 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -162,6 +162,8 @@ int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *key, size_t *start int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start); int dns_packet_append_opt_rr(DnsPacket *p, uint16_t max_udp_size, size_t *start); +void dns_packet_truncate(DnsPacket *p, size_t sz); + int dns_packet_read(DnsPacket *p, size_t sz, const void **ret, size_t *start); int dns_packet_read_blob(DnsPacket *p, void *d, size_t sz, size_t *start); int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start); diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index a8c0ae1..42478e4 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -158,12 +158,13 @@ void dns_scope_packet_lost(DnsScope *s, usec_t usec) { s->resend_timeout = MIN(s->resend_timeout * 2, MULTICAST_RESEND_TIMEOUT_MAX_USEC); } -int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) { +int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) { union in_addr_union addr; int ifindex = 0, r; int family; uint16_t port; uint32_t mtu; + size_t saved_size = 0; assert(s); assert(p); @@ -178,9 +179,19 @@ int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) { switch (s->protocol) { case DNS_PROTOCOL_DNS: + assert(server); + if (DNS_PACKET_QDCOUNT(p) > 1) return -EOPNOTSUPP; + if (server->possible_features >= DNS_SERVER_FEATURE_LEVEL_EDNS0) { + r = dns_packet_append_opt_rr(p, DNS_PACKET_UNICAST_SIZE_MAX, &saved_size); + if (r < 0) + return r; + + DNS_PACKET_HEADER(p)->arcount = htobe16(be16toh(DNS_PACKET_HEADER(p)->arcount) + 1); + } + if (p->size > DNS_PACKET_UNICAST_SIZE_MAX) return -EMSGSIZE; @@ -191,6 +202,12 @@ int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) { if (r < 0) return r; + if (saved_size > 0) { + dns_packet_truncate(p, saved_size); + + DNS_PACKET_HEADER(p)->arcount = htobe16(be16toh(DNS_PACKET_HEADER(p)->arcount) - 1); + } + break; case DNS_PROTOCOL_LLMNR: @@ -739,7 +756,7 @@ static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata return 0; } - r = dns_scope_emit(scope, -1, p); + r = dns_scope_emit(scope, -1, NULL, p); if (r < 0) log_debug_errno(r, "Failed to send conflict packet: %m"); } diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index 7876410..0480f70 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -80,7 +80,7 @@ DnsScope* dns_scope_free(DnsScope *s); void dns_scope_packet_received(DnsScope *s, usec_t rtt); void dns_scope_packet_lost(DnsScope *s, usec_t usec); -int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p); +int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p); int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server); int dns_scope_udp_dns_socket(DnsScope *s, DnsServer **server); diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index c5396a0..f8c921e 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -478,5 +478,6 @@ void manager_next_dns_server(Manager *m) { static const char* const dns_server_feature_level_table[_DNS_SERVER_FEATURE_LEVEL_MAX] = { [DNS_SERVER_FEATURE_LEVEL_TCP] = "TCP", [DNS_SERVER_FEATURE_LEVEL_UDP] = "UDP", + [DNS_SERVER_FEATURE_LEVEL_EDNS0] = "UDP+EDNS0", }; DEFINE_STRING_TABLE_LOOKUP(dns_server_feature_level, DnsServerFeatureLevel); diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h index a3e8cbc..e9b4254 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -34,6 +34,7 @@ typedef enum DnsServerType { typedef enum DnsServerFeatureLevel { DNS_SERVER_FEATURE_LEVEL_TCP, DNS_SERVER_FEATURE_LEVEL_UDP, + DNS_SERVER_FEATURE_LEVEL_EDNS0, _DNS_SERVER_FEATURE_LEVEL_MAX, _DNS_SERVER_FEATURE_LEVEL_INVALID = -1 } DnsServerFeatureLevel; diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 4398c2c..f81461a 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -545,7 +545,7 @@ static int dns_transaction_emit(DnsTransaction *t) { t->server = dns_server_ref(server); } - r = dns_scope_emit(t->scope, t->dns_udp_fd, t->sent); + r = dns_scope_emit(t->scope, t->dns_udp_fd, t->server, t->sent); if (r < 0) return r; -- 2.7.4