]>
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) {
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 for (i
= 0; i
< p
->answer
->n_rrs
; i
++) {
111 DnsResourceRecord
*rr
;
114 rr
= p
->answer
->items
[i
].rr
;
116 t
= dns_scope_find_transaction(scope
, rr
->key
, false);
118 dns_transaction_process_reply(t
, p
);
121 dns_cache_put(&scope
->cache
, NULL
, DNS_PACKET_RCODE(p
), p
->answer
,
122 p
->answer
->n_rrs
, false, 0, p
->family
, &p
->sender
);
124 } else if (dns_packet_validate_query(p
) > 0) {
125 log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p
));
127 dns_scope_process_query(scope
, NULL
, p
);
129 log_debug("Invalid mDNS UDP packet.");
134 int manager_mdns_ipv4_fd(Manager
*m
) {
135 union sockaddr_union sa
= {
136 .in
.sin_family
= AF_INET
,
137 .in
.sin_port
= htobe16(MDNS_PORT
),
139 static const int one
= 1, pmtu
= IP_PMTUDISC_DONT
, ttl
= 255;
144 if (m
->mdns_ipv4_fd
>= 0)
145 return m
->mdns_ipv4_fd
;
147 m
->mdns_ipv4_fd
= socket(AF_INET
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
148 if (m
->mdns_ipv4_fd
< 0)
151 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_TTL
, &ttl
, sizeof(ttl
));
157 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_MULTICAST_TTL
, &ttl
, sizeof(ttl
));
163 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_MULTICAST_LOOP
, &one
, sizeof(one
));
169 r
= setsockopt(m
->mdns_ipv4_fd
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof(one
));
175 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_PKTINFO
, &one
, sizeof(one
));
181 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_RECVTTL
, &one
, sizeof(one
));
187 /* Disable Don't-Fragment bit in the IP header */
188 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_MTU_DISCOVER
, &pmtu
, sizeof(pmtu
));
194 r
= bind(m
->mdns_ipv4_fd
, &sa
.sa
, sizeof(sa
.in
));
200 r
= sd_event_add_io(m
->event
, &m
->mdns_ipv4_event_source
, m
->mdns_ipv4_fd
, EPOLLIN
, on_mdns_packet
, m
);
204 return m
->mdns_ipv4_fd
;
207 m
->mdns_ipv4_fd
= safe_close(m
->mdns_ipv4_fd
);
211 int manager_mdns_ipv6_fd(Manager
*m
) {
212 union sockaddr_union sa
= {
213 .in6
.sin6_family
= AF_INET6
,
214 .in6
.sin6_port
= htobe16(MDNS_PORT
),
216 static const int one
= 1, ttl
= 255;
221 if (m
->mdns_ipv6_fd
>= 0)
222 return m
->mdns_ipv6_fd
;
224 m
->mdns_ipv6_fd
= socket(AF_INET6
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
225 if (m
->mdns_ipv6_fd
< 0)
228 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &ttl
, sizeof(ttl
));
234 /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
235 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, &ttl
, sizeof(ttl
));
241 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, &one
, sizeof(one
));
247 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &one
, sizeof(one
));
253 r
= setsockopt(m
->mdns_ipv6_fd
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof(one
));
259 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, &one
, sizeof(one
));
265 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_RECVHOPLIMIT
, &one
, sizeof(one
));
271 r
= bind(m
->mdns_ipv6_fd
, &sa
.sa
, sizeof(sa
.in6
));
277 r
= sd_event_add_io(m
->event
, &m
->mdns_ipv6_event_source
, m
->mdns_ipv6_fd
, EPOLLIN
, on_mdns_packet
, m
);
281 return m
->mdns_ipv6_fd
;
284 m
->mdns_ipv6_fd
= safe_close(m
->mdns_ipv6_fd
);