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