]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-scope.c
resolved: implement LLMNR uniqueness verification
[thirdparty/systemd.git] / src / resolve / resolved-dns-scope.c
CommitLineData
74b2466e
LP
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
ad867662
LP
22#include <netinet/tcp.h>
23
623a4c97 24#include "missing.h"
74b2466e 25#include "strv.h"
ad867662 26#include "socket-util.h"
46f08bea 27#include "af-list.h"
74b2466e
LP
28#include "resolved-dns-domain.h"
29#include "resolved-dns-scope.h"
30
0dd25fb9 31int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int family) {
74b2466e
LP
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;
1716f6dc
LP
42 s->link = l;
43 s->protocol = protocol;
44 s->family = family;
74b2466e
LP
45
46 LIST_PREPEND(scopes, m->dns_scopes, s);
47
1716f6dc
LP
48 dns_scope_llmnr_membership(s, true);
49
46f08bea 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));
1716f6dc 51
74b2466e
LP
52 *ret = s;
53 return 0;
54}
55
56DnsScope* dns_scope_free(DnsScope *s) {
ec2c5e43 57 DnsTransaction *t;
faa133f3 58
74b2466e
LP
59 if (!s)
60 return NULL;
61
46f08bea 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));
1716f6dc
LP
63
64 dns_scope_llmnr_membership(s, false);
65
faa133f3
LP
66 while ((t = s->transactions)) {
67
68 /* Abort the transaction, but make sure it is not
69 * freed while we still look at it */
74b2466e 70
faa133f3 71 t->block_gc++;
ec2c5e43 72 dns_transaction_complete(t, DNS_TRANSACTION_ABORTED);
faa133f3 73 t->block_gc--;
74b2466e 74
ec2c5e43 75 dns_transaction_free(t);
74b2466e
LP
76 }
77
322345fd 78 dns_cache_flush(&s->cache);
623a4c97 79 dns_zone_flush(&s->zone);
322345fd 80
74b2466e
LP
81 LIST_REMOVE(scopes, s->manager->dns_scopes, s);
82 strv_free(s->domains);
83 free(s);
84
85 return NULL;
86}
87
88DnsServer *dns_scope_get_server(DnsScope *s) {
89 assert(s);
90
1716f6dc
LP
91 if (s->protocol != DNS_PROTOCOL_DNS)
92 return NULL;
93
74b2466e
LP
94 if (s->link)
95 return link_get_dns_server(s->link);
96 else
97 return manager_get_dns_server(s->manager);
98}
99
100void dns_scope_next_dns_server(DnsScope *s) {
101 assert(s);
102
1716f6dc
LP
103 if (s->protocol != DNS_PROTOCOL_DNS)
104 return;
105
74b2466e
LP
106 if (s->link)
107 link_next_dns_server(s->link);
108 else
109 manager_next_dns_server(s->manager);
110}
111
112int dns_scope_send(DnsScope *s, DnsPacket *p) {
1716f6dc
LP
113 union in_addr_union addr;
114 int ifindex = 0, r;
0dd25fb9 115 int family;
1716f6dc
LP
116 uint16_t port;
117 uint32_t mtu;
118 int fd;
74b2466e
LP
119
120 assert(s);
121 assert(p);
1716f6dc 122 assert(p->protocol == s->protocol);
ad867662
LP
123
124 if (s->link) {
1716f6dc 125 mtu = s->link->mtu;
74b2466e 126 ifindex = s->link->ifindex;
1716f6dc 127 } else
e1c95994 128 mtu = manager_find_mtu(s->manager);
74b2466e 129
1716f6dc
LP
130 if (s->protocol == DNS_PROTOCOL_DNS) {
131 DnsServer *srv;
e1c95994 132
623a4c97
LP
133 if (DNS_PACKET_QDCOUNT(p) > 1)
134 return -ENOTSUP;
135
1716f6dc
LP
136 srv = dns_scope_get_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;
1716f6dc
LP
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);
1716f6dc
LP
173 } else
174 return -EAFNOSUPPORT;
175 if (fd < 0)
176 return fd;
177 } else
74b2466e
LP
178 return -EAFNOSUPPORT;
179
1716f6dc 180 r = manager_send(s->manager, fd, ifindex, family, &addr, port, p);
74b2466e
LP
181 if (r < 0)
182 return r;
183
184 return 1;
185}
186
623a4c97 187int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port) {
ad867662
LP
188 _cleanup_close_ int fd = -1;
189 union sockaddr_union sa = {};
190 socklen_t salen;
623a4c97
LP
191 static const int one = 1;
192 int ret, r;
ad867662
LP
193
194 assert(s);
623a4c97 195 assert((family == AF_UNSPEC) == !address);
ad867662 196
623a4c97
LP
197 if (family == AF_UNSPEC) {
198 DnsServer *srv;
199
200 srv = dns_scope_get_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 }
ad867662 231
623a4c97 232 fd = socket(sa.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
ad867662
LP
233 if (fd < 0)
234 return -errno;
235
623a4c97
LP
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) {
bf3f1271 255 /* RFC 4795, section 2.5 requires the TTL to be set to 1 */
623a4c97
LP
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 }
ad867662
LP
267
268 r = connect(fd, &sa.sa, salen);
269 if (r < 0 && errno != EINPROGRESS)
270 return -errno;
271
272 ret = fd;
273 fd = -1;
623a4c97 274
ad867662
LP
275 return ret;
276}
277
1716f6dc 278DnsScopeMatch dns_scope_good_domain(DnsScope *s, const char *domain) {
74b2466e
LP
279 char **i;
280
281 assert(s);
282 assert(domain);
283
284 STRV_FOREACH(i, s->domains)
faa133f3 285 if (dns_name_endswith(domain, *i) > 0)
74b2466e
LP
286 return DNS_SCOPE_YES;
287
faa133f3 288 if (dns_name_root(domain) != 0)
74b2466e
LP
289 return DNS_SCOPE_NO;
290
faec72d5
LP
291 if (is_localhost(domain))
292 return DNS_SCOPE_NO;
293
1716f6dc 294 if (s->protocol == DNS_PROTOCOL_DNS) {
faa133f3
LP
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;
1716f6dc 299
faa133f3 300 return DNS_SCOPE_NO;
1716f6dc
LP
301 }
302
303 if (s->protocol == DNS_PROTOCOL_MDNS) {
faa133f3
LP
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))
74b2466e
LP
307 return DNS_SCOPE_MAYBE;
308
309 return DNS_SCOPE_NO;
310 }
311
1716f6dc 312 if (s->protocol == DNS_PROTOCOL_LLMNR) {
b914e211
LP
313 if (dns_name_endswith(domain, "in-addr.arpa") > 0 ||
314 dns_name_endswith(domain, "ip6.arpa") > 0 ||
faa133f3 315 dns_name_single_label(domain) > 0)
1716f6dc 316 return DNS_SCOPE_MAYBE;
74b2466e 317
1716f6dc 318 return DNS_SCOPE_NO;
74b2466e
LP
319 }
320
1716f6dc
LP
321 assert_not_reached("Unknown scope protocol");
322}
323
324int 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
343int 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 if (setsockopt(fd, IPPROTO_IP, b ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0)
357 return -errno;
358
359 } else if (s->family == AF_INET6) {
360 struct ipv6_mreq mreq = {
361 .ipv6mr_multiaddr = LLMNR_MULTICAST_IPV6_ADDRESS,
362 .ipv6mr_interface = s->link->ifindex,
363 };
364
365 fd = manager_llmnr_ipv6_udp_fd(s->manager);
366 if (fd < 0)
367 return fd;
368
369 if (setsockopt(fd, IPPROTO_IPV6, b ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
370 return -errno;
371 } else
372 return -EAFNOSUPPORT;
373
374 return 0;
74b2466e 375}
623a4c97
LP
376
377int dns_scope_good_dns_server(DnsScope *s, int family, const union in_addr_union *address) {
378 assert(s);
379 assert(address);
380
381 if (s->protocol != DNS_PROTOCOL_DNS)
382 return 1;
383
384 if (s->link)
385 return !!link_find_dns_server(s->link, family, address);
386 else
387 return !!manager_find_dns_server(s->manager, family, address);
388}
389
ec2c5e43
LP
390static int dns_scope_make_reply_packet(
391 DnsScope *s,
392 uint16_t id,
393 int rcode,
394 DnsQuestion *q,
395 DnsAnswer *answer,
396 DnsAnswer *soa,
397 bool tentative,
398 DnsPacket **ret) {
399
623a4c97
LP
400 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
401 unsigned i;
402 int r;
403
404 assert(s);
405
8bf52d3d 406 if (q->n_keys <= 0 && answer->n_rrs <= 0 && soa->n_rrs <= 0)
623a4c97
LP
407 return -EINVAL;
408
409 r = dns_packet_new(&p, s->protocol, 0);
410 if (r < 0)
411 return r;
412
413 DNS_PACKET_HEADER(p)->id = id;
ea917db9
LP
414 DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
415 1 /* qr */,
416 0 /* opcode */,
417 0 /* c */,
418 0 /* tc */,
ec2c5e43 419 tentative,
ea917db9
LP
420 0 /* (ra) */,
421 0 /* (ad) */,
422 0 /* (cd) */,
423 rcode));
623a4c97
LP
424
425 if (q) {
426 for (i = 0; i < q->n_keys; i++) {
427 r = dns_packet_append_key(p, q->keys[i], NULL);
428 if (r < 0)
429 return r;
430 }
431
432 DNS_PACKET_HEADER(p)->qdcount = htobe16(q->n_keys);
433 }
434
8bf52d3d
LP
435 if (answer) {
436 for (i = 0; i < answer->n_rrs; i++) {
437 r = dns_packet_append_rr(p, answer->rrs[i], NULL);
623a4c97
LP
438 if (r < 0)
439 return r;
440 }
441
8bf52d3d
LP
442 DNS_PACKET_HEADER(p)->ancount = htobe16(answer->n_rrs);
443 }
444
445 if (soa) {
446 for (i = 0; i < soa->n_rrs; i++) {
447 r = dns_packet_append_rr(p, soa->rrs[i], NULL);
448 if (r < 0)
449 return r;
450 }
451
452 DNS_PACKET_HEADER(p)->arcount = htobe16(soa->n_rrs);
623a4c97
LP
453 }
454
455 *ret = p;
456 p = NULL;
457
458 return 0;
459}
460
461void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
462 _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
8bf52d3d 463 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
ec2c5e43 464 bool tentative = false;
623a4c97
LP
465 int r, fd;
466
467 assert(s);
468 assert(p);
469
470 if (p->protocol != DNS_PROTOCOL_LLMNR)
471 return;
472
2442b93d
LP
473 if (p->ipproto == IPPROTO_UDP) {
474 /* Don't accept UDP queries directed to anything but
475 * the LLMNR multicast addresses. See RFC 4795,
476 * section 2.5.*/
477
478 if (p->family == AF_INET && !in_addr_equal(AF_INET, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV4_ADDRESS))
479 return;
480
481 if (p->family == AF_INET6 && !in_addr_equal(AF_INET6, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV6_ADDRESS))
482 return;
483 }
484
623a4c97
LP
485 r = dns_packet_extract(p);
486 if (r < 0) {
487 log_debug("Failed to extract resources from incoming packet: %s", strerror(-r));
488 return;
489 }
490
ea917db9
LP
491 if (DNS_PACKET_C(p)) {
492 /* FIXME: Somebody notified us about a likely conflict */
493 return;
494 }
495
ec2c5e43 496 r = dns_zone_lookup(&s->zone, p->question, &answer, &soa, &tentative);
623a4c97
LP
497 if (r < 0) {
498 log_debug("Failed to lookup key: %s", strerror(-r));
499 return;
500 }
501 if (r == 0)
502 return;
503
fcf57f9c
LP
504 if (answer)
505 dns_answer_order_by_scope(answer, in_addr_is_link_local(p->family, &p->sender) > 0);
af93291c 506
ec2c5e43 507 r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, p->question, answer, soa, tentative, &reply);
623a4c97
LP
508 if (r < 0) {
509 log_debug("Failed to build reply packet: %s", strerror(-r));
510 return;
511 }
512
513 if (stream)
514 r = dns_stream_write_packet(stream, reply);
515 else {
516 if (p->family == AF_INET)
517 fd = manager_llmnr_ipv4_udp_fd(s->manager);
518 else if (p->family == AF_INET6)
519 fd = manager_llmnr_ipv6_udp_fd(s->manager);
520 else {
521 log_debug("Unknown protocol");
522 return;
523 }
524 if (fd < 0) {
525 log_debug("Failed to get reply socket: %s", strerror(-fd));
526 return;
527 }
528
529 r = manager_send(s->manager, fd, p->ifindex, p->family, &p->sender, p->sender_port, reply);
530 }
531
532 if (r < 0) {
533 log_debug("Failed to send reply packet: %s", strerror(-r));
534 return;
535 }
536}
ec2c5e43
LP
537
538DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsQuestion *question) {
539 DnsTransaction *t;
540
541 assert(scope);
542 assert(question);
543
544 /* Try to find an ongoing transaction that is a equal or a
545 * superset of the specified question */
546
547 LIST_FOREACH(transactions_by_scope, t, scope->transactions)
548 if (dns_question_is_superset(t->question, question) > 0)
549 return t;
550
551 return NULL;
552}