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