chiark / gitweb /
resolved: various bad memory access fixes to the cache
[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, ifindex, 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         if (s->link)
196                 ifindex = s->link->ifindex;
197
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);
208         } else
209                 return -EAFNOSUPPORT;
210
211         fd = socket(srv->family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
212         if (fd < 0)
213                 return -errno;
214
215         one = 1;
216         setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
217
218         r = connect(fd, &sa.sa, salen);
219         if (r < 0 && errno != EINPROGRESS)
220                 return -errno;
221
222         ret = fd;
223         fd = -1;
224         return ret;
225 }
226
227 DnsScopeMatch dns_scope_good_domain(DnsScope *s, const char *domain) {
228         char **i;
229
230         assert(s);
231         assert(domain);
232
233         STRV_FOREACH(i, s->domains)
234                 if (dns_name_endswith(domain, *i))
235                         return DNS_SCOPE_YES;
236
237         if (dns_name_root(domain))
238                 return DNS_SCOPE_NO;
239
240         if (is_localhost(domain))
241                 return DNS_SCOPE_NO;
242
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))
247                         return DNS_SCOPE_NO;
248
249                 return DNS_SCOPE_MAYBE;
250         }
251
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;
257
258                 return DNS_SCOPE_NO;
259         }
260
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;
266
267                 return DNS_SCOPE_NO;
268         }
269
270         assert_not_reached("Unknown scope protocol");
271 }
272
273 int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
274         assert(s);
275         assert(key);
276
277         if (s->protocol == DNS_PROTOCOL_DNS)
278                 return true;
279
280         /* On mDNS and LLMNR, send A and AAAA queries only on the
281          * respective scopes */
282
283         if (s->family == AF_INET && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_AAAA)
284                 return false;
285
286         if (s->family == AF_INET6 && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_A)
287                 return false;
288
289         return true;
290 }
291
292 int dns_scope_llmnr_membership(DnsScope *s, bool b) {
293         int fd;
294
295         if (s->family == AF_INET) {
296                 struct ip_mreqn mreqn = {
297                         .imr_multiaddr = LLMNR_MULTICAST_IPV4_ADDRESS,
298                         .imr_ifindex = s->link->ifindex,
299                 };
300
301                 fd = manager_llmnr_ipv4_udp_fd(s->manager);
302                 if (fd < 0)
303                         return fd;
304
305                 if (setsockopt(fd, IPPROTO_IP, b ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0)
306                         return -errno;
307
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,
312                 };
313
314                 fd = manager_llmnr_ipv6_udp_fd(s->manager);
315                 if (fd < 0)
316                         return fd;
317
318                 if (setsockopt(fd, IPPROTO_IPV6, b ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
319                         return -errno;
320         } else
321                 return -EAFNOSUPPORT;
322
323         return 0;
324 }