]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
5f402ae8 | 2 | |
5f402ae8 | 3 | #include <netinet/in.h> |
b850e513 | 4 | #include <netinet/tcp.h> |
cf0fbc49 | 5 | #include <resolv.h> |
5f402ae8 | 6 | |
4ff9bc2e | 7 | #include "errno-util.h" |
3ffd4af2 | 8 | #include "fd-util.h" |
5f402ae8 | 9 | #include "resolved-llmnr.h" |
3ffd4af2 | 10 | #include "resolved-manager.h" |
5f402ae8 DM |
11 | |
12 | void manager_llmnr_stop(Manager *m) { | |
13 | assert(m); | |
14 | ||
97935302 | 15 | m->llmnr_ipv4_udp_event_source = sd_event_source_disable_unref(m->llmnr_ipv4_udp_event_source); |
5f402ae8 DM |
16 | m->llmnr_ipv4_udp_fd = safe_close(m->llmnr_ipv4_udp_fd); |
17 | ||
97935302 | 18 | m->llmnr_ipv6_udp_event_source = sd_event_source_disable_unref(m->llmnr_ipv6_udp_event_source); |
5f402ae8 DM |
19 | m->llmnr_ipv6_udp_fd = safe_close(m->llmnr_ipv6_udp_fd); |
20 | ||
97935302 | 21 | m->llmnr_ipv4_tcp_event_source = sd_event_source_disable_unref(m->llmnr_ipv4_tcp_event_source); |
5f402ae8 DM |
22 | m->llmnr_ipv4_tcp_fd = safe_close(m->llmnr_ipv4_tcp_fd); |
23 | ||
97935302 | 24 | m->llmnr_ipv6_tcp_event_source = sd_event_source_disable_unref(m->llmnr_ipv6_tcp_event_source); |
5f402ae8 DM |
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 | ||
af49ca27 | 33 | if (m->llmnr_support == RESOLVE_SUPPORT_NO) |
5f402ae8 DM |
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: | |
007ef0a2 | 65 | log_warning("Another LLMNR responder prohibits binding the socket to the same port. Turning off LLMNR support."); |
af49ca27 | 66 | m->llmnr_support = RESOLVE_SUPPORT_NO; |
5f402ae8 DM |
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; | |
99534007 | 75 | Manager *m = ASSERT_PTR(userdata); |
5f402ae8 DM |
76 | DnsScope *scope; |
77 | int r; | |
78 | ||
b30bf55d LP |
79 | assert(s); |
80 | assert(fd >= 0); | |
b30bf55d | 81 | |
5f402ae8 DM |
82 | r = manager_recv(m, fd, DNS_PROTOCOL_LLMNR, &p); |
83 | if (r <= 0) | |
84 | return r; | |
85 | ||
94378145 | 86 | if (manager_packet_from_local_address(m, p)) |
6cae1ebe LP |
87 | return 0; |
88 | ||
5f402ae8 | 89 | scope = manager_find_scope(m, p); |
f1b1a5c4 LP |
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) { | |
b30bf55d | 96 | log_debug("Got LLMNR UDP reply packet for id %u", DNS_PACKET_ID(p)); |
5f402ae8 DM |
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) | |
43fc4baa | 102 | dns_transaction_process_reply(t, p, false); |
5f402ae8 DM |
103 | |
104 | } else if (dns_packet_validate_query(p) > 0) { | |
b30bf55d | 105 | log_debug("Got LLMNR UDP query packet for id %u", DNS_PACKET_ID(p)); |
5f402ae8 DM |
106 | |
107 | dns_scope_process_query(scope, NULL, p); | |
108 | } else | |
2c6bf498 | 109 | log_debug("Invalid LLMNR UDP packet, ignoring."); |
5f402ae8 DM |
110 | |
111 | return 0; | |
112 | } | |
113 | ||
b850e513 LP |
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 | ||
5f402ae8 DM |
139 | int manager_llmnr_ipv4_udp_fd(Manager *m) { |
140 | union sockaddr_union sa = { | |
141 | .in.sin_family = AF_INET, | |
22a37591 | 142 | .in.sin_port = htobe16(LLMNR_PORT), |
5f402ae8 | 143 | }; |
3f548fff | 144 | _cleanup_close_ int s = -1; |
5f402ae8 DM |
145 | int r; |
146 | ||
147 | assert(m); | |
148 | ||
149 | if (m->llmnr_ipv4_udp_fd >= 0) | |
150 | return m->llmnr_ipv4_udp_fd; | |
151 | ||
3f548fff YW |
152 | s = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); |
153 | if (s < 0) | |
007ef0a2 | 154 | return log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to create socket: %m"); |
5f402ae8 | 155 | |
b850e513 LP |
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); | |
3f548fff | 161 | if (r < 0) |
b850e513 | 162 | return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to set common UDP socket options: %m"); |
5f402ae8 | 163 | |
3f548fff YW |
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"); | |
5f402ae8 | 167 | |
3f548fff YW |
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"); | |
5f402ae8 | 171 | |
5f402ae8 | 172 | /* Disable Don't-Fragment bit in the IP header */ |
3f548fff YW |
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"); | |
5f402ae8 | 176 | |
007ef0a2 | 177 | /* first try to bind without SO_REUSEADDR to detect another LLMNR responder */ |
3f548fff | 178 | r = bind(s, &sa.sa, sizeof(sa.in)); |
5f402ae8 | 179 | if (r < 0) { |
3f548fff YW |
180 | if (errno != EADDRINUSE) |
181 | return log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to bind socket: %m"); | |
007ef0a2 YW |
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 */ | |
3f548fff YW |
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"); | |
007ef0a2 YW |
193 | } else { |
194 | /* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */ | |
3f548fff YW |
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"); | |
5f402ae8 DM |
198 | } |
199 | ||
3f548fff | 200 | r = sd_event_add_io(m->event, &m->llmnr_ipv4_udp_event_source, s, EPOLLIN, on_llmnr_packet, m); |
5f402ae8 | 201 | if (r < 0) |
3f548fff | 202 | return log_error_errno(r, "LLMNR-IPv4(UDP): Failed to create event source: %m"); |
5f402ae8 | 203 | |
aa4a9deb LP |
204 | (void) sd_event_source_set_description(m->llmnr_ipv4_udp_event_source, "llmnr-ipv4-udp"); |
205 | ||
3f548fff | 206 | return m->llmnr_ipv4_udp_fd = TAKE_FD(s); |
5f402ae8 DM |
207 | } |
208 | ||
209 | int manager_llmnr_ipv6_udp_fd(Manager *m) { | |
210 | union sockaddr_union sa = { | |
211 | .in6.sin6_family = AF_INET6, | |
22a37591 | 212 | .in6.sin6_port = htobe16(LLMNR_PORT), |
5f402ae8 | 213 | }; |
3f548fff | 214 | _cleanup_close_ int s = -1; |
5f402ae8 DM |
215 | int r; |
216 | ||
217 | assert(m); | |
218 | ||
219 | if (m->llmnr_ipv6_udp_fd >= 0) | |
220 | return m->llmnr_ipv6_udp_fd; | |
221 | ||
3f548fff YW |
222 | s = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); |
223 | if (s < 0) | |
007ef0a2 | 224 | return log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to create socket: %m"); |
5f402ae8 | 225 | |
b850e513 LP |
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); | |
3f548fff | 231 | if (r < 0) |
b850e513 | 232 | return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to set common UDP socket options: %m"); |
5f402ae8 DM |
233 | |
234 | /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */ | |
3f548fff YW |
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"); | |
5f402ae8 | 238 | |
3f548fff YW |
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"); | |
5f402ae8 | 242 | |
3f548fff YW |
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"); | |
5f402ae8 | 246 | |
007ef0a2 | 247 | /* first try to bind without SO_REUSEADDR to detect another LLMNR responder */ |
3f548fff | 248 | r = bind(s, &sa.sa, sizeof(sa.in6)); |
5f402ae8 | 249 | if (r < 0) { |
3f548fff YW |
250 | if (errno != EADDRINUSE) |
251 | return log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to bind socket: %m"); | |
007ef0a2 YW |
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 */ | |
3f548fff YW |
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"); | |
007ef0a2 YW |
263 | } else { |
264 | /* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */ | |
3f548fff YW |
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"); | |
5f402ae8 DM |
268 | } |
269 | ||
3f548fff | 270 | r = sd_event_add_io(m->event, &m->llmnr_ipv6_udp_event_source, s, EPOLLIN, on_llmnr_packet, m); |
35908b98 | 271 | if (r < 0) |
3f548fff | 272 | return log_error_errno(r, "LLMNR-IPv6(UDP): Failed to create event source: %m"); |
5f402ae8 | 273 | |
aa4a9deb LP |
274 | (void) sd_event_source_set_description(m->llmnr_ipv6_udp_event_source, "llmnr-ipv6-udp"); |
275 | ||
3f548fff | 276 | return m->llmnr_ipv6_udp_fd = TAKE_FD(s); |
5f402ae8 DM |
277 | } |
278 | ||
624f907e | 279 | static int on_llmnr_stream_packet(DnsStream *s, DnsPacket *p) { |
5f402ae8 DM |
280 | DnsScope *scope; |
281 | ||
282 | assert(s); | |
624f907e | 283 | assert(s->manager); |
aa337a5e LP |
284 | assert(p); |
285 | ||
286 | scope = manager_find_scope(s->manager, p); | |
b30bf55d | 287 | if (!scope) |
f1b1a5c4 | 288 | log_debug("Got LLMNR TCP packet on unknown scope. Ignoring."); |
aa337a5e LP |
289 | else if (dns_packet_validate_query(p) > 0) { |
290 | log_debug("Got LLMNR TCP query packet for id %u", DNS_PACKET_ID(p)); | |
5f402ae8 | 291 | |
aa337a5e | 292 | dns_scope_process_query(scope, s, p); |
5f402ae8 | 293 | } else |
b30bf55d | 294 | log_debug("Invalid LLMNR TCP packet, ignoring."); |
5f402ae8 | 295 | |
5f402ae8 DM |
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) { | |
4ff9bc2e | 306 | if (ERRNO_IS_ACCEPT_AGAIN(errno)) |
5f402ae8 DM |
307 | return 0; |
308 | ||
309 | return -errno; | |
310 | } | |
311 | ||
a5e2a488 | 312 | /* We don't configure a "complete" handler here, we rely on the default handler, thus freeing it */ |
18230451 YW |
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); | |
5f402ae8 DM |
315 | if (r < 0) { |
316 | safe_close(cfd); | |
317 | return r; | |
318 | } | |
319 | ||
5f402ae8 DM |
320 | return 0; |
321 | } | |
322 | ||
b850e513 LP |
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 | ||
5f402ae8 DM |
342 | int manager_llmnr_ipv4_tcp_fd(Manager *m) { |
343 | union sockaddr_union sa = { | |
344 | .in.sin_family = AF_INET, | |
22a37591 | 345 | .in.sin_port = htobe16(LLMNR_PORT), |
5f402ae8 | 346 | }; |
3f548fff | 347 | _cleanup_close_ int s = -1; |
5f402ae8 DM |
348 | int r; |
349 | ||
350 | assert(m); | |
351 | ||
352 | if (m->llmnr_ipv4_tcp_fd >= 0) | |
353 | return m->llmnr_ipv4_tcp_fd; | |
354 | ||
3f548fff YW |
355 | s = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); |
356 | if (s < 0) | |
007ef0a2 | 357 | return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to create socket: %m"); |
5f402ae8 | 358 | |
b850e513 | 359 | r = set_llmnr_common_socket_options(s, AF_INET); |
3f548fff | 360 | if (r < 0) |
b850e513 | 361 | return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set common socket options: %m"); |
5f402ae8 | 362 | |
b850e513 | 363 | r = set_llmnr_common_tcp_socket_options(s, AF_INET); |
3f548fff | 364 | if (r < 0) |
b850e513 | 365 | return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to set common TCP socket options: %m"); |
5f402ae8 DM |
366 | |
367 | /* Disable Don't-Fragment bit in the IP header */ | |
3f548fff YW |
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"); | |
5f402ae8 | 371 | |
007ef0a2 | 372 | /* first try to bind without SO_REUSEADDR to detect another LLMNR responder */ |
3f548fff | 373 | r = bind(s, &sa.sa, sizeof(sa.in)); |
5f402ae8 | 374 | if (r < 0) { |
3f548fff YW |
375 | if (errno != EADDRINUSE) |
376 | return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to bind socket: %m"); | |
007ef0a2 YW |
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 */ | |
3f548fff YW |
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"); | |
007ef0a2 YW |
388 | } else { |
389 | /* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */ | |
3f548fff YW |
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"); | |
5f402ae8 DM |
393 | } |
394 | ||
3f548fff YW |
395 | r = listen(s, SOMAXCONN); |
396 | if (r < 0) | |
397 | return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to listen the stream: %m"); | |
5f402ae8 | 398 | |
3f548fff | 399 | r = sd_event_add_io(m->event, &m->llmnr_ipv4_tcp_event_source, s, EPOLLIN, on_llmnr_stream, m); |
5f402ae8 | 400 | if (r < 0) |
3f548fff | 401 | return log_error_errno(r, "LLMNR-IPv4(TCP): Failed to create event source: %m"); |
5f402ae8 | 402 | |
aa4a9deb LP |
403 | (void) sd_event_source_set_description(m->llmnr_ipv4_tcp_event_source, "llmnr-ipv4-tcp"); |
404 | ||
3f548fff | 405 | return m->llmnr_ipv4_tcp_fd = TAKE_FD(s); |
5f402ae8 DM |
406 | } |
407 | ||
408 | int manager_llmnr_ipv6_tcp_fd(Manager *m) { | |
409 | union sockaddr_union sa = { | |
410 | .in6.sin6_family = AF_INET6, | |
22a37591 | 411 | .in6.sin6_port = htobe16(LLMNR_PORT), |
5f402ae8 | 412 | }; |
3f548fff | 413 | _cleanup_close_ int s = -1; |
5f402ae8 DM |
414 | int r; |
415 | ||
416 | assert(m); | |
417 | ||
418 | if (m->llmnr_ipv6_tcp_fd >= 0) | |
419 | return m->llmnr_ipv6_tcp_fd; | |
420 | ||
3f548fff YW |
421 | s = socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); |
422 | if (s < 0) | |
007ef0a2 | 423 | return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to create socket: %m"); |
5f402ae8 | 424 | |
3f548fff YW |
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"); | |
5f402ae8 | 428 | |
b850e513 | 429 | r = set_llmnr_common_socket_options(s, AF_INET6); |
3f548fff | 430 | if (r < 0) |
b850e513 | 431 | return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set common socket options: %m"); |
5f402ae8 | 432 | |
b850e513 | 433 | r = set_llmnr_common_tcp_socket_options(s, AF_INET6); |
3f548fff | 434 | if (r < 0) |
b850e513 | 435 | return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to set common TCP socket options: %m"); |
5f402ae8 | 436 | |
007ef0a2 | 437 | /* first try to bind without SO_REUSEADDR to detect another LLMNR responder */ |
3f548fff | 438 | r = bind(s, &sa.sa, sizeof(sa.in6)); |
5f402ae8 | 439 | if (r < 0) { |
3f548fff YW |
440 | if (errno != EADDRINUSE) |
441 | return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to bind socket: %m"); | |
007ef0a2 YW |
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 */ | |
3f548fff YW |
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"); | |
007ef0a2 YW |
453 | } else { |
454 | /* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */ | |
3f548fff YW |
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"); | |
5f402ae8 DM |
458 | } |
459 | ||
3f548fff YW |
460 | r = listen(s, SOMAXCONN); |
461 | if (r < 0) | |
462 | return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to listen the stream: %m"); | |
5f402ae8 | 463 | |
3f548fff | 464 | r = sd_event_add_io(m->event, &m->llmnr_ipv6_tcp_event_source, s, EPOLLIN, on_llmnr_stream, m); |
ee8d9305 | 465 | if (r < 0) |
3f548fff | 466 | return log_error_errno(r, "LLMNR-IPv6(TCP): Failed to create event source: %m"); |
5f402ae8 | 467 | |
aa4a9deb LP |
468 | (void) sd_event_source_set_description(m->llmnr_ipv6_tcp_event_source, "llmnr-ipv6-tcp"); |
469 | ||
3f548fff | 470 | return m->llmnr_ipv6_tcp_fd = TAKE_FD(s); |
5f402ae8 | 471 | } |