-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
s->family = family;
s->resend_timeout = MULTICAST_RESEND_TIMEOUT_MIN_USEC;
+ if (protocol == DNS_PROTOCOL_DNS) {
+ /* Copy DNSSEC mode from the link if it is set there,
+ * otherwise take the manager's DNSSEC mode. Note that
+ * we copy this only at scope creation time, and do
+ * not update it from the on, even if the setting
+ * changes. */
+
+ if (l)
+ s->dnssec_mode = link_get_dnssec_mode(l);
+ else
+ s->dnssec_mode = manager_get_dnssec_mode(m);
+ } else
+ s->dnssec_mode = DNSSEC_NO;
+
LIST_PREPEND(scopes, m->dns_scopes, s);
dns_scope_llmnr_membership(s, true);
s->resend_timeout = MIN(s->resend_timeout * 2, MULTICAST_RESEND_TIMEOUT_MAX_USEC);
}
-static int dns_scope_emit_one(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) {
+static int dns_scope_emit_one(DnsScope *s, int fd, DnsPacket *p) {
union in_addr_union addr;
int ifindex = 0, r;
int family;
uint32_t mtu;
- size_t saved_size = 0;
assert(s);
assert(p);
assert(p->protocol == s->protocol);
- assert((s->protocol == DNS_PROTOCOL_DNS) != (fd < 0));
if (s->link) {
mtu = s->link->mtu;
mtu = manager_find_mtu(s->manager);
switch (s->protocol) {
+
case DNS_PROTOCOL_DNS:
- assert(server);
+ assert(fd >= 0);
if (DNS_PACKET_QDCOUNT(p) > 1)
return -EOPNOTSUPP;
- if (server->possible_features >= DNS_SERVER_FEATURE_LEVEL_EDNS0) {
- bool edns_do;
- size_t packet_size;
-
- edns_do = server->possible_features >= DNS_SERVER_FEATURE_LEVEL_DO;
-
- if (server->possible_features >= DNS_SERVER_FEATURE_LEVEL_LARGE)
- packet_size = DNS_PACKET_UNICAST_SIZE_LARGE_MAX;
- else
- packet_size = server->received_udp_packet_max;
-
- r = dns_packet_append_opt_rr(p, packet_size, edns_do, &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 (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:
+ assert(fd < 0);
+
if (DNS_PACKET_QDCOUNT(p) > 1)
return -EOPNOTSUPP;
if (fd < 0)
return fd;
- r = manager_send(s->manager, fd, ifindex, family, &addr, LLMNR_PORT, p);
+ r = manager_send(s->manager, fd, ifindex, family, &addr, LLMNR_PORT, NULL, p);
if (r < 0)
return r;
break;
case DNS_PROTOCOL_MDNS:
+ assert(fd < 0);
+
if (!ratelimit_test(&s->ratelimit))
return -EBUSY;
if (fd < 0)
return fd;
- r = manager_send(s->manager, fd, ifindex, family, &addr, MDNS_PORT, p);
+ r = manager_send(s->manager, fd, ifindex, family, &addr, MDNS_PORT, NULL, p);
if (r < 0)
return r;
return 1;
}
-int dns_scope_emit(DnsScope *s, int fd, DnsServer *server, DnsPacket *p) {
+int dns_scope_emit_udp(DnsScope *s, int fd, DnsPacket *p) {
int r;
assert(s);
assert(p);
assert(p->protocol == s->protocol);
- assert((s->protocol == DNS_PROTOCOL_DNS) != (fd < 0));
+ assert((s->protocol == DNS_PROTOCOL_DNS) == (fd >= 0));
do {
/* If there are multiple linked packets, set the TC bit in all but the last of them */
dns_packet_set_flags(p, true, true);
}
- r = dns_scope_emit_one(s, fd, server, p);
+ r = dns_scope_emit_one(s, fd, p);
if (r < 0)
return r;
p = p->more;
- } while(p);
+ } while (p);
return 0;
}
-static int dns_scope_socket(DnsScope *s, int type, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) {
- DnsServer *srv = NULL;
+static int dns_scope_socket(
+ DnsScope *s,
+ int type,
+ int family,
+ const union in_addr_union *address,
+ DnsServer *server,
+ uint16_t port) {
+
_cleanup_close_ int fd = -1;
union sockaddr_union sa = {};
socklen_t salen;
static const int one = 1;
- int ret, r;
+ int ret, r, ifindex;
assert(s);
- assert((family == AF_UNSPEC) == !address);
- if (family == AF_UNSPEC) {
- srv = dns_scope_get_dns_server(s);
- if (!srv)
- return -ESRCH;
+ if (server) {
+ assert(family == AF_UNSPEC);
+ assert(!address);
- srv->possible_features = dns_server_possible_features(srv);
+ ifindex = dns_server_ifindex(server);
- if (type == SOCK_DGRAM && srv->possible_features < DNS_SERVER_FEATURE_LEVEL_UDP)
- return -EAGAIN;
-
- sa.sa.sa_family = srv->family;
- if (srv->family == AF_INET) {
+ sa.sa.sa_family = server->family;
+ if (server->family == AF_INET) {
sa.in.sin_port = htobe16(port);
- sa.in.sin_addr = srv->address.in;
+ sa.in.sin_addr = server->address.in;
salen = sizeof(sa.in);
- } else if (srv->family == AF_INET6) {
+ } else if (server->family == AF_INET6) {
sa.in6.sin6_port = htobe16(port);
- sa.in6.sin6_addr = srv->address.in6;
- sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
+ sa.in6.sin6_addr = server->address.in6;
+ sa.in6.sin6_scope_id = ifindex;
salen = sizeof(sa.in6);
} else
return -EAFNOSUPPORT;
} else {
+ assert(family != AF_UNSPEC);
+ assert(address);
+
sa.sa.sa_family = family;
+ ifindex = s->link ? s->link->ifindex : 0;
if (family == AF_INET) {
sa.in.sin_port = htobe16(port);
} else if (family == AF_INET6) {
sa.in6.sin6_port = htobe16(port);
sa.in6.sin6_addr = address->in6;
- sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
+ sa.in6.sin6_scope_id = ifindex;
salen = sizeof(sa.in6);
} else
return -EAFNOSUPPORT;
}
if (s->link) {
- uint32_t ifindex = htobe32(s->link->ifindex);
+ be32_t ifindex_be = htobe32(ifindex);
if (sa.sa.sa_family == AF_INET) {
- r = setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex));
+ r = setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_be, sizeof(ifindex_be));
if (r < 0)
return -errno;
} else if (sa.sa.sa_family == AF_INET6) {
- r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex));
+ r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_be, sizeof(ifindex_be));
if (r < 0)
return -errno;
}
if (r < 0 && errno != EINPROGRESS)
return -errno;
- if (server)
- *server = srv;
-
ret = fd;
fd = -1;
return ret;
}
-int dns_scope_udp_dns_socket(DnsScope *s, DnsServer **server) {
- return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, 53, server);
+int dns_scope_socket_udp(DnsScope *s, DnsServer *server, uint16_t port) {
+ return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, server, port);
}
-int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port, DnsServer **server) {
- return dns_scope_socket(s, SOCK_STREAM, family, address, port, server);
+int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port) {
+ return dns_scope_socket(s, SOCK_STREAM, family, address, server, port);
}
DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) {
}
}
-int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
+bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key) {
+ int key_family;
+
assert(s);
assert(key);
* this scope. Note that this call assumes as fully qualified
* name, i.e. the search suffixes already appended. */
+ if (key->class != DNS_CLASS_IN)
+ return false;
+
if (s->protocol == DNS_PROTOCOL_DNS) {
/* On classic DNS, looking up non-address RRs is always
* that those should be resolved via LLMNR or search
* path only, and should not be leaked onto the
* internet. */
- return !(dns_name_is_single_label(DNS_RESOURCE_KEY_NAME(key)) ||
- dns_name_is_root(DNS_RESOURCE_KEY_NAME(key)));
+ return !(dns_name_is_single_label(dns_resource_key_name(key)) ||
+ dns_name_is_root(dns_resource_key_name(key)));
}
/* On mDNS and LLMNR, send A and AAAA queries only on the
* respective scopes */
- if (s->family == AF_INET && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_AAAA)
- return false;
-
- if (s->family == AF_INET6 && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_A)
- return false;
+ key_family = dns_type_to_af(key->type);
+ if (key_family < 0)
+ return true;
- return true;
+ return key_family == s->family;
}
static int dns_scope_multicast_membership(DnsScope *s, bool b, struct in_addr in, struct in6_addr in6) {
}
int dns_scope_llmnr_membership(DnsScope *s, bool b) {
+ assert(s);
if (s->protocol != DNS_PROTOCOL_LLMNR)
return 0;
}
int dns_scope_mdns_membership(DnsScope *s, bool b) {
+ assert(s);
if (s->protocol != DNS_PROTOCOL_MDNS)
return 0;
DnsPacket **ret) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
- unsigned i;
int r;
assert(s);
assert(ret);
- if ((!q || q->n_keys <= 0)
- && (!answer || answer->n_rrs <= 0)
- && (!soa || soa->n_rrs <= 0))
+ if (dns_question_isempty(q) &&
+ dns_answer_isempty(answer) &&
+ dns_answer_isempty(soa))
return -EINVAL;
r = dns_packet_new(&p, s->protocol, 0);
0 /* (cd) */,
rcode));
- if (q) {
- for (i = 0; i < q->n_keys; i++) {
- r = dns_packet_append_key(p, q->keys[i], NULL);
- if (r < 0)
- return r;
- }
-
- DNS_PACKET_HEADER(p)->qdcount = htobe16(q->n_keys);
- }
-
- if (answer) {
- for (i = 0; i < answer->n_rrs; i++) {
- r = dns_packet_append_rr(p, answer->items[i].rr, NULL, NULL);
- if (r < 0)
- return r;
- }
-
- DNS_PACKET_HEADER(p)->ancount = htobe16(answer->n_rrs);
- }
+ r = dns_packet_append_question(p, q);
+ if (r < 0)
+ return r;
+ DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(q));
- if (soa) {
- for (i = 0; i < soa->n_rrs; i++) {
- r = dns_packet_append_rr(p, soa->items[i].rr, NULL, NULL);
- if (r < 0)
- return r;
- }
+ r = dns_packet_append_answer(p, answer);
+ if (r < 0)
+ return r;
+ DNS_PACKET_HEADER(p)->ancount = htobe16(dns_answer_size(answer));
- DNS_PACKET_HEADER(p)->arcount = htobe16(soa->n_rrs);
- }
+ r = dns_packet_append_answer(p, soa);
+ if (r < 0)
+ return r;
+ DNS_PACKET_HEADER(p)->arcount = htobe16(dns_answer_size(soa));
*ret = p;
p = NULL;
}
static void dns_scope_verify_conflicts(DnsScope *s, DnsPacket *p) {
- unsigned n;
+ DnsResourceRecord *rr;
+ DnsResourceKey *key;
assert(s);
assert(p);
- if (p->question)
- for (n = 0; n < p->question->n_keys; n++)
- dns_zone_verify_conflicts(&s->zone, p->question->keys[n]);
- if (p->answer)
- for (n = 0; n < p->answer->n_rrs; n++)
- dns_zone_verify_conflicts(&s->zone, p->answer->items[n].rr->key);
+ DNS_QUESTION_FOREACH(key, p->question)
+ dns_zone_verify_conflicts(&s->zone, key);
+
+ DNS_ANSWER_FOREACH(rr, p->answer)
+ dns_zone_verify_conflicts(&s->zone, rr->key);
}
void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
- _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
+ _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
DnsResourceKey *key = NULL;
bool tentative = false;
- int r, fd;
+ int r;
assert(s);
assert(p);
r = dns_packet_extract(p);
if (r < 0) {
- log_debug_errno(r, "Failed to extract resources from incoming packet: %m");
+ log_debug_errno(r, "Failed to extract resource records from incoming packet: %m");
return;
}
return;
}
- assert(p->question->n_keys == 1);
+ assert(dns_question_size(p->question) == 1);
key = p->question->keys[0];
- r = dns_zone_lookup(&s->zone, key, &answer, &soa, &tentative);
+ r = dns_zone_lookup(&s->zone, key, 0, &answer, &soa, &tentative);
if (r < 0) {
log_debug_errno(r, "Failed to lookup key: %m");
return;
return;
}
- if (stream)
+ if (stream) {
r = dns_stream_write_packet(stream, reply);
- else {
+ if (r < 0) {
+ log_debug_errno(r, "Failed to enqueue reply packet: %m");
+ return;
+ }
+
+ /* Let's take an extra reference on this stream, so that it stays around after returning. The reference
+ * will be dangling until the stream is disconnected, and the default completion handler of the stream
+ * will then unref the stream and destroy it */
+ if (DNS_STREAM_QUEUED(stream))
+ dns_stream_ref(stream);
+ } else {
+ int fd;
+
if (!ratelimit_test(&s->ratelimit))
return;
* verified uniqueness for all records. Also see RFC
* 4795, Section 2.7 */
- r = manager_send(s->manager, fd, p->ifindex, p->family, &p->sender, p->sender_port, reply);
- }
-
- if (r < 0) {
- log_debug_errno(r, "Failed to send reply packet: %m");
- return;
+ r = manager_send(s->manager, fd, p->ifindex, p->family, &p->sender, p->sender_port, NULL, reply);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to send reply packet: %m");
+ return;
+ }
}
}
/* Refuse reusing transactions that completed based on cached
* data instead of a real packet, if that's requested. */
if (!cache_ok &&
- IN_SET(t->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE) &&
+ IN_SET(t->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_RCODE_FAILURE) &&
t->answer_source != DNS_TRANSACTION_NETWORK)
return NULL;
return 0;
}
- r = dns_scope_emit(scope, -1, NULL, p);
+ r = dns_scope_emit_udp(scope, -1, p);
if (r < 0)
log_debug_errno(r, "Failed to send conflict packet: %m");
}
if (r < 0)
return log_debug_errno(r, "Failed to add conflict dispatch event: %m");
+ (void) sd_event_source_set_description(scope->conflict_event_source, "scope-conflict");
+
return 0;
}
return dns_name_is_single_label(name);
}
+
+bool dns_scope_network_good(DnsScope *s) {
+ /* Checks whether the network is in good state for lookups on this scope. For mDNS/LLMNR/Classic DNS scopes
+ * bound to links this is easy, as they don't even exist if the link isn't in a suitable state. For the global
+ * DNS scope we check whether there are any links that are up and have an address. */
+
+ if (s->link)
+ return true;
+
+ return manager_routable(s->manager, AF_UNSPEC);
+}
+
+int dns_scope_ifindex(DnsScope *s) {
+ assert(s);
+
+ if (s->link)
+ return s->link->ifindex;
+
+ return 0;
+}