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