chiark / gitweb /
8d03049c103aec8e3eb867faa5533b2cb8046176
[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 "missing.h"
25 #include "strv.h"
26 #include "socket-util.h"
27 #include "af-list.h"
28 #include "resolved-dns-domain.h"
29 #include "resolved-dns-scope.h"
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", l ? l->name : "*", dns_protocol_to_string(protocol), family == AF_UNSPEC ? "*" : af_to_name(family));
51
52         *ret = s;
53         return 0;
54 }
55
56 DnsScope* dns_scope_free(DnsScope *s) {
57         DnsTransaction *t;
58
59         if (!s)
60                 return NULL;
61
62         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));
63
64         dns_scope_llmnr_membership(s, false);
65
66         while ((t = s->transactions)) {
67
68                 /* Abort the transaction, but make sure it is not
69                  * freed while we still look at it */
70
71                 t->block_gc++;
72                 dns_transaction_complete(t, DNS_TRANSACTION_ABORTED);
73                 t->block_gc--;
74
75                 dns_transaction_free(t);
76         }
77
78         dns_cache_flush(&s->cache);
79         dns_zone_flush(&s->zone);
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_dns_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                 if (DNS_PACKET_QDCOUNT(p) > 1)
134                         return -ENOTSUP;
135
136                 srv = dns_scope_get_dns_server(s);
137                 if (!srv)
138                         return -ESRCH;
139
140                 family = srv->family;
141                 addr = srv->address;
142                 port = 53;
143
144                 if (p->size > DNS_PACKET_UNICAST_SIZE_MAX)
145                         return -EMSGSIZE;
146
147                 if (p->size > mtu)
148                         return -EMSGSIZE;
149
150                 if (family == AF_INET)
151                         fd = manager_dns_ipv4_fd(s->manager);
152                 else if (family == AF_INET6)
153                         fd = manager_dns_ipv6_fd(s->manager);
154                 else
155                         return -EAFNOSUPPORT;
156                 if (fd < 0)
157                         return fd;
158
159         } else if (s->protocol == DNS_PROTOCOL_LLMNR) {
160
161                 if (DNS_PACKET_QDCOUNT(p) > 1)
162                         return -ENOTSUP;
163
164                 family = s->family;
165                 port = 5355;
166
167                 if (family == AF_INET) {
168                         addr.in = LLMNR_MULTICAST_IPV4_ADDRESS;
169                         fd = manager_llmnr_ipv4_udp_fd(s->manager);
170                 } else if (family == AF_INET6) {
171                         addr.in6 = LLMNR_MULTICAST_IPV6_ADDRESS;
172                         fd = manager_llmnr_ipv6_udp_fd(s->manager);
173                 } else
174                         return -EAFNOSUPPORT;
175                 if (fd < 0)
176                         return fd;
177         } else
178                 return -EAFNOSUPPORT;
179
180         r = manager_send(s->manager, fd, ifindex, family, &addr, port, p);
181         if (r < 0)
182                 return r;
183
184         return 1;
185 }
186
187 int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port) {
188         _cleanup_close_ int fd = -1;
189         union sockaddr_union sa = {};
190         socklen_t salen;
191         static const int one = 1;
192         int ret, r;
193
194         assert(s);
195         assert((family == AF_UNSPEC) == !address);
196
197         if (family == AF_UNSPEC) {
198                 DnsServer *srv;
199
200                 srv = dns_scope_get_dns_server(s);
201                 if (!srv)
202                         return -ESRCH;
203
204                 sa.sa.sa_family = srv->family;
205                 if (srv->family == AF_INET) {
206                         sa.in.sin_port = htobe16(port);
207                         sa.in.sin_addr = srv->address.in;
208                         salen = sizeof(sa.in);
209                 } else if (srv->family == AF_INET6) {
210                         sa.in6.sin6_port = htobe16(port);
211                         sa.in6.sin6_addr = srv->address.in6;
212                         sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
213                         salen = sizeof(sa.in6);
214                 } else
215                         return -EAFNOSUPPORT;
216         } else {
217                 sa.sa.sa_family = family;
218
219                 if (family == AF_INET) {
220                         sa.in.sin_port = htobe16(port);
221                         sa.in.sin_addr = address->in;
222                         salen = sizeof(sa.in);
223                 } else if (family == AF_INET6) {
224                         sa.in6.sin6_port = htobe16(port);
225                         sa.in6.sin6_addr = address->in6;
226                         sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
227                         salen = sizeof(sa.in6);
228                 } else
229                         return -EAFNOSUPPORT;
230         }
231
232         fd = socket(sa.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
233         if (fd < 0)
234                 return -errno;
235
236         r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
237         if (r < 0)
238                 return -errno;
239
240         if (s->link) {
241                 uint32_t ifindex = htobe32(s->link->ifindex);
242
243                 if (sa.sa.sa_family == AF_INET) {
244                         r = setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex));
245                         if (r < 0)
246                                 return -errno;
247                 } else if (sa.sa.sa_family == AF_INET6) {
248                         r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex));
249                         if (r < 0)
250                                 return -errno;
251                 }
252         }
253
254         if (s->protocol == DNS_PROTOCOL_LLMNR) {
255                 /* RFC 4795, section 2.5 requires the TTL to be set to 1 */
256
257                 if (sa.sa.sa_family == AF_INET) {
258                         r = setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
259                         if (r < 0)
260                                 return -errno;
261                 } else if (sa.sa.sa_family == AF_INET6) {
262                         r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one));
263                         if (r < 0)
264                                 return -errno;
265                 }
266         }
267
268         r = connect(fd, &sa.sa, salen);
269         if (r < 0 && errno != EINPROGRESS)
270                 return -errno;
271
272         ret = fd;
273         fd = -1;
274
275         return ret;
276 }
277
278 DnsScopeMatch dns_scope_good_domain(DnsScope *s, const char *domain) {
279         char **i;
280
281         assert(s);
282         assert(domain);
283
284         STRV_FOREACH(i, s->domains)
285                 if (dns_name_endswith(domain, *i) > 0)
286                         return DNS_SCOPE_YES;
287
288         if (dns_name_root(domain) != 0)
289                 return DNS_SCOPE_NO;
290
291         if (is_localhost(domain))
292                 return DNS_SCOPE_NO;
293
294         if (s->protocol == DNS_PROTOCOL_DNS) {
295                 if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 &&
296                     dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0 &&
297                     dns_name_single_label(domain) == 0)
298                         return DNS_SCOPE_MAYBE;
299
300                 return DNS_SCOPE_NO;
301         }
302
303         if (s->protocol == DNS_PROTOCOL_MDNS) {
304                 if (dns_name_endswith(domain, "254.169.in-addr.arpa") > 0 ||
305                     dns_name_endswith(domain, "0.8.e.f.ip6.arpa") > 0 ||
306                     (dns_name_endswith(domain, "local") > 0 && dns_name_equal(domain, "local") == 0))
307                         return DNS_SCOPE_MAYBE;
308
309                 return DNS_SCOPE_NO;
310         }
311
312         if (s->protocol == DNS_PROTOCOL_LLMNR) {
313                 if (dns_name_endswith(domain, "in-addr.arpa") > 0 ||
314                     dns_name_endswith(domain, "ip6.arpa") > 0 ||
315                     dns_name_single_label(domain) > 0)
316                         return DNS_SCOPE_MAYBE;
317
318                 return DNS_SCOPE_NO;
319         }
320
321         assert_not_reached("Unknown scope protocol");
322 }
323
324 int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
325         assert(s);
326         assert(key);
327
328         if (s->protocol == DNS_PROTOCOL_DNS)
329                 return true;
330
331         /* On mDNS and LLMNR, send A and AAAA queries only on the
332          * respective scopes */
333
334         if (s->family == AF_INET && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_AAAA)
335                 return false;
336
337         if (s->family == AF_INET6 && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_A)
338                 return false;
339
340         return true;
341 }
342
343 int dns_scope_llmnr_membership(DnsScope *s, bool b) {
344         int fd;
345
346         if (s->family == AF_INET) {
347                 struct ip_mreqn mreqn = {
348                         .imr_multiaddr = LLMNR_MULTICAST_IPV4_ADDRESS,
349                         .imr_ifindex = s->link->ifindex,
350                 };
351
352                 fd = manager_llmnr_ipv4_udp_fd(s->manager);
353                 if (fd < 0)
354                         return fd;
355
356                 /* Always first try to drop membership before we add
357                  * one. This is necessary on some devices, such as
358                  * veth. */
359                 if (b)
360                         setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn));
361
362                 if (setsockopt(fd, IPPROTO_IP, b ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0)
363                         return -errno;
364
365         } else if (s->family == AF_INET6) {
366                 struct ipv6_mreq mreq = {
367                         .ipv6mr_multiaddr = LLMNR_MULTICAST_IPV6_ADDRESS,
368                         .ipv6mr_interface = s->link->ifindex,
369                 };
370
371                 fd = manager_llmnr_ipv6_udp_fd(s->manager);
372                 if (fd < 0)
373                         return fd;
374
375                 if (b)
376                         setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
377
378                 if (setsockopt(fd, IPPROTO_IPV6, b ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
379                         return -errno;
380         } else
381                 return -EAFNOSUPPORT;
382
383         return 0;
384 }
385
386 int dns_scope_good_dns_server(DnsScope *s, int family, const union in_addr_union *address) {
387         assert(s);
388         assert(address);
389
390         if (s->protocol != DNS_PROTOCOL_DNS)
391                 return 1;
392
393         if (s->link)
394                 return !!link_find_dns_server(s->link,  family, address);
395         else
396                 return !!manager_find_dns_server(s->manager, family, address);
397 }
398
399 static int dns_scope_make_reply_packet(
400                 DnsScope *s,
401                 uint16_t id,
402                 int rcode,
403                 DnsQuestion *q,
404                 DnsAnswer *answer,
405                 DnsAnswer *soa,
406                 bool tentative,
407                 DnsPacket **ret) {
408
409         _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
410         unsigned i;
411         int r;
412
413         assert(s);
414
415         if (q->n_keys <= 0 && answer->n_rrs <= 0 && soa->n_rrs <= 0)
416                 return -EINVAL;
417
418         r = dns_packet_new(&p, s->protocol, 0);
419         if (r < 0)
420                 return r;
421
422         DNS_PACKET_HEADER(p)->id = id;
423         DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
424                                                               1 /* qr */,
425                                                               0 /* opcode */,
426                                                               0 /* c */,
427                                                               0 /* tc */,
428                                                               tentative,
429                                                               0 /* (ra) */,
430                                                               0 /* (ad) */,
431                                                               0 /* (cd) */,
432                                                               rcode));
433
434         if (q) {
435                 for (i = 0; i < q->n_keys; i++) {
436                         r = dns_packet_append_key(p, q->keys[i], NULL);
437                         if (r < 0)
438                                 return r;
439                 }
440
441                 DNS_PACKET_HEADER(p)->qdcount = htobe16(q->n_keys);
442         }
443
444         if (answer) {
445                 for (i = 0; i < answer->n_rrs; i++) {
446                         r = dns_packet_append_rr(p, answer->rrs[i], NULL);
447                         if (r < 0)
448                                 return r;
449                 }
450
451                 DNS_PACKET_HEADER(p)->ancount = htobe16(answer->n_rrs);
452         }
453
454         if (soa) {
455                 for (i = 0; i < soa->n_rrs; i++) {
456                         r = dns_packet_append_rr(p, soa->rrs[i], NULL);
457                         if (r < 0)
458                                 return r;
459                 }
460
461                 DNS_PACKET_HEADER(p)->arcount = htobe16(soa->n_rrs);
462         }
463
464         *ret = p;
465         p = NULL;
466
467         return 0;
468 }
469
470 void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
471         _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
472         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
473         bool tentative = false;
474         int r, fd;
475
476         assert(s);
477         assert(p);
478
479         if (p->protocol != DNS_PROTOCOL_LLMNR)
480                 return;
481
482         if (p->ipproto == IPPROTO_UDP) {
483                 /* Don't accept UDP queries directed to anything but
484                  * the LLMNR multicast addresses. See RFC 4795,
485                  * section 2.5.*/
486
487                 if (p->family == AF_INET && !in_addr_equal(AF_INET, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV4_ADDRESS))
488                         return;
489
490                 if (p->family == AF_INET6 && !in_addr_equal(AF_INET6, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV6_ADDRESS))
491                         return;
492         }
493
494         r = dns_packet_extract(p);
495         if (r < 0) {
496                 log_debug("Failed to extract resources from incoming packet: %s", strerror(-r));
497                 return;
498         }
499
500         if (DNS_PACKET_C(p)) {
501                 /* FIXME: Somebody notified us about a likely conflict */
502                 return;
503         }
504
505         r = dns_zone_lookup(&s->zone, p->question, &answer, &soa, &tentative);
506         if (r < 0) {
507                 log_debug("Failed to lookup key: %s", strerror(-r));
508                 return;
509         }
510         if (r == 0)
511                 return;
512
513         if (answer)
514                 dns_answer_order_by_scope(answer, in_addr_is_link_local(p->family, &p->sender) > 0);
515
516         r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, p->question, answer, soa, tentative, &reply);
517         if (r < 0) {
518                 log_debug("Failed to build reply packet: %s", strerror(-r));
519                 return;
520         }
521
522         if (stream)
523                 r = dns_stream_write_packet(stream, reply);
524         else {
525                 if (p->family == AF_INET)
526                         fd = manager_llmnr_ipv4_udp_fd(s->manager);
527                 else if (p->family == AF_INET6)
528                         fd = manager_llmnr_ipv6_udp_fd(s->manager);
529                 else {
530                         log_debug("Unknown protocol");
531                         return;
532                 }
533                 if (fd < 0) {
534                         log_debug("Failed to get reply socket: %s", strerror(-fd));
535                         return;
536                 }
537
538                 r = manager_send(s->manager, fd, p->ifindex, p->family, &p->sender, p->sender_port, reply);
539         }
540
541         if (r < 0) {
542                 log_debug("Failed to send reply packet: %s", strerror(-r));
543                 return;
544         }
545 }
546
547 DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsQuestion *question) {
548         DnsTransaction *t;
549
550         assert(scope);
551         assert(question);
552
553         /* Try to find an ongoing transaction that is a equal or a
554          * superset of the specified question */
555
556         LIST_FOREACH(transactions_by_scope, t, scope->transactions)
557                 if (dns_question_is_superset(t->question, question) > 0)
558                         return t;
559
560         return NULL;
561 }