]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: implement minimal EDNS0 support
authorTom Gundersen <teg@jklm.no>
Tue, 23 Jun 2015 21:06:09 +0000 (23:06 +0200)
committerTom Gundersen <teg@jklm.no>
Fri, 27 Nov 2015 00:35:34 +0000 (01:35 +0100)
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
src/resolve/resolved-dns-packet.h
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-scope.h
src/resolve/resolved-dns-server.c
src/resolve/resolved-dns-server.h
src/resolve/resolved-dns-transaction.c

index 49c36e38f380c64a47251413d60533ae699ceefc..cb713f1f8590c1493a54f19148c2183fd1b5cc9b 100644 (file)
@@ -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;
index ca94a208baf0b6ea8d9a186b2827198ee37fc6f1..385a8af7968aecb59acb9871b34b43740d53ba4e 100644 (file)
@@ -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);
index a8c0ae15699cfd0d92cef6e161ec691a36b1ddbf..42478e41e205088a0ae096118a8c5489748c1aac 100644 (file)
@@ -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");
         }
index 7876410b7d6be7cbcd7449efefbac16bc0b7645c..0480f702f855855401d83e3c28b38cc66ba66a45 100644 (file)
@@ -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);
 
index c5396a03c82db32bebe2213a41cec921826f69e4..f8c921e4c8566a3e79da3fee71543047b37a60ba 100644 (file)
@@ -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);
index a3e8cbcc52c5ebd789ff5047e2d41657d5c4c326..e9b425430fc399b61f7ca274726b7864d0393877 100644 (file)
@@ -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;
index 4398c2cb9908cf43dc3fe256115d6aae97b8be06..f81461a4fea11813f8ec28a1a6d90d6e26ac9b2e 100644 (file)
@@ -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;