]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-llmnr.c
b7c37f1132aa1be2c901a14a2060d3f005be6ed5
[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_STREAM_LLMNR_RECV, 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 /* We don't configure a "complete" handler here, we rely on the default handler than simply drops the
306 * reference to the stream, thus freeing it */
307 return 0;
308 }
309
310 int manager_llmnr_ipv4_tcp_fd(Manager *m) {
311 union sockaddr_union sa = {
312 .in.sin_family = AF_INET,
313 .in.sin_port = htobe16(LLMNR_PORT),
314 };
315 _cleanup_close_ int s = -1;
316 int r;
317
318 assert(m);
319
320 if (m->llmnr_ipv4_tcp_fd >= 0)
321 return m->llmnr_ipv4_tcp_fd;
322
323 s = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
324 if (s < 0)
325 return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to create socket: %m");
326
327 /* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */
328 r = setsockopt_int(s, IPPROTO_IP, IP_TTL, true);
329 if (r < 0)
330 return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set IP_TTL: %m");
331
332 r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true);
333 if (r < 0)
334 return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set IP_PKTINFO: %m");
335
336 r = setsockopt_int(s, IPPROTO_IP, IP_RECVTTL, true);
337 if (r < 0)
338 return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set IP_RECVTTL: %m");
339
340 /* Disable Don't-Fragment bit in the IP header */
341 r = setsockopt_int(s, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DONT);
342 if (r < 0)
343 return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set IP_MTU_DISCOVER: %m");
344
345 /* first try to bind without SO_REUSEADDR to detect another LLMNR responder */
346 r = bind(s, &sa.sa, sizeof(sa.in));
347 if (r < 0) {
348 if (errno != EADDRINUSE)
349 return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to bind socket: %m");
350
351 log_warning("LLMNR-IPv4(TCP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers.");
352
353 /* try again with SO_REUSEADDR */
354 r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
355 if (r < 0)
356 return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set SO_REUSEADDR: %m");
357
358 r = bind(s, &sa.sa, sizeof(sa.in));
359 if (r < 0)
360 return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to bind socket: %m");
361 } else {
362 /* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */
363 r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
364 if (r < 0)
365 return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set SO_REUSEADDR: %m");
366 }
367
368 r = listen(s, SOMAXCONN);
369 if (r < 0)
370 return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to listen the stream: %m");
371
372 r = sd_event_add_io(m->event, &m->llmnr_ipv4_tcp_event_source, s, EPOLLIN, on_llmnr_stream, m);
373 if (r < 0)
374 return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to create event source: %m");
375
376 (void) sd_event_source_set_description(m->llmnr_ipv4_tcp_event_source, "llmnr-ipv4-tcp");
377
378 return m->llmnr_ipv4_tcp_fd = TAKE_FD(s);
379 }
380
381 int manager_llmnr_ipv6_tcp_fd(Manager *m) {
382 union sockaddr_union sa = {
383 .in6.sin6_family = AF_INET6,
384 .in6.sin6_port = htobe16(LLMNR_PORT),
385 };
386 _cleanup_close_ int s = -1;
387 int r;
388
389 assert(m);
390
391 if (m->llmnr_ipv6_tcp_fd >= 0)
392 return m->llmnr_ipv6_tcp_fd;
393
394 s = socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
395 if (s < 0)
396 return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to create socket: %m");
397
398 /* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */
399 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, true);
400 if (r < 0)
401 return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set IPV6_UNICAST_HOPS: %m");
402
403 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_V6ONLY, true);
404 if (r < 0)
405 return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set IPV6_V6ONLY: %m");
406
407 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, true);
408 if (r < 0)
409 return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set IPV6_RECVPKTINFO: %m");
410
411 r = setsockopt_int(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, true);
412 if (r < 0)
413 return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set IPV6_RECVHOPLIMIT: %m");
414
415 /* first try to bind without SO_REUSEADDR to detect another LLMNR responder */
416 r = bind(s, &sa.sa, sizeof(sa.in6));
417 if (r < 0) {
418 if (errno != EADDRINUSE)
419 return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to bind socket: %m");
420
421 log_warning("LLMNR-IPv6(TCP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers.");
422
423 /* try again with SO_REUSEADDR */
424 r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
425 if (r < 0)
426 return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set SO_REUSEADDR: %m");
427
428 r = bind(s, &sa.sa, sizeof(sa.in6));
429 if (r < 0)
430 return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to bind socket: %m");
431 } else {
432 /* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */
433 r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
434 if (r < 0)
435 return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set SO_REUSEADDR: %m");
436 }
437
438 r = listen(s, SOMAXCONN);
439 if (r < 0)
440 return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to listen the stream: %m");
441
442 r = sd_event_add_io(m->event, &m->llmnr_ipv6_tcp_event_source, s, EPOLLIN, on_llmnr_stream, m);
443 if (r < 0)
444 return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to create event source: %m");
445
446 (void) sd_event_source_set_description(m->llmnr_ipv6_tcp_event_source, "llmnr-ipv6-tcp");
447
448 return m->llmnr_ipv6_tcp_fd = TAKE_FD(s);
449 }