]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-scope.c
resolved: properly return start index when appending RR to packet
[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
31#define SEND_TIMEOUT_USEC (2*USEC_PER_SEC)
32
0dd25fb9 33int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int family) {
74b2466e
LP
34 DnsScope *s;
35
36 assert(m);
37 assert(ret);
38
39 s = new0(DnsScope, 1);
40 if (!s)
41 return -ENOMEM;
42
43 s->manager = m;
1716f6dc
LP
44 s->link = l;
45 s->protocol = protocol;
46 s->family = family;
74b2466e
LP
47
48 LIST_PREPEND(scopes, m->dns_scopes, s);
49
1716f6dc
LP
50 dns_scope_llmnr_membership(s, true);
51
46f08bea 52 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 53
74b2466e
LP
54 *ret = s;
55 return 0;
56}
57
58DnsScope* dns_scope_free(DnsScope *s) {
faa133f3
LP
59 DnsQueryTransaction *t;
60
74b2466e
LP
61 if (!s)
62 return NULL;
63
46f08bea 64 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
65
66 dns_scope_llmnr_membership(s, false);
67
faa133f3
LP
68 while ((t = s->transactions)) {
69
70 /* Abort the transaction, but make sure it is not
71 * freed while we still look at it */
74b2466e 72
faa133f3
LP
73 t->block_gc++;
74 dns_query_transaction_complete(t, DNS_QUERY_ABORTED);
75 t->block_gc--;
74b2466e 76
faa133f3 77 dns_query_transaction_free(t);
74b2466e
LP
78 }
79
322345fd 80 dns_cache_flush(&s->cache);
623a4c97 81 dns_zone_flush(&s->zone);
322345fd 82
74b2466e
LP
83 LIST_REMOVE(scopes, s->manager->dns_scopes, s);
84 strv_free(s->domains);
85 free(s);
86
87 return NULL;
88}
89
90DnsServer *dns_scope_get_server(DnsScope *s) {
91 assert(s);
92
1716f6dc
LP
93 if (s->protocol != DNS_PROTOCOL_DNS)
94 return NULL;
95
74b2466e
LP
96 if (s->link)
97 return link_get_dns_server(s->link);
98 else
99 return manager_get_dns_server(s->manager);
100}
101
102void dns_scope_next_dns_server(DnsScope *s) {
103 assert(s);
104
1716f6dc
LP
105 if (s->protocol != DNS_PROTOCOL_DNS)
106 return;
107
74b2466e
LP
108 if (s->link)
109 link_next_dns_server(s->link);
110 else
111 manager_next_dns_server(s->manager);
112}
113
114int dns_scope_send(DnsScope *s, DnsPacket *p) {
1716f6dc
LP
115 union in_addr_union addr;
116 int ifindex = 0, r;
0dd25fb9 117 int family;
1716f6dc
LP
118 uint16_t port;
119 uint32_t mtu;
120 int fd;
74b2466e
LP
121
122 assert(s);
123 assert(p);
1716f6dc 124 assert(p->protocol == s->protocol);
ad867662
LP
125
126 if (s->link) {
1716f6dc 127 mtu = s->link->mtu;
74b2466e 128 ifindex = s->link->ifindex;
1716f6dc 129 } else
e1c95994 130 mtu = manager_find_mtu(s->manager);
74b2466e 131
1716f6dc
LP
132 if (s->protocol == DNS_PROTOCOL_DNS) {
133 DnsServer *srv;
e1c95994 134
623a4c97
LP
135 if (DNS_PACKET_QDCOUNT(p) > 1)
136 return -ENOTSUP;
137
1716f6dc
LP
138 srv = dns_scope_get_server(s);
139 if (!srv)
140 return -ESRCH;
141
142 family = srv->family;
143 addr = srv->address;
144 port = 53;
145
146 if (p->size > DNS_PACKET_UNICAST_SIZE_MAX)
147 return -EMSGSIZE;
148
149 if (p->size > mtu)
150 return -EMSGSIZE;
151
152 if (family == AF_INET)
153 fd = manager_dns_ipv4_fd(s->manager);
154 else if (family == AF_INET6)
155 fd = manager_dns_ipv6_fd(s->manager);
156 else
157 return -EAFNOSUPPORT;
158 if (fd < 0)
159 return fd;
160
161 } else if (s->protocol == DNS_PROTOCOL_LLMNR) {
162
163 if (DNS_PACKET_QDCOUNT(p) > 1)
164 return -ENOTSUP;
165
166 family = s->family;
167 port = 5355;
168
169 if (family == AF_INET) {
170 addr.in = LLMNR_MULTICAST_IPV4_ADDRESS;
1716f6dc
LP
171 fd = manager_llmnr_ipv4_udp_fd(s->manager);
172 } else if (family == AF_INET6) {
173 addr.in6 = LLMNR_MULTICAST_IPV6_ADDRESS;
174 fd = manager_llmnr_ipv6_udp_fd(s->manager);
1716f6dc
LP
175 } else
176 return -EAFNOSUPPORT;
177 if (fd < 0)
178 return fd;
179 } else
74b2466e
LP
180 return -EAFNOSUPPORT;
181
1716f6dc 182 r = manager_send(s->manager, fd, ifindex, family, &addr, port, p);
74b2466e
LP
183 if (r < 0)
184 return r;
185
186 return 1;
187}
188
623a4c97 189int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *address, uint16_t port) {
ad867662
LP
190 _cleanup_close_ int fd = -1;
191 union sockaddr_union sa = {};
192 socklen_t salen;
623a4c97
LP
193 static const int one = 1;
194 int ret, r;
ad867662
LP
195
196 assert(s);
623a4c97 197 assert((family == AF_UNSPEC) == !address);
ad867662 198
623a4c97
LP
199 if (family == AF_UNSPEC) {
200 DnsServer *srv;
201
202 srv = dns_scope_get_server(s);
203 if (!srv)
204 return -ESRCH;
205
206 sa.sa.sa_family = srv->family;
207 if (srv->family == AF_INET) {
208 sa.in.sin_port = htobe16(port);
209 sa.in.sin_addr = srv->address.in;
210 salen = sizeof(sa.in);
211 } else if (srv->family == AF_INET6) {
212 sa.in6.sin6_port = htobe16(port);
213 sa.in6.sin6_addr = srv->address.in6;
214 sa.in6.sin6_scope_id = s->link ? s->link->ifindex : 0;
215 salen = sizeof(sa.in6);
216 } else
217 return -EAFNOSUPPORT;
218 } else {
219 sa.sa.sa_family = family;
220
221 if (family == AF_INET) {
222 sa.in.sin_port = htobe16(port);
223 sa.in.sin_addr = address->in;
224 salen = sizeof(sa.in);
225 } else if (family == AF_INET6) {
226 sa.in6.sin6_port = htobe16(port);
227 sa.in6.sin6_addr = 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 }
ad867662 233
623a4c97 234 fd = socket(sa.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
ad867662
LP
235 if (fd < 0)
236 return -errno;
237
623a4c97
LP
238 r = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
239 if (r < 0)
240 return -errno;
241
242 if (s->link) {
243 uint32_t ifindex = htobe32(s->link->ifindex);
244
245 if (sa.sa.sa_family == AF_INET) {
246 r = setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex));
247 if (r < 0)
248 return -errno;
249 } else if (sa.sa.sa_family == AF_INET6) {
250 r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex));
251 if (r < 0)
252 return -errno;
253 }
254 }
255
256 if (s->protocol == DNS_PROTOCOL_LLMNR) {
bf3f1271 257 /* RFC 4795, section 2.5 requires the TTL to be set to 1 */
623a4c97
LP
258
259 if (sa.sa.sa_family == AF_INET) {
260 r = setsockopt(fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
261 if (r < 0)
262 return -errno;
263 } else if (sa.sa.sa_family == AF_INET6) {
264 r = setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one));
265 if (r < 0)
266 return -errno;
267 }
268 }
ad867662
LP
269
270 r = connect(fd, &sa.sa, salen);
271 if (r < 0 && errno != EINPROGRESS)
272 return -errno;
273
274 ret = fd;
275 fd = -1;
623a4c97 276
ad867662
LP
277 return ret;
278}
279
1716f6dc 280DnsScopeMatch dns_scope_good_domain(DnsScope *s, const char *domain) {
74b2466e
LP
281 char **i;
282
283 assert(s);
284 assert(domain);
285
286 STRV_FOREACH(i, s->domains)
faa133f3 287 if (dns_name_endswith(domain, *i) > 0)
74b2466e
LP
288 return DNS_SCOPE_YES;
289
faa133f3 290 if (dns_name_root(domain) != 0)
74b2466e
LP
291 return DNS_SCOPE_NO;
292
faec72d5
LP
293 if (is_localhost(domain))
294 return DNS_SCOPE_NO;
295
1716f6dc 296 if (s->protocol == DNS_PROTOCOL_DNS) {
faa133f3
LP
297 if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 &&
298 dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0 &&
299 dns_name_single_label(domain) == 0)
300 return DNS_SCOPE_MAYBE;
1716f6dc 301
faa133f3 302 return DNS_SCOPE_NO;
1716f6dc
LP
303 }
304
305 if (s->protocol == DNS_PROTOCOL_MDNS) {
faa133f3
LP
306 if (dns_name_endswith(domain, "254.169.in-addr.arpa") > 0 ||
307 dns_name_endswith(domain, "0.8.e.f.ip6.arpa") > 0 ||
308 (dns_name_endswith(domain, "local") > 0 && dns_name_equal(domain, "local") == 0))
74b2466e
LP
309 return DNS_SCOPE_MAYBE;
310
311 return DNS_SCOPE_NO;
312 }
313
1716f6dc 314 if (s->protocol == DNS_PROTOCOL_LLMNR) {
b914e211
LP
315 if (dns_name_endswith(domain, "in-addr.arpa") > 0 ||
316 dns_name_endswith(domain, "ip6.arpa") > 0 ||
faa133f3 317 dns_name_single_label(domain) > 0)
1716f6dc 318 return DNS_SCOPE_MAYBE;
74b2466e 319
1716f6dc 320 return DNS_SCOPE_NO;
74b2466e
LP
321 }
322
1716f6dc
LP
323 assert_not_reached("Unknown scope protocol");
324}
325
326int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) {
327 assert(s);
328 assert(key);
329
330 if (s->protocol == DNS_PROTOCOL_DNS)
331 return true;
332
333 /* On mDNS and LLMNR, send A and AAAA queries only on the
334 * respective scopes */
335
336 if (s->family == AF_INET && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_AAAA)
337 return false;
338
339 if (s->family == AF_INET6 && key->class == DNS_CLASS_IN && key->type == DNS_TYPE_A)
340 return false;
341
342 return true;
343}
344
345int dns_scope_llmnr_membership(DnsScope *s, bool b) {
346 int fd;
347
348 if (s->family == AF_INET) {
349 struct ip_mreqn mreqn = {
350 .imr_multiaddr = LLMNR_MULTICAST_IPV4_ADDRESS,
351 .imr_ifindex = s->link->ifindex,
352 };
353
354 fd = manager_llmnr_ipv4_udp_fd(s->manager);
355 if (fd < 0)
356 return fd;
357
358 if (setsockopt(fd, IPPROTO_IP, b ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0)
359 return -errno;
360
361 } else if (s->family == AF_INET6) {
362 struct ipv6_mreq mreq = {
363 .ipv6mr_multiaddr = LLMNR_MULTICAST_IPV6_ADDRESS,
364 .ipv6mr_interface = s->link->ifindex,
365 };
366
367 fd = manager_llmnr_ipv6_udp_fd(s->manager);
368 if (fd < 0)
369 return fd;
370
371 if (setsockopt(fd, IPPROTO_IPV6, b ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
372 return -errno;
373 } else
374 return -EAFNOSUPPORT;
375
376 return 0;
74b2466e 377}
623a4c97
LP
378
379int dns_scope_good_dns_server(DnsScope *s, int family, const union in_addr_union *address) {
380 assert(s);
381 assert(address);
382
383 if (s->protocol != DNS_PROTOCOL_DNS)
384 return 1;
385
386 if (s->link)
387 return !!link_find_dns_server(s->link, family, address);
388 else
389 return !!manager_find_dns_server(s->manager, family, address);
390}
391
8bf52d3d 392static int dns_scope_make_reply_packet(DnsScope *s, uint16_t id, int rcode, DnsQuestion *q, DnsAnswer *answer, DnsAnswer *soa, DnsPacket **ret) {
623a4c97
LP
393 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
394 unsigned i;
395 int r;
396
397 assert(s);
398
8bf52d3d 399 if (q->n_keys <= 0 && answer->n_rrs <= 0 && soa->n_rrs <= 0)
623a4c97
LP
400 return -EINVAL;
401
402 r = dns_packet_new(&p, s->protocol, 0);
403 if (r < 0)
404 return r;
405
406 DNS_PACKET_HEADER(p)->id = id;
ea917db9
LP
407 DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
408 1 /* qr */,
409 0 /* opcode */,
410 0 /* c */,
411 0 /* tc */,
412 0 /* t */,
413 0 /* (ra) */,
414 0 /* (ad) */,
415 0 /* (cd) */,
416 rcode));
623a4c97
LP
417
418 if (q) {
419 for (i = 0; i < q->n_keys; i++) {
420 r = dns_packet_append_key(p, q->keys[i], NULL);
421 if (r < 0)
422 return r;
423 }
424
425 DNS_PACKET_HEADER(p)->qdcount = htobe16(q->n_keys);
426 }
427
8bf52d3d
LP
428 if (answer) {
429 for (i = 0; i < answer->n_rrs; i++) {
430 r = dns_packet_append_rr(p, answer->rrs[i], NULL);
623a4c97
LP
431 if (r < 0)
432 return r;
433 }
434
8bf52d3d
LP
435 DNS_PACKET_HEADER(p)->ancount = htobe16(answer->n_rrs);
436 }
437
438 if (soa) {
439 for (i = 0; i < soa->n_rrs; i++) {
440 r = dns_packet_append_rr(p, soa->rrs[i], NULL);
441 if (r < 0)
442 return r;
443 }
444
445 DNS_PACKET_HEADER(p)->arcount = htobe16(soa->n_rrs);
623a4c97
LP
446 }
447
448 *ret = p;
449 p = NULL;
450
451 return 0;
452}
453
454void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
455 _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
8bf52d3d 456 _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
623a4c97
LP
457 int r, fd;
458
459 assert(s);
460 assert(p);
461
462 if (p->protocol != DNS_PROTOCOL_LLMNR)
463 return;
464
2442b93d
LP
465 if (p->ipproto == IPPROTO_UDP) {
466 /* Don't accept UDP queries directed to anything but
467 * the LLMNR multicast addresses. See RFC 4795,
468 * section 2.5.*/
469
470 if (p->family == AF_INET && !in_addr_equal(AF_INET, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV4_ADDRESS))
471 return;
472
473 if (p->family == AF_INET6 && !in_addr_equal(AF_INET6, &p->destination, (union in_addr_union*) &LLMNR_MULTICAST_IPV6_ADDRESS))
474 return;
475 }
476
623a4c97
LP
477 r = dns_packet_extract(p);
478 if (r < 0) {
479 log_debug("Failed to extract resources from incoming packet: %s", strerror(-r));
480 return;
481 }
482
ea917db9
LP
483 if (DNS_PACKET_C(p)) {
484 /* FIXME: Somebody notified us about a likely conflict */
485 return;
486 }
487
8bf52d3d 488 r = dns_zone_lookup(&s->zone, p->question, &answer, &soa);
623a4c97
LP
489 if (r < 0) {
490 log_debug("Failed to lookup key: %s", strerror(-r));
491 return;
492 }
493 if (r == 0)
494 return;
495
af93291c
LP
496 dns_answer_order_by_scope(answer, in_addr_is_link_local(p->family, &p->sender) > 0);
497
8bf52d3d 498 r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, p->question, answer, soa, &reply);
623a4c97
LP
499 if (r < 0) {
500 log_debug("Failed to build reply packet: %s", strerror(-r));
501 return;
502 }
503
504 if (stream)
505 r = dns_stream_write_packet(stream, reply);
506 else {
507 if (p->family == AF_INET)
508 fd = manager_llmnr_ipv4_udp_fd(s->manager);
509 else if (p->family == AF_INET6)
510 fd = manager_llmnr_ipv6_udp_fd(s->manager);
511 else {
512 log_debug("Unknown protocol");
513 return;
514 }
515 if (fd < 0) {
516 log_debug("Failed to get reply socket: %s", strerror(-fd));
517 return;
518 }
519
520 r = manager_send(s->manager, fd, p->ifindex, p->family, &p->sender, p->sender_port, reply);
521 }
522
523 if (r < 0) {
524 log_debug("Failed to send reply packet: %s", strerror(-r));
525 return;
526 }
527}