chiark / gitweb /
Rename entire project from `fw' to `fwd'.
[fwd] / fw.c
diff --git a/fw.c b/fw.c
deleted file mode 100644 (file)
index 4b0a861..0000000
--- a/fw.c
+++ /dev/null
@@ -1,717 +0,0 @@
-/* -*-c-*-
- *
- * Port forwarding thingy
- *
- * (c) 1999 Straylight/Edgeware
- */
-
-/*----- Licensing notice --------------------------------------------------*
- *
- * This file is part of the `fw' port forwarder.
- *
- * `fw' is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * `fw' is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with `fw'; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include "fw.h"
-
-/*----- Global variables --------------------------------------------------*/
-
-sel_state *sel;                                /* Multiplexor for nonblocking I/O */
-
-/*----- Static variables --------------------------------------------------*/
-
-typedef struct conffile {
-  struct conffile *next;
-  char *name;
-} conffile;
-
-static unsigned flags = 0;             /* Global state flags */
-static unsigned active = 0;            /* Number of active things */
-static conffile *conffiles = 0;                /* List of configuration files */
-
-#define FW_SYSLOG 1u
-#define FW_QUIET 2u
-#define FW_SET 4u
-
-/*----- Configuration parsing ---------------------------------------------*/
-
-/* --- @parse@ --- *
- *
- * Arguments:  @scanner *sc@ = pointer to scanner definition
- *
- * Returns:    ---
- *
- * Use:                Parses a configuration file from the scanner.
- */
-
-static source_ops *sources[] =
-  { &xsource_ops, &fsource_ops, &ssource_ops, 0 };
-static target_ops *targets[] =
-  { &xtarget_ops, &ftarget_ops, &starget_ops, 0 };
-
-void parse(scanner *sc)
-{
-  token(sc);
-
-  for (;;) {
-    if (sc->t == CTOK_EOF)
-      break;
-    if (sc->t != CTOK_WORD)
-      error(sc, "parse error, keyword expected");
-
-    /* --- Handle a forwarding request --- */
-
-    if (strcmp(sc->d.buf, "forward") == 0 ||
-       strcmp(sc->d.buf, "fw") == 0 ||
-       strcmp(sc->d.buf, "from") == 0) {
-      source *s;
-      target *t;
-
-      token(sc);
-
-      /* --- Read a source description --- */
-
-      {
-       source_ops **sops;
-
-       /* --- Try to find a source type which understands --- */
-
-       s = 0;
-       for (sops = sources; *sops; sops++) {
-         if ((s = (*sops)->read(sc)) != 0)
-           goto found_source;
-       }
-       error(sc, "unknown source name `%s'", sc->d.buf);
-
-       /* --- Read any source-specific options --- */
-
-      found_source:
-       if (sc->t == '{') {
-         token(sc);
-         while (sc->t == CTOK_WORD) {
-           if (!s->ops->option || !s->ops->option(s, sc)) {
-             error(sc, "unknown %s source option `%s'",
-                   s->ops->name, sc->d.buf);
-           }
-           if (sc->t == ';')
-             token(sc);
-         }
-         if (sc->t != '}')
-           error(sc, "parse error, missing `}'");
-         token(sc);
-       }
-      }
-
-      /* --- Read a destination description --- */
-
-      if (sc->t == CTOK_WORD && (strcmp(sc->d.buf, "to") == 0 ||
-                                strcmp(sc->d.buf, "->") == 0))
-       token(sc);
-
-      {
-       target_ops **tops;
-
-       /* --- Try to find a target which understands --- */
-
-       t = 0;
-       for (tops = targets; *tops; tops++) {
-         if ((t = (*tops)->read(sc)) != 0)
-           goto found_target;
-       }
-       error(sc, "unknown target name `%s'", sc->d.buf);
-
-       /* --- Read any target-specific options --- */
-
-      found_target:
-       if (sc->t == '{') {
-         token(sc);
-         while (sc->t == CTOK_WORD) {
-           if (!t->ops->option || !t->ops->option(t, sc)) {
-             error(sc, "unknown %s target option `%s'",
-                   t->ops->name, sc->d.buf);
-           }
-           if (sc->t == ';')
-             token(sc);
-         }
-         if (sc->t != '}')
-           error(sc, "parse error, `}' expected");
-         token(sc);
-       }
-      }
-
-      /* --- Combine the source and target --- */
-
-      s->ops->attach(s, sc, t);
-      if (t->ops->confirm)
-       t->ops->confirm(t);
-    }
-
-    /* --- Include configuration from a file --- *
-     *
-     * Slightly tricky.  Scan the optional semicolon from the including
-     * stream, not the included one.
-     */
-
-    else if (strcmp(sc->d.buf, "include") == 0) {
-      FILE *fp;
-      dstr d = DSTR_INIT;
-
-      token(sc);
-      conf_name(sc, '/', &d);
-      if ((fp = fopen(d.buf, "r")) == 0)
-       error(sc, "can't include `%s': %s", d.buf, strerror(errno));
-      if (sc->t == ';')
-       token(sc);
-      pushback(sc);
-      scan_push(sc, scan_file(fp, d.buf, 0));
-      token(sc);
-      dstr_destroy(&d);
-      continue;                                /* Don't parse a trailing `;' */
-    }
-
-    /* --- Other configuration is handled elsewhere --- */
-
-    else {
-
-      /* --- First try among the sources --- */
-
-      {
-       source_ops **sops;
-
-       for (sops = sources; *sops; sops++) {
-         if ((*sops)->option && (*sops)->option(0, sc))
-           goto found_option;
-       }
-      }
-
-      /* --- Then try among the targets --- */
-
-      {
-       target_ops **tops;
-
-       for (tops = targets; *tops; tops++) {
-         if ((*tops)->option && (*tops)->option(0, sc))
-           goto found_option;
-       }
-      }
-
-      /* --- Nobody wants the option --- */
-
-      error(sc, "unknown global option or prefix `%s'", sc->d.buf);
-
-    found_option:;
-    }
-
-    if (sc->t == ';')
-      token(sc);
-  }
-}
-
-/*----- General utility functions -----------------------------------------*/
-
-/* --- @fw_log@ --- *
- *
- * Arguments:  @time_t t@ = when the connection occurred or (@-1@)
- *             @const char *fmt@ = format string to fill in
- *             @...@ = other arguments
- *
- * Returns:    ---
- *
- * Use:                Logs a connection.
- */
-
-void fw_log(time_t t, const char *fmt, ...)
-{
-  struct tm *tm;
-  dstr d = DSTR_INIT;
-  va_list ap;
-
-  if (flags & FW_QUIET)
-    return;
-
-  if (t == -1)
-    t = time(0);
-  tm = localtime(&t);
-  DENSURE(&d, 64);
-  d.len += strftime(d.buf, d.sz, "%Y-%m-%d %H:%M:%S ", tm);
-  va_start(ap, fmt);
-  dstr_vputf(&d, fmt, &ap);
-  va_end(ap);
-  if (flags & FW_SYSLOG)
-    syslog(LOG_NOTICE, "%s", d.buf);
-  else {
-    DPUTC(&d, '\n');
-    dstr_write(&d, stderr);
-  }
-  DDESTROY(&d);
-}
-
-/* --- @fw_inc@, @fw_dec@ --- *
- *
- * Arguments:  ---
- *
- * Returns:    ---
- *
- * Use:                Increments or decrements the active thing count.  `fw' won't
- *             quit while there are active things.
- */
-
-void fw_inc(void) { flags |= FW_SET; active++; }
-void fw_dec(void) { if (active) active--; }
-
-/* --- @fw_exit@ --- *
- *
- * Arguments:  ---
- *
- * Returns:    ---
- *
- * Use:                Exits when appropriate.
- */
-
-static void fw_exit(void)
-{
-  endpt_killall();
-  source_killall();
-}
-
-/* --- @fw_tidy@ --- *
- *
- * Arguments:  @int n@ = signal number
- *             @void *p@ = an uninteresting argument
- *
- * Returns:    ---
- *
- * Use:                Handles various signals and causes a clean tidy-up.
- */
-
-static void fw_tidy(int n, void *p)
-{
-  const char *sn = 0;
-  switch (n) {
-    case SIGTERM: sn = "SIGTERM"; break;
-    case SIGINT: sn = "SIGINT"; break;
-    default: abort();
-  }
-
-  fw_log(-1, "closing down gracefully on %s", sn);
-  source_killall();
-}
-
-/* --- @fw_die@ --- *
- *
- * Arguments:  @int n@ = signal number
- *             @void *p@ = an uninteresting argument
- *
- * Returns:    ---
- *
- * Use:                Handles various signals and causes an abrupt shutdown.
- */
-
-static void fw_die(int n, void *p)
-{
-  const char *sn = 0;
-  switch (n) {
-    case SIGQUIT: sn = "SIGQUIT"; break;
-    default: abort();
-  }
-
-  fw_log(-1, "closing down abruptly on %s", sn);
-  source_killall();
-  endpt_killall();
-}
-
-/* --- @fw_reload@ --- *
- *
- * Arguments:  @int n@ = a signal number
- *             @void *p@ = an uninteresting argument
- *
- * Returns:    ---
- *
- * Use:                Handles a hangup signal by re-reading configuration files.
- */
-
-static void fw_reload(int n, void *p)
-{
-  FILE *fp;
-  scanner sc;
-  conffile *cf;
-
-  assert(n == SIGHUP);
-  if (!conffiles) {
-    fw_log(-1, "no configuration files to reload: ignoring SIGHUP");
-    return;
-  }
-  fw_log(-1, "reloading configuration files...");
-  source_killall();
-  scan_create(&sc);
-  for (cf = conffiles; cf; cf = cf->next) {
-    if ((fp = fopen(cf->name, "r")) == 0)
-      fw_log(-1, "error loading `%s': %s", cf->name, strerror(errno));
-    else
-      scan_add(&sc, scan_file(fp, cf->name, 0));
-  }
-  parse(&sc);
-  fw_log(-1, "... reload completed OK");
-}
-
-/*----- Startup and options parsing ---------------------------------------*/
-
-/* --- Standard GNU help options --- */
-
-static void version(FILE *fp)
-{
-  pquis(fp, "$ version " VERSION "\n");
-}
-
-static void usage(FILE *fp)
-{
-  pquis(fp, "Usage: $ [-dql] [-p PIDFILE] [-f FILE] [CONFIG-STMTS...]\n");
-}
-
-static void help(FILE *fp)
-{
-  version(fp);
-  fputc('\n', fp);
-  usage(fp);
-  pquis(fp, "\n\
-An excessively full-featured port-forwarder, which subsumes large chunks\n\
-of the functionality of inetd, netcat, and normal cat.\n\
-\n\
-Configuration may be supplied in one or more configuration files, or on\n\
-the command line (or both).  If no `-f' option is present, and no\n\
-configuration is given on the command line, the standard input stream is\n\
-read.\n\
-\n\
-Configuration is free-form.  Comments begin with a `#' character and\n\
-continue to the end of the line.  Each command line argument is considered\n\
-to be a separate line.\n\
-\n\
-The grammar is fairly complicated.  For a summary, run `$ --grammar'.\n\
-For a summary of the various options, run `$ --options'.\n\
-\n\
-Options available are:\n\
-\n\
-Help options:\n\
-  -h, --help           Display this help message.\n\
-  -v, --version        Display the program's version number.\n\
-  -u, --usage          Display a terse usage summary.\n\
-\n\
-Configuration summary:\n\
-  -G, --grammar        Show a summary of the configuration language.\n\
-  -O, --options        Show a summary of the source and target options.\n\
-\n\
-Other options:\n\
-  -f, --file=FILE      Read configuration from a file.\n\
-  -q, --quiet          Don't emit any logging information.\n\
-  -d, --daemon         Fork into background after initializing.\n\
-  -p, --pidfile=FILE   Write process-id to the named FILE.\n\
-  -l, --syslog         Send log output to the system logger.\n\
-  -s, --setuid=USER    Change uid to USER after initializing sources.\n\
-  -g, --setgid=GRP     Change gid to GRP after initializing sources.\n\
-");
-}
-
-/* --- Other helpful options --- */
-
-static void grammar(FILE *fp)
-{
-  version(fp);
-  fputs("\nGrammar summary\n\n", fp);
-  fputs(grammar_text, fp);
-}
-
-static void options(FILE *fp)
-{
-  version(fp);
-  fputs("\nOption summary\n\n", fp);
-  fputs(option_text, fp);
-}
-
-/* --- @main@ --- *
- *
- * Arguments:  @int argc@ = number of command line arguments
- *             @char *argv[]@ = vector of argument strings
- *
- * Returns:    ---
- *
- * Use:                Simple port-forwarding server.
- */
-
-int main(int argc, char *argv[])
-{
-  unsigned f = 0;
-  sel_state sst;
-  sig s_term, s_quit, s_int, s_hup;
-  scanner sc;
-  uid_t drop = -1;
-  gid_t dropg = -1;
-  const char *pidfile = 0;
-  conffile *cf, **cff = &conffiles;
-
-#define f_bogus 1u
-#define f_file 2u
-#define f_syslog 4u
-#define f_fork 8u
-
-  /* --- Initialize things --- */
-
-  ego(argv[0]);
-  sel = &sst;
-  sel_init(sel);
-  sub_init();
-  sig_init(sel);
-  bres_init(sel);
-  bres_exec(0);
-  exec_init();
-  fattr_init(&fattr_global);
-  scan_create(&sc);
-
-  atexit(fw_exit);
-
-  /* --- Parse command line options --- */
-
-  for (;;) {
-    static struct option opts[] = {
-
-      /* --- Standard GNU help options --- */
-
-      { "help",        0,              0,      'h' },
-      { "version",     0,              0,      'v' },
-      { "usage",       0,              0,      'u' },
-
-      /* --- Other help options --- */
-
-      { "grammar",     0,              0,      'G' },
-      { "options",     0,              0,      'O' },
-
-      /* --- Other useful arguments --- */
-
-      { "file",                OPTF_ARGREQ,    0,      'f' },
-      { "fork",                0,              0,      'd' },
-      { "daemon",      0,              0,      'd' },
-      { "pidfile",     OPTF_ARGREQ,    0,      'p' },
-      { "syslog",      0,              0,      'l' },
-      { "log",         0,              0,      'l' },
-      { "quiet",       0,              0,      'q' },
-      { "setuid",      OPTF_ARGREQ,    0,      's' },
-      { "uid",         OPTF_ARGREQ,    0,      's' },
-      { "setgid",      OPTF_ARGREQ,    0,      'g' },
-      { "gid",         OPTF_ARGREQ,    0,      'g' },
-
-      /* --- Magic terminator --- */
-
-      { 0,             0,              0,      0 }
-    };
-    int i = mdwopt(argc, argv, "+hvu" "GO" "f:dp:ls:g:", opts, 0, 0, 0);
-
-    if (i < 0)
-      break;
-    switch (i) {
-      case 'h':
-       help(stdout);
-       exit(0);
-       break;
-      case 'v':
-       version(stdout);
-       exit(0);
-       break;
-      case 'u':
-       usage(stdout);
-       exit(0);
-       break;
-      case 'G':
-       grammar(stdout);
-       exit(0);
-       break;
-      case 'O':
-       options(stdout);
-       exit(0);
-       break;
-      case 'f':
-       if (strcmp(optarg, "-") == 0)
-         scan_add(&sc, scan_file(stdin, "<stdin>", SCF_NOCLOSE));
-       else {
-         FILE *fp;
-         if ((fp = fopen(optarg, "r")) == 0)
-           die(1, "couldn't open file `%s': %s", optarg, strerror(errno));
-         cf = CREATE(conffile);
-         cf->name = optarg;
-         *cff = cf;
-         cff = &cf->next;
-         scan_add(&sc, scan_file(fp, optarg, 0));
-       }
-       f |= f_file;
-       break;
-      case 'd':
-       f |= f_fork;
-       break;
-      case 'p':
-       pidfile = optarg;
-       break;
-      case 'l':
-       f |= f_syslog;
-       break;
-      case 'q':
-       flags |= FW_QUIET;
-       break;
-      case 's':
-       if (isdigit((unsigned char )optarg[0])) {
-         char *q;
-         drop = strtol(optarg, &q, 0);
-         if (*q)
-           die(1, "bad uid `%s'", optarg);
-       } else {
-         struct passwd *pw = getpwnam(optarg);
-         if (!pw)
-           die(1, "unknown user `%s'", optarg);
-         drop = pw->pw_uid;
-       }
-       break;
-      case 'g':
-       if (isdigit((unsigned char )optarg[0])) {
-         char *q;
-         dropg = strtol(optarg, &q, 0);
-         if (*q)
-           die(1, "bad gid `%s'", optarg);
-       } else {
-         struct group *gr = getgrnam(optarg);
-         if (!gr)
-           die(1, "unknown group `%s'", optarg);
-         dropg = gr->gr_gid;
-       }
-       break;
-      default:
-       f |= f_bogus;
-       break;
-    }
-  }
-
-  if (f & f_bogus) {
-    usage(stderr);
-    exit(1);
-  }
-  *cff = 0;
-
-  /* --- Deal with the remaining arguments --- */
-
-  if (optind < argc)
-    scan_add(&sc, scan_argv(argv + optind));
-  else if (f & f_file)
-    /* Cool */;
-  else if (!isatty(STDIN_FILENO))
-    scan_add(&sc, scan_file(stdin, "<stdin>", SCF_NOCLOSE));
-  else {
-    moan("no configuration given and stdin is a terminal.");
-    moan("type `%s --help' for usage information.", QUIS);
-    exit(1);
-  }
-
-  /* --- Parse the configuration now gathered --- */
-
-  parse(&sc);
-
-  /* --- Set up some signal handlers --- *
-   *
-   * Don't enable @SIGINT@ if the caller already disabled it.
-   */
-
-  {
-    struct sigaction sa;
-
-    sig_add(&s_term, SIGTERM, fw_tidy, 0);
-    sig_add(&s_quit, SIGQUIT, fw_die, 0);
-    sigaction(SIGINT, 0, &sa);
-    if (sa.sa_handler != SIG_IGN)
-      sig_add(&s_int, SIGINT, fw_tidy, 0);
-    sig_add(&s_hup, SIGHUP, fw_reload, 0);
-  }
-
-  /* --- Fork into the background --- */
-
-  if (f & f_fork) {
-    pid_t kid;
-
-    kid = fork();
-    if (kid == -1)
-      die(1, "couldn't fork: %s", strerror(errno));
-    if (kid != 0)
-      _exit(0);
-
-    close(0); close(1); close(2);
-    chdir("/");
-    setsid();
-
-    kid = fork();
-    if (kid != 0)
-      _exit(0);
-  }
-  if (pidfile) {
-    FILE *fp = fopen(pidfile, "w");
-    if (!fp) {
-      die(EXIT_FAILURE, "couldn't create pidfile `%s': %s",
-         pidfile, strerror(errno));
-    }
-    fprintf(fp, "%lu\n", (unsigned long)getpid());
-    if (fflush(fp) || ferror(fp) || fclose(fp)) {
-      die(EXIT_FAILURE, "couldn't write pidfile `%s': %s",
-         pidfile, strerror(errno));
-    }
-  }
-
-  if (f & f_syslog) {
-    flags |= FW_SYSLOG;
-    openlog(QUIS, 0, LOG_DAEMON);
-  }
-
-  /* --- Drop privileges --- */
-
-  if (drop != (uid_t)-1)
-    privconn_split(sel);
-#ifdef HAVE_SETGROUPS
-  if ((dropg != (gid_t)-1 && (setgid(dropg) || setgroups(1, &dropg))) ||
-      (drop != (uid_t)-1 && setuid(drop)))
-    die(1, "couldn't drop privileges: %s", strerror(errno));
-#else
-  if ((dropg != (gid_t)-1 && setgid(dropg)) ||
-      (drop != (uid_t)-1 && setuid(drop)))
-    die(1, "couldn't drop privileges: %s", strerror(errno));
-#endif
-
-  /* --- Let rip --- */
-
-  if (!(flags & FW_SET))
-    moan("nothing to do!");
-  signal(SIGPIPE, SIG_IGN);
-
-  {
-    int selerr = 0;
-    while (active) {
-      if (!sel_select(sel))
-       selerr = 0;
-      else if (errno != EINTR && errno != EAGAIN) {
-       fw_log(-1, "error from select: %s", strerror(errno));
-       selerr++;
-       if (selerr > 8) {
-         fw_log(-1, "too many consecutive select errors: bailing out");
-         exit(EXIT_FAILURE);
-       }
-      }
-    }
-  }
-
-  return (0);
-}
-
-/*----- That's all, folks -------------------------------------------------*/