]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-mdns.c
Merge pull request #2621 from keszybz/wheel-group
[thirdparty/systemd.git] / src / resolve / resolved-mdns.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2015 Daniel Mack
5
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.
10
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.
15
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/>.
18 ***/
19
20 #include <resolv.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23
24 #include "fd-util.h"
25 #include "resolved-manager.h"
26 #include "resolved-mdns.h"
27
28 void manager_mdns_stop(Manager *m) {
29 assert(m);
30
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);
33
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);
36 }
37
38 int manager_mdns_start(Manager *m) {
39 int r;
40
41 assert(m);
42
43 if (m->mdns_support == RESOLVE_SUPPORT_NO)
44 return 0;
45
46 r = manager_mdns_ipv4_fd(m);
47 if (r == -EADDRINUSE)
48 goto eaddrinuse;
49 if (r < 0)
50 return r;
51
52 if (socket_ipv6_is_supported()) {
53 r = manager_mdns_ipv6_fd(m);
54 if (r == -EADDRINUSE)
55 goto eaddrinuse;
56 if (r < 0)
57 return r;
58 }
59
60 return 0;
61
62 eaddrinuse:
63 log_warning("There appears to be another mDNS responder running. Turning off mDNS support.");
64 m->mdns_support = RESOLVE_SUPPORT_NO;
65 manager_mdns_stop(m);
66
67 return 0;
68 }
69
70 static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
71 _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
72 Manager *m = userdata;
73 DnsScope *scope;
74 int r;
75
76 r = manager_recv(m, fd, DNS_PROTOCOL_MDNS, &p);
77 if (r <= 0)
78 return r;
79
80 scope = manager_find_scope(m, p);
81 if (!scope) {
82 log_warning("Got mDNS UDP packet on unknown scope. Ignoring.");
83 return 0;
84 }
85
86 if (dns_packet_validate_reply(p) > 0) {
87 DnsResourceRecord *rr;
88
89 log_debug("Got mDNS reply packet");
90
91 /*
92 * mDNS is different from regular DNS and LLMNR with regard to handling responses.
93 * While on other protocols, we can ignore every answer that doesn't match a question
94 * we broadcast earlier, RFC6762, section 18.1 recommends looking at and caching all
95 * incoming information, regardless of the DNS packet ID.
96 *
97 * Hence, extract the packet here, and try to find a transaction for answer the we got
98 * and complete it. Also store the new information in scope's cache.
99 */
100 r = dns_packet_extract(p);
101 if (r < 0) {
102 log_debug("mDNS packet extraction failed.");
103 return 0;
104 }
105
106 dns_scope_check_conflicts(scope, p);
107
108 DNS_ANSWER_FOREACH(rr, p->answer) {
109 const char *name = dns_resource_key_name(rr->key);
110 DnsTransaction *t;
111
112 /* If the received reply packet contains ANY record that is not .local or .in-addr.arpa,
113 * we assume someone's playing tricks on us and discard the packet completely. */
114 if (!(dns_name_endswith(name, "in-addr.arpa") > 0 ||
115 dns_name_endswith(name, "local") > 0))
116 return 0;
117
118 t = dns_scope_find_transaction(scope, rr->key, false);
119 if (t)
120 dns_transaction_process_reply(t, p);
121 }
122
123 dns_cache_put(&scope->cache, NULL, DNS_PACKET_RCODE(p), p->answer, false, (uint32_t) -1, 0, p->family, &p->sender);
124
125 } else if (dns_packet_validate_query(p) > 0) {
126 log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p));
127
128 dns_scope_process_query(scope, NULL, p);
129 } else
130 log_debug("Invalid mDNS UDP packet.");
131
132 return 0;
133 }
134
135 int manager_mdns_ipv4_fd(Manager *m) {
136 union sockaddr_union sa = {
137 .in.sin_family = AF_INET,
138 .in.sin_port = htobe16(MDNS_PORT),
139 };
140 static const int one = 1, pmtu = IP_PMTUDISC_DONT, ttl = 255;
141 int r;
142
143 assert(m);
144
145 if (m->mdns_ipv4_fd >= 0)
146 return m->mdns_ipv4_fd;
147
148 m->mdns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
149 if (m->mdns_ipv4_fd < 0)
150 return -errno;
151
152 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
153 if (r < 0) {
154 r = -errno;
155 goto fail;
156 }
157
158 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
159 if (r < 0) {
160 r = -errno;
161 goto fail;
162 }
163
164 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one));
165 if (r < 0) {
166 r = -errno;
167 goto fail;
168 }
169
170 r = setsockopt(m->mdns_ipv4_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
171 if (r < 0) {
172 r = -errno;
173 goto fail;
174 }
175
176 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
177 if (r < 0) {
178 r = -errno;
179 goto fail;
180 }
181
182 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
183 if (r < 0) {
184 r = -errno;
185 goto fail;
186 }
187
188 /* Disable Don't-Fragment bit in the IP header */
189 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu));
190 if (r < 0) {
191 r = -errno;
192 goto fail;
193 }
194
195 r = bind(m->mdns_ipv4_fd, &sa.sa, sizeof(sa.in));
196 if (r < 0) {
197 r = -errno;
198 goto fail;
199 }
200
201 r = sd_event_add_io(m->event, &m->mdns_ipv4_event_source, m->mdns_ipv4_fd, EPOLLIN, on_mdns_packet, m);
202 if (r < 0)
203 goto fail;
204
205 return m->mdns_ipv4_fd;
206
207 fail:
208 m->mdns_ipv4_fd = safe_close(m->mdns_ipv4_fd);
209 return r;
210 }
211
212 int manager_mdns_ipv6_fd(Manager *m) {
213 union sockaddr_union sa = {
214 .in6.sin6_family = AF_INET6,
215 .in6.sin6_port = htobe16(MDNS_PORT),
216 };
217 static const int one = 1, ttl = 255;
218 int r;
219
220 assert(m);
221
222 if (m->mdns_ipv6_fd >= 0)
223 return m->mdns_ipv6_fd;
224
225 m->mdns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
226 if (m->mdns_ipv6_fd < 0)
227 return -errno;
228
229 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
230 if (r < 0) {
231 r = -errno;
232 goto fail;
233 }
234
235 /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
236 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl));
237 if (r < 0) {
238 r = -errno;
239 goto fail;
240 }
241
242 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one));
243 if (r < 0) {
244 r = -errno;
245 goto fail;
246 }
247
248 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
249 if (r < 0) {
250 r = -errno;
251 goto fail;
252 }
253
254 r = setsockopt(m->mdns_ipv6_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
255 if (r < 0) {
256 r = -errno;
257 goto fail;
258 }
259
260 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
261 if (r < 0) {
262 r = -errno;
263 goto fail;
264 }
265
266 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
267 if (r < 0) {
268 r = -errno;
269 goto fail;
270 }
271
272 r = bind(m->mdns_ipv6_fd, &sa.sa, sizeof(sa.in6));
273 if (r < 0) {
274 r = -errno;
275 goto fail;
276 }
277
278 r = sd_event_add_io(m->event, &m->mdns_ipv6_event_source, m->mdns_ipv6_fd, EPOLLIN, on_mdns_packet, m);
279 if (r < 0)
280 goto fail;
281
282 return m->mdns_ipv6_fd;
283
284 fail:
285 m->mdns_ipv6_fd = safe_close(m->mdns_ipv6_fd);
286 return r;
287 }