1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2015 Daniel Mack
9 #include <netinet/in.h>
10 #include <arpa/inet.h>
12 #include "alloc-util.h"
14 #include "resolved-manager.h"
15 #include "resolved-mdns.h"
17 #define CLEAR_CACHE_FLUSH(x) (~MDNS_RR_CACHE_FLUSH & (x))
19 void manager_mdns_stop(Manager
*m
) {
22 m
->mdns_ipv4_event_source
= sd_event_source_unref(m
->mdns_ipv4_event_source
);
23 m
->mdns_ipv4_fd
= safe_close(m
->mdns_ipv4_fd
);
25 m
->mdns_ipv6_event_source
= sd_event_source_unref(m
->mdns_ipv6_event_source
);
26 m
->mdns_ipv6_fd
= safe_close(m
->mdns_ipv6_fd
);
29 int manager_mdns_start(Manager
*m
) {
34 if (m
->mdns_support
== RESOLVE_SUPPORT_NO
)
37 r
= manager_mdns_ipv4_fd(m
);
43 if (socket_ipv6_is_supported()) {
44 r
= manager_mdns_ipv6_fd(m
);
54 log_warning("Another mDNS responder prohibits binding the socket to the same port. Turning off mDNS support.");
55 m
->mdns_support
= RESOLVE_SUPPORT_NO
;
61 static int mdns_rr_compare(const void *a
, const void *b
) {
62 DnsResourceRecord
**x
= (DnsResourceRecord
**) a
, **y
= (DnsResourceRecord
**) b
;
71 if (CLEAR_CACHE_FLUSH((*x
)->key
->class) < CLEAR_CACHE_FLUSH((*y
)->key
->class))
73 else if (CLEAR_CACHE_FLUSH((*x
)->key
->class) > CLEAR_CACHE_FLUSH((*y
)->key
->class))
76 if ((*x
)->key
->type
< (*y
)->key
->type
)
78 else if ((*x
)->key
->type
> (*y
)->key
->type
)
81 r
= dns_resource_record_to_wire_format(*x
, false);
83 log_warning_errno(r
, "Can't wire-format RR: %m");
87 r
= dns_resource_record_to_wire_format(*y
, false);
89 log_warning_errno(r
, "Can't wire-format RR: %m");
93 m
= MIN(DNS_RESOURCE_RECORD_RDATA_SIZE(*x
), DNS_RESOURCE_RECORD_RDATA_SIZE(*y
));
95 r
= memcmp(DNS_RESOURCE_RECORD_RDATA(*x
), DNS_RESOURCE_RECORD_RDATA(*y
), m
);
99 if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x
) < DNS_RESOURCE_RECORD_RDATA_SIZE(*y
))
101 else if (DNS_RESOURCE_RECORD_RDATA_SIZE(*x
) > DNS_RESOURCE_RECORD_RDATA_SIZE(*y
))
107 static int proposed_rrs_cmp(DnsResourceRecord
**x
, unsigned x_size
, DnsResourceRecord
**y
, unsigned y_size
) {
111 m
= MIN(x_size
, y_size
);
112 for (unsigned i
= 0; i
< m
; i
++) {
113 r
= mdns_rr_compare(&x
[i
], &y
[i
]);
126 static int mdns_packet_extract_matching_rrs(DnsPacket
*p
, DnsResourceKey
*key
, DnsResourceRecord
***ret_rrs
) {
127 _cleanup_free_ DnsResourceRecord
**list
= NULL
;
128 unsigned n
= 0, size
= 0;
134 assert_return(DNS_PACKET_NSCOUNT(p
) > 0, -EINVAL
);
136 for (size_t i
= DNS_PACKET_ANCOUNT(p
); i
< (DNS_PACKET_ANCOUNT(p
) + DNS_PACKET_NSCOUNT(p
)); i
++) {
137 r
= dns_resource_key_match_rr(key
, p
->answer
->items
[i
].rr
, NULL
);
147 list
= new(DnsResourceRecord
*, size
);
151 for (size_t i
= DNS_PACKET_ANCOUNT(p
); i
< (DNS_PACKET_ANCOUNT(p
) + DNS_PACKET_NSCOUNT(p
)); i
++) {
152 r
= dns_resource_key_match_rr(key
, p
->answer
->items
[i
].rr
, NULL
);
156 list
[n
++] = p
->answer
->items
[i
].rr
;
159 qsort_safe(list
, size
, sizeof(DnsResourceRecord
*), mdns_rr_compare
);
161 *ret_rrs
= TAKE_PTR(list
);
166 static int mdns_do_tiebreak(DnsResourceKey
*key
, DnsAnswer
*answer
, DnsPacket
*p
) {
167 _cleanup_free_ DnsResourceRecord
**our
= NULL
, **remote
= NULL
;
168 DnsResourceRecord
*rr
;
172 size
= dns_answer_size(answer
);
173 our
= new(DnsResourceRecord
*, size
);
177 DNS_ANSWER_FOREACH(rr
, answer
)
179 qsort_safe(our
, size
, sizeof(DnsResourceRecord
*), mdns_rr_compare
);
181 r
= mdns_packet_extract_matching_rrs(p
, key
, &remote
);
187 if (proposed_rrs_cmp(remote
, r
, our
, size
) > 0)
193 static int mdns_scope_process_query(DnsScope
*s
, DnsPacket
*p
) {
194 _cleanup_(dns_answer_unrefp
) DnsAnswer
*full_answer
= NULL
;
195 _cleanup_(dns_packet_unrefp
) DnsPacket
*reply
= NULL
;
196 DnsResourceKey
*key
= NULL
;
197 DnsResourceRecord
*rr
;
198 bool tentative
= false;
204 r
= dns_packet_extract(p
);
206 return log_debug_errno(r
, "Failed to extract resource records from incoming packet: %m");
208 assert_return((dns_question_size(p
->question
) > 0), -EINVAL
);
210 DNS_QUESTION_FOREACH(key
, p
->question
) {
211 _cleanup_(dns_answer_unrefp
) DnsAnswer
*answer
= NULL
, *soa
= NULL
;
213 r
= dns_zone_lookup(&s
->zone
, key
, 0, &answer
, &soa
, &tentative
);
215 return log_debug_errno(r
, "Failed to lookup key: %m");
217 if (tentative
&& DNS_PACKET_NSCOUNT(p
) > 0) {
219 * A race condition detected with the probe packet from
221 * Do simultaneous probe tiebreaking as described in
222 * RFC 6762, Section 8.2. In case we lost don't reply
223 * the question and withdraw conflicting RRs.
225 r
= mdns_do_tiebreak(key
, answer
, p
);
227 return log_debug_errno(r
, "Failed to do tiebreaking");
229 if (r
> 0) { /* we lost */
230 DNS_ANSWER_FOREACH(rr
, answer
) {
233 i
= dns_zone_get(&s
->zone
, rr
);
235 dns_zone_item_conflict(i
);
242 r
= dns_answer_extend(&full_answer
, answer
);
244 return log_debug_errno(r
, "Failed to extend answer: %m");
247 if (dns_answer_isempty(full_answer
))
250 r
= dns_scope_make_reply_packet(s
, DNS_PACKET_ID(p
), DNS_RCODE_SUCCESS
, NULL
, full_answer
, NULL
, false, &reply
);
252 return log_debug_errno(r
, "Failed to build reply packet: %m");
254 if (!ratelimit_test(&s
->ratelimit
))
257 r
= dns_scope_emit_udp(s
, -1, reply
);
259 return log_debug_errno(r
, "Failed to send reply packet: %m");
264 static int on_mdns_packet(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
265 _cleanup_(dns_packet_unrefp
) DnsPacket
*p
= NULL
;
266 Manager
*m
= userdata
;
270 r
= manager_recv(m
, fd
, DNS_PROTOCOL_MDNS
, &p
);
274 if (manager_our_packet(m
, p
))
277 scope
= manager_find_scope(m
, p
);
279 log_debug("Got mDNS UDP packet on unknown scope. Ignoring.");
283 if (dns_packet_validate_reply(p
) > 0) {
284 DnsResourceRecord
*rr
;
286 log_debug("Got mDNS reply packet");
289 * mDNS is different from regular DNS and LLMNR with regard to handling responses.
290 * While on other protocols, we can ignore every answer that doesn't match a question
291 * we broadcast earlier, RFC6762, section 18.1 recommends looking at and caching all
292 * incoming information, regardless of the DNS packet ID.
294 * Hence, extract the packet here, and try to find a transaction for answer the we got
295 * and complete it. Also store the new information in scope's cache.
297 r
= dns_packet_extract(p
);
299 log_debug("mDNS packet extraction failed.");
303 dns_scope_check_conflicts(scope
, p
);
305 DNS_ANSWER_FOREACH(rr
, p
->answer
) {
306 const char *name
= dns_resource_key_name(rr
->key
);
309 /* If the received reply packet contains ANY record that is not .local or .in-addr.arpa,
310 * we assume someone's playing tricks on us and discard the packet completely. */
311 if (!(dns_name_endswith(name
, "in-addr.arpa") > 0 ||
312 dns_name_endswith(name
, "local") > 0))
316 log_debug("Got a goodbye packet");
317 /* See the section 10.1 of RFC6762 */
321 t
= dns_scope_find_transaction(scope
, rr
->key
, false);
323 dns_transaction_process_reply(t
, p
);
325 /* Also look for the various types of ANY transactions */
326 t
= dns_scope_find_transaction(scope
, &DNS_RESOURCE_KEY_CONST(rr
->key
->class, DNS_TYPE_ANY
, dns_resource_key_name(rr
->key
)), false);
328 dns_transaction_process_reply(t
, p
);
330 t
= dns_scope_find_transaction(scope
, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_ANY
, rr
->key
->type
, dns_resource_key_name(rr
->key
)), false);
332 dns_transaction_process_reply(t
, p
);
334 t
= dns_scope_find_transaction(scope
, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_ANY
, DNS_TYPE_ANY
, dns_resource_key_name(rr
->key
)), false);
336 dns_transaction_process_reply(t
, p
);
339 dns_cache_put(&scope
->cache
, NULL
, DNS_PACKET_RCODE(p
), p
->answer
, false, (uint32_t) -1, 0, p
->family
, &p
->sender
);
341 } else if (dns_packet_validate_query(p
) > 0) {
342 log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p
));
344 r
= mdns_scope_process_query(scope
, p
);
346 log_debug_errno(r
, "mDNS query processing failed: %m");
350 log_debug("Invalid mDNS UDP packet.");
355 int manager_mdns_ipv4_fd(Manager
*m
) {
356 union sockaddr_union sa
= {
357 .in
.sin_family
= AF_INET
,
358 .in
.sin_port
= htobe16(MDNS_PORT
),
360 static const int one
= 1, pmtu
= IP_PMTUDISC_DONT
, ttl
= 255;
365 if (m
->mdns_ipv4_fd
>= 0)
366 return m
->mdns_ipv4_fd
;
368 m
->mdns_ipv4_fd
= socket(AF_INET
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
369 if (m
->mdns_ipv4_fd
< 0)
370 return log_error_errno(errno
, "mDNS-IPv4: Failed to create socket: %m");
372 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_TTL
, &ttl
, sizeof(ttl
));
374 r
= log_error_errno(errno
, "mDNS-IPv4: Failed to set IP_TTL: %m");
378 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_MULTICAST_TTL
, &ttl
, sizeof(ttl
));
380 r
= log_error_errno(errno
, "mDNS-IPv4: Failed to set IP_MULTICAST_TTL: %m");
384 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_MULTICAST_LOOP
, &one
, sizeof(one
));
386 r
= log_error_errno(errno
, "mDNS-IPv4: Failed to set IP_MULTICAST_LOOP: %m");
390 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_PKTINFO
, &one
, sizeof(one
));
392 r
= log_error_errno(errno
, "mDNS-IPv4: Failed to set IP_PKTINFO: %m");
396 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_RECVTTL
, &one
, sizeof(one
));
398 r
= log_error_errno(errno
, "mDNS-IPv4: Failed to set IP_RECVTTL: %m");
402 /* Disable Don't-Fragment bit in the IP header */
403 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_MTU_DISCOVER
, &pmtu
, sizeof(pmtu
));
405 r
= log_error_errno(errno
, "mDNS-IPv4: Failed to set IP_MTU_DISCOVER: %m");
409 /* See the section 15.1 of RFC6762 */
410 /* first try to bind without SO_REUSEADDR to detect another mDNS responder */
411 r
= bind(m
->mdns_ipv4_fd
, &sa
.sa
, sizeof(sa
.in
));
413 if (errno
!= EADDRINUSE
) {
414 r
= log_error_errno(errno
, "mDNS-IPv4: Failed to bind socket: %m");
418 log_warning("mDNS-IPv4: There appears to be another mDNS responder running, or previously systemd-resolved crashed with some outstanding transfers.");
420 /* try again with SO_REUSEADDR */
421 r
= setsockopt(m
->mdns_ipv4_fd
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof(one
));
423 r
= log_error_errno(errno
, "mDNS-IPv4: Failed to set SO_REUSEADDR: %m");
427 r
= bind(m
->mdns_ipv4_fd
, &sa
.sa
, sizeof(sa
.in
));
429 r
= log_error_errno(errno
, "mDNS-IPv4: Failed to bind socket: %m");
433 /* enable SO_REUSEADDR for the case that the user really wants multiple mDNS responders */
434 r
= setsockopt(m
->mdns_ipv4_fd
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof(one
));
436 r
= log_error_errno(errno
, "mDNS-IPv4: Failed to set SO_REUSEADDR: %m");
441 r
= sd_event_add_io(m
->event
, &m
->mdns_ipv4_event_source
, m
->mdns_ipv4_fd
, EPOLLIN
, on_mdns_packet
, m
);
445 return m
->mdns_ipv4_fd
;
448 m
->mdns_ipv4_fd
= safe_close(m
->mdns_ipv4_fd
);
452 int manager_mdns_ipv6_fd(Manager
*m
) {
453 union sockaddr_union sa
= {
454 .in6
.sin6_family
= AF_INET6
,
455 .in6
.sin6_port
= htobe16(MDNS_PORT
),
457 static const int one
= 1, ttl
= 255;
462 if (m
->mdns_ipv6_fd
>= 0)
463 return m
->mdns_ipv6_fd
;
465 m
->mdns_ipv6_fd
= socket(AF_INET6
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
466 if (m
->mdns_ipv6_fd
< 0)
467 return log_error_errno(errno
, "mDNS-IPv6: Failed to create socket: %m");
469 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &ttl
, sizeof(ttl
));
471 r
= log_error_errno(errno
, "mDNS-IPv6: Failed to set IPV6_UNICAST_HOPS: %m");
475 /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
476 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, &ttl
, sizeof(ttl
));
478 r
= log_error_errno(errno
, "mDNS-IPv6: Failed to set IPV6_MULTICAST_HOPS: %m");
482 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, &one
, sizeof(one
));
484 r
= log_error_errno(errno
, "mDNS-IPv6: Failed to set IPV6_MULTICAST_LOOP: %m");
488 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &one
, sizeof(one
));
490 r
= log_error_errno(errno
, "mDNS-IPv6: Failed to set IPV6_V6ONLY: %m");
494 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, &one
, sizeof(one
));
496 r
= log_error_errno(errno
, "mDNS-IPv6: Failed to set IPV6_RECVPKTINFO: %m");
500 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_RECVHOPLIMIT
, &one
, sizeof(one
));
502 r
= log_error_errno(errno
, "mDNS-IPv6: Failed to set IPV6_RECVHOPLIMIT: %m");
506 /* See the section 15.1 of RFC6762 */
507 /* first try to bind without SO_REUSEADDR to detect another mDNS responder */
508 r
= bind(m
->mdns_ipv6_fd
, &sa
.sa
, sizeof(sa
.in6
));
510 if (errno
!= EADDRINUSE
) {
511 r
= log_error_errno(errno
, "mDNS-IPv6: Failed to bind socket: %m");
515 log_warning("mDNS-IPv6: There appears to be another mDNS responder running, or previously systemd-resolved crashed with some outstanding transfers.");
517 /* try again with SO_REUSEADDR */
518 r
= setsockopt(m
->mdns_ipv6_fd
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof(one
));
520 r
= log_error_errno(errno
, "mDNS-IPv6: Failed to set SO_REUSEADDR: %m");
524 r
= bind(m
->mdns_ipv6_fd
, &sa
.sa
, sizeof(sa
.in6
));
526 r
= log_error_errno(errno
, "mDNS-IPv6: Failed to bind socket: %m");
530 /* enable SO_REUSEADDR for the case that the user really wants multiple mDNS responders */
531 r
= setsockopt(m
->mdns_ipv6_fd
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof(one
));
533 r
= log_error_errno(errno
, "mDNS-IPv6: Failed to set SO_REUSEADDR: %m");
538 r
= sd_event_add_io(m
->event
, &m
->mdns_ipv6_event_source
, m
->mdns_ipv6_fd
, EPOLLIN
, on_mdns_packet
, m
);
542 return m
->mdns_ipv6_fd
;
545 m
->mdns_ipv6_fd
= safe_close(m
->mdns_ipv6_fd
);