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) {
61 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));
63 dns_scope_llmnr_membership(s, false);
65 while (s->transactions) {
68 q = s->transactions->query;
69 dns_query_transaction_free(s->transactions);
74 dns_cache_flush(&s->cache);
76 LIST_REMOVE(scopes, s->manager->dns_scopes, s);
77 strv_free(s->domains);
83 DnsServer *dns_scope_get_server(DnsScope *s) {
86 if (s->protocol != DNS_PROTOCOL_DNS)
90 return link_get_dns_server(s->link);
92 return manager_get_dns_server(s->manager);
95 void dns_scope_next_dns_server(DnsScope *s) {
98 if (s->protocol != DNS_PROTOCOL_DNS)
102 link_next_dns_server(s->link);
104 manager_next_dns_server(s->manager);
107 int dns_scope_send(DnsScope *s, DnsPacket *p) {
108 union in_addr_union addr;
117 assert(p->protocol == s->protocol);
121 ifindex = s->link->ifindex;
123 mtu = manager_find_mtu(s->manager);
125 if (s->protocol == DNS_PROTOCOL_DNS) {
128 srv = dns_scope_get_server(s);
132 family = srv->family;
136 if (p->size > DNS_PACKET_UNICAST_SIZE_MAX)
142 if (family == AF_INET)
143 fd = manager_dns_ipv4_fd(s->manager);
144 else if (family == AF_INET6)
145 fd = manager_dns_ipv6_fd(s->manager);
147 return -EAFNOSUPPORT;
151 } else if (s->protocol == DNS_PROTOCOL_LLMNR) {
153 if (DNS_PACKET_QDCOUNT(p) > 1)
159 if (family == AF_INET) {
160 addr.in = LLMNR_MULTICAST_IPV4_ADDRESS;
161 /* fd = manager_dns_ipv4_fd(s->manager); */
162 fd = manager_llmnr_ipv4_udp_fd(s->manager);
163 } else if (family == AF_INET6) {
164 addr.in6 = LLMNR_MULTICAST_IPV6_ADDRESS;
165 fd = manager_llmnr_ipv6_udp_fd(s->manager);
166 /* fd = manager_dns_ipv6_fd(s->manager); */
168 return -EAFNOSUPPORT;
172 return -EAFNOSUPPORT;
174 r = manager_send(s->manager, fd, ifindex, family, &addr, port, p);
181 int dns_scope_tcp_socket(DnsScope *s) {
182 _cleanup_close_ int fd = -1;
183 union sockaddr_union sa = {};
185 int one, ifindex, ret;
191 srv = dns_scope_get_server(s);
196 ifindex = s->link->ifindex;
198 sa.sa.sa_family = srv->family;
199 if (srv->family == AF_INET) {
200 sa.in.sin_port = htobe16(53);
201 sa.in.sin_addr = srv->address.in;
202 salen = sizeof(sa.in);
203 } else if (srv->family == AF_INET6) {
204 sa.in6.sin6_port = htobe16(53);
205 sa.in6.sin6_addr = srv->address.in6;
206 sa.in6.sin6_scope_id = ifindex;
207 salen = sizeof(sa.in6);
209 return -EAFNOSUPPORT;
211 fd = socket(srv->family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
216 setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
218 r = connect(fd, &sa.sa, salen);
219 if (r < 0 && errno != EINPROGRESS)
227 DnsScopeMatch dns_scope_good_domain(DnsScope *s, const char *domain) {
233 STRV_FOREACH(i, s->domains)
234 if (dns_name_endswith(domain, *i))
235 return DNS_SCOPE_YES;
237 if (dns_name_root(domain))
240 if (is_localhost(domain))
243 if (s->protocol == DNS_PROTOCOL_DNS) {
244 if (dns_name_endswith(domain, "254.169.in-addr.arpa") ||
245 dns_name_endswith(domain, "0.8.e.f.ip6.arpa") ||
246 dns_name_single_label(domain))
249 return DNS_SCOPE_MAYBE;
252 if (s->protocol == DNS_PROTOCOL_MDNS) {
253 if (dns_name_endswith(domain, "254.169.in-addr.arpa") ||
254 dns_name_endswith(domain, "0.8.e.f.ip6.arpa") ||
255 dns_name_endswith(domain, "local"))
256 return DNS_SCOPE_MAYBE;
261 if (s->protocol == DNS_PROTOCOL_LLMNR) {
262 if (dns_name_endswith(domain, "254.169.in-addr.arpa") ||
263 dns_name_endswith(domain, "0.8.e.f.ip6.arpa") ||
264 dns_name_single_label(domain))
265 return DNS_SCOPE_MAYBE;
270 assert_not_reached("Unknown scope protocol");
273 int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
277 if (s->protocol == DNS_PROTOCOL_DNS)
280 /* On mDNS and LLMNR, send A and AAAA queries only on the
281 * respective scopes */
283 if (s->family == AF_INET && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_AAAA)
286 if (s->family == AF_INET6 && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_A)
292 int dns_scope_llmnr_membership(DnsScope *s, bool b) {
295 if (s->family == AF_INET) {
296 struct ip_mreqn mreqn = {
297 .imr_multiaddr = LLMNR_MULTICAST_IPV4_ADDRESS,
298 .imr_ifindex = s->link->ifindex,
301 fd = manager_llmnr_ipv4_udp_fd(s->manager);
305 if (setsockopt(fd, IPPROTO_IP, b ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0)
308 } else if (s->family == AF_INET6) {
309 struct ipv6_mreq mreq = {
310 .ipv6mr_multiaddr = LLMNR_MULTICAST_IPV6_ADDRESS,
311 .ipv6mr_interface = s->link->ifindex,
314 fd = manager_llmnr_ipv6_udp_fd(s->manager);
318 if (setsockopt(fd, IPPROTO_IPV6, b ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
321 return -EAFNOSUPPORT;