]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-llmnr.c
resolved: implicitly disconnect a stream from its server when a stream is closed
[thirdparty/systemd.git] / src / resolve / resolved-llmnr.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <netinet/in.h>
4 #include <resolv.h>
5
6 #include "fd-util.h"
7 #include "resolved-llmnr.h"
8 #include "resolved-manager.h"
9
10 void manager_llmnr_stop(Manager *m) {
11 assert(m);
12
13 m->llmnr_ipv4_udp_event_source = sd_event_source_unref(m->llmnr_ipv4_udp_event_source);
14 m->llmnr_ipv4_udp_fd = safe_close(m->llmnr_ipv4_udp_fd);
15
16 m->llmnr_ipv6_udp_event_source = sd_event_source_unref(m->llmnr_ipv6_udp_event_source);
17 m->llmnr_ipv6_udp_fd = safe_close(m->llmnr_ipv6_udp_fd);
18
19 m->llmnr_ipv4_tcp_event_source = sd_event_source_unref(m->llmnr_ipv4_tcp_event_source);
20 m->llmnr_ipv4_tcp_fd = safe_close(m->llmnr_ipv4_tcp_fd);
21
22 m->llmnr_ipv6_tcp_event_source = sd_event_source_unref(m->llmnr_ipv6_tcp_event_source);
23 m->llmnr_ipv6_tcp_fd = safe_close(m->llmnr_ipv6_tcp_fd);
24 }
25
26 int manager_llmnr_start(Manager *m) {
27 int r;
28
29 assert(m);
30
31 if (m->llmnr_support == RESOLVE_SUPPORT_NO)
32 return 0;
33
34 r = manager_llmnr_ipv4_udp_fd(m);
35 if (r == -EADDRINUSE)
36 goto eaddrinuse;
37 if (r < 0)
38 return r;
39
40 r = manager_llmnr_ipv4_tcp_fd(m);
41 if (r == -EADDRINUSE)
42 goto eaddrinuse;
43 if (r < 0)
44 return r;
45
46 if (socket_ipv6_is_supported()) {
47 r = manager_llmnr_ipv6_udp_fd(m);
48 if (r == -EADDRINUSE)
49 goto eaddrinuse;
50 if (r < 0)
51 return r;
52
53 r = manager_llmnr_ipv6_tcp_fd(m);
54 if (r == -EADDRINUSE)
55 goto eaddrinuse;
56 if (r < 0)
57 return r;
58 }
59
60 return 0;
61
62 eaddrinuse:
63 log_warning("Another LLMNR responder prohibits binding the socket to the same port. Turning off LLMNR support.");
64 m->llmnr_support = RESOLVE_SUPPORT_NO;
65 manager_llmnr_stop(m);
66
67 return 0;
68 }
69
70 static int on_llmnr_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
71 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
72 DnsTransaction *t = NULL;
73 Manager *m = userdata;
74 DnsScope *scope;
75 int r;
76
77 assert(s);
78 assert(fd >= 0);
79 assert(m);
80
81 r = manager_recv(m, fd, DNS_PROTOCOL_LLMNR, &p);
82 if (r <= 0)
83 return r;
84
85 if (manager_our_packet(m, p))
86 return 0;
87
88 scope = manager_find_scope(m, p);
89 if (!scope) {
90 log_debug("Got LLMNR UDP packet on unknown scope. Ignoring.");
91 return 0;
92 }
93
94 if (dns_packet_validate_reply(p) > 0) {
95 log_debug("Got LLMNR UDP reply packet for id %u", DNS_PACKET_ID(p));
96
97 dns_scope_check_conflicts(scope, p);
98
99 t = hashmap_get(m->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p)));
100 if (t)
101 dns_transaction_process_reply(t, p);
102
103 } else if (dns_packet_validate_query(p) > 0) {
104 log_debug("Got LLMNR UDP query packet for id %u", DNS_PACKET_ID(p));
105
106 dns_scope_process_query(scope, NULL, p);
107 } else
108 log_debug("Invalid LLMNR UDP packet, ignoring.");
109
110 return 0;
111 }
112
113 int manager_llmnr_ipv4_udp_fd(Manager *m) {
114 union sockaddr_union sa = {
115 .in.sin_family = AF_INET,
116 .in.sin_port = htobe16(LLMNR_PORT),
117 };
118 _cleanup_close_ int s = -1;
119 int r;
120
121 assert(m);
122
123 if (m->llmnr_ipv4_udp_fd >= 0)
124 return m->llmnr_ipv4_udp_fd;
125
126 s = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
127 if (s < 0)
128 return log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to create socket: %m");
129
130 /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
131 r = setsockopt_int(s, IPPROTO_IP, IP_TTL, 255);
132 if (r < 0)
133 return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set IP_TTL: %m");
134
135 r = setsockopt_int(s, IPPROTO_IP, IP_MULTICAST_TTL, 255);
136 if (r < 0)
137 return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set IP_MULTICAST_TTL: %m");
138
139 r = setsockopt_int(s, IPPROTO_IP, IP_MULTICAST_LOOP, true);
140 if (r < 0)
141 return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set IP_MULTICAST_LOOP: %m");
142
143 r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true);
144 if (r < 0)
145 return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set IP_PKTINFO: %m");
146
147 r = setsockopt_int(s, IPPROTO_IP, IP_RECVTTL, true);
148 if (r < 0)
149 return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set IP_RECVTTL: %m");
150
151 /* Disable Don't-Fragment bit in the IP header */
152 r = setsockopt_int(s, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DONT);
153 if (r < 0)
154 return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set IP_MTU_DISCOVER: %m");
155
156 /* first try to bind without SO_REUSEADDR to detect another LLMNR responder */
157 r = bind(s, &sa.sa, sizeof(sa.in));
158 if (r < 0) {
159 if (errno != EADDRINUSE)
160 return log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to bind socket: %m");
161
162 log_warning("LLMNR-IPv4(UDP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers.");
163
164 /* try again with SO_REUSEADDR */
165 r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
166 if (r < 0)
167 return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set SO_REUSEADDR: %m");
168
169 r = bind(s, &sa.sa, sizeof(sa.in));
170 if (r < 0)
171 return log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to bind socket: %m");
172 } else {
173 /* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */
174 r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
175 if (r < 0)
176 return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set SO_REUSEADDR: %m");
177 }
178
179 r = sd_event_add_io(m->event, &m->llmnr_ipv4_udp_event_source, s, EPOLLIN, on_llmnr_packet, m);
180 if (r < 0)
181 return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to create event source: %m");
182
183 (void) sd_event_source_set_description(m->llmnr_ipv4_udp_event_source, "llmnr-ipv4-udp");
184
185 return m->llmnr_ipv4_udp_fd = TAKE_FD(s);
186 }
187
188 int manager_llmnr_ipv6_udp_fd(Manager *m) {
189 union sockaddr_union sa = {
190 .in6.sin6_family = AF_INET6,
191 .in6.sin6_port = htobe16(LLMNR_PORT),
192 };
193 _cleanup_close_ int s = -1;
194 int r;
195
196 assert(m);
197
198 if (m->llmnr_ipv6_udp_fd >= 0)
199 return m->llmnr_ipv6_udp_fd;
200
201 s = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
202 if (s < 0)
203 return log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to create socket: %m");
204
205 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 255);
206 if (r < 0)
207 return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set IPV6_UNICAST_HOPS: %m");
208
209 /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
210 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 255);
211 if (r < 0)
212 return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set IPV6_MULTICAST_HOPS: %m");
213
214 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, true);
215 if (r < 0)
216 return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set IPV6_MULTICAST_LOOP: %m");
217
218 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_V6ONLY, true);
219 if (r < 0)
220 return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set IPV6_V6ONLY: %m");
221
222 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, true);
223 if (r < 0)
224 return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set IPV6_RECVPKTINFO: %m");
225
226 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, true);
227 if (r < 0)
228 return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set IPV6_RECVHOPLIMIT: %m");
229
230 /* first try to bind without SO_REUSEADDR to detect another LLMNR responder */
231 r = bind(s, &sa.sa, sizeof(sa.in6));
232 if (r < 0) {
233 if (errno != EADDRINUSE)
234 return log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to bind socket: %m");
235
236 log_warning("LLMNR-IPv6(UDP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers.");
237
238 /* try again with SO_REUSEADDR */
239 r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
240 if (r < 0)
241 return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set SO_REUSEADDR: %m");
242
243 r = bind(s, &sa.sa, sizeof(sa.in6));
244 if (r < 0)
245 return log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to bind socket: %m");
246 } else {
247 /* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */
248 r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
249 if (r < 0)
250 return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set SO_REUSEADDR: %m");
251 }
252
253 r = sd_event_add_io(m->event, &m->llmnr_ipv6_udp_event_source, s, EPOLLIN, on_llmnr_packet, m);
254 if (r < 0)
255 return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to create event source: %m");
256
257 (void) sd_event_source_set_description(m->llmnr_ipv6_udp_event_source, "llmnr-ipv6-udp");
258
259 return m->llmnr_ipv6_udp_fd = TAKE_FD(s);
260 }
261
262 static int on_llmnr_stream_packet(DnsStream *s) {
263 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
264 DnsScope *scope;
265
266 assert(s);
267
268 p = dns_stream_take_read_packet(s);
269 assert(p);
270
271 scope = manager_find_scope(s->manager, p);
272 if (!scope)
273 log_debug("Got LLMNR TCP packet on unknown scope. Ignoring.");
274 else if (dns_packet_validate_query(p) > 0) {
275 log_debug("Got LLMNR TCP query packet for id %u", DNS_PACKET_ID(p));
276
277 dns_scope_process_query(scope, s, p);
278 } else
279 log_debug("Invalid LLMNR TCP packet, ignoring.");
280
281 dns_stream_unref(s);
282 return 0;
283 }
284
285 static int on_llmnr_stream(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
286 DnsStream *stream;
287 Manager *m = userdata;
288 int cfd, r;
289
290 cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
291 if (cfd < 0) {
292 if (IN_SET(errno, EAGAIN, EINTR))
293 return 0;
294
295 return -errno;
296 }
297
298 r = dns_stream_new(m, &stream, DNS_PROTOCOL_LLMNR, cfd, NULL);
299 if (r < 0) {
300 safe_close(cfd);
301 return r;
302 }
303
304 stream->on_packet = on_llmnr_stream_packet;
305 return 0;
306 }
307
308 int manager_llmnr_ipv4_tcp_fd(Manager *m) {
309 union sockaddr_union sa = {
310 .in.sin_family = AF_INET,
311 .in.sin_port = htobe16(LLMNR_PORT),
312 };
313 _cleanup_close_ int s = -1;
314 int r;
315
316 assert(m);
317
318 if (m->llmnr_ipv4_tcp_fd >= 0)
319 return m->llmnr_ipv4_tcp_fd;
320
321 s = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
322 if (s < 0)
323 return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to create socket: %m");
324
325 /* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */
326 r = setsockopt_int(s, IPPROTO_IP, IP_TTL, true);
327 if (r < 0)
328 return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set IP_TTL: %m");
329
330 r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true);
331 if (r < 0)
332 return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set IP_PKTINFO: %m");
333
334 r = setsockopt_int(s, IPPROTO_IP, IP_RECVTTL, true);
335 if (r < 0)
336 return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set IP_RECVTTL: %m");
337
338 /* Disable Don't-Fragment bit in the IP header */
339 r = setsockopt_int(s, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DONT);
340 if (r < 0)
341 return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set IP_MTU_DISCOVER: %m");
342
343 /* first try to bind without SO_REUSEADDR to detect another LLMNR responder */
344 r = bind(s, &sa.sa, sizeof(sa.in));
345 if (r < 0) {
346 if (errno != EADDRINUSE)
347 return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to bind socket: %m");
348
349 log_warning("LLMNR-IPv4(TCP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers.");
350
351 /* try again with SO_REUSEADDR */
352 r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
353 if (r < 0)
354 return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set SO_REUSEADDR: %m");
355
356 r = bind(s, &sa.sa, sizeof(sa.in));
357 if (r < 0)
358 return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to bind socket: %m");
359 } else {
360 /* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */
361 r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
362 if (r < 0)
363 return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set SO_REUSEADDR: %m");
364 }
365
366 r = listen(s, SOMAXCONN);
367 if (r < 0)
368 return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to listen the stream: %m");
369
370 r = sd_event_add_io(m->event, &m->llmnr_ipv4_tcp_event_source, s, EPOLLIN, on_llmnr_stream, m);
371 if (r < 0)
372 return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to create event source: %m");
373
374 (void) sd_event_source_set_description(m->llmnr_ipv4_tcp_event_source, "llmnr-ipv4-tcp");
375
376 return m->llmnr_ipv4_tcp_fd = TAKE_FD(s);
377 }
378
379 int manager_llmnr_ipv6_tcp_fd(Manager *m) {
380 union sockaddr_union sa = {
381 .in6.sin6_family = AF_INET6,
382 .in6.sin6_port = htobe16(LLMNR_PORT),
383 };
384 _cleanup_close_ int s = -1;
385 int r;
386
387 assert(m);
388
389 if (m->llmnr_ipv6_tcp_fd >= 0)
390 return m->llmnr_ipv6_tcp_fd;
391
392 s = socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
393 if (s < 0)
394 return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to create socket: %m");
395
396 /* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */
397 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, true);
398 if (r < 0)
399 return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set IPV6_UNICAST_HOPS: %m");
400
401 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_V6ONLY, true);
402 if (r < 0)
403 return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set IPV6_V6ONLY: %m");
404
405 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, true);
406 if (r < 0)
407 return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set IPV6_RECVPKTINFO: %m");
408
409 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, true);
410 if (r < 0)
411 return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set IPV6_RECVHOPLIMIT: %m");
412
413 /* first try to bind without SO_REUSEADDR to detect another LLMNR responder */
414 r = bind(s, &sa.sa, sizeof(sa.in6));
415 if (r < 0) {
416 if (errno != EADDRINUSE)
417 return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to bind socket: %m");
418
419 log_warning("LLMNR-IPv6(TCP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers.");
420
421 /* try again with SO_REUSEADDR */
422 r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
423 if (r < 0)
424 return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set SO_REUSEADDR: %m");
425
426 r = bind(s, &sa.sa, sizeof(sa.in6));
427 if (r < 0)
428 return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to bind socket: %m");
429 } else {
430 /* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */
431 r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
432 if (r < 0)
433 return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set SO_REUSEADDR: %m");
434 }
435
436 r = listen(s, SOMAXCONN);
437 if (r < 0)
438 return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to listen the stream: %m");
439
440 r = sd_event_add_io(m->event, &m->llmnr_ipv6_tcp_event_source, s, EPOLLIN, on_llmnr_stream, m);
441 if (r < 0)
442 return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to create event source: %m");
443
444 (void) sd_event_source_set_description(m->llmnr_ipv6_tcp_event_source, "llmnr-ipv6-tcp");
445
446 return m->llmnr_ipv6_tcp_fd = TAKE_FD(s);
447 }