From: mdw Date: Thu, 1 Jul 1999 08:56:23 +0000 (+0000) Subject: Initial revision X-Git-Tag: 1.0.0~6 X-Git-Url: https://git.distorted.org.uk/~mdw/fwd/commitdiff_plain/e82f7154f65062d9ac8b9677862774498b331058 Initial revision --- e82f7154f65062d9ac8b9677862774498b331058 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