diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 49c36e38f3..cb713f1f85 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 ca94a208ba..385a8af796 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 a8c0ae1569..42478e41e2 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 7876410b7d..0480f702f8 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 c5396a03c8..f8c921e4c8 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 a3e8cbcc52..e9b425430f 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 4398c2cb99..f81461a4fe 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;