]>
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
,
126 p
->answer
->n_rrs
, false, 0, p
->family
, &p
->sender
);
128 } else if (dns_packet_validate_query(p
) > 0) {
129 log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p
));
131 dns_scope_process_query(scope
, NULL
, p
);
133 log_debug("Invalid mDNS UDP packet.");
138 int manager_mdns_ipv4_fd(Manager
*m
) {
139 union sockaddr_union sa
= {
140 .in
.sin_family
= AF_INET
,
141 .in
.sin_port
= htobe16(MDNS_PORT
),
143 static const int one
= 1, pmtu
= IP_PMTUDISC_DONT
, ttl
= 255;
148 if (m
->mdns_ipv4_fd
>= 0)
149 return m
->mdns_ipv4_fd
;
151 m
->mdns_ipv4_fd
= socket(AF_INET
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
152 if (m
->mdns_ipv4_fd
< 0)
155 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_TTL
, &ttl
, sizeof(ttl
));
161 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_MULTICAST_TTL
, &ttl
, sizeof(ttl
));
167 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_MULTICAST_LOOP
, &one
, sizeof(one
));
173 r
= setsockopt(m
->mdns_ipv4_fd
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof(one
));
179 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_PKTINFO
, &one
, sizeof(one
));
185 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_RECVTTL
, &one
, sizeof(one
));
191 /* Disable Don't-Fragment bit in the IP header */
192 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_MTU_DISCOVER
, &pmtu
, sizeof(pmtu
));
198 r
= bind(m
->mdns_ipv4_fd
, &sa
.sa
, sizeof(sa
.in
));
204 r
= sd_event_add_io(m
->event
, &m
->mdns_ipv4_event_source
, m
->mdns_ipv4_fd
, EPOLLIN
, on_mdns_packet
, m
);
208 return m
->mdns_ipv4_fd
;
211 m
->mdns_ipv4_fd
= safe_close(m
->mdns_ipv4_fd
);
215 int manager_mdns_ipv6_fd(Manager
*m
) {
216 union sockaddr_union sa
= {
217 .in6
.sin6_family
= AF_INET6
,
218 .in6
.sin6_port
= htobe16(MDNS_PORT
),
220 static const int one
= 1, ttl
= 255;
225 if (m
->mdns_ipv6_fd
>= 0)
226 return m
->mdns_ipv6_fd
;
228 m
->mdns_ipv6_fd
= socket(AF_INET6
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
229 if (m
->mdns_ipv6_fd
< 0)
232 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &ttl
, sizeof(ttl
));
238 /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
239 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, &ttl
, sizeof(ttl
));
245 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, &one
, sizeof(one
));
251 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &one
, sizeof(one
));
257 r
= setsockopt(m
->mdns_ipv6_fd
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof(one
));
263 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, &one
, sizeof(one
));
269 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_RECVHOPLIMIT
, &one
, sizeof(one
));
275 r
= bind(m
->mdns_ipv6_fd
, &sa
.sa
, sizeof(sa
.in6
));
281 r
= sd_event_add_io(m
->event
, &m
->mdns_ipv6_event_source
, m
->mdns_ipv6_fd
, EPOLLIN
, on_mdns_packet
, m
);
285 return m
->mdns_ipv6_fd
;
288 m
->mdns_ipv6_fd
= safe_close(m
->mdns_ipv6_fd
);