]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-mdns.c
resolved: cache stringified transaction key once per transaction
[thirdparty/systemd.git] / src / resolve / resolved-mdns.c
CommitLineData
bc7702b0
DM
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
30void 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
40int manager_mdns_start(Manager *m) {
41 int r;
42
43 assert(m);
44
45 if (m->mdns_support == 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
64eaddrinuse:
65 log_warning("There appears to be another mDNS responder running. Turning off mDNS support.");
66 m->mdns_support = SUPPORT_NO;
67 manager_mdns_stop(m);
68
69 return 0;
70}
71
72static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
124602ae
DM
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) {
40fa4728 89 DnsResourceRecord *rr;
124602ae
DM
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
40fa4728
DM
110 DNS_ANSWER_FOREACH(rr, p->answer) {
111 const char *name = DNS_RESOURCE_KEY_NAME(rr->key);
124602ae
DM
112 DnsTransaction *t;
113
40fa4728
DM
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;
124602ae
DM
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,
126 p->answer->n_rrs, false, 0, p->family, &p->sender);
127
128 } else if (dns_packet_validate_query(p) > 0) {
129 log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p));
130
131 dns_scope_process_query(scope, NULL, p);
132 } else
133 log_debug("Invalid mDNS UDP packet.");
134
bc7702b0
DM
135 return 0;
136}
137
138int manager_mdns_ipv4_fd(Manager *m) {
139 union sockaddr_union sa = {
140 .in.sin_family = AF_INET,
141 .in.sin_port = htobe16(MDNS_PORT),
142 };
143 static const int one = 1, pmtu = IP_PMTUDISC_DONT, ttl = 255;
144 int r;
145
146 assert(m);
147
148 if (m->mdns_ipv4_fd >= 0)
149 return m->mdns_ipv4_fd;
150
151 m->mdns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
152 if (m->mdns_ipv4_fd < 0)
153 return -errno;
154
155 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
156 if (r < 0) {
157 r = -errno;
158 goto fail;
159 }
160
161 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
162 if (r < 0) {
163 r = -errno;
164 goto fail;
165 }
166
167 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one));
168 if (r < 0) {
169 r = -errno;
170 goto fail;
171 }
172
173 r = setsockopt(m->mdns_ipv4_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
174 if (r < 0) {
175 r = -errno;
176 goto fail;
177 }
178
179 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
180 if (r < 0) {
181 r = -errno;
182 goto fail;
183 }
184
185 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
186 if (r < 0) {
187 r = -errno;
188 goto fail;
189 }
190
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));
193 if (r < 0) {
194 r = -errno;
195 goto fail;
196 }
197
198 r = bind(m->mdns_ipv4_fd, &sa.sa, sizeof(sa.in));
199 if (r < 0) {
200 r = -errno;
201 goto fail;
202 }
203
204 r = sd_event_add_io(m->event, &m->mdns_ipv4_event_source, m->mdns_ipv4_fd, EPOLLIN, on_mdns_packet, m);
205 if (r < 0)
206 goto fail;
207
208 return m->mdns_ipv4_fd;
209
210fail:
211 m->mdns_ipv4_fd = safe_close(m->mdns_ipv4_fd);
212 return r;
213}
214
215int manager_mdns_ipv6_fd(Manager *m) {
216 union sockaddr_union sa = {
217 .in6.sin6_family = AF_INET6,
218 .in6.sin6_port = htobe16(MDNS_PORT),
219 };
220 static const int one = 1, ttl = 255;
221 int r;
222
223 assert(m);
224
225 if (m->mdns_ipv6_fd >= 0)
226 return m->mdns_ipv6_fd;
227
228 m->mdns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
229 if (m->mdns_ipv6_fd < 0)
230 return -errno;
231
232 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
233 if (r < 0) {
234 r = -errno;
235 goto fail;
236 }
237
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));
240 if (r < 0) {
241 r = -errno;
242 goto fail;
243 }
244
245 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one));
246 if (r < 0) {
247 r = -errno;
248 goto fail;
249 }
250
251 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
252 if (r < 0) {
253 r = -errno;
254 goto fail;
255 }
256
257 r = setsockopt(m->mdns_ipv6_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
258 if (r < 0) {
259 r = -errno;
260 goto fail;
261 }
262
263 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
264 if (r < 0) {
265 r = -errno;
266 goto fail;
267 }
268
269 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
270 if (r < 0) {
271 r = -errno;
272 goto fail;
273 }
274
275 r = bind(m->mdns_ipv6_fd, &sa.sa, sizeof(sa.in6));
276 if (r < 0) {
277 r = -errno;
278 goto fail;
279 }
280
281 r = sd_event_add_io(m->event, &m->mdns_ipv6_event_source, m->mdns_ipv6_fd, EPOLLIN, on_mdns_packet, m);
ee8d9305 282 if (r < 0)
bc7702b0 283 goto fail;
bc7702b0
DM
284
285 return m->mdns_ipv6_fd;
286
287fail:
288 m->mdns_ipv6_fd = safe_close(m->mdns_ipv6_fd);
289 return r;
290}