]>
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("There appears to be another mDNS responder running. 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)
222 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_TTL
, &ttl
, sizeof(ttl
));
228 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_MULTICAST_TTL
, &ttl
, sizeof(ttl
));
234 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_MULTICAST_LOOP
, &one
, sizeof(one
));
240 r
= setsockopt(m
->mdns_ipv4_fd
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof(one
));
246 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_PKTINFO
, &one
, sizeof(one
));
252 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_RECVTTL
, &one
, sizeof(one
));
258 /* Disable Don't-Fragment bit in the IP header */
259 r
= setsockopt(m
->mdns_ipv4_fd
, IPPROTO_IP
, IP_MTU_DISCOVER
, &pmtu
, sizeof(pmtu
));
265 r
= bind(m
->mdns_ipv4_fd
, &sa
.sa
, sizeof(sa
.in
));
271 r
= sd_event_add_io(m
->event
, &m
->mdns_ipv4_event_source
, m
->mdns_ipv4_fd
, EPOLLIN
, on_mdns_packet
, m
);
275 return m
->mdns_ipv4_fd
;
278 m
->mdns_ipv4_fd
= safe_close(m
->mdns_ipv4_fd
);
282 int manager_mdns_ipv6_fd(Manager
*m
) {
283 union sockaddr_union sa
= {
284 .in6
.sin6_family
= AF_INET6
,
285 .in6
.sin6_port
= htobe16(MDNS_PORT
),
287 static const int one
= 1, ttl
= 255;
292 if (m
->mdns_ipv6_fd
>= 0)
293 return m
->mdns_ipv6_fd
;
295 m
->mdns_ipv6_fd
= socket(AF_INET6
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
296 if (m
->mdns_ipv6_fd
< 0)
299 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
, &ttl
, sizeof(ttl
));
305 /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
306 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_MULTICAST_HOPS
, &ttl
, sizeof(ttl
));
312 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, &one
, sizeof(one
));
318 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &one
, sizeof(one
));
324 r
= setsockopt(m
->mdns_ipv6_fd
, SOL_SOCKET
, SO_REUSEADDR
, &one
, sizeof(one
));
330 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, &one
, sizeof(one
));
336 r
= setsockopt(m
->mdns_ipv6_fd
, IPPROTO_IPV6
, IPV6_RECVHOPLIMIT
, &one
, sizeof(one
));
342 r
= bind(m
->mdns_ipv6_fd
, &sa
.sa
, sizeof(sa
.in6
));
348 r
= sd_event_add_io(m
->event
, &m
->mdns_ipv6_event_source
, m
->mdns_ipv6_fd
, EPOLLIN
, on_mdns_packet
, m
);
352 return m
->mdns_ipv6_fd
;
355 m
->mdns_ipv6_fd
= safe_close(m
->mdns_ipv6_fd
);