Correctly cast uid and gid sentinel values.
[fwd] / socket.c
index 6df5c4d..87b3d96 100644 (file)
--- a/socket.c
+++ b/socket.c
@@ -1,6 +1,6 @@
 /* -*-c-*-
  *
- * $Id: socket.c,v 1.2 1999/07/27 18:30:53 mdw Exp $
+ * $Id: socket.c,v 1.9 2002/02/23 00:08:00 mdw Exp $
  *
  * Socket source and target definitions
  *
 /*----- Revision history --------------------------------------------------* 
  *
  * $Log: socket.c,v $
+ * Revision 1.9  2002/02/23 00:08:00  mdw
+ * Fix stupid bugs from the listen(2) change.
+ *
+ * Revision 1.8  2002/02/22 23:44:44  mdw
+ * Call @xfree@ rather than @free@.  Add option to change the listen(2)
+ * parameter.
+ *
+ * Revision 1.7  2001/06/22 19:37:00  mdw
+ * New @conn_init@ interface.
+ *
+ * Revision 1.6  2001/02/03 20:30:03  mdw
+ * Support re-reading config files on SIGHUP.
+ *
+ * Revision 1.5  2000/03/23 23:20:42  mdw
+ * Remove listener even if connection option isn't SOCKOPT_LIMITED.
+ *
+ * Revision 1.4  1999/12/22 15:44:25  mdw
+ * Fix log message.
+ *
+ * Revision 1.3  1999/10/22 22:48:36  mdw
+ * New connection options: unlimited concurrent connections, and one-shot
+ * listening sockets.
+ *
  * Revision 1.2  1999/07/27 18:30:53  mdw
  * Various minor portability fixes.
  *
@@ -43,6 +66,7 @@
 
 #include <ctype.h>
 #include <errno.h>
+#include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 /* --- Socket source options --- */
 
 typedef struct ssource_opts {
+  unsigned opt;
   unsigned conn;
+  unsigned listen;
 } ssource_opts;
 
-static ssource_opts ssgo = { 256 };
+static ssource_opts ssgo = { 256, 0, 5 };
+
+#define SOCKOPT_LIMIT 0u
+#define SOCKOPT_NOLIMIT 1u
+#define SOCKOPT_ONESHOT 2u
 
 /* --- Socket source --- */
 
@@ -107,7 +137,7 @@ typedef struct starget {
 typedef struct stept {
   endpt e;
   conn c;
-  const char *desc;
+  char *desc;
 } stept;
 
 /* --- Socket source endpoint --- */
@@ -117,9 +147,6 @@ typedef struct ssept {
   ssource *s;
 } ssept;
 
-#define SKF_CONN 16u
-#define SKF_BROKEN 32u
-
 /*----- Protocol table ----------------------------------------------------*/
 
 static addr_ops *addrs[] = { &inet_ops, &un_ops, 0 };
@@ -213,11 +240,11 @@ static void ssept_close(endpt *e)
 {
   ssept *ee = (ssept *)e;
 
-  if (!ee->s->o.conn) {
-    ee->s->o.conn++;
-    ss_listen(ee->s);
-  } else
+  if (ee->s->o.opt == SOCKOPT_LIMIT) {
     ee->s->o.conn++;
+    if (ee->s->o.conn == 1)
+      ss_listen(ee->s);
+  }
   REFFD_DEC(ee->e.in);
   REFFD_DEC(ee->e.out);
   fw_dec();
@@ -230,14 +257,14 @@ static void stept_close(endpt *e)
 {
   stept *ee = (stept *)e;
 
-  if (ee->e.f & EPF_PENDING) {
-    if (ee->e.f & SKF_CONN)
-      conn_kill(&ee->c);
-  } else {
+  if (ee->e.f & EPF_PENDING)
+    conn_kill(&ee->c);
+  else {
     REFFD_DEC(ee->e.in);
     REFFD_DEC(ee->e.out);
   }
 
+  xfree(ee->desc);
   fw_dec();
   DESTROY(ee);
 }
@@ -256,35 +283,14 @@ static void stept_go(int fd, void *p)
 {
   stept *e = p;
 
-  /* --- Complicated and subtle --- *
-   *
-   * This code interacts quite closely with @starget_create@, mainly through
-   * flags in the endpoint block.
-   *
-   * If the connection failed, I log a message (that's easy enough).  The
-   * behaviour then depends on whether the endpoints have been joined yet.
-   * If not, I set @SKF_BROKEN@ and return, so that @starget_create@ can
-   * clean up the mess and return an immediate failure.  If they have, I kill
-   * the connection and everything ought to work.
-   *
-   * If the connection worked, I clear @EPF_PENDING@ (as expected, because
-   * my endpoint is now ready), and @SKF_CONN@ (to let @starget_create@ know
-   * that the connection is already going).  Then, only if this isn't the
-   * first attempt, I rejoin this endpoint to its partner.
-   */
-
   if (fd == -1) {
     fw_log(-1, "[%s] connection failed: %s", e->desc, strerror(errno));
-    e->e.f &= ~SKF_CONN;
-    if (e->e.f & EPF_PENDING)
-      endpt_kill(&e->e);
-    else
-      e->e.f |= SKF_BROKEN;
+    endpt_kill(&e->e);
   } else {
     reffd *r = reffd_init(fd);
     REFFD_INC(r);
     e->e.in = e->e.out = r;
-    e->e.f &= ~(EPF_PENDING | SKF_CONN);
+    e->e.f &= ~EPF_PENDING;
     if (e->e.other)
       endpt_join(&e->e, e->e.other);
   }
@@ -293,11 +299,11 @@ static void stept_go(int fd, void *p)
 /* --- Socket endpoint definition --- */
 
 static endpt_ops ssept_ops = {
-  0, sept_wclose, ssept_close
+  0, 0, sept_wclose, ssept_close
 };
 
 static endpt_ops stept_ops = {
-  0, sept_wclose, stept_close
+  0, 0, sept_wclose, stept_close
 };
 
 /*----- Source definition -------------------------------------------------*/
@@ -322,11 +328,32 @@ static int ssource_option(source *s, scanner *sc)
     token(sc);
     if (sc->t == '=')
       token(sc);
+    if (sc->t != CTOK_WORD)
+      error(sc, "parse error, expected `unlimited', `one-shot' or number");
+    if (isdigit((unsigned char)sc->d.buf[0])) {
+      sso->conn = atoi(sc->d.buf);
+      if (sso->conn == 0)
+       error(sc, "argument of `conn' must be positive");
+      sso->opt = SOCKOPT_LIMIT;
+      token(sc);
+    } else {
+      sso->conn = 0;
+      sso->opt = 1 + (1 & conf_enum(sc,
+                                   "unlimited,one-shot,infinite",
+                                   ENUM_ABBREV, "`conn' option"));
+    }
+    CONF_ACCEPT;
+  }
+
+  if (strcmp(sc->d.buf, "listen") == 0) {
+    token(sc);
+    if (sc->t == '=')
+      token(sc);
     if (sc->t != CTOK_WORD || !isdigit((unsigned char)sc->d.buf[0]))
-      error(sc, "parse error, argument of `conn' must be a number");
-    sso->conn = atoi(sc->d.buf);
-    if (sso->conn == 0)
-      error(sc, "argument of `conn' must be positive");
+      error(sc, "parse error, expected number");
+    sso->listen = atoi(sc->d.buf);
+    if (sso->listen == 0)
+      error(sc, "argument of `listen' must be positive");
     token(sc);
     CONF_ACCEPT;
   }
@@ -395,6 +422,8 @@ static source *ssource_read(scanner *sc)
  *             endpoint.
  */
 
+static void ssource_destroy(source */*s*/);
+
 static void ss_accept(int fd, unsigned mode, void *p)
 {
   ssource *ss = p;
@@ -431,21 +460,35 @@ static void ss_accept(int fd, unsigned mode, void *p)
     DESTROY(e);
     return;
   }
+  fw_inc();
 
   /* --- Remove the listening socket if necessary --- */
 
-  ss->o.conn--;
-  if (!ss->o.conn) {
-    fw_log(-1, "[%s] maximum connections reached", ss->s.desc);
-    sel_rmfile(&ss->r);
-    close(ss->r.fd);
-    if (ss->a->ops->unbind)
-      ss->a->ops->unbind(ss->a);
+  switch (ss->o.opt) {
+    case SOCKOPT_LIMIT:
+      ss->o.conn--;
+      if (!ss->o.conn) {
+       if (!(ss->ao->f & ADDRF_NOLOG))
+         fw_log(-1, "[%s] maximum connections reached", ss->s.desc);
+       sel_rmfile(&ss->r);
+       close(ss->r.fd);
+       if (ss->a->ops->unbind)
+         ss->a->ops->unbind(ss->a);
+      }
+      break;
+    case SOCKOPT_NOLIMIT:
+      break;
+    case SOCKOPT_ONESHOT:
+      sel_rmfile(&ss->r);
+      close(ss->r.fd);
+      if (ss->a->ops->unbind)
+       ss->a->ops->unbind(ss->a);
+      ssource_destroy(&ss->s);
+      break;
   }
 
   /* --- Let everything else happen --- */
 
-  fw_inc();
   endpt_join(&e->e, ee);
 }
 
@@ -461,14 +504,13 @@ static void ss_accept(int fd, unsigned mode, void *p)
  *             behaviour.
  */
 
-static void ssource_destroy(source */*s*/);
-
 static void ss_listen(ssource *ss)
 {
   gen_addr *ga = (gen_addr *)ss->a;
   int fd;
 
-  fw_log(-1, "[%s] reattaching listener", ss->s.desc);
+  if (!(ss->ao->f & ADDRF_NOLOG))
+    fw_log(-1, "[%s] reattaching listener", ss->s.desc);
 
   /* --- Make the socket --- */
 
@@ -489,7 +531,7 @@ static void ss_listen(ssource *ss)
   /* --- Bind it to the right port --- */
 
   if (bind(fd, &ga->sa, ga->a.sz)) {
-    fw_log(-1, "[%s] couldn't bind to %s: %s", ss->s.desc, strerror(errno));
+    fw_log(-1, "[%s] couldn't bind socket: %s", ss->s.desc, strerror(errno));
     goto fail_1;
   }
   if (ga->a.ops->bound)
@@ -497,7 +539,7 @@ static void ss_listen(ssource *ss)
 
   /* --- Set it to listen for connections --- */
 
-  if (listen(fd, 5)) {
+  if (listen(fd, ss->o.listen)) {
     fw_log(-1, "[%s] couldn't listen on socket: %s",
           ss->s.desc, strerror(errno));
     goto fail_1;
@@ -569,7 +611,7 @@ static void ssource_attach(source *s, scanner *sc, target *t)
 
     /* --- Set it to listen for connections --- */
 
-    if (listen(fd, 5))
+    if (listen(fd, ss->o.listen))
       error(sc, "couldn't listen on socket: %s", strerror(errno));
   }
 
@@ -587,7 +629,7 @@ static void ssource_destroy(source *s)
 {
   ssource *ss = (ssource *)s;
 
-  if (ss->o.conn) {
+  if (ss->o.conn || ss->o.opt != SOCKOPT_LIMIT) {
     sel_rmfile(&ss->r);
     close(ss->r.fd);
     if (ss->a->ops->unbind)
@@ -597,7 +639,7 @@ static void ssource_destroy(source *s)
     ss->a->ops->freeopts(ss->ao);
   else
     DESTROY(ss->ao);
-  /* free(ss->s.desc); */
+  xfree(ss->s.desc);
   ss->a->ops->destroy(ss->a);
   ss->t->ops->destroy(ss->t);
   source_remove(&ss->s);
@@ -656,30 +698,15 @@ static endpt *starget_create(target *t, const char *desc)
   fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
   e->e.ops = &stept_ops;
   e->e.other = 0;
-  e->e.f = EPF_FILE | SKF_CONN;
+  e->e.f = EPF_FILE | EPF_PENDING;
   e->e.t = 0;
-  e->desc = desc;
+  e->desc = xstrdup(desc);
 
-  /* --- Pay attention --- *
-   *
-   * This bit is quite subtle.  The connect can succeed or fail later: that's
-   * fine.  The problem comes if it makes its mind up right now.  The flag
-   * @SKF_CONN@ signifies that I'm trying to connect.  I set it up to begin
-   * with and @stept_go@ turns it off when it's done: @stept_close@ uses it
-   * to decide whether to kill the connection.  The flag @EPF_PENDING@ is
-   * only set after @conn_init@ returns and @SKF_CONN@ is still set (meaning
-   * that the connection is still in progress).  That's used to let
-   * @stept_go@ know whether to kill the other endpoint.  The flag
-   * @SKF_BROKEN@ is used to signify an immediate failure.
-   */
-
-  conn_init(&e->c, sel, fd, &ga->sa, ga->a.sz, stept_go, e);
-  if (e->e.f & SKF_BROKEN) {
+  if (conn_init(&e->c, sel, fd, &ga->sa, ga->a.sz, stept_go, e)) {
+    fw_log(-1, "[%s] connection failed: %s", e->desc, strerror(errno));
     DESTROY(e);
     return (0);
   }
-  if (e->e.f & SKF_CONN)
-    e->e.f |= EPF_PENDING;
   fw_inc();
   return (&e->e);
 }
@@ -690,7 +717,7 @@ static void starget_destroy(target *t)
 {
   starget *st = (starget *)t;
   st->a->ops->destroy(st->a);
-  /* free(st->t.desc); */
+  xfree(st->t.desc);
   DESTROY(st);
 }