]> git.ipfire.org Git - thirdparty/bird.git/blame - sysdep/unix/io.c
Function calling in filters works - somehow. Calling syntax is
[thirdparty/bird.git] / sysdep / unix / io.c
CommitLineData
b5d9ee5c
MM
1/*
2 * BIRD Internet Routing Daemon -- Unix I/O
3 *
b4b3b39e 4 * (c) 1998--1999 Martin Mares <mj@ucw.cz>
b5d9ee5c
MM
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL.
7 */
8
9#include <stdio.h>
10#include <stdlib.h>
abae6e9c 11#include <string.h>
b5d9ee5c
MM
12#include <sys/time.h>
13#include <sys/types.h>
14#include <sys/socket.h>
15#include <sys/fcntl.h>
16#include <unistd.h>
17#include <errno.h>
18
1b50a1e4
MM
19#ifndef HAVE_STRUCT_IP_MREQN
20#include <net/if.h>
21#endif
22
b5d9ee5c
MM
23#include "nest/bird.h"
24#include "lib/lists.h"
25#include "lib/resource.h"
26#include "lib/timer.h"
27#include "lib/socket.h"
e8f73195 28#include "lib/event.h"
b5d9ee5c
MM
29#include "nest/iface.h"
30
31#include "lib/unix.h"
32
33/*
34 * Timers
35 */
36
37#define NEAR_TIMER_LIMIT 4
38
39#ifdef TIME_T_IS_64BIT
40#define TIME_INFINITY 0x7fffffffffffffff
41#else
42#ifdef TIME_T_IS_SIGNED
43#define TIME_INFINITY 0x7fffffff
44#else
45#define TIME_INFINITY 0xffffffff
46#endif
47#endif
48
49static list near_timers, far_timers;
50static bird_clock_t first_far_timer = TIME_INFINITY;
51
52bird_clock_t now;
53
54static void
55tm_free(resource *r)
56{
57 timer *t = (timer *) r;
58
59 tm_stop(t);
60}
61
62static void
63tm_dump(resource *r)
64{
65 timer *t = (timer *) r;
66
e8f73195 67 debug("(code %p, data %p, ", t->hook, t->data);
af847acc
MM
68 if (t->randomize)
69 debug("rand %d, ", t->randomize);
70 if (t->recurrent)
71 debug("recur %d, ", t->recurrent);
b5d9ee5c
MM
72 if (t->expires)
73 debug("expires in %d sec)\n", t->expires - now);
74 else
75 debug("inactive)\n");
76}
77
78static struct resclass tm_class = {
79 "Timer",
80 sizeof(timer),
81 tm_free,
82 tm_dump
83};
84
85timer *
86tm_new(pool *p)
87{
88 timer *t = ralloc(p, &tm_class);
89 t->hook = NULL;
90 t->data = NULL;
91 t->randomize = 0;
92 t->expires = 0;
93 return t;
94}
95
96static inline void
97tm_insert_near(timer *t)
98{
99 node *n = HEAD(near_timers);
100
101 while (n->next && (SKIP_BACK(timer, n, n)->expires < t->expires))
102 n = n->next;
103 insert_node(&t->n, n->prev);
104}
105
106void
107tm_start(timer *t, unsigned after)
108{
109 bird_clock_t when;
110
111 if (t->randomize)
af847acc 112 after += random() % (t->randomize + 1);
b5d9ee5c
MM
113 when = now + after;
114 if (t->expires == when)
115 return;
116 if (t->expires)
117 rem_node(&t->n);
118 t->expires = when;
119 if (after <= NEAR_TIMER_LIMIT)
120 tm_insert_near(t);
121 else
122 {
123 if (!first_far_timer || first_far_timer > when)
124 first_far_timer = when;
125 add_tail(&far_timers, &t->n);
126 }
127}
128
129void
130tm_stop(timer *t)
131{
132 if (t->expires)
133 {
134 rem_node(&t->n);
135 t->expires = 0;
136 }
137}
138
139static void
140tm_dump_them(char *name, list *l)
141{
142 node *n;
143 timer *t;
144
145 debug("%s timers:\n", name);
146 WALK_LIST(n, *l)
147 {
148 t = SKIP_BACK(timer, n, n);
149 debug("%p ", t);
150 tm_dump(&t->r);
151 }
152 debug("\n");
153}
154
155void
156tm_dump_all(void)
157{
158 tm_dump_them("Near", &near_timers);
159 tm_dump_them("Far", &far_timers);
160}
161
162static inline time_t
163tm_first_shot(void)
164{
165 time_t x = first_far_timer;
166
167 if (!EMPTY_LIST(near_timers))
168 {
169 timer *t = SKIP_BACK(timer, n, HEAD(near_timers));
170 if (t->expires < x)
171 x = t->expires;
172 }
173 return x;
174}
175
176static void
177tm_shot(void)
178{
179 timer *t;
180 node *n, *m;
181
182 if (first_far_timer <= now)
183 {
28a9a189 184 bird_clock_t limit = now + NEAR_TIMER_LIMIT;
b5d9ee5c
MM
185 first_far_timer = TIME_INFINITY;
186 n = HEAD(far_timers);
187 while (m = n->next)
188 {
189 t = SKIP_BACK(timer, n, n);
190 if (t->expires <= limit)
191 {
192 rem_node(n);
193 tm_insert_near(t);
194 }
195 else if (t->expires < first_far_timer)
196 first_far_timer = t->expires;
197 n = m;
198 }
199 }
200 while ((n = HEAD(near_timers)) -> next)
201 {
af847acc 202 int delay;
b5d9ee5c
MM
203 t = SKIP_BACK(timer, n, n);
204 if (t->expires > now)
205 break;
206 rem_node(n);
af847acc 207 delay = t->expires - now;
b5d9ee5c 208 t->expires = 0;
af847acc
MM
209 if (t->recurrent)
210 {
211 int i = t->recurrent - delay;
212 if (i < 0)
213 i = 0;
214 tm_start(t, i);
215 }
b5d9ee5c
MM
216 t->hook(t);
217 }
218}
219
220/*
221 * Sockets
222 */
223
abae6e9c
MM
224#ifndef SOL_IP
225#define SOL_IP IPPROTO_IP
226#endif
227
b5d9ee5c
MM
228static list sock_list;
229
230static void
231sk_free(resource *r)
232{
233 sock *s = (sock *) r;
234
235 if (s->fd >= 0)
236 rem_node(&s->n);
237}
238
239static void
240sk_dump(resource *r)
241{
242 sock *s = (sock *) r;
b4b3b39e 243 static char *sk_type_names[] = { "TCP<", "TCP>", "TCP", "UDP", "UDP/MC", "IP", "IP/MC", "MAGIC" };
b5d9ee5c
MM
244
245 debug("(%s, ud=%p, sa=%08x, sp=%d, da=%08x, dp=%d, tos=%d, ttl=%d, if=%s)\n",
246 sk_type_names[s->type],
247 s->data,
248 s->saddr,
249 s->sport,
250 s->daddr,
251 s->dport,
252 s->tos,
253 s->ttl,
254 s->iface ? s->iface->name : "none");
255}
256
257static struct resclass sk_class = {
258 "Socket",
259 sizeof(sock),
260 sk_free,
261 sk_dump
262};
263
264sock *
265sk_new(pool *p)
266{
267 sock *s = ralloc(p, &sk_class);
268 s->pool = p;
269 s->data = NULL;
270 s->saddr = s->daddr = IPA_NONE;
271 s->sport = s->dport = 0;
272 s->tos = s->ttl = -1;
273 s->iface = NULL;
274 s->rbuf = NULL;
275 s->rx_hook = NULL;
276 s->rbsize = 0;
277 s->tbuf = NULL;
278 s->tx_hook = NULL;
279 s->tbsize = 0;
280 s->err_hook = NULL;
281 s->fd = -1;
282 return s;
283}
284
285#define ERR(x) do { err = x; goto bad; } while(0)
286
287static inline void
288set_inaddr(struct in_addr *ia, ip_addr a)
289{
290 a = ipa_hton(a);
291 memcpy(&ia->s_addr, &a, sizeof(a));
292}
293
4cf45766 294void
b5d9ee5c
MM
295fill_in_sockaddr(struct sockaddr_in *sa, ip_addr a, unsigned port)
296{
297 sa->sin_family = AF_INET;
298 sa->sin_port = htons(port);
299 set_inaddr(&sa->sin_addr, a);
300}
301
af847acc 302void
b5d9ee5c
MM
303get_sockaddr(struct sockaddr_in *sa, ip_addr *a, unsigned *port)
304{
af847acc 305 if (sa->sin_family != AF_INET)
08c69a77 306 bug("get_sockaddr called for wrong address family");
af847acc
MM
307 if (port)
308 *port = ntohs(sa->sin_port);
b5d9ee5c
MM
309 memcpy(a, &sa->sin_addr.s_addr, sizeof(*a));
310 *a = ipa_ntoh(*a);
311}
312
313static char *
314sk_setup(sock *s)
315{
316 int fd = s->fd;
317 int one = 1;
318 char *err;
319
320 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
321 ERR("fcntl(O_NONBLOCK)");
322 if ((s->tos >= 0) && setsockopt(fd, SOL_IP, IP_TOS, &s->tos, sizeof(s->tos)) < 0)
323 ERR("IP_TOS");
324 if (s->ttl >= 0)
325 {
326 if (setsockopt(fd, SOL_IP, IP_TTL, &s->ttl, sizeof(s->ttl)) < 0)
327 ERR("IP_TTL");
328 if (setsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &one, sizeof(one)) < 0)
329 ERR("SO_DONTROUTE");
330 }
331#ifdef IP_PMTUDISC
b4b3b39e 332 if (s->type != SK_TCP_PASSIVE && s->type != SK_TCP_ACTIVE && s->type != SK_MAGIC)
b5d9ee5c
MM
333 {
334 int dont = IP_PMTUDISC_DONT;
335 if (setsockopt(fd, SOL_IP, IP_PMTUDISC, &dont, sizeof(dont)) < 0)
336 ERR("IP_PMTUDISC");
337 }
338#endif
339 /* FIXME: Set send/receive buffers? */
340 /* FIXME: Set keepalive for TCP connections? */
341 err = NULL;
342bad:
343 return err;
344}
345
346static void
347sk_alloc_bufs(sock *s)
348{
349 if (!s->rbuf && s->rbsize)
350 s->rbuf = mb_alloc(s->pool, s->rbsize);
351 s->rpos = s->rbuf;
352 if (!s->tbuf && s->tbsize)
353 s->tbuf = mb_alloc(s->pool, s->tbsize);
354 s->tpos = s->ttx = s->tbuf;
355}
356
357void
358sk_tcp_connected(sock *s)
359{
360 s->rx_hook(s, 0);
361 s->type = SK_TCP;
362 sk_alloc_bufs(s);
363}
364
365int
366sk_open(sock *s)
367{
368 int fd, e;
369 struct sockaddr_in sa;
370 int zero = 0;
371 int one = 1;
372 int type = s->type;
373 int has_src = ipa_nonzero(s->saddr) || s->sport;
374 int has_dest = ipa_nonzero(s->daddr);
375 char *err;
376
377 switch (type)
378 {
379 case SK_TCP_ACTIVE:
380 case SK_TCP_PASSIVE:
381 fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
382 break;
383 case SK_UDP:
384 case SK_UDP_MC:
385 fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
386 break;
387 case SK_IP:
388 case SK_IP_MC:
389 fd = socket(PF_INET, SOCK_RAW, s->dport);
390 break;
b4b3b39e
MM
391 case SK_MAGIC:
392 fd = s->fd;
393 break;
b5d9ee5c 394 default:
b4b3b39e 395 bug("sk_open() called for invalid sock type %d", type);
b5d9ee5c
MM
396 }
397 if (fd < 0)
398 die("sk_open: socket: %m");
399 s->fd = fd;
400
401 if (err = sk_setup(s))
402 goto bad;
403 switch (type)
404 {
405 case SK_UDP:
406 case SK_IP:
407 if (s->iface) /* It's a broadcast socket */
408 if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)) < 0)
409 ERR("SO_BROADCAST");
410 break;
411 case SK_UDP_MC:
412 case SK_IP_MC:
413 {
61fb537c 414#ifdef HAVE_STRUCT_IP_MREQN
b5d9ee5c 415 struct ip_mreqn mreq;
af847acc 416#define mreq_add mreq
9a158361 417 ASSERT(s->iface && s->iface->addr);
b5d9ee5c 418 mreq.imr_ifindex = s->iface->index;
9a158361 419 set_inaddr(&mreq.imr_address, s->iface->addr->ip);
1b50a1e4
MM
420#else
421 struct in_addr mreq;
422 struct ip_mreq mreq_add;
9a158361
MM
423 ASSERT(s->iface && s->iface->addr);
424 set_inaddr(&mreq, s->iface->addr->ip);
36154beb
MM
425#ifdef SO_BINDTODEVICE
426 {
427 struct ifreq ifr;
428 strcpy(ifr.ifr_name, s->iface->name);
429 if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0)
430 ERR("SO_BINDTODEVICE");
4c5e5e3a 431 mreq_add.imr_interface.s_addr = INADDR_ANY;
36154beb
MM
432 }
433#else
434#error Multicasts not supported on PtP devices /* FIXME: Solve it somehow? */
c7208da0 435 mreq_add.imr_interface = mreq;
bd7f1081 436#endif
b5d9ee5c 437#endif
af847acc 438 set_inaddr(&mreq_add.imr_multiaddr, s->daddr);
b5d9ee5c
MM
439 if (has_dest)
440 {
441 if (
442#ifdef IP_DEFAULT_MULTICAST_TTL
443 s->ttl != IP_DEFAULT_MULTICAST_TTL &&
444#endif
445 setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &s->ttl, sizeof(s->ttl)) < 0)
446 ERR("IP_MULTICAST_TTL");
447 if (
448#ifdef IP_DEFAULT_MULTICAST_LOOP
449 IP_DEFAULT_MULTICAST_LOOP &&
450#endif
451 setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &zero, sizeof(zero)) < 0)
452 ERR("IP_MULTICAST_LOOP");
5a99ade4 453 /* This defines where should we send _outgoing_ multicasts */
b5d9ee5c
MM
454 if (setsockopt(fd, SOL_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq)) < 0)
455 ERR("IP_MULTICAST_IF");
456 }
5a99ade4 457 /* And this one sets interface for _receiving_ multicasts from */
af847acc 458 if (has_src && setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq_add, sizeof(mreq_add)) < 0)
b5d9ee5c
MM
459 ERR("IP_ADD_MEMBERSHIP");
460 break;
461 }
462 }
463 if (has_src)
464 {
465 int port;
466
467 if (type == SK_IP || type == SK_IP_MC)
468 port = 0;
469 else
470 {
471 port = s->sport;
472 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
473 ERR("SO_REUSEADDR");
474 }
475 fill_in_sockaddr(&sa, s->saddr, port);
476 if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0)
477 ERR("bind");
478 }
479 fill_in_sockaddr(&sa, s->daddr, s->dport);
480 switch (type)
481 {
482 case SK_TCP_ACTIVE:
483 if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) >= 0)
484 sk_tcp_connected(s);
485 else if (errno != EINTR && errno != EAGAIN)
486 ERR("connect");
487 break;
488 case SK_TCP_PASSIVE:
489 if (listen(fd, 8))
490 ERR("listen");
491 break;
492 }
493
494 sk_alloc_bufs(s);
495 add_tail(&sock_list, &s->n);
496 return 0;
497
498bad:
499 log(L_ERR "sk_open: %s: %m", err);
500 close(fd);
501 s->fd = -1;
502 return -1;
503}
504
505static int
506sk_maybe_write(sock *s)
507{
508 int e;
509
510 switch (s->type)
511 {
512 case SK_TCP:
b4b3b39e 513 case SK_MAGIC:
b5d9ee5c
MM
514 while (s->ttx != s->tpos)
515 {
516 e = write(s->fd, s->ttx, s->tpos - s->ttx);
517 if (e < 0)
518 {
519 if (errno != EINTR && errno != EAGAIN)
520 {
521 log(L_ERR "write: %m");
522 s->err_hook(s, errno);
523 return -1;
524 }
525 return 0;
526 }
527 s->ttx += e;
528 }
529 s->ttx = s->tpos = s->tbuf;
530 return 1;
531 case SK_UDP:
532 case SK_UDP_MC:
533 case SK_IP:
534 case SK_IP_MC:
535 {
536 struct sockaddr_in sa;
537
538 if (s->tbuf == s->tpos)
539 return 1;
540 fill_in_sockaddr(&sa, s->faddr, s->fport);
541 e = sendto(s->fd, s->tbuf, s->tpos - s->tbuf, 0, (struct sockaddr *) &sa, sizeof(sa));
542 if (e < 0)
543 {
544 if (errno != EINTR && errno != EAGAIN)
545 {
546 log(L_ERR "sendto: %m");
547 s->err_hook(s, errno);
548 return -1;
549 }
550 return 0;
551 }
552 s->tpos = s->tbuf;
553 return 1;
554 }
555 default:
08c69a77 556 bug("sk_maybe_write: unknown socket type %d", s->type);
b5d9ee5c
MM
557 }
558}
559
560int
561sk_send(sock *s, unsigned len)
562{
563 s->faddr = s->daddr;
564 s->fport = s->dport;
565 s->ttx = s->tbuf;
566 s->tpos = s->tbuf + len;
567 return sk_maybe_write(s);
568}
569
570int
571sk_send_to(sock *s, unsigned len, ip_addr addr, unsigned port)
572{
573 s->faddr = addr;
574 s->fport = port;
575 s->ttx = s->tbuf;
576 s->tpos = s->tbuf + len;
577 return sk_maybe_write(s);
578}
579
580static int
581sk_read(sock *s)
582{
583 switch (s->type)
584 {
585 case SK_TCP_ACTIVE:
586 {
587 struct sockaddr_in sa;
588 fill_in_sockaddr(&sa, s->daddr, s->dport);
589 if (connect(s->fd, (struct sockaddr *) &sa, sizeof(sa)) >= 0)
590 sk_tcp_connected(s);
591 else if (errno != EINTR && errno != EAGAIN)
592 {
593 log(L_ERR "connect: %m");
594 s->err_hook(s, errno);
595 }
596 return 0;
597 }
598 case SK_TCP_PASSIVE:
599 {
600 struct sockaddr_in sa;
601 int al = sizeof(sa);
602 int fd = accept(s->fd, (struct sockaddr *) &sa, &al);
603 if (fd >= 0)
604 {
605 sock *t = sk_new(s->pool);
606 char *err;
607 t->type = SK_TCP;
608 t->fd = fd;
609 add_tail(&sock_list, &t->n);
610 s->rx_hook(t, 0);
611 if (err = sk_setup(t))
612 {
613 log(L_ERR "Incoming connection: %s: %m", err);
614 s->err_hook(s, errno);
615 return 0;
616 }
617 sk_alloc_bufs(t);
618 return 1;
619 }
620 else if (errno != EINTR && errno != EAGAIN)
621 {
622 log(L_ERR "accept: %m");
623 s->err_hook(s, errno);
624 }
625 return 0;
626 }
627 case SK_TCP:
628 {
629 int c = read(s->fd, s->rpos, s->rbuf + s->rbsize - s->rpos);
630
631 if (c < 0)
632 {
633 if (errno != EINTR && errno != EAGAIN)
634 {
635 log(L_ERR "read: %m");
636 s->err_hook(s, errno);
637 }
638 }
639 else if (!c)
640 s->err_hook(s, 0);
641 else
642 {
643 s->rpos += c;
644 if (s->rx_hook(s, s->rpos - s->rbuf))
645 s->rpos = s->rbuf;
646 return 1;
647 }
648 return 0;
649 }
b4b3b39e
MM
650 case SK_MAGIC:
651 return s->rx_hook(s, 0);
b5d9ee5c
MM
652 default:
653 {
654 struct sockaddr_in sa;
655 int al = sizeof(sa);
656 int e = recvfrom(s->fd, s->rbuf, s->rbsize, 0, (struct sockaddr *) &sa, &al);
657
658 if (e < 0)
659 {
660 if (errno != EINTR && errno != EAGAIN)
661 {
662 log(L_ERR "recvfrom: %m");
663 s->err_hook(s, errno);
664 }
665 return 0;
666 }
667 s->rpos = s->rbuf + e;
668 get_sockaddr(&sa, &s->faddr, &s->fport);
669 s->rx_hook(s, e);
670 return 1;
671 }
672 }
673}
674
675static void
676sk_write(sock *s)
677{
678 while (s->ttx != s->tbuf && sk_maybe_write(s) > 0)
679 s->tx_hook(s);
680}
681
682void
683sk_dump_all(void)
684{
685 node *n;
686 sock *s;
687
688 debug("Open sockets:\n");
689 WALK_LIST(n, sock_list)
690 {
691 s = SKIP_BACK(sock, n, n);
692 debug("%p ", s);
693 sk_dump(&s->r);
694 }
695 debug("\n");
696}
697
698#undef ERR
699
700/*
701 * Main I/O Loop
702 */
703
4c9dd1e4
MM
704volatile int async_config_flag; /* Asynchronous reconfiguration/dump scheduled */
705volatile int async_dump_flag;
706
b5d9ee5c
MM
707void
708io_init(void)
709{
710 init_list(&near_timers);
711 init_list(&far_timers);
712 init_list(&sock_list);
e8f73195 713 init_list(&global_event_list);
7e5f5ffd 714 krt_io_init();
b5d9ee5c
MM
715 now = time(NULL);
716}
717
718void
719io_loop(void)
720{
721 fd_set rd, wr;
722 struct timeval timo;
723 time_t tout;
724 int hi;
725 sock *s;
726 node *n;
727
728 /* FIXME: Use poll() if available */
729
730 FD_ZERO(&rd);
731 FD_ZERO(&wr);
732 for(;;)
733 {
e8f73195 734 ev_run_list(&global_event_list);
b5d9ee5c
MM
735 now = time(NULL);
736 tout = tm_first_shot();
737 if (tout <= now)
738 {
739 tm_shot();
740 continue;
741 }
742 else
5331da6a
MM
743 {
744 timo.tv_sec = tout - now;
745 timo.tv_usec = 0;
746 }
b5d9ee5c
MM
747
748 hi = 0;
749 WALK_LIST(n, sock_list)
750 {
751 s = SKIP_BACK(sock, n, n);
752 if (s->rx_hook)
753 {
754 FD_SET(s->fd, &rd);
755 if (s->fd > hi)
756 hi = s->fd;
757 }
758 if (s->tx_hook && s->ttx != s->tpos)
759 {
760 FD_SET(s->fd, &wr);
761 if (s->fd > hi)
762 hi = s->fd;
763 }
764 }
765
4c9dd1e4
MM
766 /*
767 * Yes, this is racy. But even if the signal comes before this test
768 * and entering select(), it gets caught on the next timer tick.
769 */
770
771 if (async_config_flag)
772 {
773 async_config();
774 async_config_flag = 0;
f4aabcee 775 continue;
4c9dd1e4
MM
776 }
777 if (async_dump_flag)
778 {
779 async_dump();
780 async_dump_flag = 0;
f4aabcee
MM
781 continue;
782 }
783 if (async_shutdown_flag)
784 {
785 async_shutdown();
786 async_shutdown_flag = 0;
787 continue;
4c9dd1e4
MM
788 }
789
790 /* And finally enter select() to find active sockets */
791
b5d9ee5c
MM
792 hi = select(hi+1, &rd, &wr, NULL, &timo);
793 if (hi < 0)
794 {
795 if (errno == EINTR || errno == EAGAIN)
796 continue;
797 die("select: %m");
798 }
799 if (hi)
800 {
801 WALK_LIST(n, sock_list)
802 {
803 s = SKIP_BACK(sock, n, n);
804 if (FD_ISSET(s->fd, &rd))
805 {
806 FD_CLR(s->fd, &rd);
807 while (sk_read(s))
808 ;
809 }
810 if (FD_ISSET(s->fd, &wr))
811 {
812 FD_CLR(s->fd, &wr);
813 sk_write(s);
814 }
815 }
816 }
817 }
818}