]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-mdns.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2015 Daniel Mack
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
26 #include "resolved-manager.h"
27 #include "resolved-mdns.h"
29 void manager_mdns_stop ( Manager
* m
) {
32 m
-> mdns_ipv4_event_source
= sd_event_source_unref ( m
-> mdns_ipv4_event_source
);
33 m
-> mdns_ipv4_fd
= safe_close ( m
-> mdns_ipv4_fd
);
35 m
-> mdns_ipv6_event_source
= sd_event_source_unref ( m
-> mdns_ipv6_event_source
);
36 m
-> mdns_ipv6_fd
= safe_close ( m
-> mdns_ipv6_fd
);
39 int manager_mdns_start ( Manager
* m
) {
44 if ( m
-> mdns_support
== RESOLVE_SUPPORT_NO
)
47 r
= manager_mdns_ipv4_fd ( m
);
53 if ( socket_ipv6_is_supported ()) {
54 r
= manager_mdns_ipv6_fd ( m
);
64 log_warning ( "Another mDNS responder prohibits binding the socket to the same port. Turning off mDNS support." );
65 m
-> mdns_support
= RESOLVE_SUPPORT_NO
;
71 static int mdns_scope_process_query ( DnsScope
* s
, DnsPacket
* p
) {
72 _cleanup_ ( dns_answer_unrefp
) DnsAnswer
* answer
= NULL
, * soa
= NULL
;
73 _cleanup_ ( dns_packet_unrefp
) DnsPacket
* reply
= NULL
;
74 DnsResourceKey
* key
= NULL
;
75 bool tentative
= false ;
81 r
= dns_packet_extract ( p
);
83 return log_debug_errno ( r
, "Failed to extract resource records from incoming packet: %m" );
85 /* TODO: there might be more than one question in mDNS queries. */
86 assert_return (( dns_question_size ( p
-> question
) > 0 ), - EINVAL
);
87 key
= p
-> question
-> keys
[ 0 ];
89 r
= dns_zone_lookup (& s
-> zone
, key
, 0 , & answer
, & soa
, & tentative
);
91 return log_debug_errno ( r
, "Failed to lookup key: %m" );
95 r
= dns_scope_make_reply_packet ( s
, DNS_PACKET_ID ( p
), DNS_RCODE_SUCCESS
, NULL
, answer
, NULL
, false , & reply
);
97 return log_debug_errno ( r
, "Failed to build reply packet: %m" );
99 if (! ratelimit_test (& s
-> ratelimit
))
102 r
= dns_scope_emit_udp ( s
, - 1 , reply
);
104 return log_debug_errno ( r
, "Failed to send reply packet: %m" );
109 static int on_mdns_packet ( sd_event_source
* s
, int fd
, uint32_t revents
, void * userdata
) {
110 _cleanup_ ( dns_packet_unrefp
) DnsPacket
* p
= NULL
;
111 Manager
* m
= userdata
;
115 r
= manager_recv ( m
, fd
, DNS_PROTOCOL_MDNS
, & p
);
119 if ( manager_our_packet ( m
, p
))
122 scope
= manager_find_scope ( m
, p
);
124 log_warning ( "Got mDNS UDP packet on unknown scope. Ignoring." );
128 if ( dns_packet_validate_reply ( p
) > 0 ) {
129 DnsResourceRecord
* rr
;
131 log_debug ( "Got mDNS reply packet" );
134 * mDNS is different from regular DNS and LLMNR with regard to handling responses.
135 * While on other protocols, we can ignore every answer that doesn't match a question
136 * we broadcast earlier, RFC6762, section 18.1 recommends looking at and caching all
137 * incoming information, regardless of the DNS packet ID.
139 * Hence, extract the packet here, and try to find a transaction for answer the we got
140 * and complete it. Also store the new information in scope's cache.
142 r
= dns_packet_extract ( p
);
144 log_debug ( "mDNS packet extraction failed." );
148 dns_scope_check_conflicts ( scope
, p
);
150 DNS_ANSWER_FOREACH ( rr
, p
-> answer
) {
151 const char * name
= dns_resource_key_name ( rr
-> key
);
154 /* If the received reply packet contains ANY record that is not .local or .in-addr.arpa,
155 * we assume someone's playing tricks on us and discard the packet completely. */
156 if (!( dns_name_endswith ( name
, "in-addr.arpa" ) > 0 ||
157 dns_name_endswith ( name
, "local" ) > 0 ))
161 log_debug ( "Got a goodbye packet" );
162 /* See the section 10.1 of RFC6762 */
166 t
= dns_scope_find_transaction ( scope
, rr
-> key
, false );
168 dns_transaction_process_reply ( t
, p
);
170 /* Also look for the various types of ANY transactions */
171 t
= dns_scope_find_transaction ( scope
, & DNS_RESOURCE_KEY_CONST ( rr
-> key
-> class , DNS_TYPE_ANY
, dns_resource_key_name ( rr
-> key
)), false );
173 dns_transaction_process_reply ( t
, p
);
175 t
= dns_scope_find_transaction ( scope
, & DNS_RESOURCE_KEY_CONST ( DNS_CLASS_ANY
, rr
-> key
-> type
, dns_resource_key_name ( rr
-> key
)), false );
177 dns_transaction_process_reply ( t
, p
);
179 t
= dns_scope_find_transaction ( scope
, & DNS_RESOURCE_KEY_CONST ( DNS_CLASS_ANY
, DNS_TYPE_ANY
, dns_resource_key_name ( rr
-> key
)), false );
181 dns_transaction_process_reply ( t
, p
);
184 dns_cache_put (& scope
-> cache
, NULL
, DNS_PACKET_RCODE ( p
), p
-> answer
, false , ( uint32_t ) - 1 , 0 , p
-> family
, & p
-> sender
);
186 } else if ( dns_packet_validate_query ( p
) > 0 ) {
187 log_debug ( "Got mDNS query packet for id %u" , DNS_PACKET_ID ( p
));
189 r
= mdns_scope_process_query ( scope
, p
);
191 log_debug_errno ( r
, "mDNS query processing failed: %m" );
195 log_debug ( "Invalid mDNS UDP packet." );
200 int manager_mdns_ipv4_fd ( Manager
* m
) {
201 union sockaddr_union sa
= {
202 . in
. sin_family
= AF_INET
,
203 . in
. sin_port
= htobe16 ( MDNS_PORT
),
205 static const int one
= 1 , pmtu
= IP_PMTUDISC_DONT
, ttl
= 255 ;
210 if ( m
-> mdns_ipv4_fd
>= 0 )
211 return m
-> mdns_ipv4_fd
;
213 m
-> mdns_ipv4_fd
= socket ( AF_INET
, SOCK_DGRAM
| SOCK_CLOEXEC
| SOCK_NONBLOCK
, 0 );
214 if ( m
-> mdns_ipv4_fd
< 0 )
215 return log_error_errno ( errno
, "mDNS-IPv4: Failed to create socket: %m" );
217 r
= setsockopt ( m
-> mdns_ipv4_fd
, IPPROTO_IP
, IP_TTL
, & ttl
, sizeof ( ttl
));
219 r
= log_error_errno ( errno
, "mDNS-IPv4: Failed to set IP_TTL: %m" );
223 r
= setsockopt ( m
-> mdns_ipv4_fd
, IPPROTO_IP
, IP_MULTICAST_TTL
, & ttl
, sizeof ( ttl
));
225 r
= log_error_errno ( errno
, "mDNS-IPv4: Failed to set IP_MULTICAST_TTL: %m" );
229 r
= setsockopt ( m
-> mdns_ipv4_fd
, IPPROTO_IP
, IP_MULTICAST_LOOP
, & one
, sizeof ( one
));
231 r
= log_error_errno ( errno
, "mDNS-IPv4: Failed to set IP_MULTICAST_LOOP: %m" );
235 r
= setsockopt ( m
-> mdns_ipv4_fd
, IPPROTO_IP
, IP_PKTINFO
, & one
, sizeof ( one
));
237 r
= log_error_errno ( errno
, "mDNS-IPv4: Failed to set IP_PKTINFO: %m" );
241 r
= setsockopt ( m
-> mdns_ipv4_fd
, IPPROTO_IP
, IP_RECVTTL
, & one
, sizeof ( one
));
243 r
= log_error_errno ( errno
, "mDNS-IPv4: Failed to set IP_RECVTTL: %m" );
247 /* Disable Don't-Fragment bit in the IP header */
248 r
= setsockopt ( m
-> mdns_ipv4_fd
, IPPROTO_IP
, IP_MTU_DISCOVER
, & pmtu
, sizeof ( pmtu
));
250 r
= log_error_errno ( errno
, "mDNS-IPv4: Failed to set IP_MTU_DISCOVER: %m" );
254 /* See the section 15.1 of RFC6762 */
255 /* first try to bind without SO_REUSEADDR to detect another mDNS responder */
256 r
= bind ( m
-> mdns_ipv4_fd
, & sa
. sa
, sizeof ( sa
. in
));
258 if ( errno
!= EADDRINUSE
) {
259 r
= log_error_errno ( errno
, "mDNS-IPv4: Failed to bind socket: %m" );
263 log_warning ( "mDNS-IPv4: There appears to be another mDNS responder running, or previously systemd-resolved crashed with some outstanding transfers." );
265 /* try again with SO_REUSEADDR */
266 r
= setsockopt ( m
-> mdns_ipv4_fd
, SOL_SOCKET
, SO_REUSEADDR
, & one
, sizeof ( one
));
268 r
= log_error_errno ( errno
, "mDNS-IPv4: Failed to set SO_REUSEADDR: %m" );
272 r
= bind ( m
-> mdns_ipv4_fd
, & sa
. sa
, sizeof ( sa
. in
));
274 r
= log_error_errno ( errno
, "mDNS-IPv4: Failed to bind socket: %m" );
278 /* enable SO_REUSEADDR for the case that the user really wants multiple mDNS responders */
279 r
= setsockopt ( m
-> mdns_ipv4_fd
, SOL_SOCKET
, SO_REUSEADDR
, & one
, sizeof ( one
));
281 r
= log_error_errno ( errno
, "mDNS-IPv4: Failed to set SO_REUSEADDR: %m" );
286 r
= sd_event_add_io ( m
-> event
, & m
-> mdns_ipv4_event_source
, m
-> mdns_ipv4_fd
, EPOLLIN
, on_mdns_packet
, m
);
290 return m
-> mdns_ipv4_fd
;
293 m
-> mdns_ipv4_fd
= safe_close ( m
-> mdns_ipv4_fd
);
297 int manager_mdns_ipv6_fd ( Manager
* m
) {
298 union sockaddr_union sa
= {
299 . in6
. sin6_family
= AF_INET6
,
300 . in6
. sin6_port
= htobe16 ( MDNS_PORT
),
302 static const int one
= 1 , ttl
= 255 ;
307 if ( m
-> mdns_ipv6_fd
>= 0 )
308 return m
-> mdns_ipv6_fd
;
310 m
-> mdns_ipv6_fd
= socket ( AF_INET6
, SOCK_DGRAM
| SOCK_CLOEXEC
| SOCK_NONBLOCK
, 0 );
311 if ( m
-> mdns_ipv6_fd
< 0 )
312 return log_error_errno ( errno
, "mDNS-IPv6: Failed to create socket: %m" );
314 r
= setsockopt ( m
-> mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, & ttl
, sizeof ( ttl
));
316 r
= log_error_errno ( errno
, "mDNS-IPv6: Failed to set IPV6_UNICAST_HOPS: %m" );
320 /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
321 r
= setsockopt ( m
-> mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, & ttl
, sizeof ( ttl
));
323 r
= log_error_errno ( errno
, "mDNS-IPv6: Failed to set IPV6_MULTICAST_HOPS: %m" );
327 r
= setsockopt ( m
-> mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, & one
, sizeof ( one
));
329 r
= log_error_errno ( errno
, "mDNS-IPv6: Failed to set IPV6_MULTICAST_LOOP: %m" );
333 r
= setsockopt ( m
-> mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_V6ONLY
, & one
, sizeof ( one
));
335 r
= log_error_errno ( errno
, "mDNS-IPv6: Failed to set IPV6_V6ONLY: %m" );
339 r
= setsockopt ( m
-> mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, & one
, sizeof ( one
));
341 r
= log_error_errno ( errno
, "mDNS-IPv6: Failed to set IPV6_RECVPKTINFO: %m" );
345 r
= setsockopt ( m
-> mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_RECVHOPLIMIT
, & one
, sizeof ( one
));
347 r
= log_error_errno ( errno
, "mDNS-IPv6: Failed to set IPV6_RECVHOPLIMIT: %m" );
351 /* See the section 15.1 of RFC6762 */
352 /* first try to bind without SO_REUSEADDR to detect another mDNS responder */
353 r
= bind ( m
-> mdns_ipv6_fd
, & sa
. sa
, sizeof ( sa
. in6
));
355 if ( errno
!= EADDRINUSE
) {
356 r
= log_error_errno ( errno
, "mDNS-IPv6: Failed to bind socket: %m" );
360 log_warning ( "mDNS-IPv6: There appears to be another mDNS responder running, or previously systemd-resolved crashed with some outstanding transfers." );
362 /* try again with SO_REUSEADDR */
363 r
= setsockopt ( m
-> mdns_ipv6_fd
, SOL_SOCKET
, SO_REUSEADDR
, & one
, sizeof ( one
));
365 r
= log_error_errno ( errno
, "mDNS-IPv6: Failed to set SO_REUSEADDR: %m" );
369 r
= bind ( m
-> mdns_ipv6_fd
, & sa
. sa
, sizeof ( sa
. in6
));
371 r
= log_error_errno ( errno
, "mDNS-IPv6: Failed to bind socket: %m" );
375 /* enable SO_REUSEADDR for the case that the user really wants multiple mDNS responders */
376 r
= setsockopt ( m
-> mdns_ipv6_fd
, SOL_SOCKET
, SO_REUSEADDR
, & one
, sizeof ( one
));
378 r
= log_error_errno ( errno
, "mDNS-IPv6: Failed to set SO_REUSEADDR: %m" );
383 r
= sd_event_add_io ( m
-> event
, & m
-> mdns_ipv6_event_source
, m
-> mdns_ipv6_fd
, EPOLLIN
, on_mdns_packet
, m
);
387 return m
-> mdns_ipv6_fd
;
390 m
-> mdns_ipv6_fd
= safe_close ( m
-> mdns_ipv6_fd
);