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