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"
26 #include "resolved-dns-domain.h"
27 #include "resolved-dns-scope.h"
29 #define SEND_TIMEOUT_USEC (2*USEC_PER_SEC)
31 int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, unsigned char family) {
37 s = new0(DnsScope, 1);
43 s->protocol = protocol;
46 LIST_PREPEND(scopes, m->dns_scopes, s);
48 dns_scope_llmnr_membership(s, true);
50 log_debug("New scope on link %s, protocol %s, family %s", strna(l ? l->name : NULL), dns_protocol_to_string(protocol), family_to_string(family));
56 DnsScope* dns_scope_free(DnsScope *s) {
60 log_debug("Removing scope on link %s, protocol %s, family %s", strna(s->link ? s->link->name : NULL), dns_protocol_to_string(s->protocol), family_to_string(s->family));
62 dns_scope_llmnr_membership(s, false);
64 while (s->transactions) {
67 q = s->transactions->query;
68 dns_query_transaction_free(s->transactions);
73 dns_cache_flush(&s->cache);
75 LIST_REMOVE(scopes, s->manager->dns_scopes, s);
76 strv_free(s->domains);
82 DnsServer *dns_scope_get_server(DnsScope *s) {
85 if (s->protocol != DNS_PROTOCOL_DNS)
89 return link_get_dns_server(s->link);
91 return manager_get_dns_server(s->manager);
94 void dns_scope_next_dns_server(DnsScope *s) {
97 if (s->protocol != DNS_PROTOCOL_DNS)
101 link_next_dns_server(s->link);
103 manager_next_dns_server(s->manager);
106 int dns_scope_send(DnsScope *s, DnsPacket *p) {
107 union in_addr_union addr;
109 unsigned char family;
116 assert(p->protocol == s->protocol);
120 ifindex = s->link->ifindex;
122 mtu = manager_find_mtu(s->manager);
124 if (s->protocol == DNS_PROTOCOL_DNS) {
127 srv = dns_scope_get_server(s);
131 family = srv->family;
135 if (p->size > DNS_PACKET_UNICAST_SIZE_MAX)
141 if (family == AF_INET)
142 fd = manager_dns_ipv4_fd(s->manager);
143 else if (family == AF_INET6)
144 fd = manager_dns_ipv6_fd(s->manager);
146 return -EAFNOSUPPORT;
150 } else if (s->protocol == DNS_PROTOCOL_LLMNR) {
152 if (DNS_PACKET_QDCOUNT(p) > 1)
158 if (family == AF_INET) {
159 addr.in = LLMNR_MULTICAST_IPV4_ADDRESS;
160 /* fd = manager_dns_ipv4_fd(s->manager); */
161 fd = manager_llmnr_ipv4_udp_fd(s->manager);
162 } else if (family == AF_INET6) {
163 addr.in6 = LLMNR_MULTICAST_IPV6_ADDRESS;
164 fd = manager_llmnr_ipv6_udp_fd(s->manager);
165 /* fd = manager_dns_ipv6_fd(s->manager); */
167 return -EAFNOSUPPORT;
171 return -EAFNOSUPPORT;
173 r = manager_send(s->manager, fd, ifindex, family, &addr, port, p);
180 int dns_scope_tcp_socket(DnsScope *s) {
181 _cleanup_close_ int fd = -1;
182 union sockaddr_union sa = {};
184 int one, ifindex, ret;
190 srv = dns_scope_get_server(s);
195 ifindex = s->link->ifindex;
197 sa.sa.sa_family = srv->family;
198 if (srv->family == AF_INET) {
199 sa.in.sin_port = htobe16(53);
200 sa.in.sin_addr = srv->address.in;
201 salen = sizeof(sa.in);
202 } else if (srv->family == AF_INET6) {
203 sa.in6.sin6_port = htobe16(53);
204 sa.in6.sin6_addr = srv->address.in6;
205 sa.in6.sin6_scope_id = ifindex;
206 salen = sizeof(sa.in6);
208 return -EAFNOSUPPORT;
210 fd = socket(srv->family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
215 setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
217 r = connect(fd, &sa.sa, salen);
218 if (r < 0 && errno != EINPROGRESS)
226 DnsScopeMatch dns_scope_good_domain(DnsScope *s, const char *domain) {
232 STRV_FOREACH(i, s->domains)
233 if (dns_name_endswith(domain, *i))
234 return DNS_SCOPE_YES;
236 if (dns_name_root(domain))
239 if (is_localhost(domain))
242 if (s->protocol == DNS_PROTOCOL_DNS) {
243 if (dns_name_endswith(domain, "254.169.in-addr.arpa") ||
244 dns_name_endswith(domain, "0.8.e.f.ip6.arpa") ||
245 dns_name_single_label(domain))
248 return DNS_SCOPE_MAYBE;
251 if (s->protocol == DNS_PROTOCOL_MDNS) {
252 if (dns_name_endswith(domain, "254.169.in-addr.arpa") ||
253 dns_name_endswith(domain, "0.8.e.f.ip6.arpa") ||
254 dns_name_endswith(domain, "local"))
255 return DNS_SCOPE_MAYBE;
260 if (s->protocol == DNS_PROTOCOL_LLMNR) {
261 if (dns_name_endswith(domain, "254.169.in-addr.arpa") ||
262 dns_name_endswith(domain, "0.8.e.f.ip6.arpa") ||
263 dns_name_single_label(domain))
264 return DNS_SCOPE_MAYBE;
269 assert_not_reached("Unknown scope protocol");
272 int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
276 if (s->protocol == DNS_PROTOCOL_DNS)
279 /* On mDNS and LLMNR, send A and AAAA queries only on the
280 * respective scopes */
282 if (s->family == AF_INET && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_AAAA)
285 if (s->family == AF_INET6 && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_A)
291 int dns_scope_llmnr_membership(DnsScope *s, bool b) {
294 if (s->family == AF_INET) {
295 struct ip_mreqn mreqn = {
296 .imr_multiaddr = LLMNR_MULTICAST_IPV4_ADDRESS,
297 .imr_ifindex = s->link->ifindex,
300 fd = manager_llmnr_ipv4_udp_fd(s->manager);
304 if (setsockopt(fd, IPPROTO_IP, b ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0)
307 } else if (s->family == AF_INET6) {
308 struct ipv6_mreq mreq = {
309 .ipv6mr_multiaddr = LLMNR_MULTICAST_IPV6_ADDRESS,
310 .ipv6mr_interface = s->link->ifindex,
313 fd = manager_llmnr_ipv6_udp_fd(s->manager);
317 if (setsockopt(fd, IPPROTO_IPV6, b ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
320 return -EAFNOSUPPORT;