From e82f7154f65062d9ac8b9677862774498b331058 Mon Sep 17 00:00:00 2001 From: mdw Date: Thu, 1 Jul 1999 08:56:23 +0000 Subject: [PATCH] Initial revision --- .cvsignore | 7 + .links | 4 + .skelrc | 8 ++ Makefile.am | 49 +++++++ acconfig.h | 61 +++++++++ acl.c | 138 +++++++++++++++++++ acl.h | 111 +++++++++++++++ bres.c | 439 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bres.h | 125 +++++++++++++++++ chan.c | 254 ++++++++++++++++++++++++++++++++++ chan.h | 115 ++++++++++++++++ conf.c | 351 +++++++++++++++++++++++++++++++++++++++++++++++ conf.h | 69 ++++++++++ configure.in | 47 +++++++ forward.c | 223 ++++++++++++++++++++++++++++++ forward.h | 75 ++++++++++ fw.c | 262 +++++++++++++++++++++++++++++++++++ fw.h | 58 ++++++++ ident.c | 146 ++++++++++++++++++++ ident.h | 87 ++++++++++++ identify.c | 306 +++++++++++++++++++++++++++++++++++++++++ identify.h | 81 +++++++++++ listener.c | 189 +++++++++++++++++++++++++ listener.h | 102 ++++++++++++++ scan.c | 126 +++++++++++++++++ scan.h | 96 +++++++++++++ setup | 10 ++ 27 files changed, 3539 insertions(+) create mode 100644 .cvsignore create mode 100644 .links create mode 100644 .skelrc create mode 100644 Makefile.am create mode 100644 acconfig.h create mode 100644 acl.c create mode 100644 acl.h create mode 100644 bres.c create mode 100644 bres.h create mode 100644 chan.c create mode 100644 chan.h create mode 100644 conf.c create mode 100644 conf.h create mode 100644 configure.in create mode 100644 forward.c create mode 100644 forward.h create mode 100644 fw.c create mode 100644 fw.h create mode 100644 ident.c create mode 100644 ident.h create mode 100644 identify.c create mode 100644 identify.h create mode 100644 listener.c create mode 100644 listener.h create mode 100644 scan.c create mode 100644 scan.h create mode 100755 setup diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..19a3c54 --- /dev/null +++ b/.cvsignore @@ -0,0 +1,7 @@ +Makefile.in +Makefile +aclocal.m4 +build +config.h.in +configure +stamp-h.in diff --git a/.links b/.links new file mode 100644 index 0000000..b3f8cad --- /dev/null +++ b/.links @@ -0,0 +1,4 @@ +COPYING +install-sh +mkinstalldirs +missing diff --git a/.skelrc b/.skelrc new file mode 100644 index 0000000..225a897 --- /dev/null +++ b/.skelrc @@ -0,0 +1,8 @@ +;;; -*-emacs-lisp-*- + +(setq skel-alist + (append + '((author . "Mark Wooding") + (program . "`fw'") + (full-title . "the `fw' port forwarder")) + skel-alist)) diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..b41ffe7 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,49 @@ +## -*-makefile-*- +## +## $Id: Makefile.am,v 1.1 1999/07/01 08:56:23 mdw Exp $ +## +## Makefile for fw +## +## (c) 1999 Mark Wooding +## + +##----- 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. + +##----- Revision history ---------------------------------------------------- +## +## $Log: Makefile.am,v $ +## Revision 1.1 1999/07/01 08:56:23 mdw +## Initial revision +## + +AUTOMAKE_OPTIONS = foreign + +SUBDIRS = mLib + +bin_PROGRAMS = fw +LDADD = mLib/libmLib.a +INCLUDES = -I$(srcdir)/mLib + +fw_SOURCES = \ + acl.c bres.c chan.c conf.c forward.c fw.c \ + ident.c identify.c listener.c scan.c \ + acl.h bres.h chan.h conf.h forward.h fw.h \ + ident.h identify.h listener.h scan.h + +##----- That's all, folks --------------------------------------------------- diff --git a/acconfig.h b/acconfig.h new file mode 100644 index 0000000..7146729 --- /dev/null +++ b/acconfig.h @@ -0,0 +1,61 @@ +/* -*-c-*- + * + * $Id: acconfig.h,v 1.1 1999/07/01 08:56:23 mdw Exp $ + * + * Configuration header for fw + * + * (c) 1999 Mark Wooding + */ + +/*----- 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: acconfig.h,v $ + * Revision 1.1 1999/07/01 08:56:23 mdw + * Initial revision + * + */ + +#ifndef ACCONFIG_H +#define ACCONFIG_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Configuration macros ----------------------------------------------*/ +@TOP@ + +/* Package name. */ +#define PACKAGE "fw" + +/* Package version number. */ +#define VERSION "1.0.0" + +@BOTTOM@ + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/acl.c b/acl.c new file mode 100644 index 0000000..4dadc24 --- /dev/null +++ b/acl.c @@ -0,0 +1,138 @@ +/* -*-c-*- + * + * $Id: acl.c,v 1.1 1999/07/01 08:56:23 mdw Exp $ + * + * Access control list handling + * + * (c) 1999 Mark Wooding + */ + +/*----- 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: acl.c,v $ + * Revision 1.1 1999/07/01 08:56:23 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "acl.h" + +/*----- Static variables --------------------------------------------------*/ + +static acl_entry *global = 0; +static acl_entry **gtail = &global; + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @acl_check@ --- * + * + * Arguments: @acl_entry *a@ = pointer to ACL to check against + * @struct in_addr addr@ = address to check + * + * Returns: Nonzero if allowed. + * + * Use: Checks an address against an ACL. + */ + +int acl_check(acl_entry *a, struct in_addr addr) +{ + int act = ACL_ALLOW; + int i; + + for (i = 0; i < 2; i++) { + for (; a; a = a->next) { + if ((addr.s_addr & a->mask.s_addr) == a->addr.s_addr) + return (a->act & ACL_PERM); + act = (a->act & ACL_PERM) ^ 1; + } + a = global; + } + + return (act); +} + +/* --- @acl_dump@ --- * + * + * Arguments: @acl_entry *a@ = pointer to ACL to dump + * @FILE *fp@ = pointer to stream to dump on + * + * Returns: --- + * + * Use: Dumps an access control list to an output stream. + */ + +void acl_dump(acl_entry *a, FILE *fp) +{ + if (!a) + a = global; + for (; a; a = a->next) { + fprintf(fp, " %s from ", + (a->act & ACL_PERM) == ACL_ALLOW ? "allow" : "deny"); + fputs(inet_ntoa(a->addr), fp); + fputc('/', fp); + fputs(inet_ntoa(a->mask), fp); + fputc('\n', fp); + } +} + +/* --- @acl_add@ --- * + * + * Arguments: @acl_entry ***a@ = address of pointer to list tail + * @unsigned act@ = what to do with matching addresses + * @struct in_addr addr, mask@ = address and mask to match + * + * Returns: --- + * + * Use: Adds an entry to the end of an access control list. + */ + +void acl_add(acl_entry ***a, unsigned act, + struct in_addr addr, struct in_addr mask) +{ + acl_entry *aa = CREATE(acl_entry); + aa->act = act; + aa->addr.s_addr = addr.s_addr & mask.s_addr; + aa->mask = mask; + aa->next = 0; + if (!a) + a = >ail; + **a = aa; + *a = &aa->next; +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/acl.h b/acl.h new file mode 100644 index 0000000..8b8588c --- /dev/null +++ b/acl.h @@ -0,0 +1,111 @@ +/* -*-c-*- + * + * $Id: acl.h,v 1.1 1999/07/01 08:56:23 mdw Exp $ + * + * Access control list handling + * + * (c) 1999 Mark Wooding + */ + +/*----- 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: acl.h,v $ + * Revision 1.1 1999/07/01 08:56:23 mdw + * Initial revision + * + */ + +#ifndef ACL_H +#define ACL_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include +#include + +/*----- Data structures ---------------------------------------------------*/ + +/* --- An access control entry --- */ + +typedef struct acl_entry { + struct acl_entry *next; /* Next entry in the list */ + unsigned act; /* What to do with matching hosts */ + struct in_addr addr, mask; /* Address and netmask */ +} acl_entry; + +#define ACL_DENY 0 /* Deny access to matching hosts */ +#define ACL_ALLOW 1 /* Allow access to matching hosts */ +#define ACL_PERM 1u /* Bit mask for permission bit */ + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @acl_check@ --- * + * + * Arguments: @acl_entry *a@ = pointer to ACL to check against + * @struct in_addr addr@ = address to check + * + * Returns: Nonzero if allowed. + * + * Use: Checks an address against an ACL. + */ + +extern int acl_check(acl_entry */*a*/, struct in_addr /*addr*/); + +/* --- @acl_dump@ --- * + * + * Arguments: @acl_entry *a@ = pointer to ACL to dump + * @FILE *fp@ = pointer to stream to dump on + * + * Returns: --- + * + * Use: Dumps an access control list to an output stream. + */ + +extern void acl_dump(acl_entry */*a*/, FILE */*fp*/); + +/* --- @acl_add@ --- * + * + * Arguments: @acl_entry ***a@ = address of pointer to list tail + * @unsigned act@ = what to do with matching addresses + * @struct in_addr addr, mask@ = address and mask to match + * + * Returns: --- + * + * Use: Adds an entry to the end of an access control list. + */ + +extern void acl_add(acl_entry ***/*a*/, unsigned /*act*/, + struct in_addr /*addr*/, struct in_addr /*mask*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/bres.c b/bres.c new file mode 100644 index 0000000..d04d15f --- /dev/null +++ b/bres.c @@ -0,0 +1,439 @@ +/* -*-c-*- + * + * $Id: bres.c,v 1.1 1999/07/01 08:56:23 mdw Exp $ + * + * Background reverse name resolution + * + * (c) 1999 Mark Wooding + */ + +/*----- 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: bres.c,v $ + * Revision 1.1 1999/07/01 08:56:23 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "bres.h" + +/*----- Magic numbers -----------------------------------------------------*/ + +#define BRES_MAX 5 /* Maximum number of resolvers */ +#define BRES_IDLE 60 /* Lifetime of an idle resolver */ + +/*----- Static variables --------------------------------------------------*/ + +static bres_server servers[BRES_MAX]; /* Statically allocated servers */ + +#define FREE ((bres_server *)&freelist) +static struct { bres_server *next, *prev; } freelist = { FREE, FREE }; + +#define QUEUE ((bres_client *)&queue) +static struct { bres_client *next, *prev; } queue = { QUEUE, QUEUE }; + +static sel_state *sel; + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @zap@ --- * + * + * Arguments: @bres_server *rs@ = pointer to server block + * + * Returns: --- + * + * Use: Kills a server process, reaps the losing child and makes + * things generally clean again. + */ + +static void zap(bres_server *rs) +{ + /* --- Close the pipes, kill the child, and reap it --- */ + + if (rs->kid != -1) { + selbuf_disable(&rs->b); + close(rs->fd); + close(rs->b.reader.fd); + kill(rs->kid, SIGTERM); + waitpid(rs->kid, 0, 0); + rs->kid = -1; + } + + /* --- Move the server to the back of the list --- */ + + rs->next->prev = rs->prev; + rs->prev->next = rs->next; + rs->next = FREE; + rs->prev = FREE->prev; + FREE->prev->next = rs; + FREE->prev = rs; +} + +/* --- @bres_abort@ --- * + * + * Arguments: @bres_client *rc@ = pointer to client block + * + * Returns: --- + * + * Use: Removes a queued job. + */ + +void bres_abort(bres_client *rc) +{ + if (rc->rs) { + zap(rc->rs); + rc->rs = 0; + } else { + rc->next->prev = rc->prev; + rc->prev->next = rc->next; + } +} + +/* --- @child@ --- * + * + * Arguments: @int rfd@ = output file descriptor for resolved hostnames + * @int cfd@ = input file descriptor for raw addresses + * + * Returns: Never. + * + * Use: Asynchronous name resolving process. + */ + +static void child(int rfd, int cfd) +{ + struct in_addr addr; + FILE *fp = fdopen(rfd, "w"); + + { + int i; + int maxfd = sysconf(_SC_OPEN_MAX); + + for (i = 0; i < maxfd; i++) { + if (i != rfd && i != cfd) + close(i); + } + } + + for (;;) { + int r = read(cfd, &addr, sizeof(addr)); + struct hostent *h; + char *p; + + if (r <= 0) + break; + else if (r != sizeof(addr)) + continue; + + h = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); + if (h) + p = h->h_name; + else + p = inet_ntoa(addr); + fprintf(fp, "%s\n", p); + fflush(fp); + } + _exit(0); +} + +/* --- @idle@ --- * + * + * Arguments: @struct timeval *tv@ = pointer to the current time + * @void *vp@ = pointer to a server block + * + * Returns: --- + * + * Use: Kills off a child which has been idle for too long. + */ + +static void idle(struct timeval *tv, void *vp) +{ + bres_server *rs = vp; + zap(rs); +} + +/* --- @answer@ --- * + * + * Arguments: @char *p@ = pointer to string read + * @void *vp@ = pointer to server block + * + * Returns: --- + * + * Use: Retrieves an answer from a name resolver process. + */ + +static void attach(bres_client */*rc*/); + +static void answer(char *p, void *vp) +{ + bres_server *rs = vp; + bres_client *rc = rs->rc; + + /* --- Report the result to my client --- */ + + if (rc) + rc->func(p, rc->p); + if (!p) + zap(rs); + if (!rc) + return; + + /* --- Wrap up the various structures --- */ + + rs->rc = 0; + rc->rs = 0; + rs->next = FREE->next; + rs->prev = FREE; + FREE->next->prev = rs; + FREE->next = rs; + + /* --- Tie a timer onto the server block --- */ + + { + struct timeval tv; + + gettimeofday(&tv, 0); + tv.tv_sec += BRES_IDLE; + sel_addtimer(sel, &rs->t, &tv, idle, rs); + } + + /* --- If there are any clients waiting, attach one --- */ + + if (QUEUE->next != QUEUE) { + rc = QUEUE->next; + QUEUE->next = rc->next; + rc->next->prev = QUEUE; + attach(rc); + } +} + +/* --- @start@ --- * + * + * Arguments: @bres_server *rs@ = pointer to a server block + * + * Returns: Zero if OK, nonzero if something failed. + * + * Use: Starts up a child resolver process. + */ + +static int start(bres_server *rs) +{ + int rfd[2], cfd[2]; + pid_t kid; + + /* --- Make the pipes --- */ + + if (pipe(rfd)) + goto fail_0; + if (pipe(cfd)) + goto fail_1; + + /* --- Start up the child process --- */ + + if ((kid = fork()) < 0) + goto fail_2; + if (kid == 0) { + close(cfd[1]); + close(rfd[0]); + child(rfd[1], cfd[0]); + _exit(1); + } + + /* --- Fix up everything in the server block --- */ + + close(cfd[0]); + close(rfd[1]); + rs->fd = cfd[1]; + selbuf_init(&rs->b, sel, rfd[0], answer, rs); + rs->kid = kid; + return (0); + + /* --- Fix up after errors --- */ + +fail_2: + close(cfd[0]); + close(cfd[1]); +fail_1: + close(rfd[0]); + close(rfd[1]); +fail_0: + return (-1); +} + +/* --- @attach@ --- * + * + * Arguments: @bres_client *rc@ = pointer to a client block + * + * Returns: --- + * + * Use: Attaches a client to a spare server (which is assumed to + * exist). + */ + +static void attach(bres_client *rc) +{ + bres_server *rs; + int lose = 0; + + /* --- Fix up the server ready for the job --- * + * + * If the server has a process, remove its timer. Otherwise, fork off a + * new resolver process. This is also where I go if I find that the child + * resolver process has lost while I wasn't looking. Only one attempt at + * forking is performed. + */ + +again: + rs = FREE->next; + if (rs->kid != -1) + sel_rmtimer(&rs->t); + else { + if (lose || start(rs)) + goto lost; + lose = 1; + } + + /* --- Submit the job to the resolver --- */ + + { + struct sigaction sa, osa; + int e; + + /* --- Ignore @SIGPIPE@ for now --- * + * + * This way I can trap @EPIPE@ and reap a losing child, if there was one. + */ + + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(SIGPIPE, &sa, &osa); + + /* --- Write the new job to the child --- */ + + e = 0; + if (write(rs->fd, &rc->addr, sizeof(rc->addr)) < 0) + e = errno; + sigaction(SIGPIPE, &osa, 0); + + /* --- Sort out various errors --- */ + + if (e == EPIPE) { + zap(rs); + goto again; + } else if (e) + goto lost; + } + + /* --- Fiddle with lists so that everything's OK --- */ + + rs->next->prev = FREE; + FREE->next = rs->next; + rs->next = rs->prev = rs; + rs->rc = rc; + rc->rs = rs; + return; + +lost: + rc->func(0, rc->p); +} + +/* --- @bres_resolve@ --- * + * + * Arguments: @bres_client *rc@ = pointer to client block + * @struct in_addr addr@ = address to resolve + * @void (*func)(const char *host, void *p)@ = handler function + * @void *p@ = argument for handler function + * + * Returns: --- + * + * Use: Adds a resolver job to the queue. The job will be processed + * when there's a spare resolver process to deal with it. + */ + +void bres_resolve(bres_client *rc, struct in_addr addr, + void (*func)(const char */*host*/, void */*p*/), void *p) +{ + /* --- Fill in the structure --- */ + + rc->addr = addr; + rc->func = func; + rc->p = p; + rc->rs = 0; + + /* --- If there's a free server, plug it in --- */ + + if (FREE->next == FREE) { + rc->next = QUEUE; + rc->prev = QUEUE->prev; + QUEUE->prev->next = rc; + QUEUE->prev = rc; + } else + attach(rc); +} + +/* --- @bres_init@ --- * + * + * Arguments: @sel_state *s@ = pointer to select multiplexor + * + * Returns: --- + * + * Use: Initializes the background resolver for use. + */ + +void bres_init(sel_state *s) +{ + int i; + + sel = s; + for (i = 0; i < BRES_MAX; i++) { + servers[i].next = FREE; + servers[i].prev = FREE->prev; + servers[i].kid = -1; + servers[i].rc = 0; + FREE->prev->next = &servers[i]; + FREE->prev = &servers[i]; + } +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/bres.h b/bres.h new file mode 100644 index 0000000..f75f0bf --- /dev/null +++ b/bres.h @@ -0,0 +1,125 @@ +/* -*-c-*- + * + * $Id: bres.h,v 1.1 1999/07/01 08:56:23 mdw Exp $ + * + * Background reverse name resolution + * + * (c) 1999 Mark Wooding + */ + +/*----- 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: bres.h,v $ + * Revision 1.1 1999/07/01 08:56:23 mdw + * Initial revision + * + */ + +#ifndef RES_H +#define RES_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include +#include +#include + +#include +#include + +/*----- Data structures ---------------------------------------------------*/ + +/* --- Client allocated request block --- */ + +typedef struct bres_client { + struct bres_client *next, *prev; /* Queue of waiting resolve jobs */ + struct bres_server *rs; /* Pointer to attached server */ + struct in_addr addr; /* Address to be resolved */ + void (*func)(const char */*host*/, void */*p*/); /* Handler function */ + void *p; /* Argument for handler function */ +} bres_client; + +/* --- Server maintained resolver blocks --- */ + +typedef struct bres_server { + struct bres_server *next, *prev; /* Doubly-linked list of servers */ + pid_t kid; /* Process id of server process */ + selbuf b; /* Input line-buffer selector */ + sel_timer t; /* Timeout for idle servers */ + int fd; /* File descriptor for writing */ + struct bres_client *rc; /* Pointer to attached client */ +} bres_server; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @bres_abort@ --- * + * + * Arguments: @bres_client *rc@ = pointer to client block + * + * Returns: --- + * + * Use: Removes a queued job. + */ + +extern void bres_abort(bres_client */*rc*/); + +/* --- @bres_resolve@ --- * + * + * Arguments: @bres_client *rc@ = pointer to client block + * @struct in_addr addr@ = address to resolve + * @void (*func)(const char *host, void *p)@ = handler function + * @void *p@ = argument for handler function + * + * Returns: --- + * + * Use: Adds a resolver job to the queue. The job will be processed + * when there's a spare resolver process to deal with it. + */ + +extern void bres_resolve(bres_client */*rc*/, struct in_addr /*addr*/, + void (*/*func*/)(const char */*host*/, void */*p*/), + void */*p*/); + +/* --- @bres_init@ --- * + * + * Arguments: @sel_state *s@ = pointer to select multiplexor + * + * Returns: --- + * + * Use: Initializes the background resolver for use. + */ + +extern void bres_init(sel_state */*s*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/chan.c b/chan.c new file mode 100644 index 0000000..55e6a75 --- /dev/null +++ b/chan.c @@ -0,0 +1,254 @@ +/* -*-c-*- + * + * $Id: chan.c,v 1.1 1999/07/01 08:56:23 mdw Exp $ + * + * Channel management + * + * (c) 1999 Mark Wooding + */ + +/*----- 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: chan.c,v $ + * Revision 1.1 1999/07/01 08:56:23 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "chan.h" +#include "fw.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @writechan@ --- * + * + * Arguments: @int fd@ = file descriptor to write to + * @unsigned mode@ = what the descriptor is ready for + * @void *vp@ = pointer to channel block + * + * Returns: --- + * + * Use: Writes to a channel. + */ + +static void writechan(int fd, unsigned mode, void *vp) +{ + chan *c = vp; + int w; + unsigned base = c->base; + unsigned len = c->len; + + /* --- Write data from my buffer --- */ + + if (len) { + + /* --- Do the write --- */ + + if (base + len <= CHAN_BUFSZ) + w = write(fd, c->buf + base, len); + else { + struct iovec iov[2]; + iov[0].iov_base = c->buf + base; + iov[0].iov_len = CHAN_BUFSZ - base; + iov[1].iov_base = c->buf; + iov[1].iov_len = len - iov[0].iov_len; + w = writev(fd, iov, 2); + } + + /* --- Sift through the results --- */ + + if (w < 0) { + if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) + return; + goto close; + } + else if (w == 0) + goto close; + else if (c->len == CHAN_BUFSZ && !(c->f & CHANF_CLOSE)) + sel_addfile(&c->r); + c->len -= w; + } + if (c->len == 0) + sel_rmfile(&c->w); + + /* --- Close the output end if necessary --- */ + + if (c->len == 0 && (c->f & CHANF_CLOSE)) { + shutdown(fd, 1); + c->func(c->p); + } + return; + + /* --- Force a close if an error occurred --- */ + +close: + chan_close(c); + c->func(c->p); +} + +/* --- @readchan@ --- * + * + * Arguments: @int fd@ = file descriptor to read from + * @unsigned mode@ = what the descriptor is ready for + * @void *vp@ = pointer to channel block + * + * Returns: --- + * + * Use: Reads from a channel. + */ + +static void readchan(int fd, unsigned mode, void *vp) +{ + chan *c = vp; + int r; + unsigned base = (c->base + c->len) & (CHAN_BUFSZ - 1); + unsigned len = CHAN_BUFSZ - c->len; + + /* --- Do the read --- */ + + if (base + len <= CHAN_BUFSZ) + r = read(fd, c->buf + base, len); + else { + struct iovec iov[2]; + iov[0].iov_base = c->buf + base; + iov[0].iov_len = CHAN_BUFSZ - base; + iov[1].iov_base = c->buf; + iov[1].iov_len = len - iov[0].iov_len; + r = readv(fd, iov, 2); + } + + /* --- Sift through the results --- */ + + if (r < 0) { + if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) + return; + goto close; + } + else if (r == 0) + goto close; + else if (c->len == 0 && !(c->f & CHANF_READY)) + sel_addfile(&c->w); + c->len += r; + if (c->len == CHAN_BUFSZ) + sel_rmfile(&c->r); + return; + + /* --- Close the read end of the channel --- */ + +close: + c->f |= CHANF_CLOSE; + if (!c->len) + sel_addfile(&c->w); + sel_rmfile(&c->r); +} + +/* --- @chan_close@ --- * + * + * Arguments: @chan *c@ = pointer to channel + * + * Returns: --- + * + * Use: Closes down a channel prematurely. + */ + +void chan_close(chan *c) +{ + if (!(c->f & CHANF_CLOSE) && c->len != CHAN_BUFSZ) + sel_rmfile(&c->r); + if ((c->f & CHANF_READY) && c->len != 0) + sel_rmfile(&c->w); +} + +/* --- @chan_dest@ --- * + * + * Arguments: @chan *c@ = pointer to channel + * @int fd@ = destination file descriptor for channel + * + * Returns: --- + * + * Use: Sets the channel's destination so it knows where to put + * data. + */ + +void chan_dest(chan *c, int fd) +{ + if (c->f & CHANF_READY) + return; + sel_initfile(sel, &c->w, fd, SEL_WRITE, writechan, c); + if (c->len) + sel_addfile(&c->w); +} + +/* --- @chan_open@ --- * + * + * Arguments: @chan *c@ = pointer to channel to open + * @int from, to@ = source and destination file descriptors + * @void (*func)(void *p)@ = function to call on closure + * @void *p@ = argument to pass to function + * + * Returns: --- + * + * Use: Opens a channel. Data is copied from the source to the + * destination. The @to@ argument may be @-1@ if the file + * descriptor isn't known yet. + */ + +void chan_open(chan *c, int from, int to, + void (*func)(void */*p*/), void *p) +{ + c->func = func; + c->p = p; + + c->base = 0; + c->len = 0; + c->f = 0; + + sel_initfile(sel, &c->r, from, SEL_READ, readchan, c); + sel_addfile(&c->r); + + if (to != -1) + chan_dest(c, to); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/chan.h b/chan.h new file mode 100644 index 0000000..f8a9638 --- /dev/null +++ b/chan.h @@ -0,0 +1,115 @@ +/* -*-c-*- + * + * $Id: chan.h,v 1.1 1999/07/01 08:56:23 mdw Exp $ + * + * Channel management + * + * (c) 1999 Mark Wooding + */ + +/*----- 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: chan.h,v $ + * Revision 1.1 1999/07/01 08:56:23 mdw + * Initial revision + * + */ + +#ifndef CHAN_H +#define CHAN_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +/*----- Magic numbers -----------------------------------------------------*/ + +#define CHAN_BUFSZ 4096 + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct chan { + unsigned base, len; /* Base and length of data */ + unsigned f; /* Various interesting flags */ + void (*func)(void */*p*/); /* Function to call on closure */ + void *p; /* Argument to pass function */ + sel_file r, w; /* Reader and writer selectors */ + char buf[CHAN_BUFSZ]; /* The actual data buffer */ +} chan; + +#define CHANF_CLOSE 1u /* Close channel when buffer empty */ +#define CHANF_READY 2u /* The channel destination exists */ + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @chan_close@ --- * + * + * Arguments: @chan *c@ = pointer to channel + * + * Returns: --- + * + * Use: Closes down a channel prematurely. + */ + +extern void chan_close(chan */*c*/); + +/* --- @chan_dest@ --- * + * + * Arguments: @chan *c@ = pointer to channel + * @int fd@ = destination file descriptor for channel + * + * Returns: --- + * + * Use: Sets the channel's destination so it knows where to put + * data. + */ + +extern void chan_dest(chan */*c*/, int /*fd*/); + +/* --- @chan_open@ --- * + * + * Arguments: @chan *c@ = pointer to channel to open + * @int from, to@ = source and destination file descriptors + * @void (*func)(void *p)@ = function to call on closure + * @void *p@ = argument to pass to function + * + * Returns: --- + * + * Use: Opens a channel. Data is copied from the source to the + * destination. The @to@ argument may be @-1@ if the file + * descriptor isn't known yet. + */ + +extern void chan_open(chan */*c*/, int /*from*/, int /*to*/, + void (*/*func*/)(void */*p*/), void */*p*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/conf.c b/conf.c new file mode 100644 index 0000000..049af46 --- /dev/null +++ b/conf.c @@ -0,0 +1,351 @@ +/* -*-c-*- + * + * $Id: conf.c,v 1.1 1999/07/01 08:56:23 mdw Exp $ + * + * Configuration parsing + * + * (c) 1999 Mark Wooding + */ + +/*----- 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: conf.c,v $ + * Revision 1.1 1999/07/01 08:56:23 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "acl.h" +#include "listener.h" +#include "scan.h" + +/*----- Magic numbers -----------------------------------------------------*/ + +#define CTOK_EOF (-1) +#define CTOK_WORD 256 + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @token@ --- * + * + * Arguments: @scanner *sc@ = pointer to scanner definition + * + * Returns: Type of token scanned. + * + * Use: Reads the next token from the character scanner. + */ + +static int token(scanner *sc) +{ +#define SCAN(sc) (sc)->ops->scan((sc)) +#define UNSCAN(sc, x) (sc)->ops->unscan((x), (sc)) + + int ch; + + for (;;) { + ch = SCAN(sc); + if (ch == '\n') + sc->line++; + else if (isspace((unsigned char)ch)) + ; + else switch (ch) { + case EOF: + return (sc->t = CTOK_EOF); + case '#': + do ch = SCAN(sc); while (ch != EOF && ch != '\n'); + if (ch == '\n') + sc->line++; + break; + case '{': + case '}': + case ':': + case '/': + case ';': + return (sc->t = ch); + default: + DRESET(&sc->d); + do { + DPUTC(&sc->d, ch); + ch = SCAN(sc); + } while (ch != EOF && ch != '{' && ch != ';' && + ch != '}' && ch != ':' && ch != '/' && + !isspace((unsigned char)(ch))); + UNSCAN(sc, ch); + DPUTZ(&sc->d); + return (sc->t = CTOK_WORD); + } + } + +#undef SCAN +#undef UNSCAN +} + +/* --- @error@ --- * + * + * Arguments: @scanner *sc@ = pointer to scanner definition + * @const char *msg@ = message skeleton string + * @...@ = extra arguments for the skeleton + * + * Returns: Doesn't + * + * Use: Reports an error at the current scanner location. + */ + +static void error(scanner *sc, const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + fprintf(stderr, "%s: %s:%i: ", QUIS, sc->src, sc->line); + vfprintf(stderr, msg, ap); + fputc('\n', stderr); + exit(1); +} + +/* --- @portnum@ --- * + * + * Arguments: @scanner *sc@ = pointer to scanner (for error reporting) + * @const char *p@ = pointer to port name + * + * Returns: Port number (network byte order) + * + * Use: Converts a textual port name or number into a usable thing. + */ + +static unsigned short portnum(scanner *sc, const char *p) +{ + struct servent *s; + if (isdigit((unsigned char)*p)) + return (htons(atoi(p))); + if ((s = getservbyname(p, "tcp")) == 0) + error(sc, "unknown tcp service `%s'", p); + return (s->s_port); +} + +/* --- @getconf@ --- * + * + * Arguments: @scanner *sc@ = pointer to scanner to read from + * @listener *l@ = listener to configure (or zero) + * @acl_entry ***a@ = pointer to tail of ACL (or zero) + * + * Returns: --- + * + * Use: Reads a local or global configuration statement. + */ + +static void getconf(scanner *sc, listener *l, acl_entry ***a) +{ + unsigned act; + + /* --- Access control limitations --- */ + + if ((strcmp(sc->d.buf, "allow") == 0 && (act = ACL_ALLOW, 1)) || + (strcmp(sc->d.buf, "deny") == 0 && (act = ACL_DENY, 1))) { + struct hostent *h; + struct netent *n; + struct in_addr addr, mask; + + /* --- Find the host or network address --- */ + + token(sc); + if (sc->t == CTOK_WORD && strcmp(sc->d.buf, "from") == 0) + token(sc); + if (sc->t != CTOK_WORD) + error(sc, "parse error, address expected"); + if ((n = getnetbyname(sc->d.buf)) != 0) + addr.s_addr = htonl(n->n_net); + else if ((h = gethostbyname(sc->d.buf)) == 0) + error(sc, "couldn't resolve address `%s'", sc->d.buf); + else + memcpy(&addr, h->h_addr, sizeof(struct in_addr)); + token(sc); + + /* --- Find the netmask, if any --- */ + + if (sc->t != '/') + mask.s_addr = ~0ul; + else { + token(sc); + if (sc->t != CTOK_WORD) + error(sc, "parse error, netmask expected"); + if (strchr(sc->d.buf, '.') == 0) + mask.s_addr = htonl((~0ul << (32 - atoi(sc->d.buf))) & 0xffffffff); + else { +#ifdef HAVE_INET_ATON + if (!inet_aton(sc->d.buf, &mask)) + error(sc, "bad netmask `%s'", sc->d.buf); +#else + mask.s_addr = inet_addr(sc->d.buf); +#endif + } + token(sc); + } + + /* --- Add the access control entry --- */ + + acl_add(a, act, addr, mask); + } + + /* --- Anything unrecognized --- */ + + else + error(sc, "parse error, unknown configuration keyword `%s'", sc->d.buf); +} + +/* --- @conf_parse@ --- * + * + * Arguments: @void *scp@ = pointer to scanner definition + * + * Returns: --- + * + * Use: Parses a configuration file from the scanner. + */ + +void conf_parse(void *scp) +{ + scanner *sc = scp; + + 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) { + unsigned short sp, dp; + struct sockaddr_in sin; + struct hostent *h; + int fd; + listener *l; + + /* --- Read the source port --- */ + + token(sc); + if (sc->t == CTOK_WORD && strcmp(sc->d.buf, "port") == 0) + token(sc); + if (sc->t != CTOK_WORD) + error(sc, "parse error, source port expected"); + sp = portnum(sc, sc->d.buf); + + /* --- Read the destination address --- */ + + token(sc); + if (sc->t == CTOK_WORD && strcmp(sc->d.buf, "to") == 0) + token(sc); + if (sc->t != CTOK_WORD) + error(sc, "parse error, destination address expected"); + if ((h = gethostbyname(sc->d.buf)) == 0) + error(sc, "couldn't resolve address `%s'", sc->d.buf); + + token(sc); + if (sc->t == ':') + token(sc); + if (sc->t != CTOK_WORD) + error(sc, "parse error, destination port expected"); + dp = portnum(sc, sc->d.buf); + + /* --- Make the socket --- */ + + if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) + error(sc, "couldn't create socket: %s", strerror(errno)); + + /* --- Set it to allow address reuse --- */ + + { + int opt = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + } + + /* --- Bind it to the right port --- */ + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_port = sp; + if (bind(fd, (struct sockaddr *)&sin, sizeof(sin))) { + error(sc, "couldn't bind to port %i: %s", + ntohs(sp), strerror(errno)); + } + + /* --- Set it to listen for connections --- */ + + if (listen(fd, 5)) + error(sc, "couldn't listen on socket: %s", strerror(errno)); + + /* --- Fill in a new listener --- */ + + memcpy(&sin.sin_addr, h->h_addr, sizeof(struct in_addr)); + sin.sin_port = dp; + l = listener_add(fd, sp, &sin); + + /* --- Snarf access controls and other attributes --- */ + + token(sc); + if (sc->t == '{') { + acl_entry **a = &l->acl; + token(sc); + while (sc->t != '}') { + if (sc->t != CTOK_WORD) + error(sc, "parse error, keyword or `}' expected"); + getconf(sc, l, &a); + if (sc->t == ';') + token(sc); + } + *a = 0; + token(sc); + } + } + + /* --- Other configuration is handled elsewhere --- */ + + else + getconf(sc, 0, 0); + + if (sc->t == ';') + token(sc); + } +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/conf.h b/conf.h new file mode 100644 index 0000000..e1713ee --- /dev/null +++ b/conf.h @@ -0,0 +1,69 @@ +/* -*-c-*- + * + * $Id: conf.h,v 1.1 1999/07/01 08:56:23 mdw Exp $ + * + * Configuration parsing + * + * (c) 1999 Mark Wooding + */ + +/*----- 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: conf.h,v $ + * Revision 1.1 1999/07/01 08:56:23 mdw + * Initial revision + * + */ + +#ifndef CONF_H +#define CONF_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#ifndef SCAN_H +# include "scan.h" +#endif + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @conf_parse@ --- * + * + * Arguments: @void *scp@ = pointer to a scanner structure + * + * Returns: --- + * + * Use: Parses a configuration file fragment from the scanner + */ + +extern void conf_parse(void */*scp*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..e1341af --- /dev/null +++ b/configure.in @@ -0,0 +1,47 @@ +dnl -*-fundamental-*- +dnl +dnl $Id: configure.in,v 1.1 1999/07/01 08:56:23 mdw Exp $ +dnl +dnl Configuration script for fw +dnl +dnl (c) 1999 Mark Wooding +dnl + +dnl ----- Licensing notice -------------------------------------------------- +dnl +dnl This file is part of the `fw' port forwarder. +dnl +dnl `fw' is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl `fw' is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with `fw'; if not, write to the Free Software Foundation, +dnl Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +dnl ----- Revision history -------------------------------------------------- +dnl +dnl $Log: configure.in,v $ +dnl Revision 1.1 1999/07/01 08:56:23 mdw +dnl Initial revision +dnl + +AC_INIT(fw.c) +AM_INIT_AUTOMAKE(fw, 1.0.0) +AM_CONFIG_HEADER(config.h) + +AC_PROG_CC +mdw_GCC_FLAGS + +AC_CHECK_FUNCS(inet_aton) + +AC_CONFIG_SUBDIRS(mLib) +AC_OUTPUT(Makefile) + +dnl ----- That's all, folks ------------------------------------------------- diff --git a/forward.c b/forward.c new file mode 100644 index 0000000..1d3118a --- /dev/null +++ b/forward.c @@ -0,0 +1,223 @@ +/* -*-c-*- + * + * $Id: forward.c,v 1.1 1999/07/01 08:56:23 mdw Exp $ + * + * Port forwarding + * + * (c) 1999 Mark Wooding + */ + +/*----- 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: forward.c,v $ + * Revision 1.1 1999/07/01 08:56:23 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "chan.h" +#include "forward.h" +#include "fw.h" + +/*----- Data structures ---------------------------------------------------*/ + +/* --- Port forwarding data --- */ + +typedef struct fw { + int fd_a; /* Client's file descriptor */ + int fd_b; /* Server's file descriptor */ + unsigned state; /* Current state of the world */ + conn c; /* Nonblocking connect to server */ + chan ab; /* Channel from @a@ to @b@ */ + chan ba; /* Channel from @b@ to @a@ */ +} fw; + +#define S_AB 1u /* Channel from @a@ to @b@ open */ +#define S_BA 2u /* Channel from @b@ to @a@ open */ +#define S_NOTCONN 4u /* Not connected to @b@ yet */ +#define S_NOTID 8u /* Not finished identification */ + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @done@ --- * + * + * Arguments: @fw *f@ = pointer to forwarder + * + * Returns: --- + * + * Use: Tidies up a forwarder that nobody wants any more. + */ + +static void done(fw *f) +{ + close(f->fd_a); + close(f->fd_b); + free(f); +} + +/* --- @ident@ --- * + * + * Arguments: @void *vp@ = pointer to forwarder + * + * Returns: --- + * + * Use: Handles completion of client identification. + */ + +static void ident(void *vp) +{ + fw *f = vp; + f->state &= ~S_NOTID; + if (!f->state) + done(f); +} + +/* --- @closeba@ --- * + * + * Arguments: @void *vp@ = pointer to forwarder + * + * Returns: --- + * + * Use: Handles the closing of the %$b \rightarrow a$% channel. + */ + +static void closeba(void *vp) +{ + fw *f = vp; + f->state &= ~S_BA; + if (!f->state) + done(f); +} + +/* --- @closeab@ --- * + * + * Arguments: @void *vp@ = pointer to forwarder + * + * Returns: --- + * + * Use: Handles the closing of the %$a \rightarrow b$% channel. + */ + +static void closeab(void *vp) +{ + fw *f = vp; + f->state &= ~S_AB; + if (!f->state) + done(f); +} + +/* --- @go@ --- * + * + * Arguments: @int fd@ = newly connected socket + * @void *vp@ = pointer to forwarder + * + * Returns: --- + * + * Use: Completes the forwarder once the outbound connection is set + * up. + */ + +static void go(int fd, void *vp) +{ + fw *f = vp; + + /* --- If it all went tits-up, deal with that --- */ + + if (fd == -1) { + chan_close(&f->ab); + close(f->fd_a); + free(f); + return; + } + + /* --- OK, finish configuring the forwarder --- */ + + f->fd_b = fd; + chan_dest(&f->ab, fd); + chan_open(&f->ba, fd, f->fd_a, closeba, f); + f->state |= S_BA; + f->state &= ~S_NOTCONN; +} + +/* --- @forward@ --- * + * + * Arguments: @int fd@ = file descriptor attached to client + * @struct sockaddr_in *sin@ = pointer to destination address + * @const id_req *q@ = pointer to identification request block + * + * Returns: --- + * + * Use: Start a port forwarding job. + */ + +void forward(int fd, struct sockaddr_in *sin, const id_req *q) +{ + fw *f; + int nfd; + + /* --- Set up the new socket --- */ + + if ((nfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) + return; + + { + int opt = 1; + setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt)); + setsockopt(nfd, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt)); + } + + /* --- Initialize the easy bits --- */ + + f = xmalloc(sizeof(*f)); + f->fd_a = fd; + f->fd_b = -1; + f->state = S_NOTCONN | S_AB | S_NOTID; + + /* --- Open the %$a \rightarrow b$% channel --- */ + + chan_open(&f->ab, fd, -1, closeab, f); + conn_init(&f->c, sel, nfd, (struct sockaddr *)sin, sizeof(*sin), go, f); + identify(q, ident, f); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/forward.h b/forward.h new file mode 100644 index 0000000..b73eff7 --- /dev/null +++ b/forward.h @@ -0,0 +1,75 @@ +/* -*-c-*- + * + * $Id: forward.h,v 1.1 1999/07/01 08:56:23 mdw Exp $ + * + * Port forwarding + * + * (c) 1999 Mark Wooding + */ + +/*----- 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: forward.h,v $ + * Revision 1.1 1999/07/01 08:56:23 mdw + * Initial revision + * + */ + +#ifndef FORWARD_H +#define FORWARD_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include +#include + +#ifndef IDENTIFY_H +# include "identify.h" +#endif + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @forward@ --- * + * + * Arguments: @int fd@ = file descriptor attached to client + * @struct sockaddr_in *sin@ = pointer to destination address + * @const id_req *q@ = pointer to identification request block + * + * Returns: --- + * + * Use: Start a port forwarding job. + */ + +extern void forward(int /*fd*/, struct sockaddr_in */*sin*/, + const id_req */*q*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/fw.c b/fw.c new file mode 100644 index 0000000..ba4b565 --- /dev/null +++ b/fw.c @@ -0,0 +1,262 @@ +/* -*-c-*- + * + * $Id: fw.c,v 1.1 1999/07/01 08:56:23 mdw Exp $ + * + * Port forwarding thingy + * + * (c) 1999 Mark Wooding + */ + +/*----- 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: fw.c,v $ + * Revision 1.1 1999/07/01 08:56:23 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "acl.h" +#include "bres.h" +#include "conf.h" +#include "listener.h" +#include "scan.h" + +/*----- Static variables --------------------------------------------------*/ + +sel_state *sel; /* Multiplexor for nonblocking I/O */ + +/*----- Main code ---------------------------------------------------------*/ + +/* --- Standard GNU help options --- */ + +static void version(FILE *fp) +{ + fprintf(fp, "%s version " VERSION "\n", QUIS); +} + +static void usage(FILE *fp) +{ + fprintf(stderr, "Usage: %s [-d] [-f file] [config statements...]\n", + QUIS); +} + +static void help(FILE *fp) +{ + version(fp); + fputc('\n', fp); + usage(fp); + fputs("\n\ +A fairly full-featured port-forwarder. Options available are:\n\ +\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\ +-f, --file=FILE Read configuration from a file.\n\ +-d, --dump Dump the configuration to standard output.\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. When reading from the command line,\n\ +each argument is considered a separate line. See the manual page for the\n\ +complete syntax.\n\ +", 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; + + enum { + f_bogus = 1, + f_file = 2, + f_dump = 4, + f_fork = 8 + }; + + /* --- Initialize things --- */ + + ego(argv[0]); + sel = &sst; + sel_init(sel); + sub_init(); + bres_init(sel); + + /* --- 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 useful arguments --- */ + + { "file", OPTF_ARGREQ, 0, 'f' }, + { "dump", 0, 0, 'd' }, + { "fork", 0, 0, 'b' }, + { "background", 0, 0, 'b' }, + + /* --- Magic terminator --- */ + + { 0, 0, 0, 0 } + }; + int i = mdwopt(argc, argv, "hvu f:db", 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 'f': { + scan_filectx ctx; + FILE *fp; + if ((fp = fopen(optarg, "r")) == 0) + die(1, "couldn't open file `%s': %s", optarg, strerror(errno)); + scan_fileinit(&ctx, fp, optarg); + conf_parse(&ctx); + fclose(fp); + f |= f_file; + } break; + case 'd': + f |= f_dump; + break; + case 'b': + f |= f_fork; + break; + default: + f |= f_bogus; + break; + } + } + + if (f & f_bogus) { + usage(stderr); + exit(1); + } + + /* --- Deal with the remaining arguments --- */ + + if (optind == argc) { + if (f & f_file) + /* Cool */; + else if (isatty(STDIN_FILENO)) { + moan("no configuration given and stdin is a terminal."); + moan("type `%s --help' for usage information.", QUIS); + exit(1); + } else { + scan_filectx ctx; + scan_fileinit(&ctx, stdin, ""); + conf_parse(&ctx); + } + } else { + scan_argvctx ctx; + scan_argvinit(&ctx, argv + optind); + conf_parse(&ctx); + } + + /* --- Dump out the state --- */ + + if (f & f_dump) { + sel_file *s; + fputs("global acl:\n", stdout); + acl_dump(0, stdout); + for (s = sel->files; s; s = s->next) + listener_dump((listener *)s, stdout); + exit(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); + setsid(); + + kid = fork(); + if (kid != 0) + _exit(0); + } + + /* --- Let rip --- */ + + openlog(QUIS, 0, LOG_DAEMON); + for (;;) + sel_select(sel); + return (0); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/fw.h b/fw.h new file mode 100644 index 0000000..8c87832 --- /dev/null +++ b/fw.h @@ -0,0 +1,58 @@ +/* -*-c-*- + * + * $Id: fw.h,v 1.1 1999/07/01 08:56:23 mdw Exp $ + * + * Main header file for port forwarder + * + * (c) 1999 Mark Wooding + */ + +/*----- 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: fw.h,v $ + * Revision 1.1 1999/07/01 08:56:23 mdw + * Initial revision + * + */ + +#ifndef FW_H +#define FW_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +/*----- Global variables --------------------------------------------------*/ + +extern sel_state *sel; + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/ident.c b/ident.c new file mode 100644 index 0000000..ef1bb5c --- /dev/null +++ b/ident.c @@ -0,0 +1,146 @@ +/* -*-c-*- + * + * $Id: ident.c,v 1.1 1999/07/01 08:56:23 mdw Exp $ + * + * Parse replies from RFC931 servers + * + * (c) 1999 Mark Wooding + */ + +/*----- 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: ident.c,v $ + * Revision 1.1 1999/07/01 08:56:23 mdw + * Initial revision + * + * Revision 1.1.1.1 1999/05/21 22:22:27 mdw + * Initial import. Code not really finished yet. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include +#include + +#include "ident.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @next@ --- * + * + * Arguments: @char **pp@ = address of string pointer + * + * Returns: Address of next token. + * + * Use: Reads the next token from the result string. Tokens are + * terminated by whitespace, or `:' or `,' characters. The + * result string has had `\' escapes removed; it's stored in + * the same memory as the input string. The actual content + * of the delimiter doesn't seem to be interesting, so it + * gets thrown away. + */ + +static char *next(char **pp) +{ + char *p, *q, *r; + + /* --- Deal with reads past the end of the string --- */ + + if (!*pp) + return (""); + + /* --- Initialize various pointers into the string --- */ + + p = q = r = *pp; + + /* --- Skip past any leading whitespace --- */ + + while (isspace((unsigned char)*p)) + p++; + + /* --- Now start work on the string itself --- */ + + for (;;) { + if (*p == 0 || *p == ':' || *p == ',' || isspace((unsigned char)*p)) + break; + else if (*p == '\\') { + p++; + if (!*p) { + *q++ = '\\'; + break; + } + } + *q++ = *p++; + } + + /* --- Tidy up afterwards --- */ + + while (isspace((unsigned char)*p)) + p++; + if (*p == 0) + *pp = 0; + else if (*p == ':' || *p == ',') + *pp = p + 1; + else + *pp = p; + *q = 0; + + return (r); +} + +/* --- @ident_parse@ --- * + * + * Arguments: @char *p@ = pointer to input string from identd + * @ident *i@ = pointer to output block + * + * Returns: --- + * + * Use: Parses a result string from an RFC931 (identd) server. + */ + +void ident_parse(char *p, ident *i) +{ + char *q; + + /* --- Read the source and destination port numbers --- */ + + i->sport = atoi(next(&p)); + i->dport = atoi(next(&p)); + + /* --- Find out what sort of a reply this is --- */ + + q = next(&p); + if (strcmp(q, "USERID") == 0) { + i->type = ident_userid; + i->u.userid.os = next(&p); + i->u.userid.user = next(&p); + } else if (strcmp(q, "ERROR") == 0) { + i->type = ident_error; + i->u.error = next(&p); + } else + i->type = ident_bad; +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/ident.h b/ident.h new file mode 100644 index 0000000..deda7e7 --- /dev/null +++ b/ident.h @@ -0,0 +1,87 @@ +/* -*-c-*- + * + * $Id: ident.h,v 1.1 1999/07/01 08:56:23 mdw Exp $ + * + * Parse replies from RFC931 servers + * + * (c) 1999 Mark Wooding + */ + +/*----- 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: ident.h,v $ + * Revision 1.1 1999/07/01 08:56:23 mdw + * Initial revision + * + * Revision 1.1.1.1 1999/05/21 22:22:27 mdw + * Initial import. Code not really finished yet. + * + */ + +#ifndef IDENT_H +#define IDENT_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct ident { + unsigned short sport, dport; /* Source and destination ports */ + unsigned type; /* Type of reply from server */ + union { + struct { + char *os; /* Operating system name */ + char *user; /* User name */ + } userid; + char *error; /* Error message from server */ + } u; +} ident; + +enum { + ident_userid, + ident_error, + ident_bad +}; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @ident_parse@ --- * + * + * Arguments: @char *p@ = pointer to input string from identd + * @ident *i@ = pointer to output block + * + * Returns: --- + * + * Use: Parses a result string from an RFC931 (identd) server. + */ + +extern void ident_parse(char */*p*/, ident */*i*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/identify.c b/identify.c new file mode 100644 index 0000000..db6df93 --- /dev/null +++ b/identify.c @@ -0,0 +1,306 @@ +/* -*-c-*- + * + * $Id: identify.c,v 1.1 1999/07/01 08:56:23 mdw Exp $ + * + * Identifies and logs the client of a connection + * + * (c) 1999 Mark Wooding + */ + +/*----- 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: identify.c,v $ + * Revision 1.1 1999/07/01 08:56:23 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "bres.h" +#include "fw.h" +#include "ident.h" +#include "identify.h" + +/*----- Magic numbers -----------------------------------------------------*/ + +#define TIMEOUT 15 /* Seconds to wait for answers */ + +/*----- Data structures ---------------------------------------------------*/ + +/* --- Structure to track the progress of an identification --- */ + +typedef struct id { + id_req q; /* Copy of client's request block */ + void (*func)(void */*p*/); /* Function to call when done */ + void *p; /* Argument to pass to function */ + time_t when; /* When the connection occurred */ + conn c; /* Connection selector */ + unsigned state; /* Current state of the world */ + bres_client r; /* Backgd resolver client block */ + char host[64]; /* Resolved hostname */ + char user[32]; /* Authenticated client user */ + sel_timer t; /* Timeout selector */ + selbuf id; /* Reader for the RFC931 client */ +} id; + +#define S_HOST 1u /* Read the hostname from resolver */ +#define S_USER 2u /* Read the username from RFC931 */ +#define S_UCONN 4u /* Connected to remote RFC931 */ +#define S_TIMER 8u /* Timeout has completed */ + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @id_done@ --- * + * + * Arguments: @id *i@ = pointer to identification block + * + * Returns: --- + * + * Use: Finishes with an identification block. + */ + +static void id_done(id *i) +{ + char buf[64]; + struct tm *tm; + + /* --- Close down the various dependent bits --- */ + + if (!(i->state & S_HOST)) + bres_abort(&i->r); + if (!(i->state & S_UCONN)) + conn_kill(&i->c); + else if (!(i->state & S_USER)) + selbuf_disable(&i->id); + if (!(i->state & S_TIMER)) + sel_rmtimer(&i->t); + + /* --- Report the final result --- */ + + tm = localtime(&i->when); + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm); + syslog(LOG_NOTICE, "%s %s %s from %s@%s\n", + buf, i->q.desc, i->q.act, i->user, i->host); + + /* --- Dispose of the block --- */ + + i->func(i->p); + free(i); +} + +/* --- @id_res@ --- * + * + * Arguments: @const char *host@ = name of the resolved host + * @void *vp@ = pointer to identification block + * + * Returns: --- + * + * Use: Responds to a completed reverse name resolution. + */ + +static void id_res(const char *host, void *vp) +{ + id *i = vp; + str_sanitize(i->host, host, sizeof(i->host)); + i->state |= S_HOST; + if (i->state & S_USER) + id_done(i); +} + +/* --- @id_ident@ --- * + * + * Arguments: @char *p@ = pointer to string read from server + * @void *vp@ = pointer to identification block + * + * Returns: --- + * + * Use: Responds to a line read from the remote RFC931 server. + */ + +static void id_ident(char *p, void *vp) +{ + id *i = vp; + + /* --- Get rid of the connection --- */ + + i->state |= S_USER; + selbuf_disable(&i->id); + close(i->id.reader.fd); + + /* --- Read the information from the returned line --- */ + + if (p) { + ident idbuf; + ident_parse(p, &idbuf); + if (idbuf.type == ident_userid) + str_sanitize(i->user, idbuf.u.userid.user, sizeof(i->user)); + } + + /* --- Maybe finish off this identification --- */ + + if (i->state & S_HOST) + id_done(i); +} + +/* --- @id_conn@ --- * + * + * Arguments: @int fd@ = file descriptor connected + * @void *vp@ = pointer to identification block + * + * Returns: --- + * + * Use: Responds to a completed connection to the remote RFC931 + * server. + */ + +static void id_conn(int fd, void *vp) +{ + id *i = vp; + + if (fd == -1) { + i->state |= S_USER | S_UCONN; + if (i->state & S_HOST) + id_done(i); + } else { + dstr d = DSTR_INIT; + dstr_putf(&d, "%u, %u\n", + ntohs(i->q.rsin.sin_port), ntohs(i->q.lsin.sin_port)); + write(fd, d.buf, d.len); + dstr_destroy(&d); + i->state |= S_UCONN; + selbuf_init(&i->id, sel, fd, id_ident, i); + } +} + +/* --- @id_timer@ --- * + * + * Arguments: @struct timeval *tv@ = pointer to the current time + * @void *vp@ = pointer to identification block + * + * Returns: --- + * + * Use: Times an identification job out. + */ + +static void id_timer(struct timeval *tv, void *vp) +{ + id *i = vp; + id_done(i); +} + +/* --- @identify@ --- * + * + * Arguments: @const id_req *q@ = pointer to request block + * @void (*func)(void *p)@ = function to call when done + * @void *p@ = argument to pass to function + * + * Returns: --- + * + * Use: Starts a background ident lookup and reverse-resolve job + * which will, eventually, report a message to the system log. + */ + +void identify(const id_req *q, + void (*func)(void */*p*/), void *p) +{ + id *i; + + /* --- Initialize the block with stuff --- */ + + i = xmalloc(sizeof(*i)); + i->q = *q; + i->func = func; + i->p = p; + + str_sanitize(i->host, inet_ntoa(q->rsin.sin_addr), sizeof(i->host)); + strcpy(i->user, ""); + i->state = 0; + i->when = time(0); + + /* --- Set up the connection to the identity server --- */ + + { + int fd; + struct sockaddr_in sin; + + if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) + id_conn(-1, i); + else { + sin.sin_family = AF_INET; + sin.sin_addr = q->lsin.sin_addr; + sin.sin_port = 0; + if (bind(fd, (struct sockaddr *)&sin, sizeof(sin))) { + close(fd); + id_conn(-1, i); + } else { + sin.sin_family = AF_INET; + sin.sin_addr = q->rsin.sin_addr; + sin.sin_port = htons(113); + conn_init(&i->c, sel, fd, + (struct sockaddr *)&sin, sizeof(sin), + id_conn, i); + } + } + } + + /* --- Set up the name resolver --- */ + + bres_resolve(&i->r, q->rsin.sin_addr, id_res, i); + + /* --- Set up the time limiter --- */ + + { + struct timeval tv; + gettimeofday(&tv, 0); + tv.tv_sec += TIMEOUT; + sel_addtimer(sel, &i->t, &tv, id_timer, i); + } +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/identify.h b/identify.h new file mode 100644 index 0000000..c4019b6 --- /dev/null +++ b/identify.h @@ -0,0 +1,81 @@ +/* -*-c-*- + * + * $Id: identify.h,v 1.1 1999/07/01 08:56:23 mdw Exp $ + * + * Identifies and logs the client of a connection + * + * (c) 1999 Mark Wooding + */ + +/*----- 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: identify.h,v $ + * Revision 1.1 1999/07/01 08:56:23 mdw + * Initial revision + * + */ + +#ifndef IDENTIFY_H +#define IDENTIFY_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include +#include + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct id_req { + struct sockaddr_in lsin; /* Local address of connection */ + struct sockaddr_in rsin; /* Remote address of connection */ + const char *desc; /* Description of connection */ + const char *act; /* Action taken by server */ +} id_req; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @identify@ --- * + * + * Arguments: @const id_req *q@ = pointer to request block + * @void (*func)(void *p)@ = function to call when done + * @void *p@ = argument to pass to function + * + * Returns: --- + * + * Use: Starts a background ident lookup and reverse-resolve job + * which will, eventually, report a message to the system log. + */ + +extern void identify(const id_req */*q*/, + void (*/*func*/)(void */*p*/), void */*p*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/listener.c b/listener.c new file mode 100644 index 0000000..aa1b6c8 --- /dev/null +++ b/listener.c @@ -0,0 +1,189 @@ +/* -*-c-*- + * + * $Id: listener.c,v 1.1 1999/07/01 08:56:23 mdw Exp $ + * + * A port forwarding listener + * + * (c) 1999 Mark Wooding + */ + +/*----- 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: listener.c,v $ + * Revision 1.1 1999/07/01 08:56:23 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "acl.h" +#include "forward.h" +#include "fw.h" +#include "identify.h" +#include "listener.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @doclose@ --- * + * + * Arguments: @void *vp@ = pointer to file descriptor to close + * + * Returns: --- + * + * Use: Closes a file descriptor once it's no longer useful. + */ + +static void doclose(void *vp) +{ + int *fd = vp; + close(*fd); + DESTROY(fd); +} + +/* --- @newconn@ --- * + * + * Arguments: @int fd@ = file descriptor which is ready + * @unsigned state@ = state in which file descriptor is + * @void *vp@ = pointer to listener block + * + * Returns: --- + * + * Use: Responds to a new connection. + */ + +static void newconn(int fd, unsigned state, void *vp) +{ + int nfd; + id_req q; + int lsinsz = sizeof(q.lsin), rsinsz = sizeof(q.rsin); + int f; + listener *l = vp; + + /* --- Accept the new connection --- */ + + if ((nfd = accept(fd, (struct sockaddr *)&q.rsin, &rsinsz)) < 0) + return; + if (getsockname(nfd, (struct sockaddr *)&q.lsin, &lsinsz)) { + close(nfd); + return; + } + q.desc = l->desc; + + /* --- Find out whether this connection is allowed --- */ + + if (!acl_check(l->acl, q.rsin.sin_addr)) { + int *fdp = CREATE(int); + *fdp = nfd; + q.act = "refused"; + identify(&q, doclose, fdp); + return; + } + + /* --- Set the socket nonblocking --- */ + + if ((f = fcntl(nfd, F_GETFL)) >= 0) + fcntl(nfd, F_SETFL, f | O_NONBLOCK); + + /* --- Open a new forwarding context for the connection --- */ + + q.act = "accepted"; + forward(nfd, &l->sin, &q); +} + +/* --- @listener_dump@ --- * + * + * Arguments: @listener *l@ = pointer to listener block + * @FILE *fp@ = stream to dump on + * + * Returns: --- + * + * Use: Dumps a listener to an output stream. + */ + +void listener_dump(listener *l, FILE *fp) +{ + struct sockaddr_in sin; + int sinsz = sizeof(sin); + + getsockname(l->rd.fd, (struct sockaddr *)&sin, &sinsz); + fprintf(fp, "forward port %u to ", ntohs(sin.sin_port)); + fputs(inet_ntoa(l->sin.sin_addr), fp); + fprintf(fp, ":%u; acl:\n", ntohs(l->sin.sin_port)); + if (l->acl) + acl_dump(l->acl, fp); +} + +/* --- @listener_add@ --- * + * + * Arguments: @int fd@ = created listening socket + * @unsigned sp@ = source port number + * @struct sockaddr_in *sin@ = pointer to destination address + * + * Returns: The address of the new listener. + * + * Use: Adds a forwarding listener. + */ + +listener *listener_add(int fd, unsigned sp, struct sockaddr_in *sin) +{ + int f; + listener *l = CREATE(listener); + dstr d = DSTR_INIT; + + l->sin = *sin; + l->acl = 0; + f = fcntl(fd, F_GETFL); + if (f != -1) + fcntl(fd, F_SETFL, f | O_NONBLOCK); + sel_initfile(sel, &l->rd, fd, SEL_READ, newconn, l); + sel_addfile(&l->rd); + dstr_putf(&d, "fw %u -> %s:%u", + ntohs(sp), inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); + l->desc = xstrdup(d.buf); + dstr_destroy(&d); + return (l); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/listener.h b/listener.h new file mode 100644 index 0000000..40d27ef --- /dev/null +++ b/listener.h @@ -0,0 +1,102 @@ +/* -*-c-*- + * + * $Id: listener.h,v 1.1 1999/07/01 08:56:23 mdw Exp $ + * + * A port forwarding listener + * + * (c) 1999 Mark Wooding + */ + +/*----- 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: listener.h,v $ + * Revision 1.1 1999/07/01 08:56:23 mdw + * Initial revision + * + */ + +#ifndef LISTENER_H +#define LISTENER_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include +#include + +#include + +#ifndef ACL_H +# include "acl.h" +#endif + +/*----- Data structures ---------------------------------------------------*/ + +/* --- A listener object --- */ + +typedef struct listener { + sel_file rd; /* Read selector for new connects */ + struct sockaddr_in sin; /* Destination address */ + const char *desc; /* Description of forwarder */ + acl_entry *acl; /* Access controls */ +} listener; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @listener_dump@ --- * + * + * Arguments: @listener *l@ = pointer to listener block + * @FILE *fp@ = stream to dump on + * + * Returns: --- + * + * Use: Dumps a listener to an output stream. + */ + +extern void listener_dump(listener */*l*/, FILE */*fp*/); + +/* --- @listener_add@ --- * + * + * Arguments: @int fd@ = created listening socket + * @unsigned sp@ = source port number + * @struct sockaddr_in *sin@ = pointer to destination address + * + * Returns: The address of the new listener. + * + * Use: Adds a forwarding listener. + */ + +extern listener *listener_add(int /*fd*/, + unsigned /*sp*/, struct sockaddr_in */*sin*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/scan.c b/scan.c new file mode 100644 index 0000000..808ef8f --- /dev/null +++ b/scan.c @@ -0,0 +1,126 @@ +/* -*-c-*- + * + * $Id: scan.c,v 1.1 1999/07/01 08:56:23 mdw Exp $ + * + * Character scanners + * + * (c) 1999 Mark Wooding + */ + +/*----- 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: scan.c,v $ + * Revision 1.1 1999/07/01 08:56:23 mdw + * Initial revision + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "config.h" + +#include +#include +#include + +#include + +#include "scan.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- Generic scanner setup --- */ + +static void scan_init(scanner *sc, scan_ops *ops, const char *src) +{ + sc->ops = ops; + sc->line = 1; + sc->src = src; + dstr_create(&sc->d); +} + +/* --- @argv@ scanner --- */ + +static int argv_scan(void *p) +{ + scan_argvctx *c = p; + int ch; + + if (c->ch != EOF) { + ch = c->ch; + c->ch = EOF; + } else if (*c->p) + ch = *c->p++; + else if (*c->pp) { + c->p = *c->pp++; + ch = '\n'; + } else + ch = EOF; + + return (ch); +} + +static void argv_unscan(int ch, void *p) +{ + scan_argvctx *c = p; + c->ch = ch; +} + +void scan_argvinit(scan_argvctx *c, char **pp) +{ + static struct scan_ops ops = { argv_scan, argv_unscan }; + c->p = *pp++; + c->pp = pp; + c->ch = EOF; + scan_init(&c->sc, &ops, ""); +} + +/* --- File scanner --- */ + +static int file_scan(void *p) +{ + scan_filectx *c = p; + return (getc(c->fp)); +} + +static void file_unscan(int ch, void *p) +{ + scan_filectx *c = p; + ungetc(ch, c->fp); +} + +void scan_fileinit(scan_filectx *c, FILE *fp, const char *file) +{ + static struct scan_ops ops = { file_scan, file_unscan }; + c->fp = fp; + scan_init(&c->sc, &ops, file); +} + +/* --- Miscellaneous functions --- */ + +void scan_destroy(void *p) +{ + scanner *sc = p; + dstr_destroy(&sc->d); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/scan.h b/scan.h new file mode 100644 index 0000000..f08fe01 --- /dev/null +++ b/scan.h @@ -0,0 +1,96 @@ +/* -*-c-*- + * + * $Id: scan.h,v 1.1 1999/07/01 08:56:23 mdw Exp $ + * + * Character scanners + * + * (c) 1999 Mark Wooding + */ + +/*----- 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. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: scan.h,v $ + * Revision 1.1 1999/07/01 08:56:23 mdw + * Initial revision + * + */ + +#ifndef SCAN_H +#define SCAN_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include + +/*----- Data structures ---------------------------------------------------*/ + +/* --- A configuration scanner --- */ + +typedef struct scan_ops { + int (*scan)(void */*p*/); /* Character scan function */ + void (*unscan)(int /*ch*/, void */*p*/); /* Character pushback function */ +} scan_ops; + +typedef struct scanner { + scan_ops *ops; /* Pointer to scanner operations */ + const char *src; /* Name of this source */ + int line; /* Current line number */ + int t; /* Token type */ + dstr d; /* Current token value */ +} scanner; + +/*----- Scanners provided -------------------------------------------------*/ + +/* --- The @argv@ scanner --- */ + +typedef struct scan_argvctx { + scanner sc; /* Scanner base structure */ + char *p; /* Pointer to next character */ + char **pp; /* Pointer to next string */ + int ch; /* Pushback character */ +} scan_argvctx; + +extern void scan_argvinit(scan_argvctx */*c*/, char **/*pp*/); + +/* --- The file scanner --- */ + +typedef struct scan_filectx { + scanner sc; /* Scanner base structure */ + FILE *fp; /* Stream pointer */ +} scan_filectx; + +extern void scan_fileinit(scan_filectx */*c*/, + FILE */*fp*/, const char */*file*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/setup b/setup new file mode 100755 index 0000000..048c599 --- /dev/null +++ b/setup @@ -0,0 +1,10 @@ +#! /bin/sh + +set -e +mklinks +ln -s ../mLib . +mkaclocal +autoconf +autoheader +automake +mkdir build -- 2.11.0