1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <netinet/tcp.h>
26 #include "socket-util.h"
28 #include "resolved-dns-domain.h"
29 #include "resolved-dns-scope.h"
31 #define SEND_TIMEOUT_USEC (2*USEC_PER_SEC)
33 int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int family) {
39 s = new0(DnsScope, 1);
45 s->protocol = protocol;
48 LIST_PREPEND(scopes, m->dns_scopes, s);
50 dns_scope_llmnr_membership(s, true);
52 log_debug("New scope on link %s, protocol %s, family %s", l ? l->name : "*", dns_protocol_to_string(protocol), family == AF_UNSPEC ? "*" : af_to_name(family));
58 DnsScope* dns_scope_free(DnsScope *s) {
59 DnsQueryTransaction *t;
64 log_debug("Removing scope on link %s, protocol %s, family %s", s->link ? s->link->name : "*", dns_protocol_to_string(s->protocol), s->family == AF_UNSPEC ? "*" : af_to_name(s->family));
66 dns_scope_llmnr_membership(s, false);
68 while ((t = s->transactions)) {
70 /* Abort the transaction, but make sure it is not
71 * freed while we still look at it */
74 dns_query_transaction_complete(t, DNS_QUERY_ABORTED);
77 dns_query_transaction_free(t);
80 dns_cache_flush(&s->cache);
81 dns_zone_flush(&s->zone);
83 LIST_REMOVE(scopes, s->manager->dns_scopes, s);
84 strv_free(s->domains);
90 DnsServer *dns_scope_get_server(DnsScope *s) {
93 if (s->protocol != DNS_PROTOCOL_DNS)
97 return link_get_dns_server(s->link);
99 return manager_get_dns_server(s->manager);
102 void dns_scope_next_dns_server(DnsScope *s) {
105 if (s->protocol != DNS_PROTOCOL_DNS)
109 link_next_dns_server(s->link);
111 manager_next_dns_server(s->manager);
114 int dns_scope_send(DnsScope *s, DnsPacket *p) {
115 union in_addr_union addr;
124 assert(p->protocol == s->protocol);
128 ifindex = s->link->ifindex;
130 mtu = manager_find_mtu(s->manager);
132 if (s->protocol == DNS_PROTOCOL_DNS) {
135 if (DNS_PACKET_QDCOUNT(p) > 1)
138 srv = dns_scope_get_server(s);
142 family = srv->family;
146 if (p->size > DNS_PACKET_UNICAST_SIZE_MAX)
152 if (family == AF_INET)
153 fd = manager_dns_ipv4_fd(s->manager);
154 else if (family == AF_INET6)
155 fd = manager_dns_ipv6_fd(s->manager);
157 return -EAFNOSUPPORT;
161 } else if (s->protocol == DNS_PROTOCOL_LLMNR) {
163 if (DNS_PACKET_QDCOUNT(p) > 1)
169 if (family == AF_INET) {
170 addr.in = LLMNR_MULTICAST_IPV4_ADDRESS;
171 fd = manager_llmnr_ipv4_udp_fd(s->manager);
172 } else if (family == AF_INET6) {
173 addr.in6 = LLMNR_MULTICAST_IPV6_ADDRESS;
174 fd = manager_llmnr_ipv6_udp_fd(s->manager);
176 return -EAFNOSUPPORT;
180 return -EAFNOSUPPORT;
182 r = manager_send(s->manager, fd, ifindex, family, &addr, port, p);
189 int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port) {
190 _cleanup_close_ int fd = -1;
191 union sockaddr_union sa = {};
193 static const int one = 1;
197 assert((family == AF_UNSPEC) == !address);
199 if (family == AF_UNSPEC) {
202 srv = dns_scope_get_server(s);
206 sa.sa.sa_family = srv->family;
207 if (srv->family == AF_INET) {
208 sa.in.sin_port = htobe16(port);
209 sa.in.sin_addr = srv->address.in;
210 salen = sizeof(sa.in);
211 } else if (srv->family == AF_INET6) {
212 sa.in6.sin6_port = htobe16(port);
213 sa.in6.sin6_addr = srv->address.in6;
214 sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
215 salen = sizeof(sa.in6);
217 return -EAFNOSUPPORT;
219 sa.sa.sa_family = family;
221 if (family == AF_INET) {
222 sa.in.sin_port = htobe16(port);
223 sa.in.sin_addr = address->in;
224 salen = sizeof(sa.in);
225 } else if (family == AF_INET6) {
226 sa.in6.sin6_port = htobe16(port);
227 sa.in6.sin6_addr = address->in6;
228 sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
229 salen = sizeof(sa.in6);
231 return -EAFNOSUPPORT;
234 fd = socket(sa.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
238 r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
243 uint32_t ifindex = htobe32(s->link->ifindex);
245 if (sa.sa.sa_family == AF_INET) {
246 r = setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex));
249 } else if (sa.sa.sa_family == AF_INET6) {
250 r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex));
256 if (s->protocol == DNS_PROTOCOL_LLMNR) {
257 /* RFC 4795, section 2.5 requires the TTL to be set to 1 */
259 if (sa.sa.sa_family == AF_INET) {
260 r = setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
263 } else if (sa.sa.sa_family == AF_INET6) {
264 r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one));
270 r = connect(fd, &sa.sa, salen);
271 if (r < 0 && errno != EINPROGRESS)
280 DnsScopeMatch dns_scope_good_domain(DnsScope *s, const char *domain) {
286 STRV_FOREACH(i, s->domains)
287 if (dns_name_endswith(domain, *i) > 0)
288 return DNS_SCOPE_YES;
290 if (dns_name_root(domain) != 0)
293 if (is_localhost(domain))
296 if (s->protocol == DNS_PROTOCOL_DNS) {
297 if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 &&
298 dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0 &&
299 dns_name_single_label(domain) == 0)
300 return DNS_SCOPE_MAYBE;
305 if (s->protocol == DNS_PROTOCOL_MDNS) {
306 if (dns_name_endswith(domain, "254.169.in-addr.arpa") > 0 ||
307 dns_name_endswith(domain, "0.8.e.f.ip6.arpa") > 0 ||
308 (dns_name_endswith(domain, "local") > 0 && dns_name_equal(domain, "local") == 0))
309 return DNS_SCOPE_MAYBE;
314 if (s->protocol == DNS_PROTOCOL_LLMNR) {
315 if (dns_name_endswith(domain, "in-addr.arpa") > 0 ||
316 dns_name_endswith(domain, "ip6.arpa") > 0 ||
317 dns_name_single_label(domain) > 0)
318 return DNS_SCOPE_MAYBE;
323 assert_not_reached("Unknown scope protocol");
326 int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
330 if (s->protocol == DNS_PROTOCOL_DNS)
333 /* On mDNS and LLMNR, send A and AAAA queries only on the
334 * respective scopes */
336 if (s->family == AF_INET && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_AAAA)
339 if (s->family == AF_INET6 && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_A)
345 int dns_scope_llmnr_membership(DnsScope *s, bool b) {
348 if (s->family == AF_INET) {
349 struct ip_mreqn mreqn = {
350 .imr_multiaddr = LLMNR_MULTICAST_IPV4_ADDRESS,
351 .imr_ifindex = s->link->ifindex,
354 fd = manager_llmnr_ipv4_udp_fd(s->manager);
358 if (setsockopt(fd, IPPROTO_IP, b ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0)
361 } else if (s->family == AF_INET6) {
362 struct ipv6_mreq mreq = {
363 .ipv6mr_multiaddr = LLMNR_MULTICAST_IPV6_ADDRESS,
364 .ipv6mr_interface = s->link->ifindex,
367 fd = manager_llmnr_ipv6_udp_fd(s->manager);
371 if (setsockopt(fd, IPPROTO_IPV6, b ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
374 return -EAFNOSUPPORT;
379 int dns_scope_good_dns_server(DnsScope *s, int family, const union in_addr_union *address) {
383 if (s->protocol != DNS_PROTOCOL_DNS)
387 return !!link_find_dns_server(s->link, family, address);
389 return !!manager_find_dns_server(s->manager, family, address);
392 static int dns_scope_make_reply_packet(DnsScope *s, uint16_t id, int rcode, DnsQuestion *q, DnsAnswer *answer, DnsAnswer *soa, DnsPacket **ret) {
393 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
399 if (q->n_keys <= 0 && answer->n_rrs <= 0 && soa->n_rrs <= 0)
402 r = dns_packet_new(&p, s->protocol, 0);
406 DNS_PACKET_HEADER(p)->id = id;
407 DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
419 for (i = 0; i < q->n_keys; i++) {
420 r = dns_packet_append_key(p, q->keys[i], NULL);
425 DNS_PACKET_HEADER(p)->qdcount = htobe16(q->n_keys);
429 for (i = 0; i < answer->n_rrs; i++) {
430 r = dns_packet_append_rr(p, answer->rrs[i], NULL);
435 DNS_PACKET_HEADER(p)->ancount = htobe16(answer->n_rrs);
439 for (i = 0; i < soa->n_rrs; i++) {
440 r = dns_packet_append_rr(p, soa->rrs[i], NULL);
445 DNS_PACKET_HEADER(p)->arcount = htobe16(soa->n_rrs);
454 void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
455 _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
456 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
462 if (p->protocol != DNS_PROTOCOL_LLMNR)
465 if (p->ipproto == IPPROTO_UDP) {
466 /* Don't accept UDP queries directed to anything but
467 * the LLMNR multicast addresses. See RFC 4795,
470 if (p->family == AF_INET && !in_addr_equal(AF_INET, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV4_ADDRESS))
473 if (p->family == AF_INET6 && !in_addr_equal(AF_INET6, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV6_ADDRESS))
477 r = dns_packet_extract(p);
479 log_debug("Failed to extract resources from incoming packet: %s", strerror(-r));
483 if (DNS_PACKET_C(p)) {
484 /* FIXME: Somebody notified us about a likely conflict */
488 r = dns_zone_lookup(&s->zone, p->question, &answer, &soa);
490 log_debug("Failed to lookup key: %s", strerror(-r));
496 dns_answer_order_by_scope(answer, in_addr_is_link_local(p->family, &p->sender) > 0);
498 r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, p->question, answer, soa, &reply);
500 log_debug("Failed to build reply packet: %s", strerror(-r));
505 r = dns_stream_write_packet(stream, reply);
507 if (p->family == AF_INET)
508 fd = manager_llmnr_ipv4_udp_fd(s->manager);
509 else if (p->family == AF_INET6)
510 fd = manager_llmnr_ipv6_udp_fd(s->manager);
512 log_debug("Unknown protocol");
516 log_debug("Failed to get reply socket: %s", strerror(-fd));
520 r = manager_send(s->manager, fd, p->ifindex, p->family, &p->sender, p->sender_port, reply);
524 log_debug("Failed to send reply packet: %s", strerror(-r));