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