]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-mdns.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2015 Daniel Mack
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
27 #include "resolved-manager.h"
28 #include "resolved-mdns.h"
30 void manager_mdns_stop(Manager
*m
) {
33 m
->mdns_ipv4_event_source
= sd_event_source_unref(m
->mdns_ipv4_event_source
);
34 m
->mdns_ipv4_fd
= safe_close(m
->mdns_ipv4_fd
);
36 m
->mdns_ipv6_event_source
= sd_event_source_unref(m
->mdns_ipv6_event_source
);
37 m
->mdns_ipv6_fd
= safe_close(m
->mdns_ipv6_fd
);
40 int manager_mdns_start(Manager
*m
) {
45 if (m
->mdns_support
== SUPPORT_NO
)
48 r
= manager_mdns_ipv4_fd(m
);
54 if (socket_ipv6_is_supported()) {
55 r
= manager_mdns_ipv6_fd(m
);
65 log_warning("There appears to be another mDNS responder running. Turning off mDNS support.");
66 m
->mdns_support
= SUPPORT_NO
;
72 static int on_mdns_packet(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
73 _cleanup_(dns_packet_unrefp
) DnsPacket
*p
= NULL
;
74 Manager
*m
= userdata
;
78 r
= manager_recv(m
, fd
, DNS_PROTOCOL_MDNS
, &p
);
82 scope
= manager_find_scope(m
, p
);
84 log_warning("Got mDNS UDP packet on unknown scope. Ignoring.");
88 if (dns_packet_validate_reply(p
) > 0) {
89 DnsResourceRecord
*rr
;
91 log_debug("Got mDNS reply packet");
94 * mDNS is different from regular DNS and LLMNR with regard to handling responses.
95 * While on other protocols, we can ignore every answer that doesn't match a question
96 * we broadcast earlier, RFC6762, section 18.1 recommends looking at and caching all
97 * incoming information, regardless of the DNS packet ID.
99 * Hence, extract the packet here, and try to find a transaction for answer the we got
100 * and complete it. Also store the new information in scope's cache.
102 r
= dns_packet_extract(p
);
104 log_debug("mDNS packet extraction failed.");
108 dns_scope_check_conflicts(scope
, p
);
110 DNS_ANSWER_FOREACH(rr
, p
->answer
) {
111 const char *name
= DNS_RESOURCE_KEY_NAME(rr
->key
);
114 /* If the received reply packet contains ANY record that is not .local or .in-addr.arpa,
115 * we assume someone's playing tricks on us and discard the packet completely. */
116 if (!(dns_name_endswith(name
, "in-addr.arpa") > 0 ||
117 dns_name_endswith(name
, "local") > 0))
120 t
= dns_scope_find_transaction(scope
, rr
->key
, false);
122 dns_transaction_process_reply(t
, p
);
125 dns_cache_put(&scope
->cache
, NULL
, DNS_PACKET_RCODE(p
), p
->answer
, false, (uint32_t) -1, 0, p
->family
, &p
->sender
);
127 } else if (dns_packet_validate_query(p
) > 0) {
128 log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p
));
130 dns_scope_process_query(scope
, NULL
, p
);
132 log_debug("Invalid mDNS UDP packet.");
137 int manager_mdns_ipv4_fd(Manager
*m
) {
138 union sockaddr_union sa
= {
139 .in
.sin_family
= AF_INET
,
140 .in
.sin_port
= htobe16(MDNS_PORT
),
142 static const int one
= 1, pmtu
= IP_PMTUDISC_DONT
, ttl
= 255;
147 if (m
->mdns_ipv4_fd
>= 0)
148 return m
->mdns_ipv4_fd
;
150 m
->mdns_ipv4_fd
= socket(AF_INET
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
151 if (m
->mdns_ipv4_fd
< 0)
154 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_TTL
, &ttl
, sizeof(ttl
));
160 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_MULTICAST_TTL
, &ttl
, sizeof(ttl
));
166 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_MULTICAST_LOOP
, &one
, sizeof(one
));
172 r
= setsockopt(m
->mdns_ipv4_fd
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof(one
));
178 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_PKTINFO
, &one
, sizeof(one
));
184 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_RECVTTL
, &one
, sizeof(one
));
190 /* Disable Don't-Fragment bit in the IP header */
191 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_MTU_DISCOVER
, &pmtu
, sizeof(pmtu
));
197 r
= bind(m
->mdns_ipv4_fd
, &sa
.sa
, sizeof(sa
.in
));
203 r
= sd_event_add_io(m
->event
, &m
->mdns_ipv4_event_source
, m
->mdns_ipv4_fd
, EPOLLIN
, on_mdns_packet
, m
);
207 return m
->mdns_ipv4_fd
;
210 m
->mdns_ipv4_fd
= safe_close(m
->mdns_ipv4_fd
);
214 int manager_mdns_ipv6_fd(Manager
*m
) {
215 union sockaddr_union sa
= {
216 .in6
.sin6_family
= AF_INET6
,
217 .in6
.sin6_port
= htobe16(MDNS_PORT
),
219 static const int one
= 1, ttl
= 255;
224 if (m
->mdns_ipv6_fd
>= 0)
225 return m
->mdns_ipv6_fd
;
227 m
->mdns_ipv6_fd
= socket(AF_INET6
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
228 if (m
->mdns_ipv6_fd
< 0)
231 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &ttl
, sizeof(ttl
));
237 /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
238 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, &ttl
, sizeof(ttl
));
244 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, &one
, sizeof(one
));
250 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &one
, sizeof(one
));
256 r
= setsockopt(m
->mdns_ipv6_fd
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof(one
));
262 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, &one
, sizeof(one
));
268 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_RECVHOPLIMIT
, &one
, sizeof(one
));
274 r
= bind(m
->mdns_ipv6_fd
, &sa
.sa
, sizeof(sa
.in6
));
280 r
= sd_event_add_io(m
->event
, &m
->mdns_ipv6_event_source
, m
->mdns_ipv6_fd
, EPOLLIN
, on_mdns_packet
, m
);
284 return m
->mdns_ipv6_fd
;
287 m
->mdns_ipv6_fd
= safe_close(m
->mdns_ipv6_fd
);