]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-mdns.c
Merge pull request #2086 from evverx/fix-journal-upload-installation
[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 == 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 = 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 unsigned i;
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 for (i = 0; i < p->answer->n_rrs; i++) {
111 DnsResourceRecord *rr;
112 DnsTransaction *t;
113
114 rr = p->answer->items[i].rr;
115
116 t = dns_scope_find_transaction(scope, rr->key, false);
117 if (t)
118 dns_transaction_process_reply(t, p);
119 }
120
121 dns_cache_put(&scope->cache, NULL, DNS_PACKET_RCODE(p), p->answer,
122 p->answer->n_rrs, false, 0, p->family, &p->sender);
123
124 } else if (dns_packet_validate_query(p) > 0) {
125 log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p));
126
127 dns_scope_process_query(scope, NULL, p);
128 } else
129 log_debug("Invalid mDNS UDP packet.");
130
131 return 0;
132 }
133
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),
138 };
139 static const int one = 1, pmtu = IP_PMTUDISC_DONT, ttl = 255;
140 int r;
141
142 assert(m);
143
144 if (m->mdns_ipv4_fd >= 0)
145 return m->mdns_ipv4_fd;
146
147 m->mdns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
148 if (m->mdns_ipv4_fd < 0)
149 return -errno;
150
151 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
152 if (r < 0) {
153 r = -errno;
154 goto fail;
155 }
156
157 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
158 if (r < 0) {
159 r = -errno;
160 goto fail;
161 }
162
163 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one));
164 if (r < 0) {
165 r = -errno;
166 goto fail;
167 }
168
169 r = setsockopt(m->mdns_ipv4_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
170 if (r < 0) {
171 r = -errno;
172 goto fail;
173 }
174
175 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
176 if (r < 0) {
177 r = -errno;
178 goto fail;
179 }
180
181 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
182 if (r < 0) {
183 r = -errno;
184 goto fail;
185 }
186
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));
189 if (r < 0) {
190 r = -errno;
191 goto fail;
192 }
193
194 r = bind(m->mdns_ipv4_fd, &sa.sa, sizeof(sa.in));
195 if (r < 0) {
196 r = -errno;
197 goto fail;
198 }
199
200 r = sd_event_add_io(m->event, &m->mdns_ipv4_event_source, m->mdns_ipv4_fd, EPOLLIN, on_mdns_packet, m);
201 if (r < 0)
202 goto fail;
203
204 return m->mdns_ipv4_fd;
205
206 fail:
207 m->mdns_ipv4_fd = safe_close(m->mdns_ipv4_fd);
208 return r;
209 }
210
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),
215 };
216 static const int one = 1, ttl = 255;
217 int r;
218
219 assert(m);
220
221 if (m->mdns_ipv6_fd >= 0)
222 return m->mdns_ipv6_fd;
223
224 m->mdns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
225 if (m->mdns_ipv6_fd < 0)
226 return -errno;
227
228 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
229 if (r < 0) {
230 r = -errno;
231 goto fail;
232 }
233
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));
236 if (r < 0) {
237 r = -errno;
238 goto fail;
239 }
240
241 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one));
242 if (r < 0) {
243 r = -errno;
244 goto fail;
245 }
246
247 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
248 if (r < 0) {
249 r = -errno;
250 goto fail;
251 }
252
253 r = setsockopt(m->mdns_ipv6_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
254 if (r < 0) {
255 r = -errno;
256 goto fail;
257 }
258
259 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
260 if (r < 0) {
261 r = -errno;
262 goto fail;
263 }
264
265 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
266 if (r < 0) {
267 r = -errno;
268 goto fail;
269 }
270
271 r = bind(m->mdns_ipv6_fd, &sa.sa, sizeof(sa.in6));
272 if (r < 0) {
273 r = -errno;
274 goto fail;
275 }
276
277 r = sd_event_add_io(m->event, &m->mdns_ipv6_event_source, m->mdns_ipv6_fd, EPOLLIN, on_mdns_packet, m);
278 if (r < 0)
279 goto fail;
280
281 return m->mdns_ipv6_fd;
282
283 fail:
284 m->mdns_ipv6_fd = safe_close(m->mdns_ipv6_fd);
285 return r;
286 }