]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/resolve/resolved-dns-stream.c
NEWS: minor formatting tweaks
[thirdparty/systemd.git] / src / resolve / resolved-dns-stream.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
623a4c97
LP
2
3#include <netinet/tcp.h>
f5947a5e 4#include <unistd.h>
623a4c97 5
b5efdb8a 6#include "alloc-util.h"
3ffd4af2 7#include "fd-util.h"
afc5dbf3 8#include "io-util.h"
2aaf6bb6 9#include "macro.h"
f5947a5e 10#include "missing_network.h"
623a4c97 11#include "resolved-dns-stream.h"
be28f72d 12#include "resolved-manager.h"
623a4c97 13
623a4c97
LP
14#define DNS_STREAMS_MAX 128
15
b412af57
LP
16#define DNS_QUERIES_PER_STREAM 32
17
623a4c97
LP
18static void dns_stream_stop(DnsStream *s) {
19 assert(s);
20
97935302
ZJS
21 s->io_event_source = sd_event_source_disable_unref(s->io_event_source);
22 s->timeout_event_source = sd_event_source_disable_unref(s->timeout_event_source);
623a4c97 23 s->fd = safe_close(s->fd);
808089ae
LP
24
25 /* Disconnect us from the server object if we are now not usable anymore */
26 dns_stream_detach(s);
623a4c97
LP
27}
28
29static int dns_stream_update_io(DnsStream *s) {
30 int f = 0;
31
32 assert(s);
33
34 if (s->write_packet && s->n_written < sizeof(s->write_size) + s->write_packet->size)
35 f |= EPOLLOUT;
98767d75
IT
36 else if (!ordered_set_isempty(s->write_queue)) {
37 dns_packet_unref(s->write_packet);
38 s->write_packet = ordered_set_steal_first(s->write_queue);
39 s->write_size = htobe16(s->write_packet->size);
40 s->n_written = 0;
41 f |= EPOLLOUT;
42 }
b412af57
LP
43
44 /* Let's read a packet if we haven't queued any yet. Except if we already hit a limit of parallel
45 * queries for this connection. */
46 if ((!s->read_packet || s->n_read < sizeof(s->read_size) + s->read_packet->size) &&
47 set_size(s->queries) < DNS_QUERIES_PER_STREAM)
623a4c97
LP
48 f |= EPOLLIN;
49
ba6aaf57
IT
50#if ENABLE_DNS_OVER_TLS
51 /* For handshake and clean closing purposes, TLS can override requested events */
57bdb749 52 if (s->dnstls_events != 0)
ba6aaf57
IT
53 f = s->dnstls_events;
54#endif
55
623a4c97
LP
56 return sd_event_source_set_io_events(s->io_event_source, f);
57}
58
b914e211 59static int dns_stream_complete(DnsStream *s, int error) {
d973d94d
LP
60 _cleanup_(dns_stream_unrefp) _unused_ DnsStream *ref = dns_stream_ref(s); /* Protect stream while we process it */
61
623a4c97 62 assert(s);
f447d9e3
LP
63 assert(error >= 0);
64
65 /* Error is > 0 when the connection failed for some reason in the network stack. It's == 0 if we sent
5238e957 66 * and received exactly one packet each (in the LLMNR client case). */
623a4c97 67
56ddbf10 68#if ENABLE_DNS_OVER_TLS
6016fcb0 69 if (s->encrypted) {
5d67a7ae
IT
70 int r;
71
6016fcb0
IT
72 r = dnstls_stream_shutdown(s, error);
73 if (r != -EAGAIN)
5d67a7ae
IT
74 dns_stream_stop(s);
75 } else
76#endif
77 dns_stream_stop(s);
623a4c97 78
7172e4ee
LP
79 dns_stream_detach(s);
80
623a4c97
LP
81 if (s->complete)
82 s->complete(s, error);
b30bf55d
LP
83 else /* the default action if no completion function is set is to close the stream */
84 dns_stream_unref(s);
623a4c97
LP
85
86 return 0;
87}
88
b914e211 89static int dns_stream_identify(DnsStream *s) {
fb29cdbe 90 CMSG_BUFFER_TYPE(CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo))
08ab1861 91 + CMSG_SPACE(int) + /* for the TTL */
fb29cdbe 92 + EXTRA_CMSG_SPACE /* kernel appears to require extra space */) control;
b914e211
LP
93 struct msghdr mh = {};
94 struct cmsghdr *cmsg;
95 socklen_t sl;
96 int r;
97
98 assert(s);
99
100 if (s->identified)
101 return 0;
102
103 /* Query the local side */
104 s->local_salen = sizeof(s->local);
105 r = getsockname(s->fd, &s->local.sa, &s->local_salen);
106 if (r < 0)
107 return -errno;
108 if (s->local.sa.sa_family == AF_INET6 && s->ifindex <= 0)
109 s->ifindex = s->local.in6.sin6_scope_id;
110
111 /* Query the remote side */
112 s->peer_salen = sizeof(s->peer);
113 r = getpeername(s->fd, &s->peer.sa, &s->peer_salen);
114 if (r < 0)
115 return -errno;
116 if (s->peer.sa.sa_family == AF_INET6 && s->ifindex <= 0)
117 s->ifindex = s->peer.in6.sin6_scope_id;
118
119 /* Check consistency */
120 assert(s->peer.sa.sa_family == s->local.sa.sa_family);
121 assert(IN_SET(s->peer.sa.sa_family, AF_INET, AF_INET6));
122
123 /* Query connection meta information */
124 sl = sizeof(control);
125 if (s->peer.sa.sa_family == AF_INET) {
126 r = getsockopt(s->fd, IPPROTO_IP, IP_PKTOPTIONS, &control, &sl);
127 if (r < 0)
128 return -errno;
129 } else if (s->peer.sa.sa_family == AF_INET6) {
130
131 r = getsockopt(s->fd, IPPROTO_IPV6, IPV6_2292PKTOPTIONS, &control, &sl);
132 if (r < 0)
133 return -errno;
134 } else
135 return -EAFNOSUPPORT;
136
137 mh.msg_control = &control;
138 mh.msg_controllen = sl;
2a1288ff
LP
139
140 CMSG_FOREACH(cmsg, &mh) {
b914e211
LP
141
142 if (cmsg->cmsg_level == IPPROTO_IPV6) {
143 assert(s->peer.sa.sa_family == AF_INET6);
144
145 switch (cmsg->cmsg_type) {
146
147 case IPV6_PKTINFO: {
148 struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
149
150 if (s->ifindex <= 0)
151 s->ifindex = i->ipi6_ifindex;
152 break;
153 }
154
155 case IPV6_HOPLIMIT:
156 s->ttl = *(int *) CMSG_DATA(cmsg);
157 break;
158 }
159
160 } else if (cmsg->cmsg_level == IPPROTO_IP) {
161 assert(s->peer.sa.sa_family == AF_INET);
162
163 switch (cmsg->cmsg_type) {
164
165 case IP_PKTINFO: {
166 struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
167
168 if (s->ifindex <= 0)
169 s->ifindex = i->ipi_ifindex;
170 break;
171 }
172
173 case IP_TTL:
174 s->ttl = *(int *) CMSG_DATA(cmsg);
175 break;
176 }
177 }
178 }
179
180 /* The Linux kernel sets the interface index to the loopback
181 * device if the connection came from the local host since it
182 * avoids the routing table in such a case. Let's unset the
183 * interface index in such a case. */
a5f03596 184 if (s->ifindex == LOOPBACK_IFINDEX)
b914e211
LP
185 s->ifindex = 0;
186
187 /* If we don't know the interface index still, we look for the
188 * first local interface with a matching address. Yuck! */
189 if (s->ifindex <= 0)
b1dea5cf 190 s->ifindex = manager_find_ifindex(s->manager, s->local.sa.sa_family, sockaddr_in_addr(&s->local.sa));
b914e211
LP
191
192 if (s->protocol == DNS_PROTOCOL_LLMNR && s->ifindex > 0) {
b914e211 193 /* Make sure all packets for this connection are sent on the same interface */
5d0fe423
LP
194 r = socket_set_unicast_if(s->fd, s->local.sa.sa_family, s->ifindex);
195 if (r < 0)
196 log_debug_errno(errno, "Failed to invoke IP_UNICAST_IF/IPV6_UNICAST_IF: %m");
b914e211
LP
197 }
198
199 s->identified = true;
200
201 return 0;
202}
203
6016fcb0 204ssize_t dns_stream_writev(DnsStream *s, const struct iovec *iov, size_t iovcnt, int flags) {
e6dc5556 205 ssize_t m;
91ccab1e
IT
206
207 assert(s);
208 assert(iov);
209
56ddbf10 210#if ENABLE_DNS_OVER_TLS
6016fcb0 211 if (s->encrypted && !(flags & DNS_STREAM_WRITE_TLS_DATA)) {
5d67a7ae
IT
212 ssize_t ss;
213 size_t i;
214
e6dc5556 215 m = 0;
5d67a7ae 216 for (i = 0; i < iovcnt; i++) {
6016fcb0
IT
217 ss = dnstls_stream_write(s, iov[i].iov_base, iov[i].iov_len);
218 if (ss < 0)
219 return ss;
5d67a7ae 220
e6dc5556 221 m += ss;
5d67a7ae
IT
222 if (ss != (ssize_t) iov[i].iov_len)
223 continue;
224 }
225 } else
226#endif
91ccab1e
IT
227 if (s->tfo_salen > 0) {
228 struct msghdr hdr = {
229 .msg_iov = (struct iovec*) iov,
230 .msg_iovlen = iovcnt,
231 .msg_name = &s->tfo_address.sa,
232 .msg_namelen = s->tfo_salen
233 };
234
e6dc5556
LP
235 m = sendmsg(s->fd, &hdr, MSG_FASTOPEN);
236 if (m < 0) {
91ccab1e
IT
237 if (errno == EOPNOTSUPP) {
238 s->tfo_salen = 0;
e6dc5556 239 if (connect(s->fd, &s->tfo_address.sa, s->tfo_salen) < 0)
91ccab1e
IT
240 return -errno;
241
e6dc5556
LP
242 return -EAGAIN;
243 }
244 if (errno == EINPROGRESS)
245 return -EAGAIN;
246
247 return -errno;
91ccab1e
IT
248 } else
249 s->tfo_salen = 0; /* connection is made */
f6c9c5f8 250 } else {
e6dc5556
LP
251 m = writev(s->fd, iov, iovcnt);
252 if (m < 0)
253 return -errno;
f6c9c5f8 254 }
91ccab1e 255
e6dc5556 256 return m;
91ccab1e
IT
257}
258
5d67a7ae
IT
259static ssize_t dns_stream_read(DnsStream *s, void *buf, size_t count) {
260 ssize_t ss;
261
56ddbf10 262#if ENABLE_DNS_OVER_TLS
6016fcb0
IT
263 if (s->encrypted)
264 ss = dnstls_stream_read(s, buf, count);
265 else
5d67a7ae 266#endif
f6c9c5f8 267 {
5d67a7ae 268 ss = read(s->fd, buf, count);
f6c9c5f8 269 if (ss < 0)
94fdb4d9 270 return -errno;
f6c9c5f8 271 }
5d67a7ae
IT
272
273 return ss;
274}
275
623a4c97
LP
276static int on_stream_timeout(sd_event_source *es, usec_t usec, void *userdata) {
277 DnsStream *s = userdata;
278
279 assert(s);
280
b914e211 281 return dns_stream_complete(s, ETIMEDOUT);
623a4c97
LP
282}
283
624f907e
YW
284static DnsPacket *dns_stream_take_read_packet(DnsStream *s) {
285 assert(s);
286
4aa61298
YW
287 /* Note, dns_stream_update() should be called after this is called. When this is called, the
288 * stream may be already full and the EPOLLIN flag is dropped from the stream IO event source.
289 * Even this makes a room to read in the stream, this does not call dns_stream_update(), hence
290 * EPOLLIN flag is not set automatically. So, to read further packets from the stream,
291 * dns_stream_update() must be called explicitly. Currently, this is only called from
292 * on_stream_io_impl(), and there dns_stream_update() is called. */
293
624f907e
YW
294 if (!s->read_packet)
295 return NULL;
296
297 if (s->n_read < sizeof(s->read_size))
298 return NULL;
299
300 if (s->n_read < sizeof(s->read_size) + be16toh(s->read_size))
301 return NULL;
302
303 s->n_read = 0;
304 return TAKE_PTR(s->read_packet);
305}
306
2aaf6bb6 307static int on_stream_io_impl(DnsStream *s, uint32_t revents) {
5971dffd 308 bool progressed = false;
623a4c97
LP
309 int r;
310
311 assert(s);
312
2aaf6bb6
JB
313 /* This returns 1 when possible remaining stream exists, 0 on completed
314 stream or recoverable error, and negative errno on failure. */
315
56ddbf10 316#if ENABLE_DNS_OVER_TLS
6016fcb0 317 if (s->encrypted) {
04c4d919 318 r = dnstls_stream_on_io(s, revents);
ba6aaf57 319 if (r == DNSTLS_STREAM_CLOSED)
6016fcb0 320 return 0;
b2cf6704 321 if (r == -EAGAIN)
ba6aaf57 322 return dns_stream_update_io(s);
b2cf6704 323 if (r < 0)
6016fcb0 324 return dns_stream_complete(s, -r);
b2cf6704
LP
325
326 r = dns_stream_update_io(s);
327 if (r < 0)
328 return r;
5d67a7ae
IT
329 }
330#endif
331
91ccab1e
IT
332 /* only identify after connecting */
333 if (s->tfo_salen == 0) {
334 r = dns_stream_identify(s);
335 if (r < 0)
336 return dns_stream_complete(s, -r);
337 }
b914e211 338
623a4c97
LP
339 if ((revents & EPOLLOUT) &&
340 s->write_packet &&
341 s->n_written < sizeof(s->write_size) + s->write_packet->size) {
342
cf1e6e62
ZJS
343 struct iovec iov[] = {
344 IOVEC_MAKE(&s->write_size, sizeof(s->write_size)),
345 IOVEC_MAKE(DNS_PACKET_DATA(s->write_packet), s->write_packet->size),
346 };
623a4c97 347
cf1e6e62 348 IOVEC_INCREMENT(iov, ELEMENTSOF(iov), s->n_written);
623a4c97 349
cf1e6e62 350 ssize_t ss = dns_stream_writev(s, iov, ELEMENTSOF(iov), 0);
623a4c97 351 if (ss < 0) {
8add30a0 352 if (!ERRNO_IS_TRANSIENT(ss))
f6c9c5f8 353 return dns_stream_complete(s, -ss);
5971dffd
LP
354 } else {
355 progressed = true;
623a4c97 356 s->n_written += ss;
5971dffd 357 }
623a4c97
LP
358
359 /* Are we done? If so, disable the event source for EPOLLOUT */
360 if (s->n_written >= sizeof(s->write_size) + s->write_packet->size) {
361 r = dns_stream_update_io(s);
362 if (r < 0)
b914e211 363 return dns_stream_complete(s, -r);
623a4c97
LP
364 }
365 }
366
367 if ((revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) &&
368 (!s->read_packet ||
369 s->n_read < sizeof(s->read_size) + s->read_packet->size)) {
370
371 if (s->n_read < sizeof(s->read_size)) {
372 ssize_t ss;
373
5d67a7ae 374 ss = dns_stream_read(s, (uint8_t*) &s->read_size + s->n_read, sizeof(s->read_size) - s->n_read);
623a4c97 375 if (ss < 0) {
8add30a0 376 if (!ERRNO_IS_TRANSIENT(ss))
f6c9c5f8 377 return dns_stream_complete(s, -ss);
623a4c97 378 } else if (ss == 0)
b914e211 379 return dns_stream_complete(s, ECONNRESET);
5971dffd
LP
380 else {
381 progressed = true;
623a4c97 382 s->n_read += ss;
5971dffd 383 }
623a4c97
LP
384 }
385
386 if (s->n_read >= sizeof(s->read_size)) {
387
388 if (be16toh(s->read_size) < DNS_PACKET_HEADER_SIZE)
b914e211 389 return dns_stream_complete(s, EBADMSG);
623a4c97
LP
390
391 if (s->n_read < sizeof(s->read_size) + be16toh(s->read_size)) {
392 ssize_t ss;
393
394 if (!s->read_packet) {
51027656 395 r = dns_packet_new(&s->read_packet, s->protocol, be16toh(s->read_size), DNS_PACKET_SIZE_MAX);
623a4c97 396 if (r < 0)
b914e211 397 return dns_stream_complete(s, -r);
623a4c97
LP
398
399 s->read_packet->size = be16toh(s->read_size);
400 s->read_packet->ipproto = IPPROTO_TCP;
401 s->read_packet->family = s->peer.sa.sa_family;
402 s->read_packet->ttl = s->ttl;
403 s->read_packet->ifindex = s->ifindex;
5777c613 404 s->read_packet->timestamp = now(clock_boottime_or_monotonic());
623a4c97
LP
405
406 if (s->read_packet->family == AF_INET) {
407 s->read_packet->sender.in = s->peer.in.sin_addr;
408 s->read_packet->sender_port = be16toh(s->peer.in.sin_port);
409 s->read_packet->destination.in = s->local.in.sin_addr;
410 s->read_packet->destination_port = be16toh(s->local.in.sin_port);
411 } else {
412 assert(s->read_packet->family == AF_INET6);
413 s->read_packet->sender.in6 = s->peer.in6.sin6_addr;
414 s->read_packet->sender_port = be16toh(s->peer.in6.sin6_port);
415 s->read_packet->destination.in6 = s->local.in6.sin6_addr;
416 s->read_packet->destination_port = be16toh(s->local.in6.sin6_port);
417
418 if (s->read_packet->ifindex == 0)
419 s->read_packet->ifindex = s->peer.in6.sin6_scope_id;
420 if (s->read_packet->ifindex == 0)
421 s->read_packet->ifindex = s->local.in6.sin6_scope_id;
422 }
423 }
424
5d67a7ae 425 ss = dns_stream_read(s,
623a4c97
LP
426 (uint8_t*) DNS_PACKET_DATA(s->read_packet) + s->n_read - sizeof(s->read_size),
427 sizeof(s->read_size) + be16toh(s->read_size) - s->n_read);
428 if (ss < 0) {
8add30a0 429 if (!ERRNO_IS_TRANSIENT(ss))
99521cab 430 return dns_stream_complete(s, -ss);
623a4c97 431 } else if (ss == 0)
b914e211 432 return dns_stream_complete(s, ECONNRESET);
623a4c97
LP
433 else
434 s->n_read += ss;
435 }
436
18230451
YW
437 /* Are we done? If so, call the packet handler and re-enable EPOLLIN for the
438 * event source if necessary. */
624f907e
YW
439 _cleanup_(dns_packet_unrefp) DnsPacket *p = dns_stream_take_read_packet(s);
440 if (p) {
18230451 441 assert(s->on_packet);
624f907e 442 r = s->on_packet(s, p);
18230451
YW
443 if (r < 0)
444 return r;
98767d75
IT
445
446 r = dns_stream_update_io(s);
447 if (r < 0)
448 return dns_stream_complete(s, -r);
a5e2a488
YW
449
450 s->packet_received = true;
623a4c97
LP
451 }
452 }
453 }
454
a5e2a488
YW
455 if (s->type == DNS_STREAM_LLMNR_SEND && s->packet_received) {
456 uint32_t events;
457
458 /* Complete the stream if finished reading and writing one packet, and there's nothing
459 * else left to write. */
460
461 r = sd_event_source_get_io_events(s->io_event_source, &events);
462 if (r < 0)
463 return r;
464
465 if (!FLAGS_SET(events, EPOLLOUT))
466 return dns_stream_complete(s, 0);
467 }
623a4c97 468
5971dffd
LP
469 /* If we did something, let's restart the timeout event source */
470 if (progressed && s->timeout_event_source) {
e1158539 471 r = sd_event_source_set_time_relative(s->timeout_event_source, DNS_STREAM_ESTABLISHED_TIMEOUT_USEC);
5971dffd
LP
472 if (r < 0)
473 log_warning_errno(errno, "Couldn't restart TCP connection timeout, ignoring: %m");
474 }
475
2aaf6bb6
JB
476 return 1;
477}
478
479static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
480 _cleanup_(dns_stream_unrefp) DnsStream *s = dns_stream_ref(userdata); /* Protect stream while we process it */
481 int r;
482
483 assert(s);
484
485 r = on_stream_io_impl(s, revents);
486 if (r <= 0)
487 return r;
488
489#if ENABLE_DNS_OVER_TLS
490 if (!s->encrypted)
491 return 0;
492
493 /* When using DNS-over-TLS, the underlying TLS library may read the entire TLS record
494 and buffer it internally. If this happens, we will not receive further EPOLLIN events,
495 and unless there's some unrelated activity on the socket, we will hang until time out.
496 To avoid this, if there's buffered TLS data, generate a "fake" EPOLLIN event.
497 This is hacky, but it makes this case transparent to the rest of the IO code. */
498 while (dnstls_stream_has_buffered_data(s)) {
499 uint32_t events;
500
501 /* Make sure the stream still wants to process more data... */
502 r = sd_event_source_get_io_events(s->io_event_source, &events);
503 if (r < 0)
504 return r;
505 if (!FLAGS_SET(events, EPOLLIN))
506 break;
507
508 r = on_stream_io_impl(s, EPOLLIN);
509 if (r <= 0)
510 return r;
511 }
512#endif
513
623a4c97
LP
514 return 0;
515}
516
8301aa0b 517static DnsStream *dns_stream_free(DnsStream *s) {
98767d75 518 DnsPacket *p;
98767d75 519
8301aa0b 520 assert(s);
b30bf55d 521
623a4c97
LP
522 dns_stream_stop(s);
523
524 if (s->manager) {
525 LIST_REMOVE(streams, s->manager->dns_streams, s);
652ba568 526 s->manager->n_dns_streams[s->type]--;
623a4c97
LP
527 }
528
56ddbf10 529#if ENABLE_DNS_OVER_TLS
6016fcb0
IT
530 if (s->encrypted)
531 dnstls_stream_free(s);
5d67a7ae
IT
532#endif
533
90e74a66 534 ORDERED_SET_FOREACH(p, s->write_queue)
98767d75
IT
535 dns_packet_unref(ordered_set_remove(s->write_queue, p));
536
623a4c97
LP
537 dns_packet_unref(s->write_packet);
538 dns_packet_unref(s->read_packet);
98767d75
IT
539 dns_server_unref(s->server);
540
541 ordered_set_free(s->write_queue);
623a4c97 542
6b430fdb 543 return mfree(s);
623a4c97
LP
544}
545
8301aa0b 546DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsStream, dns_stream, dns_stream_free);
623a4c97 547
b27a32a0
LP
548int dns_stream_new(
549 Manager *m,
550 DnsStream **ret,
652ba568 551 DnsStreamType type,
b27a32a0
LP
552 DnsProtocol protocol,
553 int fd,
e1158539 554 const union sockaddr_union *tfo_address,
624f907e 555 int (on_packet)(DnsStream*, DnsPacket*),
18230451 556 int (complete)(DnsStream*, int), /* optional */
e1158539 557 usec_t connect_timeout_usec) {
b27a32a0 558
b30bf55d 559 _cleanup_(dns_stream_unrefp) DnsStream *s = NULL;
623a4c97
LP
560 int r;
561
562 assert(m);
499aa1d3 563 assert(ret);
652ba568
LP
564 assert(type >= 0);
565 assert(type < _DNS_STREAM_TYPE_MAX);
566 assert(protocol >= 0);
567 assert(protocol < _DNS_PROTOCOL_MAX);
623a4c97 568 assert(fd >= 0);
18230451 569 assert(on_packet);
623a4c97 570
652ba568 571 if (m->n_dns_streams[type] > DNS_STREAMS_MAX)
623a4c97
LP
572 return -EBUSY;
573
898892e8 574 s = new(DnsStream, 1);
623a4c97
LP
575 if (!s)
576 return -ENOMEM;
577
898892e8
LP
578 *s = (DnsStream) {
579 .n_ref = 1,
580 .fd = -1,
581 .protocol = protocol,
1c089741 582 .type = type,
898892e8
LP
583 };
584
98767d75
IT
585 r = ordered_set_ensure_allocated(&s->write_queue, &dns_packet_hash_ops);
586 if (r < 0)
587 return r;
588
623a4c97
LP
589 r = sd_event_add_io(m->event, &s->io_event_source, fd, EPOLLIN, on_stream_io, s);
590 if (r < 0)
591 return r;
592
aa4a9deb
LP
593 (void) sd_event_source_set_description(s->io_event_source, "dns-stream-io");
594
39cf0351 595 r = sd_event_add_time_relative(
9a015429
LP
596 m->event,
597 &s->timeout_event_source,
598 clock_boottime_or_monotonic(),
e1158539 599 connect_timeout_usec, 0,
9a015429 600 on_stream_timeout, s);
623a4c97
LP
601 if (r < 0)
602 return r;
603
aa4a9deb
LP
604 (void) sd_event_source_set_description(s->timeout_event_source, "dns-stream-timeout");
605
623a4c97 606 LIST_PREPEND(streams, m->dns_streams, s);
652ba568 607 m->n_dns_streams[type]++;
623a4c97 608 s->manager = m;
08e254c8 609
623a4c97 610 s->fd = fd;
18230451
YW
611 s->on_packet = on_packet;
612 s->complete = complete;
08e254c8 613
91ccab1e
IT
614 if (tfo_address) {
615 s->tfo_address = *tfo_address;
616 s->tfo_salen = tfo_address->sa.sa_family == AF_INET6 ? sizeof(tfo_address->in6) : sizeof(tfo_address->in);
617 }
618
1cc6c93a 619 *ret = TAKE_PTR(s);
623a4c97
LP
620
621 return 0;
622}
623
624int dns_stream_write_packet(DnsStream *s, DnsPacket *p) {
98767d75
IT
625 int r;
626
623a4c97 627 assert(s);
499aa1d3 628 assert(p);
623a4c97 629
98767d75
IT
630 r = ordered_set_put(s->write_queue, p);
631 if (r < 0)
632 return r;
623a4c97 633
98767d75 634 dns_packet_ref(p);
623a4c97
LP
635
636 return dns_stream_update_io(s);
637}
aa337a5e 638
808089ae
LP
639void dns_stream_detach(DnsStream *s) {
640 assert(s);
641
642 if (!s->server)
643 return;
644
645 if (s->server->stream != s)
646 return;
647
648 dns_server_unref_stream(s->server);
649}