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>
25 #include "socket-util.h"
27 #include "resolved-dns-domain.h"
28 #include "resolved-dns-scope.h"
30 #define SEND_TIMEOUT_USEC (2*USEC_PER_SEC)
32 int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int family) {
38 s = new0(DnsScope, 1);
44 s->protocol = protocol;
47 LIST_PREPEND(scopes, m->dns_scopes, s);
49 dns_scope_llmnr_membership(s, true);
51 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));
57 DnsScope* dns_scope_free(DnsScope *s) {
58 DnsQueryTransaction *t;
63 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));
65 dns_scope_llmnr_membership(s, false);
67 while ((t = s->transactions)) {
69 /* Abort the transaction, but make sure it is not
70 * freed while we still look at it */
73 dns_query_transaction_complete(t, DNS_QUERY_ABORTED);
76 dns_query_transaction_free(t);
79 dns_cache_flush(&s->cache);
81 LIST_REMOVE(scopes, s->manager->dns_scopes, s);
82 strv_free(s->domains);
88 DnsServer *dns_scope_get_server(DnsScope *s) {
91 if (s->protocol != DNS_PROTOCOL_DNS)
95 return link_get_dns_server(s->link);
97 return manager_get_dns_server(s->manager);
100 void dns_scope_next_dns_server(DnsScope *s) {
103 if (s->protocol != DNS_PROTOCOL_DNS)
107 link_next_dns_server(s->link);
109 manager_next_dns_server(s->manager);
112 int dns_scope_send(DnsScope *s, DnsPacket *p) {
113 union in_addr_union addr;
122 assert(p->protocol == s->protocol);
126 ifindex = s->link->ifindex;
128 mtu = manager_find_mtu(s->manager);
130 if (s->protocol == DNS_PROTOCOL_DNS) {
133 srv = dns_scope_get_server(s);
137 family = srv->family;
141 if (p->size > DNS_PACKET_UNICAST_SIZE_MAX)
147 if (family == AF_INET)
148 fd = manager_dns_ipv4_fd(s->manager);
149 else if (family == AF_INET6)
150 fd = manager_dns_ipv6_fd(s->manager);
152 return -EAFNOSUPPORT;
156 } else if (s->protocol == DNS_PROTOCOL_LLMNR) {
158 if (DNS_PACKET_QDCOUNT(p) > 1)
164 if (family == AF_INET) {
165 addr.in = LLMNR_MULTICAST_IPV4_ADDRESS;
166 /* fd = manager_dns_ipv4_fd(s->manager); */
167 fd = manager_llmnr_ipv4_udp_fd(s->manager);
168 } else if (family == AF_INET6) {
169 addr.in6 = LLMNR_MULTICAST_IPV6_ADDRESS;
170 fd = manager_llmnr_ipv6_udp_fd(s->manager);
171 /* fd = manager_dns_ipv6_fd(s->manager); */
173 return -EAFNOSUPPORT;
177 return -EAFNOSUPPORT;
179 r = manager_send(s->manager, fd, ifindex, family, &addr, port, p);
186 int dns_scope_tcp_socket(DnsScope *s) {
187 _cleanup_close_ int fd = -1;
188 union sockaddr_union sa = {};
196 srv = dns_scope_get_server(s);
200 sa.sa.sa_family = srv->family;
201 if (srv->family == AF_INET) {
202 sa.in.sin_port = htobe16(53);
203 sa.in.sin_addr = srv->address.in;
204 salen = sizeof(sa.in);
205 } else if (srv->family == AF_INET6) {
206 sa.in6.sin6_port = htobe16(53);
207 sa.in6.sin6_addr = srv->address.in6;
208 sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
209 salen = sizeof(sa.in6);
211 return -EAFNOSUPPORT;
213 fd = socket(srv->family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
218 setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
220 r = connect(fd, &sa.sa, salen);
221 if (r < 0 && errno != EINPROGRESS)
229 DnsScopeMatch dns_scope_good_domain(DnsScope *s, const char *domain) {
235 STRV_FOREACH(i, s->domains)
236 if (dns_name_endswith(domain, *i) > 0)
237 return DNS_SCOPE_YES;
239 if (dns_name_root(domain) != 0)
242 if (is_localhost(domain))
245 if (s->protocol == DNS_PROTOCOL_DNS) {
246 if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 &&
247 dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0 &&
248 dns_name_single_label(domain) == 0)
249 return DNS_SCOPE_MAYBE;
254 if (s->protocol == DNS_PROTOCOL_MDNS) {
255 if (dns_name_endswith(domain, "254.169.in-addr.arpa") > 0 ||
256 dns_name_endswith(domain, "0.8.e.f.ip6.arpa") > 0 ||
257 (dns_name_endswith(domain, "local") > 0 && dns_name_equal(domain, "local") == 0))
258 return DNS_SCOPE_MAYBE;
263 if (s->protocol == DNS_PROTOCOL_LLMNR) {
264 if (dns_name_endswith(domain, "254.169.in-addr.arpa") > 0 ||
265 dns_name_endswith(domain, "0.8.e.f.ip6.arpa") > 0 ||
266 dns_name_single_label(domain) > 0)
267 return DNS_SCOPE_MAYBE;
272 assert_not_reached("Unknown scope protocol");
275 int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
279 if (s->protocol == DNS_PROTOCOL_DNS)
282 /* On mDNS and LLMNR, send A and AAAA queries only on the
283 * respective scopes */
285 if (s->family == AF_INET && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_AAAA)
288 if (s->family == AF_INET6 && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_A)
294 int dns_scope_llmnr_membership(DnsScope *s, bool b) {
297 if (s->family == AF_INET) {
298 struct ip_mreqn mreqn = {
299 .imr_multiaddr = LLMNR_MULTICAST_IPV4_ADDRESS,
300 .imr_ifindex = s->link->ifindex,
303 fd = manager_llmnr_ipv4_udp_fd(s->manager);
307 if (setsockopt(fd, IPPROTO_IP, b ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0)
310 } else if (s->family == AF_INET6) {
311 struct ipv6_mreq mreq = {
312 .ipv6mr_multiaddr = LLMNR_MULTICAST_IPV6_ADDRESS,
313 .ipv6mr_interface = s->link->ifindex,
316 fd = manager_llmnr_ipv6_udp_fd(s->manager);
320 if (setsockopt(fd, IPPROTO_IPV6, b ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
323 return -EAFNOSUPPORT;