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