]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-stream.c
resolved: be more careful with types in dns_stream_writev()
[thirdparty/systemd.git] / src / resolve / resolved-dns-stream.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
623a4c97
LP
2
3#include <netinet/tcp.h>
4
b5efdb8a 5#include "alloc-util.h"
3ffd4af2 6#include "fd-util.h"
afc5dbf3 7#include "io-util.h"
623a4c97
LP
8#include "missing.h"
9#include "resolved-dns-stream.h"
10
11#define DNS_STREAM_TIMEOUT_USEC (10 * USEC_PER_SEC)
12#define DNS_STREAMS_MAX 128
13
14static void dns_stream_stop(DnsStream *s) {
15 assert(s);
16
17 s->io_event_source = sd_event_source_unref(s->io_event_source);
18 s->timeout_event_source = sd_event_source_unref(s->timeout_event_source);
19 s->fd = safe_close(s->fd);
20}
21
22static int dns_stream_update_io(DnsStream *s) {
23 int f = 0;
24
25 assert(s);
26
27 if (s->write_packet && s->n_written < sizeof(s->write_size) + s->write_packet->size)
28 f |= EPOLLOUT;
98767d75
IT
29 else if (!ordered_set_isempty(s->write_queue)) {
30 dns_packet_unref(s->write_packet);
31 s->write_packet = ordered_set_steal_first(s->write_queue);
32 s->write_size = htobe16(s->write_packet->size);
33 s->n_written = 0;
34 f |= EPOLLOUT;
35 }
623a4c97
LP
36 if (!s->read_packet || s->n_read < sizeof(s->read_size) + s->read_packet->size)
37 f |= EPOLLIN;
38
ba6aaf57
IT
39#if ENABLE_DNS_OVER_TLS
40 /* For handshake and clean closing purposes, TLS can override requested events */
41 if (s->dnstls_events)
42 f = s->dnstls_events;
43#endif
44
623a4c97
LP
45 return sd_event_source_set_io_events(s->io_event_source, f);
46}
47
b914e211 48static int dns_stream_complete(DnsStream *s, int error) {
623a4c97
LP
49 assert(s);
50
56ddbf10 51#if ENABLE_DNS_OVER_TLS
6016fcb0 52 if (s->encrypted) {
5d67a7ae
IT
53 int r;
54
6016fcb0
IT
55 r = dnstls_stream_shutdown(s, error);
56 if (r != -EAGAIN)
5d67a7ae
IT
57 dns_stream_stop(s);
58 } else
59#endif
60 dns_stream_stop(s);
623a4c97
LP
61
62 if (s->complete)
63 s->complete(s, error);
b30bf55d
LP
64 else /* the default action if no completion function is set is to close the stream */
65 dns_stream_unref(s);
623a4c97
LP
66
67 return 0;
68}
69
b914e211
LP
70static int dns_stream_identify(DnsStream *s) {
71 union {
72 struct cmsghdr header; /* For alignment */
40a1eebd 73 uint8_t buffer[CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo))
b914e211
LP
74 + EXTRA_CMSG_SPACE /* kernel appears to require extra space */];
75 } control;
76 struct msghdr mh = {};
77 struct cmsghdr *cmsg;
78 socklen_t sl;
79 int r;
80
81 assert(s);
82
83 if (s->identified)
84 return 0;
85
86 /* Query the local side */
87 s->local_salen = sizeof(s->local);
88 r = getsockname(s->fd, &s->local.sa, &s->local_salen);
89 if (r < 0)
90 return -errno;
91 if (s->local.sa.sa_family == AF_INET6 && s->ifindex <= 0)
92 s->ifindex = s->local.in6.sin6_scope_id;
93
94 /* Query the remote side */
95 s->peer_salen = sizeof(s->peer);
96 r = getpeername(s->fd, &s->peer.sa, &s->peer_salen);
97 if (r < 0)
98 return -errno;
99 if (s->peer.sa.sa_family == AF_INET6 && s->ifindex <= 0)
100 s->ifindex = s->peer.in6.sin6_scope_id;
101
102 /* Check consistency */
103 assert(s->peer.sa.sa_family == s->local.sa.sa_family);
104 assert(IN_SET(s->peer.sa.sa_family, AF_INET, AF_INET6));
105
106 /* Query connection meta information */
107 sl = sizeof(control);
108 if (s->peer.sa.sa_family == AF_INET) {
109 r = getsockopt(s->fd, IPPROTO_IP, IP_PKTOPTIONS, &control, &sl);
110 if (r < 0)
111 return -errno;
112 } else if (s->peer.sa.sa_family == AF_INET6) {
113
114 r = getsockopt(s->fd, IPPROTO_IPV6, IPV6_2292PKTOPTIONS, &control, &sl);
115 if (r < 0)
116 return -errno;
117 } else
118 return -EAFNOSUPPORT;
119
120 mh.msg_control = &control;
121 mh.msg_controllen = sl;
2a1288ff
LP
122
123 CMSG_FOREACH(cmsg, &mh) {
b914e211
LP
124
125 if (cmsg->cmsg_level == IPPROTO_IPV6) {
126 assert(s->peer.sa.sa_family == AF_INET6);
127
128 switch (cmsg->cmsg_type) {
129
130 case IPV6_PKTINFO: {
131 struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
132
133 if (s->ifindex <= 0)
134 s->ifindex = i->ipi6_ifindex;
135 break;
136 }
137
138 case IPV6_HOPLIMIT:
139 s->ttl = *(int *) CMSG_DATA(cmsg);
140 break;
141 }
142
143 } else if (cmsg->cmsg_level == IPPROTO_IP) {
144 assert(s->peer.sa.sa_family == AF_INET);
145
146 switch (cmsg->cmsg_type) {
147
148 case IP_PKTINFO: {
149 struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
150
151 if (s->ifindex <= 0)
152 s->ifindex = i->ipi_ifindex;
153 break;
154 }
155
156 case IP_TTL:
157 s->ttl = *(int *) CMSG_DATA(cmsg);
158 break;
159 }
160 }
161 }
162
163 /* The Linux kernel sets the interface index to the loopback
164 * device if the connection came from the local host since it
165 * avoids the routing table in such a case. Let's unset the
166 * interface index in such a case. */
a5f03596 167 if (s->ifindex == LOOPBACK_IFINDEX)
b914e211
LP
168 s->ifindex = 0;
169
170 /* If we don't know the interface index still, we look for the
171 * first local interface with a matching address. Yuck! */
172 if (s->ifindex <= 0)
173 s->ifindex = manager_find_ifindex(s->manager, s->local.sa.sa_family, s->local.sa.sa_family == AF_INET ? (union in_addr_union*) &s->local.in.sin_addr : (union in_addr_union*) &s->local.in6.sin6_addr);
174
175 if (s->protocol == DNS_PROTOCOL_LLMNR && s->ifindex > 0) {
176 uint32_t ifindex = htobe32(s->ifindex);
177
178 /* Make sure all packets for this connection are sent on the same interface */
179 if (s->local.sa.sa_family == AF_INET) {
180 r = setsockopt(s->fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex));
181 if (r < 0)
56f64d95 182 log_debug_errno(errno, "Failed to invoke IP_UNICAST_IF: %m");
b914e211
LP
183 } else if (s->local.sa.sa_family == AF_INET6) {
184 r = setsockopt(s->fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex));
185 if (r < 0)
56f64d95 186 log_debug_errno(errno, "Failed to invoke IPV6_UNICAST_IF: %m");
b914e211
LP
187 }
188 }
189
190 s->identified = true;
191
192 return 0;
193}
194
6016fcb0 195ssize_t dns_stream_writev(DnsStream *s, const struct iovec *iov, size_t iovcnt, int flags) {
e6dc5556 196 ssize_t m;
91ccab1e
IT
197
198 assert(s);
199 assert(iov);
200
56ddbf10 201#if ENABLE_DNS_OVER_TLS
6016fcb0 202 if (s->encrypted && !(flags & DNS_STREAM_WRITE_TLS_DATA)) {
5d67a7ae
IT
203 ssize_t ss;
204 size_t i;
205
e6dc5556 206 m = 0;
5d67a7ae 207 for (i = 0; i < iovcnt; i++) {
6016fcb0
IT
208 ss = dnstls_stream_write(s, iov[i].iov_base, iov[i].iov_len);
209 if (ss < 0)
210 return ss;
5d67a7ae 211
e6dc5556 212 m += ss;
5d67a7ae
IT
213 if (ss != (ssize_t) iov[i].iov_len)
214 continue;
215 }
216 } else
217#endif
91ccab1e
IT
218 if (s->tfo_salen > 0) {
219 struct msghdr hdr = {
220 .msg_iov = (struct iovec*) iov,
221 .msg_iovlen = iovcnt,
222 .msg_name = &s->tfo_address.sa,
223 .msg_namelen = s->tfo_salen
224 };
225
e6dc5556
LP
226 m = sendmsg(s->fd, &hdr, MSG_FASTOPEN);
227 if (m < 0) {
91ccab1e
IT
228 if (errno == EOPNOTSUPP) {
229 s->tfo_salen = 0;
e6dc5556 230 if (connect(s->fd, &s->tfo_address.sa, s->tfo_salen) < 0)
91ccab1e
IT
231 return -errno;
232
e6dc5556
LP
233 return -EAGAIN;
234 }
235 if (errno == EINPROGRESS)
236 return -EAGAIN;
237
238 return -errno;
91ccab1e
IT
239 } else
240 s->tfo_salen = 0; /* connection is made */
f6c9c5f8 241 } else {
e6dc5556
LP
242 m = writev(s->fd, iov, iovcnt);
243 if (m < 0)
244 return -errno;
f6c9c5f8 245 }
91ccab1e 246
e6dc5556 247 return m;
91ccab1e
IT
248}
249
5d67a7ae
IT
250static ssize_t dns_stream_read(DnsStream *s, void *buf, size_t count) {
251 ssize_t ss;
252
56ddbf10 253#if ENABLE_DNS_OVER_TLS
6016fcb0
IT
254 if (s->encrypted)
255 ss = dnstls_stream_read(s, buf, count);
256 else
5d67a7ae 257#endif
f6c9c5f8 258 {
5d67a7ae 259 ss = read(s->fd, buf, count);
f6c9c5f8
IT
260 if (ss < 0)
261 ss = -errno;
262 }
5d67a7ae
IT
263
264 return ss;
265}
266
623a4c97
LP
267static int on_stream_timeout(sd_event_source *es, usec_t usec, void *userdata) {
268 DnsStream *s = userdata;
269
270 assert(s);
271
b914e211 272 return dns_stream_complete(s, ETIMEDOUT);
623a4c97
LP
273}
274
275static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
276 DnsStream *s = userdata;
277 int r;
278
279 assert(s);
280
56ddbf10 281#if ENABLE_DNS_OVER_TLS
6016fcb0 282 if (s->encrypted) {
04c4d919 283 r = dnstls_stream_on_io(s, revents);
ba6aaf57 284 if (r == DNSTLS_STREAM_CLOSED)
6016fcb0 285 return 0;
b2cf6704 286 if (r == -EAGAIN)
ba6aaf57 287 return dns_stream_update_io(s);
b2cf6704 288 if (r < 0)
6016fcb0 289 return dns_stream_complete(s, -r);
b2cf6704
LP
290
291 r = dns_stream_update_io(s);
292 if (r < 0)
293 return r;
5d67a7ae
IT
294 }
295#endif
296
91ccab1e
IT
297 /* only identify after connecting */
298 if (s->tfo_salen == 0) {
299 r = dns_stream_identify(s);
300 if (r < 0)
301 return dns_stream_complete(s, -r);
302 }
b914e211 303
623a4c97
LP
304 if ((revents & EPOLLOUT) &&
305 s->write_packet &&
306 s->n_written < sizeof(s->write_size) + s->write_packet->size) {
307
308 struct iovec iov[2];
309 ssize_t ss;
310
5cfa2c3d
LP
311 iov[0] = IOVEC_MAKE(&s->write_size, sizeof(s->write_size));
312 iov[1] = IOVEC_MAKE(DNS_PACKET_DATA(s->write_packet), s->write_packet->size);
623a4c97
LP
313
314 IOVEC_INCREMENT(iov, 2, s->n_written);
315
5d67a7ae 316 ss = dns_stream_writev(s, iov, 2, 0);
623a4c97 317 if (ss < 0) {
f6c9c5f8
IT
318 if (!IN_SET(-ss, EINTR, EAGAIN))
319 return dns_stream_complete(s, -ss);
623a4c97
LP
320 } else
321 s->n_written += ss;
322
323 /* Are we done? If so, disable the event source for EPOLLOUT */
324 if (s->n_written >= sizeof(s->write_size) + s->write_packet->size) {
325 r = dns_stream_update_io(s);
326 if (r < 0)
b914e211 327 return dns_stream_complete(s, -r);
623a4c97
LP
328 }
329 }
330
331 if ((revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) &&
332 (!s->read_packet ||
333 s->n_read < sizeof(s->read_size) + s->read_packet->size)) {
334
335 if (s->n_read < sizeof(s->read_size)) {
336 ssize_t ss;
337
5d67a7ae 338 ss = dns_stream_read(s, (uint8_t*) &s->read_size + s->n_read, sizeof(s->read_size) - s->n_read);
623a4c97 339 if (ss < 0) {
f6c9c5f8
IT
340 if (!IN_SET(-ss, EINTR, EAGAIN))
341 return dns_stream_complete(s, -ss);
623a4c97 342 } else if (ss == 0)
b914e211 343 return dns_stream_complete(s, ECONNRESET);
623a4c97
LP
344 else
345 s->n_read += ss;
346 }
347
348 if (s->n_read >= sizeof(s->read_size)) {
349
350 if (be16toh(s->read_size) < DNS_PACKET_HEADER_SIZE)
b914e211 351 return dns_stream_complete(s, EBADMSG);
623a4c97
LP
352
353 if (s->n_read < sizeof(s->read_size) + be16toh(s->read_size)) {
354 ssize_t ss;
355
356 if (!s->read_packet) {
51027656 357 r = dns_packet_new(&s->read_packet, s->protocol, be16toh(s->read_size), DNS_PACKET_SIZE_MAX);
623a4c97 358 if (r < 0)
b914e211 359 return dns_stream_complete(s, -r);
623a4c97
LP
360
361 s->read_packet->size = be16toh(s->read_size);
362 s->read_packet->ipproto = IPPROTO_TCP;
363 s->read_packet->family = s->peer.sa.sa_family;
364 s->read_packet->ttl = s->ttl;
365 s->read_packet->ifindex = s->ifindex;
366
367 if (s->read_packet->family == AF_INET) {
368 s->read_packet->sender.in = s->peer.in.sin_addr;
369 s->read_packet->sender_port = be16toh(s->peer.in.sin_port);
370 s->read_packet->destination.in = s->local.in.sin_addr;
371 s->read_packet->destination_port = be16toh(s->local.in.sin_port);
372 } else {
373 assert(s->read_packet->family == AF_INET6);
374 s->read_packet->sender.in6 = s->peer.in6.sin6_addr;
375 s->read_packet->sender_port = be16toh(s->peer.in6.sin6_port);
376 s->read_packet->destination.in6 = s->local.in6.sin6_addr;
377 s->read_packet->destination_port = be16toh(s->local.in6.sin6_port);
378
379 if (s->read_packet->ifindex == 0)
380 s->read_packet->ifindex = s->peer.in6.sin6_scope_id;
381 if (s->read_packet->ifindex == 0)
382 s->read_packet->ifindex = s->local.in6.sin6_scope_id;
383 }
384 }
385
5d67a7ae 386 ss = dns_stream_read(s,
623a4c97
LP
387 (uint8_t*) DNS_PACKET_DATA(s->read_packet) + s->n_read - sizeof(s->read_size),
388 sizeof(s->read_size) + be16toh(s->read_size) - s->n_read);
389 if (ss < 0) {
99521cab
YW
390 if (!IN_SET(-ss, EINTR, EAGAIN))
391 return dns_stream_complete(s, -ss);
623a4c97 392 } else if (ss == 0)
b914e211 393 return dns_stream_complete(s, ECONNRESET);
623a4c97
LP
394 else
395 s->n_read += ss;
396 }
397
398 /* Are we done? If so, disable the event source for EPOLLIN */
399 if (s->n_read >= sizeof(s->read_size) + be16toh(s->read_size)) {
623a4c97
LP
400 /* If there's a packet handler
401 * installed, call that. Note that
402 * this is optional... */
98767d75
IT
403 if (s->on_packet) {
404 r = s->on_packet(s);
405 if (r < 0)
406 return r;
407 }
408
409 r = dns_stream_update_io(s);
410 if (r < 0)
411 return dns_stream_complete(s, -r);
623a4c97
LP
412 }
413 }
414 }
415
416 if ((s->write_packet && s->n_written >= sizeof(s->write_size) + s->write_packet->size) &&
417 (s->read_packet && s->n_read >= sizeof(s->read_size) + s->read_packet->size))
b914e211 418 return dns_stream_complete(s, 0);
623a4c97
LP
419
420 return 0;
421}
422
8301aa0b 423static DnsStream *dns_stream_free(DnsStream *s) {
98767d75
IT
424 DnsPacket *p;
425 Iterator i;
426
8301aa0b 427 assert(s);
b30bf55d 428
623a4c97
LP
429 dns_stream_stop(s);
430
98767d75
IT
431 if (s->server && s->server->stream == s)
432 s->server->stream = NULL;
433
623a4c97
LP
434 if (s->manager) {
435 LIST_REMOVE(streams, s->manager->dns_streams, s);
436 s->manager->n_dns_streams--;
437 }
438
56ddbf10 439#if ENABLE_DNS_OVER_TLS
6016fcb0
IT
440 if (s->encrypted)
441 dnstls_stream_free(s);
5d67a7ae
IT
442#endif
443
98767d75
IT
444 ORDERED_SET_FOREACH(p, s->write_queue, i)
445 dns_packet_unref(ordered_set_remove(s->write_queue, p));
446
623a4c97
LP
447 dns_packet_unref(s->write_packet);
448 dns_packet_unref(s->read_packet);
98767d75
IT
449 dns_server_unref(s->server);
450
451 ordered_set_free(s->write_queue);
623a4c97 452
6b430fdb 453 return mfree(s);
623a4c97
LP
454}
455
8301aa0b 456DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsStream, dns_stream, dns_stream_free);
623a4c97 457
b27a32a0
LP
458int dns_stream_new(
459 Manager *m,
460 DnsStream **ret,
461 DnsProtocol protocol,
462 int fd,
463 const union sockaddr_union *tfo_address) {
464
b30bf55d 465 _cleanup_(dns_stream_unrefp) DnsStream *s = NULL;
623a4c97
LP
466 int r;
467
468 assert(m);
469 assert(fd >= 0);
470
471 if (m->n_dns_streams > DNS_STREAMS_MAX)
472 return -EBUSY;
473
898892e8 474 s = new(DnsStream, 1);
623a4c97
LP
475 if (!s)
476 return -ENOMEM;
477
898892e8
LP
478 *s = (DnsStream) {
479 .n_ref = 1,
480 .fd = -1,
481 .protocol = protocol,
482 };
483
98767d75
IT
484 r = ordered_set_ensure_allocated(&s->write_queue, &dns_packet_hash_ops);
485 if (r < 0)
486 return r;
487
623a4c97
LP
488 r = sd_event_add_io(m->event, &s->io_event_source, fd, EPOLLIN, on_stream_io, s);
489 if (r < 0)
490 return r;
491
aa4a9deb
LP
492 (void) sd_event_source_set_description(s->io_event_source, "dns-stream-io");
493
9a015429
LP
494 r = sd_event_add_time(
495 m->event,
496 &s->timeout_event_source,
497 clock_boottime_or_monotonic(),
498 now(clock_boottime_or_monotonic()) + DNS_STREAM_TIMEOUT_USEC, 0,
499 on_stream_timeout, s);
623a4c97
LP
500 if (r < 0)
501 return r;
502
aa4a9deb
LP
503 (void) sd_event_source_set_description(s->timeout_event_source, "dns-stream-timeout");
504
623a4c97 505 LIST_PREPEND(streams, m->dns_streams, s);
08e254c8 506 m->n_dns_streams++;
623a4c97 507 s->manager = m;
08e254c8 508
623a4c97 509 s->fd = fd;
08e254c8 510
91ccab1e
IT
511 if (tfo_address) {
512 s->tfo_address = *tfo_address;
513 s->tfo_salen = tfo_address->sa.sa_family == AF_INET6 ? sizeof(tfo_address->in6) : sizeof(tfo_address->in);
514 }
515
1cc6c93a 516 *ret = TAKE_PTR(s);
623a4c97
LP
517
518 return 0;
519}
520
521int dns_stream_write_packet(DnsStream *s, DnsPacket *p) {
98767d75
IT
522 int r;
523
623a4c97
LP
524 assert(s);
525
98767d75
IT
526 r = ordered_set_put(s->write_queue, p);
527 if (r < 0)
528 return r;
623a4c97 529
98767d75 530 dns_packet_ref(p);
623a4c97
LP
531
532 return dns_stream_update_io(s);
533}