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