chiark / gitweb /
Fix stupid bugs from the listen(2) change.
[fwd] / socket.c
1 /* -*-c-*-
2  *
3  * $Id: socket.c,v 1.9 2002/02/23 00:08:00 mdw Exp $
4  *
5  * Socket source and target definitions
6  *
7  * (c) 1999 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of the `fw' port forwarder.
13  *
14  * `fw' is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  * 
19  * `fw' is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * 
24  * You should have received a copy of the GNU General Public License
25  * along with `fw'; if not, write to the Free Software Foundation,
26  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 /*----- Revision history --------------------------------------------------* 
30  *
31  * $Log: socket.c,v $
32  * Revision 1.9  2002/02/23 00:08:00  mdw
33  * Fix stupid bugs from the listen(2) change.
34  *
35  * Revision 1.8  2002/02/22 23:44:44  mdw
36  * Call @xfree@ rather than @free@.  Add option to change the listen(2)
37  * parameter.
38  *
39  * Revision 1.7  2001/06/22 19:37:00  mdw
40  * New @conn_init@ interface.
41  *
42  * Revision 1.6  2001/02/03 20:30:03  mdw
43  * Support re-reading config files on SIGHUP.
44  *
45  * Revision 1.5  2000/03/23 23:20:42  mdw
46  * Remove listener even if connection option isn't SOCKOPT_LIMITED.
47  *
48  * Revision 1.4  1999/12/22 15:44:25  mdw
49  * Fix log message.
50  *
51  * Revision 1.3  1999/10/22 22:48:36  mdw
52  * New connection options: unlimited concurrent connections, and one-shot
53  * listening sockets.
54  *
55  * Revision 1.2  1999/07/27 18:30:53  mdw
56  * Various minor portability fixes.
57  *
58  * Revision 1.1  1999/07/26 23:33:32  mdw
59  * New sources and targets.
60  *
61  */
62
63 /*----- Header files ------------------------------------------------------*/
64
65 #include "config.h"
66
67 #include <ctype.h>
68 #include <errno.h>
69 #include <limits.h>
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73
74 #include <sys/types.h>
75 #include <unistd.h>
76
77 #include <sys/socket.h>
78 #include <netinet/in.h>
79 #include <arpa/inet.h>
80 #include <netdb.h>
81
82 #include <mLib/alloc.h>
83 #include <mLib/conn.h>
84 #include <mLib/dstr.h>
85 #include <mLib/fdflags.h>
86 #include <mLib/sel.h>
87 #include <mLib/sub.h>
88
89 #include "addr.h"
90 #include "conf.h"
91 #include "endpt.h"
92 #include "fw.h"
93 #include "scan.h"
94 #include "socket.h"
95 #include "target.h"
96
97 #include "inet.h"
98 #include "un.h"
99
100 /*----- Data structures ---------------------------------------------------*/
101
102 /* --- Socket source options --- */
103
104 typedef struct ssource_opts {
105   unsigned opt;
106   unsigned conn;
107   unsigned listen;
108 } ssource_opts;
109
110 static ssource_opts ssgo = { 256, 0, 5 };
111
112 #define SOCKOPT_LIMIT 0u
113 #define SOCKOPT_NOLIMIT 1u
114 #define SOCKOPT_ONESHOT 2u
115
116 /* --- Socket source --- */
117
118 typedef struct ssource {
119   source s;
120   addr *a;
121   target *t;
122   addr_opts *ao;
123   ssource_opts o;
124   sel_file r;
125 } ssource;
126
127 /* --- Socket target --- */
128
129 typedef struct starget {
130   target t;
131   addr *a;
132   addr_opts *ao;
133 } starget;
134
135 /* --- Socket target endpoint --- */
136
137 typedef struct stept {
138   endpt e;
139   conn c;
140   char *desc;
141 } stept;
142
143 /* --- Socket source endpoint --- */
144
145 typedef struct ssept {
146   endpt e;
147   ssource *s;
148 } ssept;
149
150 /*----- Protocol table ----------------------------------------------------*/
151
152 static addr_ops *addrs[] = { &inet_ops, &un_ops, 0 };
153
154 /*----- Other persistent variables ----------------------------------------*/
155
156 static addr_opts gao = { 0 };
157
158 /*----- Parsing address types ---------------------------------------------*/
159
160 /* --- @getaddrtype@ --- *
161  *
162  * Arguments:   @scanner *sc@ = pointer to scanner (for error reporting)
163  *              @const char *p@ = pointer to protocol name
164  *              @int abbrev@ = nonzero to allow abbreviations
165  *
166  * Returns:     Pointer to address operations table or null.
167  *
168  * Use:         Looks up a protocol name.  Handy when parsing addresses and
169  *              other bits of configuration.  Returns null if no matching
170  *              address was found.
171  */
172
173 static addr_ops *getaddrtype(scanner *sc, const char *p, int abbrev)
174 {
175   addr_ops **ops;
176   addr_ops *chosen = 0;
177   size_t sz = strlen(p);
178
179   for (ops = addrs; *ops; ops++) {
180     if (strncmp((*ops)->name, p, sz) == 0) {
181       if ((*ops)->name[sz] == 0)
182         return (*ops);
183       else if (chosen && abbrev)
184         error(sc, "ambiguous socket address type `%s'", p);
185       chosen = *ops;
186     }
187   }
188   if (!abbrev)
189     return (0);
190   return (chosen);
191 }
192
193 /* --- @getaddr@ --- *
194  *
195  * Arguments:   @scanner *sc@ = pointer to scanner to read from
196  *              @unsigned type@ = address type (@ADDR_SRC@ or @ADDR_DEST@)
197  *
198  * Returns:     Pointer to an address successfully read.
199  *
200  * Use:         Reads an optionally qualified address.
201  */
202
203 static addr *getaddr(scanner *sc, unsigned type)
204 {
205   addr_ops *ops = 0;
206   int abbrev = 0;
207
208   if (sc->t == ':') {
209     token(sc);
210     abbrev = 1;
211   }
212   if (sc->t == CTOK_WORD)
213     ops = getaddrtype(sc, sc->d.buf, abbrev);
214   if (ops)
215     token(sc);
216   else if (abbrev)
217     error(sc, "unknown socket address type `%s'", sc->d.buf);
218   else
219     ops = &inet_ops;
220   if (sc->t == ':')
221     token(sc);
222
223   return (ops->read(sc, type));
224 }
225
226 /*----- Socket endpoints --------------------------------------------------*/
227
228 /* --- @wclose@ --- */
229
230 static void sept_wclose(endpt *e)
231 {
232   shutdown(e->out->fd, 1);
233 }
234
235 /* --- @close@ (source) --- */
236
237 static void ss_listen(ssource */*ss*/);
238
239 static void ssept_close(endpt *e)
240 {
241   ssept *ee = (ssept *)e;
242
243   if (ee->s->o.opt == SOCKOPT_LIMIT) {
244     ee->s->o.conn++;
245     if (ee->s->o.conn == 1)
246       ss_listen(ee->s);
247   }
248   REFFD_DEC(ee->e.in);
249   REFFD_DEC(ee->e.out);
250   fw_dec();
251   DESTROY(ee);
252 }
253
254 /* --- @close@ (target) --- */
255
256 static void stept_close(endpt *e)
257 {
258   stept *ee = (stept *)e;
259
260   if (ee->e.f & EPF_PENDING)
261     conn_kill(&ee->c);
262   else {
263     REFFD_DEC(ee->e.in);
264     REFFD_DEC(ee->e.out);
265   }
266
267   xfree(ee->desc);
268   fw_dec();
269   DESTROY(ee);
270 }
271
272 /* --- @stept_go@ --- *
273  *
274  * Arguments:   @int fd@ = file descriptor now ready for use
275  *              @void *p@ = pointer to an endpoint structure
276  *
277  * Returns:     ---
278  *
279  * Use:         Handles successful connection of the target endpoint.
280  */
281
282 static void stept_go(int fd, void *p)
283 {
284   stept *e = p;
285
286   if (fd == -1) {
287     fw_log(-1, "[%s] connection failed: %s", e->desc, strerror(errno));
288     endpt_kill(&e->e);
289   } else {
290     reffd *r = reffd_init(fd);
291     REFFD_INC(r);
292     e->e.in = e->e.out = r;
293     e->e.f &= ~EPF_PENDING;
294     if (e->e.other)
295       endpt_join(&e->e, e->e.other);
296   }
297 }
298
299 /* --- Socket endpoint definition --- */
300
301 static endpt_ops ssept_ops = {
302   0, 0, sept_wclose, ssept_close
303 };
304
305 static endpt_ops stept_ops = {
306   0, 0, sept_wclose, stept_close
307 };
308
309 /*----- Source definition -------------------------------------------------*/
310
311 /* --- @option@ --- */
312
313 static int ssource_option(source *s, scanner *sc)
314 {
315   ssource *ss = (ssource *)s;
316   ssource_opts *sso = ss ? &ss->o : &ssgo;
317
318   CONF_BEGIN(sc, "socket", "socket")
319
320   /* --- Make sure the next token is a word --- */
321
322   if (sc->t != CTOK_WORD)
323     error(sc, "parse error, option keyword expected");
324
325   /* --- Handle options at this level --- */
326
327   if (strcmp(sc->d.buf, "conn") == 0) {
328     token(sc);
329     if (sc->t == '=')
330       token(sc);
331     if (sc->t != CTOK_WORD)
332       error(sc, "parse error, expected `unlimited', `one-shot' or number");
333     if (isdigit((unsigned char)sc->d.buf[0])) {
334       sso->conn = atoi(sc->d.buf);
335       if (sso->conn == 0)
336         error(sc, "argument of `conn' must be positive");
337       sso->opt = SOCKOPT_LIMIT;
338       token(sc);
339     } else {
340       sso->conn = 0;
341       sso->opt = 1 + (1 & conf_enum(sc,
342                                     "unlimited,one-shot,infinite",
343                                     ENUM_ABBREV, "`conn' option"));
344     }
345     CONF_ACCEPT;
346   }
347
348   if (strcmp(sc->d.buf, "listen") == 0) {
349     token(sc);
350     if (sc->t == '=')
351       token(sc);
352     if (sc->t != CTOK_WORD || !isdigit((unsigned char)sc->d.buf[0]))
353       error(sc, "parse error, expected number");
354     sso->listen = atoi(sc->d.buf);
355     if (sso->listen == 0)
356       error(sc, "argument of `listen' must be positive");
357     token(sc);
358     CONF_ACCEPT;
359   }
360
361   if (strcmp(sc->d.buf, "logging") == 0 ||
362       strcmp(sc->d.buf, "log") == 0) {
363     addr_opts *ao = ss ? ss->ao : &gao;
364     token(sc);
365     if (sc->t == '=')
366       token(sc);
367     if (conf_enum(sc, "no,yes", ENUM_ABBREV, "logging status"))
368       ao->f &= ~ADDRF_NOLOG;
369     else
370       ao->f |= ADDRF_NOLOG;
371     CONF_ACCEPT;
372   }
373
374   /* --- Pass the option around the various address types --- */
375
376   if (ss) {
377     if (ss->a->ops->option && ss->a->ops->option(sc, ss ? ss->ao : 0))
378       CONF_ACCEPT;
379   } else {
380     addr_ops **a;
381     for (a = addrs; *a; a++) {
382       if ((*a)->option && (*a)->option(sc, 0))
383         CONF_ACCEPT;
384     }
385   }
386
387   /* --- Nobody understood the option --- */
388
389   CONF_END;
390 }
391
392 /* --- @read@ --- */
393
394 static source *ssource_read(scanner *sc)
395 {
396   ssource *ss;
397
398   (void)(conf_prefix(sc, "socket") || conf_prefix(sc, "sk"));
399   ss = CREATE(ssource);
400   ss->s.ops = &ssource_ops;
401   ss->s.desc = 0;
402   ss->t = 0;
403   ss->a = getaddr(sc, ADDR_SRC);
404   if (ss->a->ops->initopts)
405     ss->ao = ss->a->ops->initopts();
406   else
407     ss->ao = CREATE(addr_opts);
408   *ss->ao = gao;
409   ss->o = ssgo;
410   return (&ss->s);
411 }
412
413 /* --- @ss_accept@ --- *
414  *
415  * Arguments:   @int fd@ = file descriptor to accept from
416  *              @unsigned mode@ = what's ready with the descriptor
417  *              @void *p@ = pointer to the source definition
418  *
419  * Returns:     ---
420  *
421  * Use:         Accepts an incoming connection and attaches it to a target
422  *              endpoint.
423  */
424
425 static void ssource_destroy(source */*s*/);
426
427 static void ss_accept(int fd, unsigned mode, void *p)
428 {
429   ssource *ss = p;
430   ssept *e;
431   endpt *ee;
432   reffd *r;
433
434   /* --- Make the file descriptor --- */
435
436   {
437     int opt = 1;
438     if ((r = ss->a->ops->accept(fd, ss->ao, ss->s.desc)) == 0)
439       return;
440     setsockopt(r->fd, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt));
441     fdflags(r->fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
442   }
443
444   /* --- Make an endpoint --- */
445
446   e = CREATE(ssept);
447   e->e.ops = &ssept_ops;
448   e->e.other = 0;
449   e->e.f = EPF_FILE;
450   e->e.t = 0;
451   e->e.in = e->e.out = r;
452   e->s = ss;
453   REFFD_INC(r);
454
455   /* --- Obtain the target endpoint and let rip --- */
456
457   if ((ee = ss->t->ops->create(ss->t, ss->s.desc)) == 0) {
458     REFFD_DEC(r);
459     REFFD_DEC(r);
460     DESTROY(e);
461     return;
462   }
463   fw_inc();
464
465   /* --- Remove the listening socket if necessary --- */
466
467   switch (ss->o.opt) {
468     case SOCKOPT_LIMIT:
469       ss->o.conn--;
470       if (!ss->o.conn) {
471         if (!(ss->ao->f & ADDRF_NOLOG))
472           fw_log(-1, "[%s] maximum connections reached", ss->s.desc);
473         sel_rmfile(&ss->r);
474         close(ss->r.fd);
475         if (ss->a->ops->unbind)
476           ss->a->ops->unbind(ss->a);
477       }
478       break;
479     case SOCKOPT_NOLIMIT:
480       break;
481     case SOCKOPT_ONESHOT:
482       sel_rmfile(&ss->r);
483       close(ss->r.fd);
484       if (ss->a->ops->unbind)
485         ss->a->ops->unbind(ss->a);
486       ssource_destroy(&ss->s);
487       break;
488   }
489
490   /* --- Let everything else happen --- */
491
492   endpt_join(&e->e, ee);
493 }
494
495 /* --- @ss_listen@ --- *
496  *
497  * Arguments:   @ssource *ss@ = source to listen on
498  *
499  * Returns:     ---
500  *
501  * Use:         Sets the socket to listen again, if it stopped for some
502  *              reason.  This is a copy of the code in the @read@ function,
503  *              because it has different (wildly different) error handling
504  *              behaviour.
505  */
506
507 static void ss_listen(ssource *ss)
508 {
509   gen_addr *ga = (gen_addr *)ss->a;
510   int fd;
511
512   if (!(ss->ao->f & ADDRF_NOLOG))
513     fw_log(-1, "[%s] reattaching listener", ss->s.desc);
514
515   /* --- Make the socket --- */
516
517   if ((fd = socket(ga->a.ops->pf, SOCK_STREAM, 0)) < 0) {
518     fw_log(-1, "[%s] couldn't create socket: %s",
519            ss->s.desc, strerror(errno));
520     goto fail_0;
521   }
522
523   /* --- Set it to allow address reuse --- */
524
525   {
526     int opt = 1;
527     setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
528     fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
529   }
530
531   /* --- Bind it to the right port --- */
532
533   if (bind(fd, &ga->sa, ga->a.sz)) {
534     fw_log(-1, "[%s] couldn't bind socket: %s", ss->s.desc, strerror(errno));
535     goto fail_1;
536   }
537   if (ga->a.ops->bound)
538     ga->a.ops->bound(&ga->a, ss->ao);
539
540   /* --- Set it to listen for connections --- */
541
542   if (listen(fd, ss->o.listen)) {
543     fw_log(-1, "[%s] couldn't listen on socket: %s",
544            ss->s.desc, strerror(errno));
545     goto fail_1;
546   }
547
548   /* --- Set the listener up again --- */
549
550   ss->r.fd = fd;
551   sel_addfile(&ss->r);
552   return;
553
554   /* --- Tidy up if it failed --- *
555    *
556    * I'll just remove the entire source.
557    */
558
559 fail_1:
560   close(fd);
561 fail_0:
562   ss->o.conn = 0;
563   ssource_destroy(&ss->s);
564 }
565
566 /* --- @attach@ --- */
567
568 static void ssource_attach(source *s, scanner *sc, target *t)
569 {
570   ssource *ss = (ssource *)s;
571   int fd;
572
573   ss->t = t;
574
575   /* --- Initialize the description string --- */
576
577   {
578     dstr d = DSTR_INIT;
579     dstr_puts(&d, "socket.");
580     ss->a->ops->print(ss->a, ADDR_SRC, &d);
581     dstr_puts(&d, " -> ");
582     dstr_puts(&d, ss->t->desc);
583     ss->s.desc = xstrdup(d.buf);
584     dstr_destroy(&d);
585   }
586
587   /* --- Initialize the socket for listening --- */
588
589   {
590     gen_addr *ga = (gen_addr *)ss->a;
591
592     /* --- Make the socket --- */
593
594     if ((fd = socket(ga->a.ops->pf, SOCK_STREAM, 0)) < 0)
595       error(sc, "couldn't create socket: %s", strerror(errno));
596
597     /* --- Set it to allow address reuse --- */
598
599     {
600       int opt = 1;
601       setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
602       fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
603     }
604
605     /* --- Bind it to the right port --- */
606
607     if (bind(fd, &ga->sa, ga->a.sz))
608       error(sc, "couldn't bind to %s: %s", ss->s.desc, strerror(errno));
609     if (ga->a.ops->bound)
610       ga->a.ops->bound(&ga->a, ss->ao);
611
612     /* --- Set it to listen for connections --- */
613
614     if (listen(fd, ss->o.listen))
615       error(sc, "couldn't listen on socket: %s", strerror(errno));
616   }
617
618   /* --- We're ready to go now --- */
619
620   sel_initfile(sel, &ss->r, fd, SEL_READ, ss_accept, ss);
621   sel_addfile(&ss->r);
622   source_add(&ss->s);
623   fw_inc();
624 }
625
626 /* --- @destroy@ --- */
627
628 static void ssource_destroy(source *s)
629 {
630   ssource *ss = (ssource *)s;
631
632   if (ss->o.conn || ss->o.opt != SOCKOPT_LIMIT) {
633     sel_rmfile(&ss->r);
634     close(ss->r.fd);
635     if (ss->a->ops->unbind)
636       ss->a->ops->unbind(ss->a);
637   }
638   if (ss->a->ops->freeopts)
639     ss->a->ops->freeopts(ss->ao);
640   else
641     DESTROY(ss->ao);
642   xfree(ss->s.desc);
643   ss->a->ops->destroy(ss->a);
644   ss->t->ops->destroy(ss->t);
645   source_remove(&ss->s);
646   DESTROY(ss);
647   fw_dec();
648 }
649
650 /* --- Source definition block --- */
651
652 source_ops ssource_ops = {
653   "socket",
654   ssource_option, ssource_read, ssource_attach, ssource_destroy
655 };
656
657 /*----- Target definition -------------------------------------------------*/
658
659 /* --- @read@ --- */
660
661 static target *starget_read(scanner *sc)
662 {
663   starget *st;
664   dstr d = DSTR_INIT;
665
666   (void)(conf_prefix(sc, "socket") || conf_prefix(sc, "sk"));
667   st = CREATE(starget);
668   st->t.ops = &starget_ops;
669   st->a = getaddr(sc, ADDR_DEST);
670   dstr_puts(&d, "socket.");
671   st->a->ops->print(st->a, ADDR_DEST, &d);
672   st->t.desc = xstrdup(d.buf);
673   dstr_destroy(&d);
674   return (&st->t);
675 }
676
677 /* --- @create@ --- *
678  *
679  * Arguments:   @target *t@ = pointer to target
680  *              @const char *desc@ = description of connection
681  *
682  * Returns:     Pointer to a created endpoint.
683  *
684  * Use:         Generates a target endpoint for communication.
685  */
686
687 static endpt *starget_create(target *t, const char *desc)
688 {
689   starget *st = (starget *)t;
690   stept *e = CREATE(stept);
691   int fd;
692   gen_addr *ga = (gen_addr *)st->a;
693   int opt;
694
695   if ((fd = socket(st->a->ops->pf, SOCK_STREAM, 0)) < 0)
696     return (0);
697   setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt));
698   fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
699   e->e.ops = &stept_ops;
700   e->e.other = 0;
701   e->e.f = EPF_FILE | EPF_PENDING;
702   e->e.t = 0;
703   e->desc = xstrdup(desc);
704
705   if (conn_init(&e->c, sel, fd, &ga->sa, ga->a.sz, stept_go, e)) {
706     fw_log(-1, "[%s] connection failed: %s", e->desc, strerror(errno));
707     DESTROY(e);
708     return (0);
709   }
710   fw_inc();
711   return (&e->e);
712 }
713
714 /* --- @destroy@ --- */
715
716 static void starget_destroy(target *t)
717 {
718   starget *st = (starget *)t;
719   st->a->ops->destroy(st->a);
720   xfree(st->t.desc);
721   DESTROY(st);
722 }
723
724 /* --- Socket target definition block --- */
725
726 target_ops starget_ops = {
727   "socket",
728   0, starget_read, starget_create, starget_destroy
729 };
730
731 /*----- That's all, folks -------------------------------------------------*/