]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-scope.c
networkd: print nice warnings if people configure invalid domain names
[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
aea2429d
LP
31#define MULTICAST_RATELIMIT_INTERVAL_USEC (1*USEC_PER_SEC)
32#define MULTICAST_RATELIMIT_BURST 1000
33
0dd25fb9 34int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int family) {
74b2466e
LP
35 DnsScope *s;
36
37 assert(m);
38 assert(ret);
39
40 s = new0(DnsScope, 1);
41 if (!s)
42 return -ENOMEM;
43
44 s->manager = m;
1716f6dc
LP
45 s->link = l;
46 s->protocol = protocol;
47 s->family = family;
74b2466e
LP
48
49 LIST_PREPEND(scopes, m->dns_scopes, s);
50
1716f6dc
LP
51 dns_scope_llmnr_membership(s, true);
52
46f08bea 53 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 54
aea2429d
LP
55 /* Enforce ratelimiting for the multicast protocols */
56 RATELIMIT_INIT(s->ratelimit, MULTICAST_RATELIMIT_INTERVAL_USEC, MULTICAST_RATELIMIT_BURST);
57
74b2466e
LP
58 *ret = s;
59 return 0;
60}
61
62DnsScope* dns_scope_free(DnsScope *s) {
ec2c5e43 63 DnsTransaction *t;
a4076574 64 DnsResourceRecord *rr;
faa133f3 65
74b2466e
LP
66 if (!s)
67 return NULL;
68
46f08bea 69 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
70
71 dns_scope_llmnr_membership(s, false);
72
faa133f3
LP
73 while ((t = s->transactions)) {
74
75 /* Abort the transaction, but make sure it is not
76 * freed while we still look at it */
74b2466e 77
faa133f3 78 t->block_gc++;
ec2c5e43 79 dns_transaction_complete(t, DNS_TRANSACTION_ABORTED);
faa133f3 80 t->block_gc--;
74b2466e 81
ec2c5e43 82 dns_transaction_free(t);
74b2466e
LP
83 }
84
a4076574
LP
85 while ((rr = hashmap_steal_first(s->conflict_queue)))
86 dns_resource_record_unref(rr);
87
88 hashmap_free(s->conflict_queue);
89 sd_event_source_unref(s->conflict_event_source);
90
322345fd 91 dns_cache_flush(&s->cache);
623a4c97 92 dns_zone_flush(&s->zone);
322345fd 93
74b2466e
LP
94 LIST_REMOVE(scopes, s->manager->dns_scopes, s);
95 strv_free(s->domains);
96 free(s);
97
98 return NULL;
99}
100
2c27fbca 101DnsServer *dns_scope_get_dns_server(DnsScope *s) {
74b2466e
LP
102 assert(s);
103
1716f6dc
LP
104 if (s->protocol != DNS_PROTOCOL_DNS)
105 return NULL;
106
74b2466e
LP
107 if (s->link)
108 return link_get_dns_server(s->link);
109 else
110 return manager_get_dns_server(s->manager);
111}
112
113void dns_scope_next_dns_server(DnsScope *s) {
114 assert(s);
115
1716f6dc
LP
116 if (s->protocol != DNS_PROTOCOL_DNS)
117 return;
118
74b2466e
LP
119 if (s->link)
120 link_next_dns_server(s->link);
121 else
122 manager_next_dns_server(s->manager);
123}
124
a4076574 125int dns_scope_emit(DnsScope *s, DnsPacket *p) {
1716f6dc
LP
126 union in_addr_union addr;
127 int ifindex = 0, r;
0dd25fb9 128 int family;
1716f6dc
LP
129 uint16_t port;
130 uint32_t mtu;
131 int fd;
74b2466e
LP
132
133 assert(s);
134 assert(p);
1716f6dc 135 assert(p->protocol == s->protocol);
ad867662
LP
136
137 if (s->link) {
1716f6dc 138 mtu = s->link->mtu;
74b2466e 139 ifindex = s->link->ifindex;
1716f6dc 140 } else
e1c95994 141 mtu = manager_find_mtu(s->manager);
74b2466e 142
1716f6dc
LP
143 if (s->protocol == DNS_PROTOCOL_DNS) {
144 DnsServer *srv;
e1c95994 145
623a4c97
LP
146 if (DNS_PACKET_QDCOUNT(p) > 1)
147 return -ENOTSUP;
148
2c27fbca 149 srv = dns_scope_get_dns_server(s);
1716f6dc
LP
150 if (!srv)
151 return -ESRCH;
152
153 family = srv->family;
154 addr = srv->address;
155 port = 53;
156
157 if (p->size > DNS_PACKET_UNICAST_SIZE_MAX)
158 return -EMSGSIZE;
159
160 if (p->size > mtu)
161 return -EMSGSIZE;
162
163 if (family == AF_INET)
164 fd = manager_dns_ipv4_fd(s->manager);
165 else if (family == AF_INET6)
166 fd = manager_dns_ipv6_fd(s->manager);
167 else
168 return -EAFNOSUPPORT;
169 if (fd < 0)
170 return fd;
171
172 } else if (s->protocol == DNS_PROTOCOL_LLMNR) {
173
174 if (DNS_PACKET_QDCOUNT(p) > 1)
175 return -ENOTSUP;
176
aea2429d
LP
177 if (!ratelimit_test(&s->ratelimit))
178 return -EBUSY;
179
1716f6dc
LP
180 family = s->family;
181 port = 5355;
182
183 if (family == AF_INET) {
184 addr.in = LLMNR_MULTICAST_IPV4_ADDRESS;
1716f6dc
LP
185 fd = manager_llmnr_ipv4_udp_fd(s->manager);
186 } else if (family == AF_INET6) {
187 addr.in6 = LLMNR_MULTICAST_IPV6_ADDRESS;
188 fd = manager_llmnr_ipv6_udp_fd(s->manager);
1716f6dc
LP
189 } else
190 return -EAFNOSUPPORT;
191 if (fd < 0)
192 return fd;
193 } else
74b2466e
LP
194 return -EAFNOSUPPORT;
195
1716f6dc 196 r = manager_send(s->manager, fd, ifindex, family, &addr, port, p);
74b2466e
LP
197 if (r < 0)
198 return r;
199
200 return 1;
201}
202
623a4c97 203int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port) {
ad867662
LP
204 _cleanup_close_ int fd = -1;
205 union sockaddr_union sa = {};
206 socklen_t salen;
623a4c97
LP
207 static const int one = 1;
208 int ret, r;
ad867662
LP
209
210 assert(s);
623a4c97 211 assert((family == AF_UNSPEC) == !address);
ad867662 212
623a4c97
LP
213 if (family == AF_UNSPEC) {
214 DnsServer *srv;
215
2c27fbca 216 srv = dns_scope_get_dns_server(s);
623a4c97
LP
217 if (!srv)
218 return -ESRCH;
219
220 sa.sa.sa_family = srv->family;
221 if (srv->family == AF_INET) {
222 sa.in.sin_port = htobe16(port);
223 sa.in.sin_addr = srv->address.in;
224 salen = sizeof(sa.in);
225 } else if (srv->family == AF_INET6) {
226 sa.in6.sin6_port = htobe16(port);
227 sa.in6.sin6_addr = srv->address.in6;
228 sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
229 salen = sizeof(sa.in6);
230 } else
231 return -EAFNOSUPPORT;
232 } else {
233 sa.sa.sa_family = family;
234
235 if (family == AF_INET) {
236 sa.in.sin_port = htobe16(port);
237 sa.in.sin_addr = address->in;
238 salen = sizeof(sa.in);
239 } else if (family == AF_INET6) {
240 sa.in6.sin6_port = htobe16(port);
241 sa.in6.sin6_addr = address->in6;
242 sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
243 salen = sizeof(sa.in6);
244 } else
245 return -EAFNOSUPPORT;
246 }
ad867662 247
623a4c97 248 fd = socket(sa.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
ad867662
LP
249 if (fd < 0)
250 return -errno;
251
623a4c97
LP
252 r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
253 if (r < 0)
254 return -errno;
255
256 if (s->link) {
257 uint32_t ifindex = htobe32(s->link->ifindex);
258
259 if (sa.sa.sa_family == AF_INET) {
260 r = setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex));
261 if (r < 0)
262 return -errno;
263 } else if (sa.sa.sa_family == AF_INET6) {
264 r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex));
265 if (r < 0)
266 return -errno;
267 }
268 }
269
270 if (s->protocol == DNS_PROTOCOL_LLMNR) {
bf3f1271 271 /* RFC 4795, section 2.5 requires the TTL to be set to 1 */
623a4c97
LP
272
273 if (sa.sa.sa_family == AF_INET) {
274 r = setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
275 if (r < 0)
276 return -errno;
277 } else if (sa.sa.sa_family == AF_INET6) {
278 r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one));
279 if (r < 0)
280 return -errno;
281 }
282 }
ad867662
LP
283
284 r = connect(fd, &sa.sa, salen);
285 if (r < 0 && errno != EINPROGRESS)
286 return -errno;
287
288 ret = fd;
289 fd = -1;
623a4c97 290
ad867662
LP
291 return ret;
292}
293
51323288 294DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) {
74b2466e
LP
295 char **i;
296
297 assert(s);
298 assert(domain);
299
51323288
LP
300 if (ifindex != 0 && (!s->link || s->link->ifindex != ifindex))
301 return DNS_SCOPE_NO;
302
303 if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family) & flags) == 0)
304 return DNS_SCOPE_NO;
305
74b2466e 306 STRV_FOREACH(i, s->domains)
faa133f3 307 if (dns_name_endswith(domain, *i) > 0)
74b2466e
LP
308 return DNS_SCOPE_YES;
309
faa133f3 310 if (dns_name_root(domain) != 0)
74b2466e
LP
311 return DNS_SCOPE_NO;
312
faec72d5
LP
313 if (is_localhost(domain))
314 return DNS_SCOPE_NO;
315
1716f6dc 316 if (s->protocol == DNS_PROTOCOL_DNS) {
faa133f3
LP
317 if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 &&
318 dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0 &&
319 dns_name_single_label(domain) == 0)
320 return DNS_SCOPE_MAYBE;
1716f6dc 321
faa133f3 322 return DNS_SCOPE_NO;
1716f6dc
LP
323 }
324
325 if (s->protocol == DNS_PROTOCOL_MDNS) {
faa133f3
LP
326 if (dns_name_endswith(domain, "254.169.in-addr.arpa") > 0 ||
327 dns_name_endswith(domain, "0.8.e.f.ip6.arpa") > 0 ||
328 (dns_name_endswith(domain, "local") > 0 && dns_name_equal(domain, "local") == 0))
74b2466e
LP
329 return DNS_SCOPE_MAYBE;
330
331 return DNS_SCOPE_NO;
332 }
333
1716f6dc 334 if (s->protocol == DNS_PROTOCOL_LLMNR) {
b914e211
LP
335 if (dns_name_endswith(domain, "in-addr.arpa") > 0 ||
336 dns_name_endswith(domain, "ip6.arpa") > 0 ||
faa133f3 337 dns_name_single_label(domain) > 0)
1716f6dc 338 return DNS_SCOPE_MAYBE;
74b2466e 339
1716f6dc 340 return DNS_SCOPE_NO;
74b2466e
LP
341 }
342
1716f6dc
LP
343 assert_not_reached("Unknown scope protocol");
344}
345
346int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
347 assert(s);
348 assert(key);
349
350 if (s->protocol == DNS_PROTOCOL_DNS)
351 return true;
352
353 /* On mDNS and LLMNR, send A and AAAA queries only on the
354 * respective scopes */
355
356 if (s->family == AF_INET && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_AAAA)
357 return false;
358
359 if (s->family == AF_INET6 && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_A)
360 return false;
361
362 return true;
363}
364
365int dns_scope_llmnr_membership(DnsScope *s, bool b) {
366 int fd;
367
5ba73e9b
LP
368 assert(s);
369 assert(s->protocol == DNS_PROTOCOL_LLMNR);
370 assert(s->link);
371
1716f6dc
LP
372 if (s->family == AF_INET) {
373 struct ip_mreqn mreqn = {
374 .imr_multiaddr = LLMNR_MULTICAST_IPV4_ADDRESS,
375 .imr_ifindex = s->link->ifindex,
376 };
377
378 fd = manager_llmnr_ipv4_udp_fd(s->manager);
379 if (fd < 0)
380 return fd;
381
7b4c2ee7
LP
382 /* Always first try to drop membership before we add
383 * one. This is necessary on some devices, such as
384 * veth. */
385 if (b)
386 setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn));
387
1716f6dc
LP
388 if (setsockopt(fd, IPPROTO_IP, b ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0)
389 return -errno;
390
391 } else if (s->family == AF_INET6) {
392 struct ipv6_mreq mreq = {
393 .ipv6mr_multiaddr = LLMNR_MULTICAST_IPV6_ADDRESS,
394 .ipv6mr_interface = s->link->ifindex,
395 };
396
397 fd = manager_llmnr_ipv6_udp_fd(s->manager);
398 if (fd < 0)
399 return fd;
400
7b4c2ee7
LP
401 if (b)
402 setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
403
1716f6dc
LP
404 if (setsockopt(fd, IPPROTO_IPV6, b ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
405 return -errno;
406 } else
407 return -EAFNOSUPPORT;
408
409 return 0;
74b2466e 410}
623a4c97
LP
411
412int dns_scope_good_dns_server(DnsScope *s, int family, const union in_addr_union *address) {
413 assert(s);
414 assert(address);
415
416 if (s->protocol != DNS_PROTOCOL_DNS)
417 return 1;
418
419 if (s->link)
420 return !!link_find_dns_server(s->link, family, address);
421 else
2c27fbca 422 return !!manager_find_dns_server(s->manager, family, address);
623a4c97
LP
423}
424
ec2c5e43
LP
425static int dns_scope_make_reply_packet(
426 DnsScope *s,
427 uint16_t id,
428 int rcode,
429 DnsQuestion *q,
430 DnsAnswer *answer,
431 DnsAnswer *soa,
432 bool tentative,
433 DnsPacket **ret) {
434
623a4c97
LP
435 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
436 unsigned i;
437 int r;
438
439 assert(s);
a4076574 440 assert(ret);
623a4c97 441
75cd513e
TA
442 if ((!q || q->n_keys <= 0)
443 && (!answer || answer->n_rrs <= 0)
444 && (!soa || soa->n_rrs <= 0))
623a4c97
LP
445 return -EINVAL;
446
447 r = dns_packet_new(&p, s->protocol, 0);
448 if (r < 0)
449 return r;
450
451 DNS_PACKET_HEADER(p)->id = id;
ea917db9
LP
452 DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
453 1 /* qr */,
454 0 /* opcode */,
455 0 /* c */,
456 0 /* tc */,
ec2c5e43 457 tentative,
ea917db9
LP
458 0 /* (ra) */,
459 0 /* (ad) */,
460 0 /* (cd) */,
461 rcode));
623a4c97
LP
462
463 if (q) {
464 for (i = 0; i < q->n_keys; i++) {
465 r = dns_packet_append_key(p, q->keys[i], NULL);
466 if (r < 0)
467 return r;
468 }
469
470 DNS_PACKET_HEADER(p)->qdcount = htobe16(q->n_keys);
471 }
472
8bf52d3d
LP
473 if (answer) {
474 for (i = 0; i < answer->n_rrs; i++) {
475 r = dns_packet_append_rr(p, answer->rrs[i], NULL);
623a4c97
LP
476 if (r < 0)
477 return r;
478 }
479
8bf52d3d
LP
480 DNS_PACKET_HEADER(p)->ancount = htobe16(answer->n_rrs);
481 }
482
483 if (soa) {
484 for (i = 0; i < soa->n_rrs; i++) {
485 r = dns_packet_append_rr(p, soa->rrs[i], NULL);
486 if (r < 0)
487 return r;
488 }
489
490 DNS_PACKET_HEADER(p)->arcount = htobe16(soa->n_rrs);
623a4c97
LP
491 }
492
493 *ret = p;
494 p = NULL;
495
496 return 0;
497}
498
a4076574
LP
499static void dns_scope_verify_conflicts(DnsScope *s, DnsPacket *p) {
500 unsigned n;
501
502 assert(s);
503 assert(p);
504
505 if (p->question)
506 for (n = 0; n < p->question->n_keys; n++)
507 dns_zone_verify_conflicts(&s->zone, p->question->keys[n]);
508 if (p->answer)
509 for (n = 0; n < p->answer->n_rrs; n++)
510 dns_zone_verify_conflicts(&s->zone, p->answer->rrs[n]->key);
511}
512
623a4c97
LP
513void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
514 _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
8bf52d3d 515 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
ec2c5e43 516 bool tentative = false;
623a4c97
LP
517 int r, fd;
518
519 assert(s);
520 assert(p);
521
522 if (p->protocol != DNS_PROTOCOL_LLMNR)
523 return;
524
2442b93d
LP
525 if (p->ipproto == IPPROTO_UDP) {
526 /* Don't accept UDP queries directed to anything but
527 * the LLMNR multicast addresses. See RFC 4795,
528 * section 2.5.*/
529
530 if (p->family == AF_INET && !in_addr_equal(AF_INET, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV4_ADDRESS))
531 return;
532
533 if (p->family == AF_INET6 && !in_addr_equal(AF_INET6, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV6_ADDRESS))
534 return;
535 }
536
623a4c97
LP
537 r = dns_packet_extract(p);
538 if (r < 0) {
539 log_debug("Failed to extract resources from incoming packet: %s", strerror(-r));
540 return;
541 }
542
ea917db9 543 if (DNS_PACKET_C(p)) {
a4076574
LP
544 /* Somebody notified us about a possible conflict */
545 dns_scope_verify_conflicts(s, p);
ea917db9
LP
546 return;
547 }
548
ec2c5e43 549 r = dns_zone_lookup(&s->zone, p->question, &answer, &soa, &tentative);
623a4c97
LP
550 if (r < 0) {
551 log_debug("Failed to lookup key: %s", strerror(-r));
552 return;
553 }
554 if (r == 0)
555 return;
556
fcf57f9c
LP
557 if (answer)
558 dns_answer_order_by_scope(answer, in_addr_is_link_local(p->family, &p->sender) > 0);
af93291c 559
ec2c5e43 560 r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, p->question, answer, soa, tentative, &reply);
623a4c97
LP
561 if (r < 0) {
562 log_debug("Failed to build reply packet: %s", strerror(-r));
563 return;
564 }
565
566 if (stream)
567 r = dns_stream_write_packet(stream, reply);
568 else {
aea2429d
LP
569 if (!ratelimit_test(&s->ratelimit))
570 return;
571
623a4c97
LP
572 if (p->family == AF_INET)
573 fd = manager_llmnr_ipv4_udp_fd(s->manager);
574 else if (p->family == AF_INET6)
575 fd = manager_llmnr_ipv6_udp_fd(s->manager);
576 else {
577 log_debug("Unknown protocol");
578 return;
579 }
580 if (fd < 0) {
581 log_debug("Failed to get reply socket: %s", strerror(-fd));
582 return;
583 }
584
6e068472
LP
585 /* Note that we always immediately reply to all LLMNR
586 * requests, and do not wait any time, since we
587 * verified uniqueness for all records. Also see RFC
588 * 4795, Section 2.7 */
589
623a4c97
LP
590 r = manager_send(s->manager, fd, p->ifindex, p->family, &p->sender, p->sender_port, reply);
591 }
592
593 if (r < 0) {
594 log_debug("Failed to send reply packet: %s", strerror(-r));
595 return;
596 }
597}
ec2c5e43 598
dc4d47e2 599DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsQuestion *question, bool cache_ok) {
ec2c5e43
LP
600 DnsTransaction *t;
601
602 assert(scope);
603 assert(question);
604
605 /* Try to find an ongoing transaction that is a equal or a
606 * superset of the specified question */
607
dc4d47e2
LP
608 LIST_FOREACH(transactions_by_scope, t, scope->transactions) {
609
610 /* Refuse reusing transactions that completed based on
611 * cached data instead of a real packet, if that's
612 * requested. */
613 if (!cache_ok &&
614 IN_SET(t->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE) &&
615 !t->received)
616 continue;
617
ec2c5e43
LP
618 if (dns_question_is_superset(t->question, question) > 0)
619 return t;
dc4d47e2 620 }
ec2c5e43
LP
621
622 return NULL;
623}
a4076574
LP
624
625static int dns_scope_make_conflict_packet(
626 DnsScope *s,
627 DnsResourceRecord *rr,
628 DnsPacket **ret) {
629
630 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
631 int r;
632
633 assert(s);
634 assert(rr);
635 assert(ret);
636
637 r = dns_packet_new(&p, s->protocol, 0);
638 if (r < 0)
639 return r;
640
641 DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
642 0 /* qr */,
643 0 /* opcode */,
644 1 /* conflict */,
645 0 /* tc */,
646 0 /* t */,
647 0 /* (ra) */,
648 0 /* (ad) */,
649 0 /* (cd) */,
650 0));
651 random_bytes(&DNS_PACKET_HEADER(p)->id, sizeof(uint16_t));
652 DNS_PACKET_HEADER(p)->qdcount = htobe16(1);
653 DNS_PACKET_HEADER(p)->arcount = htobe16(1);
654
655 r = dns_packet_append_key(p, rr->key, NULL);
656 if (r < 0)
657 return r;
658
659 r = dns_packet_append_rr(p, rr, NULL);
660 if (r < 0)
661 return r;
662
663 *ret = p;
664 p = NULL;
665
666 return 0;
667}
668
669static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata) {
670 DnsScope *scope = userdata;
671 int r;
672
673 assert(es);
674 assert(scope);
675
676 scope->conflict_event_source = sd_event_source_unref(scope->conflict_event_source);
677
678 for (;;) {
679 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
680 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
681
682 rr = hashmap_steal_first(scope->conflict_queue);
683 if (!rr)
684 break;
685
686 r = dns_scope_make_conflict_packet(scope, rr, &p);
687 if (r < 0) {
688 log_error("Failed to make conflict packet: %s", strerror(-r));
689 return 0;
690 }
691
692 r = dns_scope_emit(scope, p);
693 if (r < 0)
694 log_debug("Failed to send conflict packet: %s", strerror(-r));
695 }
696
697 return 0;
698}
699
700int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr) {
701 usec_t jitter;
702 int r;
703
704 assert(scope);
705 assert(rr);
706
707 /* We don't send these queries immediately. Instead, we queue
708 * them, and send them after some jitter delay. */
709 r = hashmap_ensure_allocated(&scope->conflict_queue, dns_resource_key_hash_func, dns_resource_key_compare_func);
710 if (r < 0) {
711 log_oom();
712 return r;
713 }
714
715 /* We only place one RR per key in the conflict
716 * messages, not all of them. That should be enough to
717 * indicate where there might be a conflict */
718 r = hashmap_put(scope->conflict_queue, rr->key, rr);
719 if (r == -EEXIST || r == 0)
720 return 0;
721 if (r < 0) {
722 log_debug("Failed to queue conflicting RR: %s", strerror(-r));
723 return r;
724 }
725
726 dns_resource_record_ref(rr);
727
728 if (scope->conflict_event_source)
729 return 0;
730
731 random_bytes(&jitter, sizeof(jitter));
732 jitter %= LLMNR_JITTER_INTERVAL_USEC;
733
734 r = sd_event_add_time(scope->manager->event,
735 &scope->conflict_event_source,
736 clock_boottime_or_monotonic(),
737 now(clock_boottime_or_monotonic()) + jitter,
738 LLMNR_JITTER_INTERVAL_USEC,
739 on_conflict_dispatch, scope);
740 if (r < 0) {
741 log_debug("Failed to add conflict dispatch event: %s", strerror(-r));
742 return r;
743 }
744
745 return 0;
746}
747
748void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) {
749 unsigned i;
750 int r;
751
752 assert(scope);
753 assert(p);
754
755 if (p->protocol != DNS_PROTOCOL_LLMNR)
756 return;
757
758 if (DNS_PACKET_RRCOUNT(p) <= 0)
759 return;
760
761 if (DNS_PACKET_C(p) != 0)
762 return;
763
764 if (DNS_PACKET_T(p) != 0)
765 return;
766
767 if (manager_our_packet(scope->manager, p))
768 return;
769
770 r = dns_packet_extract(p);
771 if (r < 0) {
772 log_debug("Failed to extract packet: %s", strerror(-r));
773 return;
774 }
775
776 log_debug("Checking for conflicts...");
777
778 for (i = 0; i < p->answer->n_rrs; i++) {
779
780 /* Check for conflicts against the local zone. If we
781 * found one, we won't check any further */
782 r = dns_zone_check_conflicts(&scope->zone, p->answer->rrs[i]);
783 if (r != 0)
784 continue;
785
786 /* Check for conflicts against the local cache. If so,
787 * send out an advisory query, to inform everybody */
788 r = dns_cache_check_conflicts(&scope->cache, p->answer->rrs[i], p->family, &p->sender);
789 if (r <= 0)
790 continue;
791
792 dns_scope_notify_conflict(scope, p->answer->rrs[i]);
793 }
794}