]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-stub.c
resolve: use sd_event_source_set_io_fd_own() for stub listners
[thirdparty/systemd.git] / src / resolve / resolved-dns-stub.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
b30bf55d 2
ca8b62b5
YW
3#include <net/if_arp.h>
4
4ff9bc2e 5#include "errno-util.h"
b30bf55d 6#include "fd-util.h"
ef118d00 7#include "missing_network.h"
af8b1384 8#include "missing_socket.h"
b30bf55d 9#include "resolved-dns-stub.h"
1f05101f 10#include "socket-netlink.h"
b30bf55d
LP
11#include "socket-util.h"
12
13/* The MTU of the loopback device is 64K on Linux, advertise that as maximum datagram size, but subtract the Ethernet,
14 * IP and UDP header sizes */
15#define ADVERTISE_DATAGRAM_SIZE_MAX (65536U-14U-20U-8U)
16
424e490b
ZJS
17static int manager_dns_stub_udp_fd(Manager *m);
18static int manager_dns_stub_tcp_fd(Manager *m);
19
bf22f231 20int dns_stub_listener_extra_new(DNSStubListenerExtra **ret) {
1f05101f
SS
21 DNSStubListenerExtra *l;
22
7216a3b5 23 l = new0(DNSStubListenerExtra, 1);
1f05101f
SS
24 if (!l)
25 return -ENOMEM;
26
1f05101f
SS
27 *ret = TAKE_PTR(l);
28
29 return 0;
30}
31
bf22f231
YW
32DNSStubListenerExtra *dns_stub_listener_extra_free(DNSStubListenerExtra *p) {
33 if (!p)
34 return NULL;
35
36 p->udp_event_source = sd_event_source_unref(p->udp_event_source);
37 p->tcp_event_source = sd_event_source_unref(p->tcp_event_source);
bf22f231
YW
38
39 return mfree(p);
40}
41
b30bf55d 42static int dns_stub_make_reply_packet(
e8d23f92 43 DnsPacket **p,
51027656 44 size_t max_size,
b30bf55d 45 DnsQuestion *q,
51027656
LP
46 DnsAnswer *answer,
47 bool *ret_truncated) {
b30bf55d 48
51027656 49 bool truncated = false;
b30bf55d
LP
50 DnsResourceRecord *rr;
51 unsigned c = 0;
52 int r;
53
e8d23f92
LP
54 assert(p);
55
b30bf55d
LP
56 /* Note that we don't bother with any additional RRs, as this is stub is for local lookups only, and hence
57 * roundtrips aren't expensive. */
58
e8d23f92 59 if (!*p) {
51027656 60 r = dns_packet_new(p, DNS_PROTOCOL_DNS, 0, max_size);
e8d23f92
LP
61 if (r < 0)
62 return r;
b30bf55d 63
e8d23f92
LP
64 r = dns_packet_append_question(*p, q);
65 if (r < 0)
66 return r;
b30bf55d 67
e8d23f92
LP
68 DNS_PACKET_HEADER(*p)->qdcount = htobe16(dns_question_size(q));
69 }
b30bf55d
LP
70
71 DNS_ANSWER_FOREACH(rr, answer) {
e8d23f92 72
b30bf55d
LP
73 r = dns_question_matches_rr(q, rr, NULL);
74 if (r < 0)
75 return r;
76 if (r > 0)
77 goto add;
78
79 r = dns_question_matches_cname_or_dname(q, rr, NULL);
80 if (r < 0)
81 return r;
82 if (r > 0)
83 goto add;
84
85 continue;
86 add:
01c901e2 87 r = dns_packet_append_rr(*p, rr, 0, NULL, NULL);
51027656
LP
88 if (r == -EMSGSIZE) {
89 truncated = true;
90 break;
91 }
b30bf55d
LP
92 if (r < 0)
93 return r;
94
95 c++;
96 }
e8d23f92 97
51027656
LP
98 if (ret_truncated)
99 *ret_truncated = truncated;
100 else if (truncated)
101 return -EMSGSIZE;
102
e8d23f92
LP
103 DNS_PACKET_HEADER(*p)->ancount = htobe16(be16toh(DNS_PACKET_HEADER(*p)->ancount) + c);
104
105 return 0;
106}
107
108static int dns_stub_finish_reply_packet(
109 DnsPacket *p,
110 uint16_t id,
111 int rcode,
51027656 112 bool tc, /* set the Truncated bit? */
e8d23f92
LP
113 bool add_opt, /* add an OPT RR to this packet? */
114 bool edns0_do, /* set the EDNS0 DNSSEC OK bit? */
115 bool ad) { /* set the DNSSEC authenticated data bit? */
116
117 int r;
118
119 assert(p);
120
ff4caaae
LP
121 if (add_opt) {
122 r = dns_packet_append_opt(p, ADVERTISE_DATAGRAM_SIZE_MAX, edns0_do, rcode, NULL);
123 if (r == -EMSGSIZE) /* Hit the size limit? then indicate truncation */
124 tc = true;
125 else if (r < 0)
126 return r;
127
128 } else {
941dd294
LP
129 /* If the client can't to EDNS0, don't do DO either */
130 edns0_do = false;
131
132 /* If the client didn't do EDNS, clamp the rcode to 4 bit */
133 if (rcode > 0xF)
134 rcode = DNS_RCODE_SERVFAIL;
135 }
136
137 /* Don't set the AD bit unless DO is on, too */
138 if (!edns0_do)
139 ad = false;
e8d23f92
LP
140
141 DNS_PACKET_HEADER(p)->id = id;
142
143 DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS(
51027656
LP
144 1 /* qr */,
145 0 /* opcode */,
146 0 /* aa */,
147 tc /* tc */,
148 1 /* rd */,
149 1 /* ra */,
e8d23f92 150 ad /* ad */,
51027656 151 0 /* cd */,
e8d23f92 152 rcode));
b30bf55d 153
b30bf55d
LP
154 return 0;
155}
156
b30bf55d
LP
157static int dns_stub_send(Manager *m, DnsStream *s, DnsPacket *p, DnsPacket *reply) {
158 int r;
159
160 assert(m);
161 assert(p);
162 assert(reply);
163
164 if (s)
165 r = dns_stream_write_packet(s, reply);
166 else {
167 int fd;
168
b30bf55d
LP
169 fd = manager_dns_stub_udp_fd(m);
170 if (fd < 0)
171 return log_debug_errno(fd, "Failed to get reply socket: %m");
172
173 /* Note that it is essential here that we explicitly choose the source IP address for this packet. This
174 * is because otherwise the kernel will choose it automatically based on the routing table and will
175 * thus pick 127.0.0.1 rather than 127.0.0.53. */
176
177 r = manager_send(m, fd, LOOPBACK_IFINDEX, p->family, &p->sender, p->sender_port, &p->destination, reply);
178 }
179 if (r < 0)
180 return log_debug_errno(r, "Failed to send reply packet: %m");
181
182 return 0;
183}
184
2b2d98c1 185static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rcode, bool authenticated) {
b30bf55d
LP
186 _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
187 int r;
188
189 assert(m);
190 assert(p);
191
51027656 192 r = dns_stub_make_reply_packet(&reply, DNS_PACKET_PAYLOAD_SIZE_MAX(p), p->question, NULL, NULL);
e8d23f92
LP
193 if (r < 0)
194 return log_debug_errno(r, "Failed to make failure packet: %m");
195
51027656 196 r = dns_stub_finish_reply_packet(reply, DNS_PACKET_ID(p), rcode, false, !!p->opt, DNS_PACKET_DO(p), authenticated);
b30bf55d
LP
197 if (r < 0)
198 return log_debug_errno(r, "Failed to build failure packet: %m");
199
200 return dns_stub_send(m, s, p, reply);
201}
202
203static void dns_stub_query_complete(DnsQuery *q) {
204 int r;
205
206 assert(q);
207 assert(q->request_dns_packet);
208
209 switch (q->state) {
210
51027656
LP
211 case DNS_TRANSACTION_SUCCESS: {
212 bool truncated;
e8d23f92 213
51027656 214 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);
e8d23f92
LP
215 if (r < 0) {
216 log_debug_errno(r, "Failed to build reply packet: %m");
217 break;
218 }
b30bf55d 219
aa11cab9
LP
220 if (!truncated) {
221 r = dns_query_process_cname(q);
222 if (r == -ELOOP) {
223 (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL, false);
224 break;
225 }
226 if (r < 0) {
227 log_debug_errno(r, "Failed to process CNAME: %m");
228 break;
229 }
230 if (r == DNS_QUERY_RESTARTED)
231 return;
e8d23f92 232 }
e8d23f92
LP
233
234 r = dns_stub_finish_reply_packet(
235 q->reply_dns_packet,
b30bf55d
LP
236 DNS_PACKET_ID(q->request_dns_packet),
237 q->answer_rcode,
51027656 238 truncated,
b30bf55d
LP
239 !!q->request_dns_packet->opt,
240 DNS_PACKET_DO(q->request_dns_packet),
941dd294 241 dns_query_fully_authenticated(q));
b30bf55d 242 if (r < 0) {
e8d23f92 243 log_debug_errno(r, "Failed to finish reply packet: %m");
b30bf55d
LP
244 break;
245 }
246
e8d23f92 247 (void) dns_stub_send(q->manager, q->request_dns_stream, q->request_dns_packet, q->reply_dns_packet);
b30bf55d 248 break;
51027656 249 }
b30bf55d
LP
250
251 case DNS_TRANSACTION_RCODE_FAILURE:
2b2d98c1 252 (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, q->answer_rcode, dns_query_fully_authenticated(q));
b30bf55d
LP
253 break;
254
255 case DNS_TRANSACTION_NOT_FOUND:
2b2d98c1 256 (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_NXDOMAIN, dns_query_fully_authenticated(q));
b30bf55d
LP
257 break;
258
259 case DNS_TRANSACTION_TIMEOUT:
260 case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED:
261 /* Propagate a timeout as a no packet, i.e. that the client also gets a timeout */
262 break;
263
264 case DNS_TRANSACTION_NO_SERVERS:
265 case DNS_TRANSACTION_INVALID_REPLY:
266 case DNS_TRANSACTION_ERRNO:
267 case DNS_TRANSACTION_ABORTED:
268 case DNS_TRANSACTION_DNSSEC_FAILED:
269 case DNS_TRANSACTION_NO_TRUST_ANCHOR:
270 case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
271 case DNS_TRANSACTION_NETWORK_DOWN:
2b2d98c1 272 (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL, false);
b30bf55d
LP
273 break;
274
275 case DNS_TRANSACTION_NULL:
276 case DNS_TRANSACTION_PENDING:
277 case DNS_TRANSACTION_VALIDATING:
278 default:
279 assert_not_reached("Impossible state");
280 }
281
b30bf55d
LP
282 dns_query_free(q);
283}
284
285static int dns_stub_stream_complete(DnsStream *s, int error) {
286 assert(s);
287
b412af57
LP
288 log_debug_errno(error, "DNS TCP connection terminated, destroying queries: %m");
289
290 for (;;) {
291 DnsQuery *q;
292
293 q = set_first(s->queries);
294 if (!q)
295 break;
b30bf55d 296
b412af57
LP
297 dns_query_free(q);
298 }
b30bf55d 299
b412af57
LP
300 /* This drops the implicit ref we keep around since it was allocated, as incoming stub connections
301 * should be kept as long as the client wants to. */
302 dns_stream_unref(s);
b30bf55d
LP
303 return 0;
304}
305
306static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
ceb17827 307 _cleanup_(dns_query_freep) DnsQuery *q = NULL;
b30bf55d
LP
308 int r;
309
310 assert(m);
311 assert(p);
312 assert(p->protocol == DNS_PROTOCOL_DNS);
313
b30bf55d
LP
314 if (in_addr_is_localhost(p->family, &p->sender) <= 0 ||
315 in_addr_is_localhost(p->family, &p->destination) <= 0) {
316 log_error("Got packet on unexpected IP range, refusing.");
2b2d98c1 317 dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
ceb17827 318 return;
b30bf55d
LP
319 }
320
321 r = dns_packet_extract(p);
322 if (r < 0) {
323 log_debug_errno(r, "Failed to extract resources from incoming packet, ignoring packet: %m");
2b2d98c1 324 dns_stub_send_failure(m, s, p, DNS_RCODE_FORMERR, false);
ceb17827 325 return;
b30bf55d
LP
326 }
327
328 if (!DNS_PACKET_VERSION_SUPPORTED(p)) {
329 log_debug("Got EDNS OPT field with unsupported version number.");
2b2d98c1 330 dns_stub_send_failure(m, s, p, DNS_RCODE_BADVERS, false);
ceb17827 331 return;
b30bf55d
LP
332 }
333
334 if (dns_type_is_obsolete(p->question->keys[0]->type)) {
335 log_debug("Got message with obsolete key type, refusing.");
2b2d98c1 336 dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
ceb17827 337 return;
b30bf55d
LP
338 }
339
340 if (dns_type_is_zone_transer(p->question->keys[0]->type)) {
341 log_debug("Got request for zone transfer, refusing.");
2b2d98c1 342 dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
ceb17827 343 return;
b30bf55d
LP
344 }
345
346 if (!DNS_PACKET_RD(p)) {
347 /* If the "rd" bit is off (i.e. recursion was not requested), then refuse operation */
348 log_debug("Got request with recursion disabled, refusing.");
2b2d98c1 349 dns_stub_send_failure(m, s, p, DNS_RCODE_REFUSED, false);
ceb17827 350 return;
b30bf55d
LP
351 }
352
353 if (DNS_PACKET_DO(p) && DNS_PACKET_CD(p)) {
354 log_debug("Got request with DNSSEC CD bit set, refusing.");
2b2d98c1 355 dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
ceb17827 356 return;
b30bf55d
LP
357 }
358
e8d23f92 359 r = dns_query_new(m, &q, p->question, p->question, 0, SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_SEARCH);
b30bf55d
LP
360 if (r < 0) {
361 log_error_errno(r, "Failed to generate query object: %m");
2b2d98c1 362 dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
ceb17827 363 return;
b30bf55d
LP
364 }
365
366 /* Request that the TTL is corrected by the cached time for this lookup, so that we return vaguely useful TTLs */
367 q->clamp_ttl = true;
368
369 q->request_dns_packet = dns_packet_ref(p);
370 q->request_dns_stream = dns_stream_ref(s); /* make sure the stream stays around until we can send a reply through it */
371 q->complete = dns_stub_query_complete;
372
373 if (s) {
b412af57
LP
374 /* Remember which queries belong to this stream, so that we can cancel them when the stream
375 * is disconnected early */
376
ceb17827 377 r = set_ensure_put(&s->queries, NULL, q);
b412af57
LP
378 if (r < 0) {
379 log_oom();
ceb17827 380 return;
b412af57 381 }
ceb17827 382 assert(r > 0);
b30bf55d
LP
383 }
384
385 r = dns_query_go(q);
386 if (r < 0) {
387 log_error_errno(r, "Failed to start query: %m");
2b2d98c1 388 dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
ceb17827 389 return;
b30bf55d
LP
390 }
391
52e63427 392 log_debug("Processing query...");
ceb17827 393 TAKE_PTR(q);
b30bf55d
LP
394}
395
396static int on_dns_stub_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
397 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
398 Manager *m = userdata;
399 int r;
400
401 r = manager_recv(m, fd, DNS_PROTOCOL_DNS, &p);
402 if (r <= 0)
403 return r;
404
405 if (dns_packet_validate_query(p) > 0) {
406 log_debug("Got DNS stub UDP query packet for id %u", DNS_PACKET_ID(p));
407
408 dns_stub_process_query(m, NULL, p);
409 } else
410 log_debug("Invalid DNS stub UDP packet, ignoring.");
411
412 return 0;
413}
414
af8b1384 415static int set_dns_stub_common_socket_options(int fd, int family) {
1f05101f
SS
416 int r;
417
418 assert(fd >= 0);
af8b1384 419 assert(IN_SET(family, AF_INET, AF_INET6));
1f05101f
SS
420
421 r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEADDR, true);
422 if (r < 0)
423 return r;
424
af8b1384
YW
425 if (family == AF_INET) {
426 r = setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, true);
427 if (r < 0)
428 return r;
1f05101f 429
af8b1384
YW
430 r = setsockopt_int(fd, IPPROTO_IP, IP_RECVTTL, true);
431 if (r < 0)
432 return r;
433 } else {
434 r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, true);
435 if (r < 0)
436 return r;
437
438 r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, true);
439 if (r < 0)
440 return r;
441 }
442
443 return 0;
1f05101f
SS
444}
445
424e490b 446static int manager_dns_stub_udp_fd(Manager *m) {
b30bf55d
LP
447 union sockaddr_union sa = {
448 .in.sin_family = AF_INET,
449 .in.sin_port = htobe16(53),
450 .in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB),
451 };
424e490b 452 _cleanup_close_ int fd = -1;
b30bf55d
LP
453 int r;
454
7216a3b5
YW
455 if (m->dns_stub_udp_event_source)
456 return sd_event_source_get_io_fd(m->dns_stub_udp_event_source);
b30bf55d 457
424e490b
ZJS
458 fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
459 if (fd < 0)
b30bf55d
LP
460 return -errno;
461
af8b1384 462 r = set_dns_stub_common_socket_options(fd, AF_INET);
2ff48e98
LP
463 if (r < 0)
464 return r;
b30bf55d
LP
465
466 /* Make sure no traffic from outside the local host can leak to onto this socket */
953a02d1
LP
467 r = socket_bind_to_ifindex(fd, LOOPBACK_IFINDEX);
468 if (r < 0)
469 return r;
b30bf55d 470
424e490b
ZJS
471 if (bind(fd, &sa.sa, sizeof(sa.in)) < 0)
472 return -errno;
b30bf55d 473
424e490b 474 r = sd_event_add_io(m->event, &m->dns_stub_udp_event_source, fd, EPOLLIN, on_dns_stub_packet, m);
b30bf55d 475 if (r < 0)
424e490b 476 return r;
b30bf55d 477
7216a3b5
YW
478 r = sd_event_source_set_io_fd_own(m->dns_stub_udp_event_source, true);
479 if (r < 0)
480 return r;
481
b30bf55d
LP
482 (void) sd_event_source_set_description(m->dns_stub_udp_event_source, "dns-stub-udp");
483
7216a3b5 484 return TAKE_FD(fd);
b30bf55d
LP
485}
486
1f05101f
SS
487static int manager_dns_stub_udp_fd_extra(Manager *m, DNSStubListenerExtra *l) {
488 _cleanup_free_ char *pretty = NULL;
489 _cleanup_close_ int fd = -1;
ca8b62b5 490 union sockaddr_union sa;
1f05101f
SS
491 int r;
492
7216a3b5 493 if (l->udp_event_source)
1f05101f
SS
494 return 0;
495
ca8b62b5
YW
496 if (l->family == AF_INET)
497 sa = (union sockaddr_union) {
498 .in.sin_family = l->family,
499 .in.sin_port = htobe16(l->port != 0 ? l->port : 53U),
500 .in.sin_addr = l->address.in,
501 };
502 else
503 sa = (union sockaddr_union) {
504 .in6.sin6_family = l->family,
505 .in6.sin6_port = htobe16(l->port != 0 ? l->port : 53U),
506 .in6.sin6_addr = l->address.in6,
507 };
508
509 fd = socket(l->family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
1f05101f
SS
510 if (fd < 0) {
511 r = -errno;
512 goto fail;
513 }
514
af8b1384
YW
515 if (l->family == AF_INET) {
516 r = setsockopt_int(fd, IPPROTO_IP, IP_FREEBIND, true);
517 if (r < 0)
518 goto fail;
519 }
1f05101f 520
af8b1384 521 r = set_dns_stub_common_socket_options(fd, l->family);
1f05101f
SS
522 if (r < 0)
523 goto fail;
524
ca8b62b5 525 if (bind(fd, &sa.sa, SOCKADDR_LEN(sa)) < 0) {
1f05101f
SS
526 r = -errno;
527 goto fail;
528 }
529
7314b397 530 r = sd_event_add_io(m->event, &l->udp_event_source, fd, EPOLLIN, on_dns_stub_packet, m);
1f05101f
SS
531 if (r < 0)
532 goto fail;
533
7216a3b5
YW
534 r = sd_event_source_set_io_fd_own(l->udp_event_source, true);
535 if (r < 0)
536 goto fail;
537
7314b397 538 (void) sd_event_source_set_description(l->udp_event_source, "dns-stub-udp-extra");
1f05101f
SS
539
540 if (DEBUG_LOGGING) {
ca8b62b5 541 (void) in_addr_port_to_string(l->family, &l->address, l->port, &pretty);
1f05101f
SS
542 log_debug("Listening on UDP socket %s.", strnull(pretty));
543 }
544
7216a3b5 545 return TAKE_FD(fd);
1f05101f 546
b4b7ea1b 547fail:
1c17bcb3 548 assert(r < 0);
ca8b62b5 549 (void) in_addr_port_to_string(l->family, &l->address, l->port, &pretty);
b4b7ea1b 550 if (r == -EADDRINUSE)
1c17bcb3
YW
551 return log_warning_errno(r, "Another process is already listening on UDP socket %s: %m", strnull(pretty));
552 return log_warning_errno(r, "Failed to listen on UDP socket %s: %m", strnull(pretty));
1f05101f
SS
553}
554
b30bf55d 555static int on_dns_stub_stream_packet(DnsStream *s) {
aa337a5e
LP
556 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
557
b30bf55d 558 assert(s);
b30bf55d 559
aa337a5e
LP
560 p = dns_stream_take_read_packet(s);
561 assert(p);
562
563 if (dns_packet_validate_query(p) > 0) {
564 log_debug("Got DNS stub TCP query packet for id %u", DNS_PACKET_ID(p));
b30bf55d 565
aa337a5e 566 dns_stub_process_query(s->manager, s, p);
b30bf55d
LP
567 } else
568 log_debug("Invalid DNS stub TCP packet, ignoring.");
569
b30bf55d
LP
570 return 0;
571}
572
573static int on_dns_stub_stream(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
574 DnsStream *stream;
575 Manager *m = userdata;
576 int cfd, r;
577
578 cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
579 if (cfd < 0) {
4ff9bc2e 580 if (ERRNO_IS_ACCEPT_AGAIN(errno))
b30bf55d
LP
581 return 0;
582
583 return -errno;
584 }
585
652ba568 586 r = dns_stream_new(m, &stream, DNS_STREAM_STUB, DNS_PROTOCOL_DNS, cfd, NULL);
b30bf55d
LP
587 if (r < 0) {
588 safe_close(cfd);
589 return r;
590 }
591
592 stream->on_packet = on_dns_stub_stream_packet;
b412af57 593 stream->complete = dns_stub_stream_complete;
b30bf55d 594
b412af57 595 /* We let the reference to the stream dangle here, it will be dropped later by the complete callback. */
b30bf55d
LP
596
597 return 0;
598}
599
424e490b 600static int manager_dns_stub_tcp_fd(Manager *m) {
b30bf55d
LP
601 union sockaddr_union sa = {
602 .in.sin_family = AF_INET,
603 .in.sin_addr.s_addr = htobe32(INADDR_DNS_STUB),
604 .in.sin_port = htobe16(53),
605 };
424e490b 606 _cleanup_close_ int fd = -1;
b30bf55d
LP
607 int r;
608
7216a3b5
YW
609 if (m->dns_stub_tcp_event_source)
610 return sd_event_source_get_io_fd(m->dns_stub_tcp_event_source);
b30bf55d 611
424e490b
ZJS
612 fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
613 if (fd < 0)
b30bf55d
LP
614 return -errno;
615
af8b1384 616 r = set_dns_stub_common_socket_options(fd, AF_INET);
2ff48e98
LP
617 if (r < 0)
618 return r;
b30bf55d 619
5b496410 620 r = setsockopt_int(fd, IPPROTO_IP, IP_TTL, 1);
2ff48e98
LP
621 if (r < 0)
622 return r;
b30bf55d
LP
623
624 /* Make sure no traffic from outside the local host can leak to onto this socket */
953a02d1
LP
625 r = socket_bind_to_ifindex(fd, LOOPBACK_IFINDEX);
626 if (r < 0)
627 return r;
b30bf55d 628
424e490b
ZJS
629 if (bind(fd, &sa.sa, sizeof(sa.in)) < 0)
630 return -errno;
b30bf55d 631
424e490b
ZJS
632 if (listen(fd, SOMAXCONN) < 0)
633 return -errno;
b30bf55d 634
424e490b 635 r = sd_event_add_io(m->event, &m->dns_stub_tcp_event_source, fd, EPOLLIN, on_dns_stub_stream, m);
b30bf55d 636 if (r < 0)
424e490b 637 return r;
b30bf55d 638
7216a3b5
YW
639 r = sd_event_source_set_io_fd_own(m->dns_stub_tcp_event_source, true);
640 if (r < 0)
641 return r;
642
b30bf55d
LP
643 (void) sd_event_source_set_description(m->dns_stub_tcp_event_source, "dns-stub-tcp");
644
7216a3b5 645 return TAKE_FD(fd);
b30bf55d
LP
646}
647
1f05101f
SS
648static int manager_dns_stub_tcp_fd_extra(Manager *m, DNSStubListenerExtra *l) {
649 _cleanup_free_ char *pretty = NULL;
650 _cleanup_close_ int fd = -1;
ca8b62b5 651 union sockaddr_union sa;
1f05101f
SS
652 int r;
653
7216a3b5
YW
654 if (l->tcp_event_source)
655 return sd_event_source_get_io_fd(l->tcp_event_source);;
1f05101f 656
ca8b62b5
YW
657 if (l->family == AF_INET)
658 sa = (union sockaddr_union) {
659 .in.sin_family = l->family,
660 .in.sin_port = htobe16(l->port != 0 ? l->port : 53U),
661 .in.sin_addr = l->address.in,
662 };
663 else
664 sa = (union sockaddr_union) {
665 .in6.sin6_family = l->family,
666 .in6.sin6_port = htobe16(l->port != 0 ? l->port : 53U),
667 .in6.sin6_addr = l->address.in6,
668 };
669
670 fd = socket(l->family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
1f05101f
SS
671 if (fd < 0) {
672 r = -errno;
673 goto fail;
674 }
675
af8b1384 676 r = set_dns_stub_common_socket_options(fd, l->family);
1f05101f
SS
677 if (r < 0)
678 goto fail;
679
d40f00d7
YW
680 /* Do not set IP_TTL for extra DNS stub listners, as the address may not be local and in that
681 * case people may want ttl > 1. */
1f05101f 682
af8b1384
YW
683 if (l->family == AF_INET)
684 r = setsockopt_int(fd, IPPROTO_IP, IP_FREEBIND, true);
685 else
686 r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_FREEBIND, true);
1f05101f
SS
687 if (r < 0)
688 goto fail;
689
ca8b62b5 690 if (bind(fd, &sa.sa, SOCKADDR_LEN(sa)) < 0) {
1f05101f
SS
691 r = -errno;
692 goto fail;
693 }
694
695 if (listen(fd, SOMAXCONN) < 0) {
696 r = -errno;
697 goto fail;
698 }
699
7314b397 700 r = sd_event_add_io(m->event, &l->tcp_event_source, fd, EPOLLIN, on_dns_stub_packet, m);
1f05101f
SS
701 if (r < 0)
702 goto fail;
703
7216a3b5
YW
704 r = sd_event_source_set_io_fd_own(l->tcp_event_source, true);
705 if (r < 0)
706 goto fail;
707
7314b397 708 (void) sd_event_source_set_description(l->tcp_event_source, "dns-stub-tcp-extra");
1f05101f
SS
709
710 if (DEBUG_LOGGING) {
ca8b62b5 711 (void) in_addr_port_to_string(l->family, &l->address, l->port, &pretty);
1f05101f
SS
712 log_debug("Listening on TCP socket %s.", strnull(pretty));
713 }
714
7216a3b5 715 return TAKE_FD(fd);
1f05101f 716
b4b7ea1b 717fail:
1c17bcb3 718 assert(r < 0);
ca8b62b5 719 (void) in_addr_port_to_string(l->family, &l->address, l->port, &pretty);
b4b7ea1b 720 if (r == -EADDRINUSE)
1c17bcb3
YW
721 return log_warning_errno(r, "Another process is already listening on TCP socket %s: %m", strnull(pretty));
722 return log_warning_errno(r, "Failed to listen on TCP socket %s: %m", strnull(pretty));
1f05101f
SS
723}
724
b30bf55d 725int manager_dns_stub_start(Manager *m) {
424e490b 726 const char *t = "UDP";
01b0669e 727 int r = 0;
b30bf55d
LP
728
729 assert(m);
730
d5da7707
ZJS
731 if (m->dns_stub_listener_mode == DNS_STUB_LISTENER_NO)
732 log_debug("Not creating stub listener.");
733 else
734 log_debug("Creating stub listener using %s.",
735 m->dns_stub_listener_mode == DNS_STUB_LISTENER_UDP ? "UDP" :
736 m->dns_stub_listener_mode == DNS_STUB_LISTENER_TCP ? "TCP" :
737 "UDP/TCP");
738
88d2cb7c 739 if (FLAGS_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_UDP))
1ae43295 740 r = manager_dns_stub_udp_fd(m);
b30bf55d 741
424e490b 742 if (r >= 0 &&
88d2cb7c 743 FLAGS_SET(m->dns_stub_listener_mode, DNS_STUB_LISTENER_TCP)) {
424e490b 744 t = "TCP";
1ae43295 745 r = manager_dns_stub_tcp_fd(m);
1ae43295 746 }
b30bf55d 747
0f4db364
ZJS
748 if (IN_SET(r, -EADDRINUSE, -EPERM)) {
749 if (r == -EADDRINUSE)
750 log_warning_errno(r,
751 "Another process is already listening on %s socket 127.0.0.53:53.\n"
752 "Turning off local DNS stub support.", t);
753 else
754 log_warning_errno(r,
755 "Failed to listen on %s socket 127.0.0.53:53: %m.\n"
756 "Turning off local DNS stub support.", t);
424e490b
ZJS
757 manager_dns_stub_stop(m);
758 } else if (r < 0)
759 return log_error_errno(r, "Failed to listen on %s socket 127.0.0.53:53: %m", t);
b30bf55d 760
1f05101f
SS
761 if (!ordered_set_isempty(m->dns_extra_stub_listeners)) {
762 DNSStubListenerExtra *l;
763 Iterator i;
764
dce65cd4 765 log_debug("Creating extra stub listeners.");
1f05101f 766
7314b397
YW
767 ORDERED_SET_FOREACH(l, m->dns_extra_stub_listeners, i) {
768 if (FLAGS_SET(l->mode, DNS_STUB_LISTENER_UDP))
1f05101f 769 (void) manager_dns_stub_udp_fd_extra(m, l);
7314b397 770 if (FLAGS_SET(l->mode, DNS_STUB_LISTENER_TCP))
1f05101f 771 (void) manager_dns_stub_tcp_fd_extra(m, l);
7314b397 772 }
1f05101f
SS
773 }
774
b30bf55d
LP
775 return 0;
776}
777
778void manager_dns_stub_stop(Manager *m) {
779 assert(m);
780
781 m->dns_stub_udp_event_source = sd_event_source_unref(m->dns_stub_udp_event_source);
782 m->dns_stub_tcp_event_source = sd_event_source_unref(m->dns_stub_tcp_event_source);
b30bf55d 783}