chiark / gitweb /
38b64905a4565827724583d8c4284ec2771b781d
[elogind.git] / src / resolve / resolved-dns-scope.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2014 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <netinet/tcp.h>
23
24 #include "strv.h"
25 #include "socket-util.h"
26 #include "resolved-dns-domain.h"
27 #include "resolved-dns-scope.h"
28
29 #define SEND_TIMEOUT_USEC (2*USEC_PER_SEC)
30
31 int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int family) {
32         DnsScope *s;
33
34         assert(m);
35         assert(ret);
36
37         s = new0(DnsScope, 1);
38         if (!s)
39                 return -ENOMEM;
40
41         s->manager = m;
42         s->link = l;
43         s->protocol = protocol;
44         s->family = family;
45
46         LIST_PREPEND(scopes, m->dns_scopes, s);
47
48         dns_scope_llmnr_membership(s, true);
49
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));
51
52         *ret = s;
53         return 0;
54 }
55
56 DnsScope* dns_scope_free(DnsScope *s) {
57         if (!s)
58                 return NULL;
59
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));
61
62         dns_scope_llmnr_membership(s, false);
63
64         while (s->transactions) {
65                 DnsQuery *q;
66
67                 q = s->transactions->query;
68                 dns_query_transaction_free(s->transactions);
69
70                 dns_query_finish(q);
71         }
72
73         dns_cache_flush(&s->cache);
74
75         LIST_REMOVE(scopes, s->manager->dns_scopes, s);
76         strv_free(s->domains);
77         free(s);
78
79         return NULL;
80 }
81
82 DnsServer *dns_scope_get_server(DnsScope *s) {
83         assert(s);
84
85         if (s->protocol != DNS_PROTOCOL_DNS)
86                 return NULL;
87
88         if (s->link)
89                 return link_get_dns_server(s->link);
90         else
91                 return manager_get_dns_server(s->manager);
92 }
93
94 void dns_scope_next_dns_server(DnsScope *s) {
95         assert(s);
96
97         if (s->protocol != DNS_PROTOCOL_DNS)
98                 return;
99
100         if (s->link)
101                 link_next_dns_server(s->link);
102         else
103                 manager_next_dns_server(s->manager);
104 }
105
106 int dns_scope_send(DnsScope *s, DnsPacket *p) {
107         union in_addr_union addr;
108         int ifindex = 0, r;
109         int family;
110         uint16_t port;
111         uint32_t mtu;
112         int fd;
113
114         assert(s);
115         assert(p);
116         assert(p->protocol == s->protocol);
117
118         if (s->link) {
119                 mtu = s->link->mtu;
120                 ifindex = s->link->ifindex;
121         } else
122                 mtu = manager_find_mtu(s->manager);
123
124         if (s->protocol == DNS_PROTOCOL_DNS) {
125                 DnsServer *srv;
126
127                 srv = dns_scope_get_server(s);
128                 if (!srv)
129                         return -ESRCH;
130
131                 family = srv->family;
132                 addr = srv->address;
133                 port = 53;
134
135                 if (p->size > DNS_PACKET_UNICAST_SIZE_MAX)
136                         return -EMSGSIZE;
137
138                 if (p->size > mtu)
139                         return -EMSGSIZE;
140
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);
145                 else
146                         return -EAFNOSUPPORT;
147                 if (fd < 0)
148                         return fd;
149
150         } else if (s->protocol == DNS_PROTOCOL_LLMNR) {
151
152                 if (DNS_PACKET_QDCOUNT(p) > 1)
153                         return -ENOTSUP;
154
155                 family = s->family;
156                 port = 5355;
157
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); */
166                 } else
167                         return -EAFNOSUPPORT;
168                 if (fd < 0)
169                         return fd;
170         } else
171                 return -EAFNOSUPPORT;
172
173         r = manager_send(s->manager, fd, ifindex, family, &addr, port, p);
174         if (r < 0)
175                 return r;
176
177         return 1;
178 }
179
180 int dns_scope_tcp_socket(DnsScope *s) {
181         _cleanup_close_ int fd = -1;
182         union sockaddr_union sa = {};
183         socklen_t salen;
184         int one, ifindex, ret;
185         DnsServer *srv;
186         int r;
187
188         assert(s);
189
190         srv = dns_scope_get_server(s);
191         if (!srv)
192                 return -ESRCH;
193
194         if (s->link)
195                 ifindex = s->link->ifindex;
196
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);
207         } else
208                 return -EAFNOSUPPORT;
209
210         fd = socket(srv->family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
211         if (fd < 0)
212                 return -errno;
213
214         one = 1;
215         setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
216
217         r = connect(fd, &sa.sa, salen);
218         if (r < 0 && errno != EINPROGRESS)
219                 return -errno;
220
221         ret = fd;
222         fd = -1;
223         return ret;
224 }
225
226 DnsScopeMatch dns_scope_good_domain(DnsScope *s, const char *domain) {
227         char **i;
228
229         assert(s);
230         assert(domain);
231
232         STRV_FOREACH(i, s->domains)
233                 if (dns_name_endswith(domain, *i))
234                         return DNS_SCOPE_YES;
235
236         if (dns_name_root(domain))
237                 return DNS_SCOPE_NO;
238
239         if (is_localhost(domain))
240                 return DNS_SCOPE_NO;
241
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))
246                         return DNS_SCOPE_NO;
247
248                 return DNS_SCOPE_MAYBE;
249         }
250
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;
256
257                 return DNS_SCOPE_NO;
258         }
259
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;
265
266                 return DNS_SCOPE_NO;
267         }
268
269         assert_not_reached("Unknown scope protocol");
270 }
271
272 int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
273         assert(s);
274         assert(key);
275
276         if (s->protocol == DNS_PROTOCOL_DNS)
277                 return true;
278
279         /* On mDNS and LLMNR, send A and AAAA queries only on the
280          * respective scopes */
281
282         if (s->family == AF_INET && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_AAAA)
283                 return false;
284
285         if (s->family == AF_INET6 && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_A)
286                 return false;
287
288         return true;
289 }
290
291 int dns_scope_llmnr_membership(DnsScope *s, bool b) {
292         int fd;
293
294         if (s->family == AF_INET) {
295                 struct ip_mreqn mreqn = {
296                         .imr_multiaddr = LLMNR_MULTICAST_IPV4_ADDRESS,
297                         .imr_ifindex = s->link->ifindex,
298                 };
299
300                 fd = manager_llmnr_ipv4_udp_fd(s->manager);
301                 if (fd < 0)
302                         return fd;
303
304                 if (setsockopt(fd, IPPROTO_IP, b ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0)
305                         return -errno;
306
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,
311                 };
312
313                 fd = manager_llmnr_ipv6_udp_fd(s->manager);
314                 if (fd < 0)
315                         return fd;
316
317                 if (setsockopt(fd, IPPROTO_IPV6, b ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
318                         return -errno;
319         } else
320                 return -EAFNOSUPPORT;
321
322         return 0;
323 }