]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-mdns.c
resolved,networkd: unify ResolveSupport enum
[thirdparty/systemd.git] / src / resolve / resolved-mdns.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2015 Daniel Mack
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <resolv.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
25
26 #include "fd-util.h"
27 #include "resolved-manager.h"
28 #include "resolved-mdns.h"
29
30 void manager_mdns_stop(Manager *m) {
31 assert(m);
32
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);
35
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);
38 }
39
40 int manager_mdns_start(Manager *m) {
41 int r;
42
43 assert(m);
44
45 if (m->mdns_support == RESOLVE_SUPPORT_NO)
46 return 0;
47
48 r = manager_mdns_ipv4_fd(m);
49 if (r == -EADDRINUSE)
50 goto eaddrinuse;
51 if (r < 0)
52 return r;
53
54 if (socket_ipv6_is_supported()) {
55 r = manager_mdns_ipv6_fd(m);
56 if (r == -EADDRINUSE)
57 goto eaddrinuse;
58 if (r < 0)
59 return r;
60 }
61
62 return 0;
63
64 eaddrinuse:
65 log_warning("There appears to be another mDNS responder running. Turning off mDNS support.");
66 m->mdns_support = RESOLVE_SUPPORT_NO;
67 manager_mdns_stop(m);
68
69 return 0;
70 }
71
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;
75 DnsScope *scope;
76 int r;
77
78 r = manager_recv(m, fd, DNS_PROTOCOL_MDNS, &p);
79 if (r <= 0)
80 return r;
81
82 scope = manager_find_scope(m, p);
83 if (!scope) {
84 log_warning("Got mDNS UDP packet on unknown scope. Ignoring.");
85 return 0;
86 }
87
88 if (dns_packet_validate_reply(p) > 0) {
89 DnsResourceRecord *rr;
90
91 log_debug("Got mDNS reply packet");
92
93 /*
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.
98 *
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.
101 */
102 r = dns_packet_extract(p);
103 if (r < 0) {
104 log_debug("mDNS packet extraction failed.");
105 return 0;
106 }
107
108 dns_scope_check_conflicts(scope, p);
109
110 DNS_ANSWER_FOREACH(rr, p->answer) {
111 const char *name = DNS_RESOURCE_KEY_NAME(rr->key);
112 DnsTransaction *t;
113
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))
118 return 0;
119
120 t = dns_scope_find_transaction(scope, rr->key, false);
121 if (t)
122 dns_transaction_process_reply(t, p);
123 }
124
125 dns_cache_put(&scope->cache, NULL, DNS_PACKET_RCODE(p), p->answer, false, (uint32_t) -1, 0, p->family, &p->sender);
126
127 } else if (dns_packet_validate_query(p) > 0) {
128 log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p));
129
130 dns_scope_process_query(scope, NULL, p);
131 } else
132 log_debug("Invalid mDNS UDP packet.");
133
134 return 0;
135 }
136
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),
141 };
142 static const int one = 1, pmtu = IP_PMTUDISC_DONT, ttl = 255;
143 int r;
144
145 assert(m);
146
147 if (m->mdns_ipv4_fd >= 0)
148 return m->mdns_ipv4_fd;
149
150 m->mdns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
151 if (m->mdns_ipv4_fd < 0)
152 return -errno;
153
154 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
155 if (r < 0) {
156 r = -errno;
157 goto fail;
158 }
159
160 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
161 if (r < 0) {
162 r = -errno;
163 goto fail;
164 }
165
166 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one));
167 if (r < 0) {
168 r = -errno;
169 goto fail;
170 }
171
172 r = setsockopt(m->mdns_ipv4_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
173 if (r < 0) {
174 r = -errno;
175 goto fail;
176 }
177
178 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
179 if (r < 0) {
180 r = -errno;
181 goto fail;
182 }
183
184 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
185 if (r < 0) {
186 r = -errno;
187 goto fail;
188 }
189
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));
192 if (r < 0) {
193 r = -errno;
194 goto fail;
195 }
196
197 r = bind(m->mdns_ipv4_fd, &sa.sa, sizeof(sa.in));
198 if (r < 0) {
199 r = -errno;
200 goto fail;
201 }
202
203 r = sd_event_add_io(m->event, &m->mdns_ipv4_event_source, m->mdns_ipv4_fd, EPOLLIN, on_mdns_packet, m);
204 if (r < 0)
205 goto fail;
206
207 return m->mdns_ipv4_fd;
208
209 fail:
210 m->mdns_ipv4_fd = safe_close(m->mdns_ipv4_fd);
211 return r;
212 }
213
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),
218 };
219 static const int one = 1, ttl = 255;
220 int r;
221
222 assert(m);
223
224 if (m->mdns_ipv6_fd >= 0)
225 return m->mdns_ipv6_fd;
226
227 m->mdns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
228 if (m->mdns_ipv6_fd < 0)
229 return -errno;
230
231 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
232 if (r < 0) {
233 r = -errno;
234 goto fail;
235 }
236
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));
239 if (r < 0) {
240 r = -errno;
241 goto fail;
242 }
243
244 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one));
245 if (r < 0) {
246 r = -errno;
247 goto fail;
248 }
249
250 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
251 if (r < 0) {
252 r = -errno;
253 goto fail;
254 }
255
256 r = setsockopt(m->mdns_ipv6_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
257 if (r < 0) {
258 r = -errno;
259 goto fail;
260 }
261
262 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
263 if (r < 0) {
264 r = -errno;
265 goto fail;
266 }
267
268 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
269 if (r < 0) {
270 r = -errno;
271 goto fail;
272 }
273
274 r = bind(m->mdns_ipv6_fd, &sa.sa, sizeof(sa.in6));
275 if (r < 0) {
276 r = -errno;
277 goto fail;
278 }
279
280 r = sd_event_add_io(m->event, &m->mdns_ipv6_event_source, m->mdns_ipv6_fd, EPOLLIN, on_mdns_packet, m);
281 if (r < 0)
282 goto fail;
283
284 return m->mdns_ipv6_fd;
285
286 fail:
287 m->mdns_ipv6_fd = safe_close(m->mdns_ipv6_fd);
288 return r;
289 }