]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-mdns.c
2 This file is part of systemd.
4 Copyright 2015 Daniel Mack
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
25 #include "resolved-manager.h"
26 #include "resolved-mdns.h"
28 void manager_mdns_stop ( Manager
* m
) {
31 m
-> mdns_ipv4_event_source
= sd_event_source_unref ( m
-> mdns_ipv4_event_source
);
32 m
-> mdns_ipv4_fd
= safe_close ( m
-> mdns_ipv4_fd
);
34 m
-> mdns_ipv6_event_source
= sd_event_source_unref ( m
-> mdns_ipv6_event_source
);
35 m
-> mdns_ipv6_fd
= safe_close ( m
-> mdns_ipv6_fd
);
38 int manager_mdns_start ( Manager
* m
) {
43 if ( m
-> mdns_support
== RESOLVE_SUPPORT_NO
)
46 r
= manager_mdns_ipv4_fd ( m
);
52 if ( socket_ipv6_is_supported ()) {
53 r
= manager_mdns_ipv6_fd ( m
);
63 log_warning ( "Another mDNS responder prohibits binding the socket to the same port. Turning off mDNS support." );
64 m
-> mdns_support
= RESOLVE_SUPPORT_NO
;
70 static int mdns_scope_process_query ( DnsScope
* s
, DnsPacket
* p
) {
71 _cleanup_ ( dns_answer_unrefp
) DnsAnswer
* answer
= NULL
, * soa
= NULL
;
72 _cleanup_ ( dns_packet_unrefp
) DnsPacket
* reply
= NULL
;
73 DnsResourceKey
* key
= NULL
;
74 bool tentative
= false ;
80 r
= dns_packet_extract ( p
);
82 return log_debug_errno ( r
, "Failed to extract resource records from incoming packet: %m" );
84 /* TODO: there might be more than one question in mDNS queries. */
85 assert_return (( dns_question_size ( p
-> question
) > 0 ), - EINVAL
);
86 key
= p
-> question
-> keys
[ 0 ];
88 r
= dns_zone_lookup (& s
-> zone
, key
, 0 , & answer
, & soa
, & tentative
);
90 log_debug_errno ( r
, "Failed to lookup key: %m" );
96 r
= dns_scope_make_reply_packet ( s
, DNS_PACKET_ID ( p
), DNS_RCODE_SUCCESS
, NULL
, answer
, NULL
, false , & reply
);
98 log_debug_errno ( r
, "Failed to build reply packet: %m" );
102 if (! ratelimit_test (& s
-> ratelimit
))
105 r
= dns_scope_emit_udp ( s
, - 1 , reply
);
107 log_debug_errno ( r
, "Failed to send reply packet: %m" );
114 static int on_mdns_packet ( sd_event_source
* s
, int fd
, uint32_t revents
, void * userdata
) {
115 _cleanup_ ( dns_packet_unrefp
) DnsPacket
* p
= NULL
;
116 Manager
* m
= userdata
;
120 r
= manager_recv ( m
, fd
, DNS_PROTOCOL_MDNS
, & p
);
124 if ( manager_our_packet ( m
, p
))
127 scope
= manager_find_scope ( m
, p
);
129 log_warning ( "Got mDNS UDP packet on unknown scope. Ignoring." );
133 if ( dns_packet_validate_reply ( p
) > 0 ) {
134 DnsResourceRecord
* rr
;
136 log_debug ( "Got mDNS reply packet" );
139 * mDNS is different from regular DNS and LLMNR with regard to handling responses.
140 * While on other protocols, we can ignore every answer that doesn't match a question
141 * we broadcast earlier, RFC6762, section 18.1 recommends looking at and caching all
142 * incoming information, regardless of the DNS packet ID.
144 * Hence, extract the packet here, and try to find a transaction for answer the we got
145 * and complete it. Also store the new information in scope's cache.
147 r
= dns_packet_extract ( p
);
149 log_debug ( "mDNS packet extraction failed." );
153 dns_scope_check_conflicts ( scope
, p
);
155 DNS_ANSWER_FOREACH ( rr
, p
-> answer
) {
156 const char * name
= dns_resource_key_name ( rr
-> key
);
159 /* If the received reply packet contains ANY record that is not .local or .in-addr.arpa,
160 * we assume someone's playing tricks on us and discard the packet completely. */
161 if (!( dns_name_endswith ( name
, "in-addr.arpa" ) > 0 ||
162 dns_name_endswith ( name
, "local" ) > 0 ))
166 log_debug ( "Got a goodbye packet" );
167 /* See the section 10.1 of RFC6762 */
171 t
= dns_scope_find_transaction ( scope
, rr
-> key
, false );
173 dns_transaction_process_reply ( t
, p
);
175 /* Also look for the various types of ANY transactions */
176 t
= dns_scope_find_transaction ( scope
, & DNS_RESOURCE_KEY_CONST ( rr
-> key
-> class , DNS_TYPE_ANY
, dns_resource_key_name ( rr
-> key
)), false );
178 dns_transaction_process_reply ( t
, p
);
180 t
= dns_scope_find_transaction ( scope
, & DNS_RESOURCE_KEY_CONST ( DNS_CLASS_ANY
, rr
-> key
-> type
, dns_resource_key_name ( rr
-> key
)), false );
182 dns_transaction_process_reply ( t
, p
);
184 t
= dns_scope_find_transaction ( scope
, & DNS_RESOURCE_KEY_CONST ( DNS_CLASS_ANY
, DNS_TYPE_ANY
, dns_resource_key_name ( rr
-> key
)), false );
186 dns_transaction_process_reply ( t
, p
);
189 dns_cache_put (& scope
-> cache
, NULL
, DNS_PACKET_RCODE ( p
), p
-> answer
, false , ( uint32_t ) - 1 , 0 , p
-> family
, & p
-> sender
);
191 } else if ( dns_packet_validate_query ( p
) > 0 ) {
192 log_debug ( "Got mDNS query packet for id %u" , DNS_PACKET_ID ( p
));
194 r
= mdns_scope_process_query ( scope
, p
);
196 log_debug_errno ( r
, "mDNS query processing failed: %m" );
200 log_debug ( "Invalid mDNS UDP packet." );
205 int manager_mdns_ipv4_fd ( Manager
* m
) {
206 union sockaddr_union sa
= {
207 . in
. sin_family
= AF_INET
,
208 . in
. sin_port
= htobe16 ( MDNS_PORT
),
210 static const int one
= 1 , pmtu
= IP_PMTUDISC_DONT
, ttl
= 255 ;
215 if ( m
-> mdns_ipv4_fd
>= 0 )
216 return m
-> mdns_ipv4_fd
;
218 m
-> mdns_ipv4_fd
= socket ( AF_INET
, SOCK_DGRAM
| SOCK_CLOEXEC
| SOCK_NONBLOCK
, 0 );
219 if ( m
-> mdns_ipv4_fd
< 0 )
220 return log_error_errno ( errno
, "mDNS-IPv4: Failed to create socket: %m" );
222 r
= setsockopt ( m
-> mdns_ipv4_fd
, IPPROTO_IP
, IP_TTL
, & ttl
, sizeof ( ttl
));
224 r
= log_error_errno ( errno
, "mDNS-IPv4: Failed to set IP_TTL: %m" );
228 r
= setsockopt ( m
-> mdns_ipv4_fd
, IPPROTO_IP
, IP_MULTICAST_TTL
, & ttl
, sizeof ( ttl
));
230 r
= log_error_errno ( errno
, "mDNS-IPv4: Failed to set IP_MULTICAST_TTL: %m" );
234 r
= setsockopt ( m
-> mdns_ipv4_fd
, IPPROTO_IP
, IP_MULTICAST_LOOP
, & one
, sizeof ( one
));
236 r
= log_error_errno ( errno
, "mDNS-IPv4: Failed to set IP_MULTICAST_LOOP: %m" );
240 r
= setsockopt ( m
-> mdns_ipv4_fd
, IPPROTO_IP
, IP_PKTINFO
, & one
, sizeof ( one
));
242 r
= log_error_errno ( errno
, "mDNS-IPv4: Failed to set IP_PKTINFO: %m" );
246 r
= setsockopt ( m
-> mdns_ipv4_fd
, IPPROTO_IP
, IP_RECVTTL
, & one
, sizeof ( one
));
248 r
= log_error_errno ( errno
, "mDNS-IPv4: Failed to set IP_RECVTTL: %m" );
252 /* Disable Don't-Fragment bit in the IP header */
253 r
= setsockopt ( m
-> mdns_ipv4_fd
, IPPROTO_IP
, IP_MTU_DISCOVER
, & pmtu
, sizeof ( pmtu
));
255 r
= log_error_errno ( errno
, "mDNS-IPv4: Failed to set IP_MTU_DISCOVER: %m" );
259 /* See the section 15.1 of RFC6762 */
260 /* first try to bind without SO_REUSEADDR to detect another mDNS responder */
261 r
= bind ( m
-> mdns_ipv4_fd
, & sa
. sa
, sizeof ( sa
. in
));
263 if ( errno
!= EADDRINUSE
) {
264 r
= log_error_errno ( errno
, "mDNS-IPv4: Failed to bind socket: %m" );
268 log_warning ( "mDNS-IPv4: There appears to be another mDNS responder running, or previously systemd-resolved crashed with some outstanding transfers." );
270 /* try again with SO_REUSEADDR */
271 r
= setsockopt ( m
-> mdns_ipv4_fd
, SOL_SOCKET
, SO_REUSEADDR
, & one
, sizeof ( one
));
273 r
= log_error_errno ( errno
, "mDNS-IPv4: Failed to set SO_REUSEADDR: %m" );
277 r
= bind ( m
-> mdns_ipv4_fd
, & sa
. sa
, sizeof ( sa
. in
));
279 r
= log_error_errno ( errno
, "mDNS-IPv4: Failed to bind socket: %m" );
283 /* enable SO_REUSEADDR for the case that the user really wants multiple mDNS responders */
284 r
= setsockopt ( m
-> mdns_ipv4_fd
, SOL_SOCKET
, SO_REUSEADDR
, & one
, sizeof ( one
));
286 r
= log_error_errno ( errno
, "mDNS-IPv4: Failed to set SO_REUSEADDR: %m" );
291 r
= sd_event_add_io ( m
-> event
, & m
-> mdns_ipv4_event_source
, m
-> mdns_ipv4_fd
, EPOLLIN
, on_mdns_packet
, m
);
295 return m
-> mdns_ipv4_fd
;
298 m
-> mdns_ipv4_fd
= safe_close ( m
-> mdns_ipv4_fd
);
302 int manager_mdns_ipv6_fd ( Manager
* m
) {
303 union sockaddr_union sa
= {
304 . in6
. sin6_family
= AF_INET6
,
305 . in6
. sin6_port
= htobe16 ( MDNS_PORT
),
307 static const int one
= 1 , ttl
= 255 ;
312 if ( m
-> mdns_ipv6_fd
>= 0 )
313 return m
-> mdns_ipv6_fd
;
315 m
-> mdns_ipv6_fd
= socket ( AF_INET6
, SOCK_DGRAM
| SOCK_CLOEXEC
| SOCK_NONBLOCK
, 0 );
316 if ( m
-> mdns_ipv6_fd
< 0 )
317 return log_error_errno ( errno
, "mDNS-IPv6: Failed to create socket: %m" );
319 r
= setsockopt ( m
-> mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, & ttl
, sizeof ( ttl
));
321 r
= log_error_errno ( errno
, "mDNS-IPv6: Failed to set IPV6_UNICAST_HOPS: %m" );
325 /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
326 r
= setsockopt ( m
-> mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, & ttl
, sizeof ( ttl
));
328 r
= log_error_errno ( errno
, "mDNS-IPv6: Failed to set IPV6_MULTICAST_HOPS: %m" );
332 r
= setsockopt ( m
-> mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, & one
, sizeof ( one
));
334 r
= log_error_errno ( errno
, "mDNS-IPv6: Failed to set IPV6_MULTICAST_LOOP: %m" );
338 r
= setsockopt ( m
-> mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_V6ONLY
, & one
, sizeof ( one
));
340 r
= log_error_errno ( errno
, "mDNS-IPv6: Failed to set IPV6_V6ONLY: %m" );
344 r
= setsockopt ( m
-> mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, & one
, sizeof ( one
));
346 r
= log_error_errno ( errno
, "mDNS-IPv6: Failed to set IPV6_RECVPKTINFO: %m" );
350 r
= setsockopt ( m
-> mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_RECVHOPLIMIT
, & one
, sizeof ( one
));
352 r
= log_error_errno ( errno
, "mDNS-IPv6: Failed to set IPV6_RECVHOPLIMIT: %m" );
356 /* See the section 15.1 of RFC6762 */
357 /* first try to bind without SO_REUSEADDR to detect another mDNS responder */
358 r
= bind ( m
-> mdns_ipv6_fd
, & sa
. sa
, sizeof ( sa
. in6
));
360 if ( errno
!= EADDRINUSE
) {
361 r
= log_error_errno ( errno
, "mDNS-IPv6: Failed to bind socket: %m" );
365 log_warning ( "mDNS-IPv6: There appears to be another mDNS responder running, or previously systemd-resolved crashed with some outstanding transfers." );
367 /* try again with SO_REUSEADDR */
368 r
= setsockopt ( m
-> mdns_ipv6_fd
, SOL_SOCKET
, SO_REUSEADDR
, & one
, sizeof ( one
));
370 r
= log_error_errno ( errno
, "mDNS-IPv6: Failed to set SO_REUSEADDR: %m" );
374 r
= bind ( m
-> mdns_ipv6_fd
, & sa
. sa
, sizeof ( sa
. in6
));
376 r
= log_error_errno ( errno
, "mDNS-IPv6: Failed to bind socket: %m" );
380 /* enable SO_REUSEADDR for the case that the user really wants multiple mDNS responders */
381 r
= setsockopt ( m
-> mdns_ipv6_fd
, SOL_SOCKET
, SO_REUSEADDR
, & one
, sizeof ( one
));
383 r
= log_error_errno ( errno
, "mDNS-IPv6: Failed to set SO_REUSEADDR: %m" );
388 r
= sd_event_add_io ( m
-> event
, & m
-> mdns_ipv6_event_source
, m
-> mdns_ipv6_fd
, EPOLLIN
, on_mdns_packet
, m
);
392 return m
-> mdns_ipv6_fd
;
395 m
-> mdns_ipv6_fd
= safe_close ( m
-> mdns_ipv6_fd
);