]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-dns-stub.c
tree-wide: port users over to use new ERRNO_IS_ACCEPT_AGAIN() call
[thirdparty/systemd.git] / src / resolve / resolved-dns-stub.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include "errno-util.h"
4 #include "fd-util.h"
5 #include "missing_network.h"
6 #include "resolved-dns-stub.h"
7 #include "socket-util.h"
8
9 /* The MTU of the loopback device is 64K on Linux, advertise that as maximum datagram size, but subtract the Ethernet,
10 * IP and UDP header sizes */
11 #define ADVERTISE_DATAGRAM_SIZE_MAX (65536U-14U-20U-8U)
12
13 static int manager_dns_stub_udp_fd(Manager *m);
14 static int manager_dns_stub_tcp_fd(Manager *m);
15
16 static int dns_stub_make_reply_packet(
17 DnsPacket **p,
18 size_t max_size,
19 DnsQuestion *q,
20 DnsAnswer *answer,
21 bool *ret_truncated) {
22
23 bool truncated = false;
24 DnsResourceRecord *rr;
25 unsigned c = 0;
26 int r;
27
28 assert(p);
29
30 /* Note that we don't bother with any additional RRs, as this is stub is for local lookups only, and hence
31 * roundtrips aren't expensive. */
32
33 if (!*p) {
34 r = dns_packet_new(p, DNS_PROTOCOL_DNS, 0, max_size);
35 if (r < 0)
36 return r;
37
38 r = dns_packet_append_question(*p, q);
39 if (r < 0)
40 return r;
41
42 DNS_PACKET_HEADER(*p)->qdcount = htobe16(dns_question_size(q));
43 }
44
45 DNS_ANSWER_FOREACH(rr, answer) {
46
47 r = dns_question_matches_rr(q, rr, NULL);
48 if (r < 0)
49 return r;
50 if (r > 0)
51 goto add;
52
53 r = dns_question_matches_cname_or_dname(q, rr, NULL);
54 if (r < 0)
55 return r;
56 if (r > 0)
57 goto add;
58
59 continue;
60 add:
61 r = dns_packet_append_rr(*p, rr, 0, NULL, NULL);
62 if (r == -EMSGSIZE) {
63 truncated = true;
64 break;
65 }
66 if (r < 0)
67 return r;
68
69 c++;
70 }
71
72 if (ret_truncated)
73 *ret_truncated = truncated;
74 else if (truncated)
75 return -EMSGSIZE;
76
77 DNS_PACKET_HEADER(*p)->ancount = htobe16(be16toh(DNS_PACKET_HEADER(*p)->ancount) + c);
78
79 return 0;
80 }
81
82 static int dns_stub_finish_reply_packet(
83 DnsPacket *p,
84 uint16_t id,
85 int rcode,
86 bool tc, /* set the Truncated bit? */
87 bool add_opt, /* add an OPT RR to this packet? */
88 bool edns0_do, /* set the EDNS0 DNSSEC OK bit? */
89 bool ad) { /* set the DNSSEC authenticated data bit? */
90
91 int r;
92
93 assert(p);
94
95 if (add_opt) {
96 r = dns_packet_append_opt(p, ADVERTISE_DATAGRAM_SIZE_MAX, edns0_do, rcode, NULL);
97 if (r == -EMSGSIZE) /* Hit the size limit? then indicate truncation */
98 tc = true;
99 else if (r < 0)
100 return r;
101
102 } else {
103 /* If the client can't to EDNS0, don't do DO either */
104 edns0_do = false;
105
106 /* If the client didn't do EDNS, clamp the rcode to 4 bit */
107 if (rcode > 0xF)
108 rcode = DNS_RCODE_SERVFAIL;
109 }
110
111 /* Don't set the AD bit unless DO is on, too */
112 if (!edns0_do)
113 ad = false;
114
115 DNS_PACKET_HEADER(p)->id = id;
116
117 DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
118 1 /* qr */,
119 0 /* opcode */,
120 0 /* aa */,
121 tc /* tc */,
122 1 /* rd */,
123 1 /* ra */,
124 ad /* ad */,
125 0 /* cd */,
126 rcode));
127
128 return 0;
129 }
130
131 static int dns_stub_send(Manager *m, DnsStream *s, DnsPacket *p, DnsPacket *reply) {
132 int r;
133
134 assert(m);
135 assert(p);
136 assert(reply);
137
138 if (s)
139 r = dns_stream_write_packet(s, reply);
140 else {
141 int fd;
142
143 fd = manager_dns_stub_udp_fd(m);
144 if (fd < 0)
145 return log_debug_errno(fd, "Failed to get reply socket: %m");
146
147 /* Note that it is essential here that we explicitly choose the source IP address for this packet. This
148 * is because otherwise the kernel will choose it automatically based on the routing table and will
149 * thus pick 127.0.0.1 rather than 127.0.0.53. */
150
151 r = manager_send(m, fd, LOOPBACK_IFINDEX, p->family, &p->sender, p->sender_port, &p->destination, reply);
152 }
153 if (r < 0)
154 return log_debug_errno(r, "Failed to send reply packet: %m");
155
156 return 0;
157 }
158
159 static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rcode, bool authenticated) {
160 _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
161 int r;
162
163 assert(m);
164 assert(p);
165
166 r = dns_stub_make_reply_packet(&reply, DNS_PACKET_PAYLOAD_SIZE_MAX(p), p->question, NULL, NULL);
167 if (r < 0)
168 return log_debug_errno(r, "Failed to make failure packet: %m");
169
170 r = dns_stub_finish_reply_packet(reply, DNS_PACKET_ID(p), rcode, false, !!p->opt, DNS_PACKET_DO(p), authenticated);
171 if (r < 0)
172 return log_debug_errno(r, "Failed to build failure packet: %m");
173
174 return dns_stub_send(m, s, p, reply);
175 }
176
177 static void dns_stub_query_complete(DnsQuery *q) {
178 int r;
179
180 assert(q);
181 assert(q->request_dns_packet);
182
183 switch (q->state) {
184
185 case DNS_TRANSACTION_SUCCESS: {
186 bool truncated;
187
188 r = dns_stub_make_reply_packet(&q->reply_dns_packet, DNS_PACKET_PAYLOAD_SIZE_MAX(q->request_dns_packet), q->question_idna, q->answer, &truncated);
189 if (r < 0) {
190 log_debug_errno(r, "Failed to build reply packet: %m");
191 break;
192 }
193
194 if (!truncated) {
195 r = dns_query_process_cname(q);
196 if (r == -ELOOP) {
197 (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL, false);
198 break;
199 }
200 if (r < 0) {
201 log_debug_errno(r, "Failed to process CNAME: %m");
202 break;
203 }
204 if (r == DNS_QUERY_RESTARTED)
205 return;
206 }
207
208 r = dns_stub_finish_reply_packet(
209 q->reply_dns_packet,
210 DNS_PACKET_ID(q->request_dns_packet),
211 q->answer_rcode,
212 truncated,
213 !!q->request_dns_packet->opt,
214 DNS_PACKET_DO(q->request_dns_packet),
215 dns_query_fully_authenticated(q));
216 if (r < 0) {
217 log_debug_errno(r, "Failed to finish reply packet: %m");
218 break;
219 }
220
221 (void) dns_stub_send(q->manager, q->request_dns_stream, q->request_dns_packet, q->reply_dns_packet);
222 break;
223 }
224
225 case DNS_TRANSACTION_RCODE_FAILURE:
226 (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, q->answer_rcode, dns_query_fully_authenticated(q));
227 break;
228
229 case DNS_TRANSACTION_NOT_FOUND:
230 (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_NXDOMAIN, dns_query_fully_authenticated(q));
231 break;
232
233 case DNS_TRANSACTION_TIMEOUT:
234 case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED:
235 /* Propagate a timeout as a no packet, i.e. that the client also gets a timeout */
236 break;
237
238 case DNS_TRANSACTION_NO_SERVERS:
239 case DNS_TRANSACTION_INVALID_REPLY:
240 case DNS_TRANSACTION_ERRNO:
241 case DNS_TRANSACTION_ABORTED:
242 case DNS_TRANSACTION_DNSSEC_FAILED:
243 case DNS_TRANSACTION_NO_TRUST_ANCHOR:
244 case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
245 case DNS_TRANSACTION_NETWORK_DOWN:
246 (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL, false);
247 break;
248
249 case DNS_TRANSACTION_NULL:
250 case DNS_TRANSACTION_PENDING:
251 case DNS_TRANSACTION_VALIDATING:
252 default:
253 assert_not_reached("Impossible state");
254 }
255
256 dns_query_free(q);
257 }
258
259 static int dns_stub_stream_complete(DnsStream *s, int error) {
260 assert(s);
261
262 log_debug_errno(error, "DNS TCP connection terminated, destroying queries: %m");
263
264 for (;;) {
265 DnsQuery *q;
266
267 q = set_first(s->queries);
268 if (!q)
269 break;
270
271 dns_query_free(q);
272 }
273
274 /* This drops the implicit ref we keep around since it was allocated, as incoming stub connections
275 * should be kept as long as the client wants to. */
276 dns_stream_unref(s);
277 return 0;
278 }
279
280 static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
281 DnsQuery *q = NULL;
282 int r;
283
284 assert(m);
285 assert(p);
286 assert(p->protocol == DNS_PROTOCOL_DNS);
287
288 if (in_addr_is_localhost(p->family, &p->sender) <= 0 ||
289 in_addr_is_localhost(p->family, &p->destination) <= 0) {
290 log_error("Got packet on unexpected IP range, refusing.");
291 dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
292 goto fail;
293 }
294
295 r = dns_packet_extract(p);
296 if (r < 0) {
297 log_debug_errno(r, "Failed to extract resources from incoming packet, ignoring packet: %m");
298 dns_stub_send_failure(m, s, p, DNS_RCODE_FORMERR, false);
299 goto fail;
300 }
301
302 if (!DNS_PACKET_VERSION_SUPPORTED(p)) {
303 log_debug("Got EDNS OPT field with unsupported version number.");
304 dns_stub_send_failure(m, s, p, DNS_RCODE_BADVERS, false);
305 goto fail;
306 }
307
308 if (dns_type_is_obsolete(p->question->keys[0]->type)) {
309 log_debug("Got message with obsolete key type, refusing.");
310 dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
311 goto fail;
312 }
313
314 if (dns_type_is_zone_transer(p->question->keys[0]->type)) {
315 log_debug("Got request for zone transfer, refusing.");
316 dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
317 goto fail;
318 }
319
320 if (!DNS_PACKET_RD(p)) {
321 /* If the "rd" bit is off (i.e. recursion was not requested), then refuse operation */
322 log_debug("Got request with recursion disabled, refusing.");
323 dns_stub_send_failure(m, s, p, DNS_RCODE_REFUSED, false);
324 goto fail;
325 }
326
327 if (DNS_PACKET_DO(p) && DNS_PACKET_CD(p)) {
328 log_debug("Got request with DNSSEC CD bit set, refusing.");
329 dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
330 goto fail;
331 }
332
333 r = dns_query_new(m, &q, p->question, p->question, 0, SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_SEARCH);
334 if (r < 0) {
335 log_error_errno(r, "Failed to generate query object: %m");
336 dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
337 goto fail;
338 }
339
340 /* Request that the TTL is corrected by the cached time for this lookup, so that we return vaguely useful TTLs */
341 q->clamp_ttl = true;
342
343 q->request_dns_packet = dns_packet_ref(p);
344 q->request_dns_stream = dns_stream_ref(s); /* make sure the stream stays around until we can send a reply through it */
345 q->complete = dns_stub_query_complete;
346
347 if (s) {
348 /* Remember which queries belong to this stream, so that we can cancel them when the stream
349 * is disconnected early */
350
351 r = set_ensure_allocated(&s->queries, &trivial_hash_ops);
352 if (r < 0) {
353 log_oom();
354 goto fail;
355 }
356
357 if (set_put(s->queries, q) < 0) {
358 log_oom();
359 goto fail;
360 }
361 }
362
363 r = dns_query_go(q);
364 if (r < 0) {
365 log_error_errno(r, "Failed to start query: %m");
366 dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
367 goto fail;
368 }
369
370 log_debug("Processing query...");
371 return;
372
373 fail:
374 dns_query_free(q);
375 }
376
377 static int on_dns_stub_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
378 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
379 Manager *m = userdata;
380 int r;
381
382 r = manager_recv(m, fd, DNS_PROTOCOL_DNS, &p);
383 if (r <= 0)
384 return r;
385
386 if (dns_packet_validate_query(p) > 0) {
387 log_debug("Got DNS stub UDP query packet for id %u", DNS_PACKET_ID(p));
388
389 dns_stub_process_query(m, NULL, p);
390 } else
391 log_debug("Invalid DNS stub UDP packet, ignoring.");
392
393 return 0;
394 }
395
396 static int manager_dns_stub_udp_fd(Manager *m) {
397 union sockaddr_union sa = {
398 .in.sin_family = AF_INET,
399 .in.sin_port = htobe16(53),
400 .in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB),
401 };
402 _cleanup_close_ int fd = -1;
403 int r;
404
405 if (m->dns_stub_udp_fd >= 0)
406 return m->dns_stub_udp_fd;
407
408 fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
409 if (fd < 0)
410 return -errno;
411
412 r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEADDR, true);
413 if (r < 0)
414 return r;
415
416 r = setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, true);
417 if (r < 0)
418 return r;
419
420 r = setsockopt_int(fd, IPPROTO_IP, IP_RECVTTL, true);
421 if (r < 0)
422 return r;
423
424 /* Make sure no traffic from outside the local host can leak to onto this socket */
425 r = socket_bind_to_ifindex(fd, LOOPBACK_IFINDEX);
426 if (r < 0)
427 return r;
428
429 if (bind(fd, &sa.sa, sizeof(sa.in)) < 0)
430 return -errno;
431
432 r = sd_event_add_io(m->event, &m->dns_stub_udp_event_source, fd, EPOLLIN, on_dns_stub_packet, m);
433 if (r < 0)
434 return r;
435
436 (void) sd_event_source_set_description(m->dns_stub_udp_event_source, "dns-stub-udp");
437
438 return m->dns_stub_udp_fd = TAKE_FD(fd);
439 }
440
441 static int on_dns_stub_stream_packet(DnsStream *s) {
442 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
443
444 assert(s);
445
446 p = dns_stream_take_read_packet(s);
447 assert(p);
448
449 if (dns_packet_validate_query(p) > 0) {
450 log_debug("Got DNS stub TCP query packet for id %u", DNS_PACKET_ID(p));
451
452 dns_stub_process_query(s->manager, s, p);
453 } else
454 log_debug("Invalid DNS stub TCP packet, ignoring.");
455
456 return 0;
457 }
458
459 static int on_dns_stub_stream(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
460 DnsStream *stream;
461 Manager *m = userdata;
462 int cfd, r;
463
464 cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
465 if (cfd < 0) {
466 if (ERRNO_IS_ACCEPT_AGAIN(errno))
467 return 0;
468
469 return -errno;
470 }
471
472 r = dns_stream_new(m, &stream, DNS_STREAM_STUB, DNS_PROTOCOL_DNS, cfd, NULL);
473 if (r < 0) {
474 safe_close(cfd);
475 return r;
476 }
477
478 stream->on_packet = on_dns_stub_stream_packet;
479 stream->complete = dns_stub_stream_complete;
480
481 /* We let the reference to the stream dangle here, it will be dropped later by the complete callback. */
482
483 return 0;
484 }
485
486 static int manager_dns_stub_tcp_fd(Manager *m) {
487 union sockaddr_union sa = {
488 .in.sin_family = AF_INET,
489 .in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB),
490 .in.sin_port = htobe16(53),
491 };
492 _cleanup_close_ int fd = -1;
493 int r;
494
495 if (m->dns_stub_tcp_fd >= 0)
496 return m->dns_stub_tcp_fd;
497
498 fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
499 if (fd < 0)
500 return -errno;
501
502 r = setsockopt_int(fd, IPPROTO_IP, IP_TTL, true);
503 if (r < 0)
504 return r;
505
506 r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEADDR, true);
507 if (r < 0)
508 return r;
509
510 r = setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, true);
511 if (r < 0)
512 return r;
513
514 r = setsockopt_int(fd, IPPROTO_IP, IP_RECVTTL, true);
515 if (r < 0)
516 return r;
517
518 /* Make sure no traffic from outside the local host can leak to onto this socket */
519 r = socket_bind_to_ifindex(fd, LOOPBACK_IFINDEX);
520 if (r < 0)
521 return r;
522
523 if (bind(fd, &sa.sa, sizeof(sa.in)) < 0)
524 return -errno;
525
526 if (listen(fd, SOMAXCONN) < 0)
527 return -errno;
528
529 r = sd_event_add_io(m->event, &m->dns_stub_tcp_event_source, fd, EPOLLIN, on_dns_stub_stream, m);
530 if (r < 0)
531 return r;
532
533 (void) sd_event_source_set_description(m->dns_stub_tcp_event_source, "dns-stub-tcp");
534
535 return m->dns_stub_tcp_fd = TAKE_FD(fd);
536 }
537
538 int manager_dns_stub_start(Manager *m) {
539 const char *t = "UDP";
540 int r = 0;
541
542 assert(m);
543
544 if (m->dns_stub_listener_mode == DNS_STUB_LISTENER_NO)
545 log_debug("Not creating stub listener.");
546 else
547 log_debug("Creating stub listener using %s.",
548 m->dns_stub_listener_mode == DNS_STUB_LISTENER_UDP ? "UDP" :
549 m->dns_stub_listener_mode == DNS_STUB_LISTENER_TCP ? "TCP" :
550 "UDP/TCP");
551
552 if (IN_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_YES, DNS_STUB_LISTENER_UDP))
553 r = manager_dns_stub_udp_fd(m);
554
555 if (r >= 0 &&
556 IN_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_YES, DNS_STUB_LISTENER_TCP)) {
557 t = "TCP";
558 r = manager_dns_stub_tcp_fd(m);
559 }
560
561 if (IN_SET(r, -EADDRINUSE, -EPERM)) {
562 if (r == -EADDRINUSE)
563 log_warning_errno(r,
564 "Another process is already listening on %s socket 127.0.0.53:53.\n"
565 "Turning off local DNS stub support.", t);
566 else
567 log_warning_errno(r,
568 "Failed to listen on %s socket 127.0.0.53:53: %m.\n"
569 "Turning off local DNS stub support.", t);
570 manager_dns_stub_stop(m);
571 } else if (r < 0)
572 return log_error_errno(r, "Failed to listen on %s socket 127.0.0.53:53: %m", t);
573
574 return 0;
575 }
576
577 void manager_dns_stub_stop(Manager *m) {
578 assert(m);
579
580 m->dns_stub_udp_event_source = sd_event_source_unref(m->dns_stub_udp_event_source);
581 m->dns_stub_tcp_event_source = sd_event_source_unref(m->dns_stub_tcp_event_source);
582
583 m->dns_stub_udp_fd = safe_close(m->dns_stub_udp_fd);
584 m->dns_stub_tcp_fd = safe_close(m->dns_stub_tcp_fd);
585 }