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