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).
-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;
Iterator i;
char *s;
void *n;
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);
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);
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);
s->resend_timeout = MIN(s->resend_timeout * 2, MULTICAST_RESEND_TIMEOUT_MAX_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;
union in_addr_union addr;
int ifindex = 0, r;
int family;
uint16_t port;
uint32_t mtu;
switch (s->protocol) {
case DNS_PROTOCOL_DNS:
switch (s->protocol) {
case DNS_PROTOCOL_DNS:
if (DNS_PACKET_QDCOUNT(p) > 1)
return -EOPNOTSUPP;
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;
if (p->size > DNS_PACKET_UNICAST_SIZE_MAX)
return -EMSGSIZE;
+ 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:
break;
case DNS_PROTOCOL_LLMNR:
- 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");
}
if (r < 0)
log_debug_errno(r, "Failed to send conflict packet: %m");
}
void dns_scope_packet_received(DnsScope *s, usec_t rtt);
void dns_scope_packet_lost(DnsScope *s, usec_t usec);
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);
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);
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",
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);
};
DEFINE_STRING_TABLE_LOOKUP(dns_server_feature_level, DnsServerFeatureLevel);
typedef enum DnsServerFeatureLevel {
DNS_SERVER_FEATURE_LEVEL_TCP,
DNS_SERVER_FEATURE_LEVEL_UDP,
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;
_DNS_SERVER_FEATURE_LEVEL_MAX,
_DNS_SERVER_FEATURE_LEVEL_INVALID = -1
} DnsServerFeatureLevel;
t->server = dns_server_ref(server);
}
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);