commit 57f4f99a0bea2857f400ce559024b2e36dd805cf Author: Mario Fetka Date: Thu Mar 30 14:48:05 2017 +0200 Imported Upstream version 0.62 diff --git a/.CHANGES.swp b/.CHANGES.swp new file mode 100644 index 0000000..93350d9 Binary files /dev/null and b/.CHANGES.swp differ diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..c4f5a0c --- /dev/null +++ b/CHANGES @@ -0,0 +1,55 @@ +Version 0.1: original version. + +Version 0.2: fixed bug when several reads are necessary +on one end or the other before a write flushes them. +Fixed bug which threw away data not yet sent to the +other side on close, when running under Linux. Fixed +associated bugs that probably affected other operating +systems as well. Fixed bug causing long, perhaps +indefinite pauses when a possible connection to a +server socket went away before the accept() call, +resulting in a blocking call. + +Version 0.3: fixed additional bugs relating to +the code previously used only by non-Linux OSes. +This should fix problems such as connections not +going away when they should or connections being +mysteriously closed. Most of that code is now used by +Linux also, so it is likely that rinetd is much closer +to bug-free on non-Linux platforms. Of course, I don't +actually have any to play with it on. + +Version 0.4: added support for kill -1 (SIGHUP) +and specification of service names instead of +port numbers. Removed calls to realloc(), replacing +them with code that should fail gracefully without +crashing the program or breaking existing connections +when another application is hogging memory. + +Version 0.5: added logging in both tab-delimited +and web-server-style formats. No longer exits if +an individual configuration file line generates +an error. Added allow and deny rules. Added +-c command line option to specify a configuration file. + +Version 0.51: fixed failure to check for an open +log file before writing log entries. + +Version 0.52: documentation added regarding the +ability to bind to all IP addresses, if desired, +using the special address 0.0.0.0. + +Version 0.6: ported to Win32. Various compatibility +fixes were made and some minor oversights without +functional consequences were corrected. + +Version 0.61: fixed a bug in 0.6 which completely +broke rinetd under Linux. Oops. + +Version 0.62: fixed a potential buffer overrun; +prior versions failed to reallocate one of the +arrays correctly when reallocating memory to +accommodate more connections. Thanks to +Sam Hocevar. + + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d598b18 --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +CFLAGS=-DLINUX -g + +rinetd: rinetd.o match.o + gcc rinetd.o match.o -o rinetd + +install: rinetd + install -m 700 rinetd /usr/sbin + install -m 644 rinetd.8 /usr/man/man8 + diff --git a/README b/README new file mode 100644 index 0000000..73a896b --- /dev/null +++ b/README @@ -0,0 +1,22 @@ +rinetd version 0.62, by Thomas Boutell. Released under +the terms of the GNU General Public License, version 2 or later. + +This program is used to efficiently redirect connections +from one IP address/port combination to another. It is +useful when operating virtual servers, firewalls +and the like. + +A binary for 32-bit Windows (95, 98, NT) is included (see the +file rinetd.exe). Windows 3.1 is not supported. + +To build under Unix, check the Makefile for platform- +specific details and then type make. To install, type +"make install" as root. + +To build under Windows, use the provided project +files with Microsoft Visual C++. Windows 3.1 is not supported. + +For documentation run "make install", then type +"man rinetd" for details. Or, read index.html in +your browser. + diff --git a/getopt.c b/getopt.c new file mode 100755 index 0000000..9b7eebc --- /dev/null +++ b/getopt.c @@ -0,0 +1,756 @@ +/* THIS IS HERE FOR WIN32's BENEFIT ONLY. */ + +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 + Free Software Foundation, Inc. + + This program 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, or (at your option) any + later version. + + This program 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 this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef __STDC__ +# ifndef const +# define const +# endif +#endif + +/* This tells Alpha OSF/1 not to define a getopt prototype in . */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include +#endif /* GNU C library. */ + +/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a + long-named option. Because this is not POSIX.2 compliant, it is + being phased out. */ +/* #define GETOPT_COMPAT */ + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = 0; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* XXX 1003.2 says this must be 1 before any call. */ +int optind = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +#define BAD_OPTION '\0' +int optopt = BAD_OPTION; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include +#define my_index strchr +#define my_strlen strlen +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +#if __STDC__ || defined(PROTO) +extern char *getenv(const char *name); +extern int strcmp (const char *s1, const char *s2); +extern int strncmp(const char *s1, const char *s2, int n); + +static int my_strlen(const char *s); +static char *my_index (const char *str, int chr); +#else +extern char *getenv (); +#endif + +static int +my_strlen (str) + const char *str; +{ + int n = 0; + while (*str++) + n++; + return n; +} + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +#endif /* GNU C library. */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. + + To perform the swap, we first reverse the order of all elements. So + all options now come before all non options, but they are in the + wrong order. So we put back the options and non options in original + order by reversing them again. For example: + original input: a b c -x -y + reverse all: -y -x c b a + reverse options: -x -y c b a + reverse non options: -x -y a b c +*/ + +#if __STDC__ || defined(PROTO) +static void exchange (char **argv); +#endif + +static void +exchange (argv) + char **argv; +{ + char *temp, **first, **last; + + /* Reverse all the elements [first_nonopt, optind) */ + first = &argv[first_nonopt]; + last = &argv[optind-1]; + while (first < last) { + temp = *first; *first = *last; *last = temp; first++; last--; + } + /* Put back the options in order */ + first = &argv[first_nonopt]; + first_nonopt += (optind - last_nonopt); + last = &argv[first_nonopt - 1]; + while (first < last) { + temp = *first; *first = *last; *last = temp; first++; last--; + } + + /* Put back the non options in order */ + first = &argv[first_nonopt]; + last_nonopt = optind; + last = &argv[last_nonopt-1]; + while (first < last) { + temp = *first; *first = *last; *last = temp; first++; last--; + } +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return BAD_OPTION after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return BAD_OPTION. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + int option_index; + + optarg = 0; + + /* Initialize the internal data when the first call is made. + Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + if (optind == 0) + { + first_nonopt = last_nonopt = optind = 1; + + nextchar = NULL; + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (getenv ("POSIXLY_CORRECT") != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + } + + if (nextchar == NULL || *nextchar == '\0') + { + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Now skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + optind++; + last_nonopt = optind; + } + + /* Special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if ((argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Start decoding its characters. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + if (longopts != NULL + && ((argv[optind][0] == '-' + && (argv[optind][1] == '-' || long_only)) +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + )) + { + const struct option *p; + char *s = nextchar; + int exact = 0; + int ambig = 0; + const struct option *pfound = NULL; + int indfound = 0; + + while (*s && *s != '=') + s++; + + /* Test all options for either exact match or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; + p++, option_index++) + if (!strncmp (p->name, nextchar, s - nextchar)) + { + if (s - nextchar == my_strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += my_strlen (nextchar); + optind++; + return BAD_OPTION; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*s) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = s + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + "%s: option `--%s' doesn't allow an argument\n", + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + "%s: option `%c%s' doesn't allow an argument\n", + argv[0], argv[optind - 1][0], pfound->name); + } + nextchar += my_strlen (nextchar); + return BAD_OPTION; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += my_strlen (nextchar); + return optstring[0] == ':' ? ':' : BAD_OPTION; + } + } + nextchar += my_strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, "%s: unrecognized option `--%s'\n", + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + return BAD_OPTION; + } + } + + /* Look at and handle the next option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { +#if 0 + if (c < 040 || c >= 0177) + fprintf (stderr, "%s: unrecognized option, character code 0%o\n", + argv[0], c); + else + fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); +#endif + } + optopt = c; + return BAD_OPTION; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = 0; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { +#if 0 + fprintf (stderr, "%s: option `-%c' requires an argument\n", + argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: option requires an argument -- %c\n", + argv[0], c); +#endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = BAD_OPTION; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case BAD_OPTION: + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ diff --git a/getopt.h b/getopt.h new file mode 100755 index 0000000..c22d37f --- /dev/null +++ b/getopt.h @@ -0,0 +1,129 @@ +/* THIS IS HERE FOR WIN32's BENEFIT ONLY. */ + +/* Declarations for getopt. + Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. + + This program 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, or (at your option) any + later version. + + This program 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 this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if __STDC__ || defined(PROTO) +#if defined(__GNU_LIBRARY__) +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int argc, char *const *argv, const char *shortopts); +#endif /* not __GNU_LIBRARY__ */ +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ +extern int getopt (); +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +#endif /* not __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H */ diff --git a/index.html b/index.html new file mode 100644 index 0000000..5a58675 --- /dev/null +++ b/index.html @@ -0,0 +1,241 @@ + + +RINETD(8) + + + + + + +
RINETD(8) +Unix System Manager's Manual +RINETD(8) +
+

+NAME +

+rinetd -- internet ``redirection server'' +

+SYNOPSIS +

+/usr/sbin/rinetd +

+VERSION +

+Version 0.62, 04/13/2003. Version 0.62 corrects a potential +buffer overflow when reallocating memory to accommodate more +connections. Upgrading is strongly recommended. +

+WHERE TO GET +

+For Linux: +By +anonymous FTP from ftp.boutell.com in the subdirectory +boutell/rinetd as the file rinetd.tar.gz. +

+For Windows 95/98/NT: +By +anonymous FTP from ftp.boutell.com in the subdirectory +boutell/rinetd as the file rinetd.zip. +

+DESCRIPTION +

+Redirects TCP connections from one IP address and port to another. rinetd +is a single-process server which handles any number of connections to +the address/port pairs specified in the file /etc/rinetd.conf. +Since rinetd runs as a single process using nonblocking I/O, it is +able to redirect a large number of connections without a severe +impact on the machine. This makes it practical to run TCP services +on machines inside an IP masquerading firewall. rinetd does not +redirect FTP, because FTP requires more than one socket. +

+rinetd is typically launched at boot time, using the following syntax: +

+/usr/sbin/rinetd +

+The configuration file is found in the file +/etc/rinetd.conf, unless +another file is specified using the -c command line option. +

+FORWARDING RULES +

+Most entries in the configuration file are forwarding rules. The +format of a forwarding rule is as follows: +

+bindaddress bindport connectaddress connectport
+
+For example: +
+206.125.69.81 80 10.1.1.2 80
+
+Would redirect all connections to port 80 of the "real" IP address +206.125.69.81, which could be a virtual interface, through +rinetd to port 80 of the address 10.1.1.2, which would typically +be a machine on the inside of a firewall which has no +direct routing to the outside world. +

+Although responding on individual interfaces rather than on all +interfaces is one of rinetd's primary features, sometimes it is +preferable to respond on all IP addresses that belong to the server. +In this situation, the special IP address 0.0.0.0 +can be used. For example: +

+0.0.0.0 23 10.1.1.2 23
+
+Would redirect all connections to port 23, for all IP addresses +assigned to the server. This is the default behavior for most +other programs. +

+Service names can be specified instead of port numbers. On most systems, +service names are defined in the file /etc/services. +

+Both IP addresses and hostnames are accepted for +bindaddress and connectaddress. +

+ALLOW AND DENY RULES +

+Configuration files can also contain allow and deny rules. +

+Allow rules which appear before the first forwarding rule are +applied globally: if at least one global allow rule exists, +and the address of a new connection does not +satisfy at least one of the global allow rules, that connection +is immediately rejected, regardless of any other rules. +

+Allow rules which appear after a specific forwarding rule apply +to that forwarding rule only. If at least one allow rule +exists for a particular forwarding rule, and the address of a new +connection does not satisfy at least one of the allow rules +for that forwarding rule, that connection is immediately +rejected, regardless of any other rules. +

+Deny rules which appear before the first forwarding rule are +applied globally: if the address of a new connection satisfies +any of the global allow rules, that connection +is immediately rejected, regardless of any other rules. +

+Deny rules which appear after a specific forwarding rule apply +to that forwarding rule only. If the address of a new +connection satisfies any of the deny rules for that forwarding rule, +that connection is immediately rejected, regardless of any other rules. +

+The format of an allow rule is as follows: +

+allow pattern
+
+Patterns can contain the following characters: 0, 1, 2, 3, 4, 5, +6, 7, 8, 9, . (period), ?, and *. The ? wildcard matches any one +character. The * wildcard matches any number of characters, including +zero. +

+For example: +

+

+allow 206.125.69.*
+
+This allow rule matches all IP addresses in the 206.125.69 class C domain. +

+Host names are NOT permitted in allow and deny rules. The performance +cost of looking up IP addresses to find their corresponding names +is prohibitive. Since rinetd is a single process server, all other +connections would be forced to pause during the address lookup. +

+LOGGING +

+rinetd is able to produce a log file in either of two formats: +tab-delimited and web server-style "common log format." +

+By default, rinetd does not produce a log file. To activate logging, add +the following line to the configuration file: +

+logfile log-file-location
+
+Example: +
+logfile /var/log/rinetd.log
+
+By default, rinetd logs in a simple tab-delimited format containing +the following information: +

+Date and time
+Client address
+ +Listening host +
+Listening port +
+Forwarded-to host +
+Forwarded-to port +
+Bytes received from client +
+Bytes sent to client +
+Result message +

+To activate web server-style "common log format" logging, +add the following line to the configuration file: +

+logcommon
+
+

+COMMAND LINE OPTIONS +

+The -c command line option is used to specify an alternate +configuration file. +

+The -h command line option produces a short help message. +

+The -v command line option displays the version number. +

+REINITIALIZING RINETD +

+The kill -1 signal (SIGHUP) can be used to cause rinetd +to reload its configuration file without interrupting existing +connections. Under Linux(tm) the process id +is saved in the file /var/run/rinetd.pid +to facilitate the kill -HUP. An alternate +file name can be provided by using the pidlogfile +configuration file option. +

+BUGS +

+The server redirected to is not able to identify the host the +client really came from. This cannot be corrected; however, +the log produced by rinetd provides a way to obtain this +information. Under Unix, sockets would theoretically lose data when closed +with SO_LINGER turned off, but in Linux this is not the case +(kernel source comments support this belief on my part). On non-Linux Unix +platforms, alternate code which uses a different trick to work around +blocking close() is provided, but this code is untested. +

+The logging is inadequate. The duration of the connection should be logged. +

+LICENSE +

+Copyright (c) 1997, 1998, 1999, +Thomas Boutell and +Boutell.Com, Inc. +This software is released for free use under the terms of +the GNU General Public License, version 2 or higher. +

+CONTACT INFORMATION +

+See the rinetd web page +for the latest release. +Thomas Boutell can be reached by email: +boutell@boutell.com +

+THANKS +

+Thanks are due to Bill Davidsen, Libor Pechachek, Sascha Ziemann, +Joel S. Noble, the Apache Group, and many others who have contributed +advice, encouragement and/or source code to this and other open +software projects. + + diff --git a/match.c b/match.c new file mode 100644 index 0000000..bfb40a2 --- /dev/null +++ b/match.c @@ -0,0 +1,195 @@ +#include +#include +#include "match.h" + +int match(char *sorig, char *p) +{ + return matchBody(sorig, p, 0); +} + +int matchNoCase(char *sorig, char *p) +{ + return matchBody(sorig, p, 1); +} + +#define CASE(x) (nocase ? tolower(x) : (x)) + +int matchBody(char *sorig, char *p, int nocase) +{ + static int dummy = 0; + /* Algorithm: + + Word separator: *. End-of-string + is considered to be a word constituent. + ? is similarly considered to be a specialized + word constituent. + + Match the word to the current position in s. + Empty words automatically succeed. + + If the word matches s, and the word + and s contain end-of-string at that + point, return success. + + \ escapes the next character, including \ itself (6.0). + + For each *: + + Find the next occurrence of the next word + and advance beyond it in both p and s. + If the next word ends in end-of-string + and is found successfully, return success, + otherwise advance past the *. + + If the word is not found, return failure. + + If the next word is empty, advance past the *. + + Behavior of ?: advance one character in s and p. + + Addendum: consider the | character to be a logical OR + separating distinct patterns. */ + + char *s = sorig; + int escaped = 0; + if (strstr(p, "WS-0000")) { + if (strstr(s, "ws_ftp_pro.html")) { + dummy = 1; + } + } + while (1) { + char *word; + int wordLen; + int wordPos; + if (escaped) { + /* This is like the default case, + except that | doesn't end the pattern. */ + escaped = 0; + if ((*s == '\0') && (*p == '\0')) { + return 1; + } + if (CASE(*p) != CASE(*s)) { + goto nextPattern; + } + p++; + s++; + continue; + } + switch(*p) { + case '\\': + /* Escape the next character. */ + escaped = 1; + p++; + continue; + case '*': + /* Find the next occurrence of the next word + and advance beyond it in both p and s. + If the next word ends in end-of-string + and is found successfully, return success, + otherwise advance past the *. + + If the word is not found, return failure. + + If the next word is empty, advance. */ + p++; + wordLen = 0; + word = p; + while (1) { + if ((*p) == '*') { + break; + } + wordLen++; + if ((*p == '\0') || (*p == '|')) { + break; + } + p++; + } + wordPos = 0; + while (1) { + if (wordPos == wordLen) { + if ((*p == '\0') || (*p == '|')) { + return 1; + } + break; + } + if ((((CASE(*s)) == CASE(word[wordPos])) || + ((*s == '\0') && + (word[wordPos] == '|'))) || + (((*s != '\0') && (*s != '|')) && + (word[wordPos] == '?'))) + { + wordPos++; + s++; + } else { + s -= wordPos; + if (!(*s)) { + goto nextPattern; + } + s++; + wordPos = 0; + } + } + break; + case '?': + p++; + s++; + break; + default: + if ((*s == '\0') && ((*p == '\0') || + (*p == '|'))) { + return 1; + } + if (CASE(*p) != CASE(*s)) { + goto nextPattern; + } + p++; + s++; + break; + } + continue; +nextPattern: + while (1) { + if (*p == '\0') { + return 0; + } + if (*p == '|') { + p++; + s = sorig; + break; + } + p++; + } + } +} + +#ifdef TEST_MATCH + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + char s[1024]; + if (argc != 2) { + fprintf(stderr, "Usage: match pattern\n"); + return 1; + } + while (1) { + if (!fgets(s, sizeof(s), stdin)) { + break; + } + while (isspace(s[strlen(s) - 1])) { + s[strlen(s) - 1] = '\0'; + } + printf("%s --> %s\n", s, argv[1]); + if (match(s, argv[1])) { + printf("Match\n"); + } else { + printf("No Match\n"); + } + } +} + +#endif /* TEST_MATCH */ + diff --git a/match.h b/match.h new file mode 100644 index 0000000..a38990a --- /dev/null +++ b/match.h @@ -0,0 +1,9 @@ +#ifndef MATCH_H +#define MATCH_H 1 + +extern int match(char *s, char *p); +extern int matchNoCase(char *s, char *p); +extern int matchBody(char *s, char *p, int nocase); + +#endif /* MATCH_H */ + diff --git a/rinetd.8 b/rinetd.8 new file mode 100644 index 0000000..3b3f329 --- /dev/null +++ b/rinetd.8 @@ -0,0 +1,190 @@ +.\" Copyright (c) 1997, 1998, 1999, Thomas Boutell and Boutell.Com, Inc. +.\" This software is released for free use under the terms of +.\" the GNU Public License, version 2 or higher. +.\" +.Dd February 18, 1999 +.Dt RINETD 8 +.Os LINUX +.Sh NAME +.Nm rinetd +.Nd internet +.Dq redirection server +.Sh SYNOPSIS +.Nm /usr/sbin/rinetd +.Sh VERSION +Version 0.62, 04/14/2003. +.Sh DESCRIPTION +.Nm rinetd +redirects TCP connections from one IP address and port to another. rinetd +is a single-process server which handles any number of connections to +the address/port pairs specified in the file /etc/rinetd.conf. +Since rinetd runs as a single process using nonblocking I/O, it is +able to redirect a large number of connections without a severe +impact on the machine. This makes it practical to run TCP services +on machines inside an IP masquerading firewall. rinetd does not +redirect FTP, because FTP requires more than one socket. +.Pp +rinetd is typically launched at boot time, using the following syntax: +.Pp +/usr/sbin/rinetd +.Pp +The configuration file is found in the file /etc/rinetd.conf, unless +another file is specified using the -c command line option. +.Sh FORWARDING RULES +Most entries in the configuration file are forwarding rules. The +format of a forwarding rule is as follows: +.Pp +bindaddress bindport connectaddress connectport +.Pp +For example: +.Pp +206.125.69.81 80 10.1.1.2 80 +.Pp +Would redirect all connections to port 80 of the "real" IP address +206.125.69.81, which could be a virtual interface, through +rinetd to port 80 of the address 10.1.1.2, which would typically +be a machine on the inside of a firewall which has no +direct routing to the outside world. +.Pp +Although responding on individual interfaces rather than on all +interfaces is one of rinetd's primary features, sometimes it is +preferable to respond on all IP addresses that belong to the server. +In this situation, the special IP address 0.0.0.0 +can be used. For example: +.Pp +0.0.0.0 23 10.1.1.2 23 +.Pp +Would redirect all connections to port 23, for all IP addresses +assigned to the server. This is the default behavior for most +other programs. +.Pp +Service names can be specified instead of port numbers. On most systems, +service names are defined in the file /etc/services. +.Pp +Both IP addresses and hostnames are accepted for +bindaddress and connectaddress. +.Pp +.Sh ALLOW AND DENY RULES +Configuration files can also contain allow and deny rules. +.Pp +Allow rules which appear before the first forwarding rule are +applied globally: if at least one global allow rule exists, +and the address of a new connection does not +satisfy at least one of the global allow rules, that connection +is immediately rejected, regardless of any other rules. +.Pp +Allow rules which appear after a specific forwarding rule apply +to that forwarding rule only. If at least one allow rule +exists for a particular forwarding rule, and the address of a new +connection does not satisfy at least one of the allow rules +for that forwarding rule, that connection is immediately +rejected, regardless of any other rules. +.Pp +Deny rules which appear before the first forwarding rule are +applied globally: if the address of a new connection satisfies +any of the global allow rules, that connection +is immediately rejected, regardless of any other rules. +.Pp +Deny rules which appear after a specific forwarding rule apply +to that forwarding rule only. If the address of a new +connection satisfies any of the deny rules for that forwarding rule, +that connection is immediately rejected, regardless of any other rules. +.Pp +The format of an allow rule is as follows: +.Pp +allow pattern +.Pp +Patterns can contain the following characters: 0, 1, 2, 3, 4, 5, +6, 7, 8, 9, . (period), ?, and *. The ? wildcard matches any one +character. The * wildcard matches any number of characters, including +zero. +.Pp +For example: +.Pp +allow 206.125.69.* +.Pp +This allow rule matches all IP addresses in the 206.125.69 class C domain. +.Pp +Host names are NOT permitted in allow and deny rules. The performance +cost of looking up IP addresses to find their corresponding names +is prohibitive. Since rinetd is a single process server, all other +connections would be forced to pause during the address lookup. +.Pp +.Sh LOGGING +rinetd is able to produce a log file in either of two formats: +tab-delimited and web server-style "common log format." +.Pp +By default, rinetd does not produce a log file. To activate logging, add +the following line to the configuration file: +.Pp +logfile log-file-location +.Pp +Example: logfile /var/log/rinetd.log +.Pp +By default, rinetd logs in a simple tab-delimited format containing +the following information: +.Pp +Date and time +.Pp +Client address +.Pp +Listening host +.Pp +Listening port +.Pp +Forwarded-to host +.Pp +Forwarded-to port +.Pp +Bytes received from client +.Pp +Bytes sent to client +.Pp +Result message +.Pp +To activate web server-style "common log format" logging, +add the following line to the configuration file: +.Pp +logcommon +.Sh COMMAND LINE OPTIONS +The -c command line option is used to specify an alternate +configuration file. +.Pp +The -h command line option produces a short help message. +.Pp +The -v command line option displays the version number. +.Sh REINITIALIZING RINETD +The kill -1 signal (SIGHUP) can be used to cause rinetd +to reload its configuration file without interrupting existing +connections. +Under Linux\(tm the process id is saved in the file \fI/var/run/rinetd.pid\fR +to facilitate the kill -HUP. An alternate +filename can be provided by using the pidlogfile +configuration file option. + +.Sh LIMITATIONS +rinetd redirects TCP connections only. There is +no support for UDP. rinetd only redirects protocols which +use a single TCP socket. This rules out FTP. +.Sh BUGS +The server redirected to is not able to identify the host the +client really came from. This cannot be corrected; however, +the log produced by rinetd provides a way to obtain this +information. Under Unix, Sockets would theoretically lose data when closed +with SO_LINGER turned off, but in Linux this is not the case (kernel +source comments support this belief on my part). On non-Linux Unix platforms, +alternate code which uses a different trick to work around blocking close() +is provided, but this code is untested. The logging is inadequate. +The duration of each connection should be logged. +.Sh LICENSE +Copyright (c) 1997, 1998, 1999, Thomas Boutell and Boutell.Com, Inc. +This software is released for free use under the terms of +the GNU Public License, version 2 or higher. NO WARRANTY +IS EXPRESSED OR IMPLIED. USE THIS SOFTWARE AT YOUR OWN RISK. +.Sh CONTACT INFORMATION +See http://www.boutell.com/rinetd/ for the latest release. +Thomas Boutell can be reached by email: boutell@boutell.com +.Sh THANKS +Thanks are due to Bill Davidsen, Libor Pechachek, Sascha Ziemann, the +Apache Group, and many others who have contributed advice +and/or source code to this and other free software projects. diff --git a/rinetd.c b/rinetd.c new file mode 100755 index 0000000..78027d3 --- /dev/null +++ b/rinetd.c @@ -0,0 +1,1565 @@ +#define VERSION "0.62" + +#ifdef WIN32 +#include +#include +#include "getopt.h" +#else +#include +#include +#include +#include +#include +#include +#include +#define INVALID_SOCKET (-1) +#include +#endif /* WIN32 */ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef WIN32 +/* Windows sockets compatibility defines */ +#define INVALID_SOCKET (-1) +#define SOCKET_ERROR (-1) +int closesocket(int s); + +int closesocket(int s) { + return close(s); +} +#define ioctlsocket ioctl +#define MAKEWORD(a, b) +#define WSAStartup(a, b) (0) +#define WSACleanup() +#ifdef __MAC__ +/* The constants for these are a little screwy in the prelinked + MSL GUSI lib and we can't rebuild it, so roll with it */ +#define WSAEWOULDBLOCK EWOULDBLOCK +#define WSAEAGAIN EAGAIN +#define WSAEINPROGRESS EINPROGRESS +#else +#define WSAEWOULDBLOCK EWOULDBLOCK +#define WSAEAGAIN EAGAIN +#define WSAEINPROGRESS EINPROGRESS +#endif /* __MAC__ */ +#define WSAEINTR EINTR +#define SOCKET int +#define GetLastError() (errno) +typedef struct { + int dummy; +} WSADATA; + +void Sleep(long ms); + +void Sleep(long ms) +{ + struct timeval tv; + tv.tv_sec = ms / 1000; + tv.tv_usec = ms * 1000; + select(0, 0, 0, 0, &tv); +} +#else +/* WIN32 doesn't really have WSAEAGAIN */ +#ifndef WSAEAGAIN +#define WSAEAGAIN WSAEWOULDBLOCK +#endif +#endif /* WIN32 */ + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifdef DEBUG +#define PERROR perror +#else +#define PERROR(x) +#endif /* DEBUG */ + +/* We've got to get FIONBIO from somewhere. Try the Solaris location + if it isn't defined yet by the above includes. */ +#ifndef FIONBIO +#include +#endif /* FIONBIO */ + +#include "match.h" + +SOCKET *seFds = 0; +/* In network order, for network purposes */ +struct in_addr *seLocalAddrs = 0; +unsigned short *seLocalPorts = 0; +/* In ASCII and local byte order, for logging purposes */ +char **seFromHosts; +int *seFromPorts; +char **seToHosts; +int *seToPorts; + +/* Offsets into list of allow and deny rules. Any rules + prior to globalAllowRules and globalDenyRules are global rules. */ + +int *seAllowRules = 0; +int *seAllowRulesTotal = 0; +int globalAllowRules = 0; +int *seDenyRules = 0; +int *seDenyRulesTotal = 0; +int globalDenyRules = 0; + +SOCKET *reFds = 0; +SOCKET *loFds = 0; +unsigned char *reAddresses = 0; +int *coInputRPos = 0; +int *coInputWPos = 0; +int *coOutputRPos = 0; +int *coOutputWPos = 0; +int *coClosed = 0; +int *coClosing = 0; +int *reClosed = 0; +int *loClosed = 0; +int *coBytesInput = 0; +int *coBytesOutput = 0; +int *coLog = 0; +int *coSe = 0; +char **coInput = 0; +char **coOutput = 0; +char **allowRules = 0; +char **denyRules = 0; +int *denyRulesFor = 0; +int seTotal = 0; +int coTotal = 0; +int allowRulesTotal = 0; +int denyRulesTotal = 0; +int maxfd = 0; +char *logFileName = 0; +char *pidLogFileName = 0; +int logFormatCommon = 0; +FILE *logFile = 0; + +/* If 'newsize' bytes can be allocated, *data is set to point + to them, the previous data is copied, and 1 is returned. + If 'size' bytes cannot be allocated, *data is UNCHANGED, + and 0 is returned. */ + +#define SAFE_REALLOC(x, y, z) safeRealloc((void **) (x), (y), (z)) + +int safeRealloc(void **data, int oldsize, int newsize); + +/* + se: (se)rver sockets + re: (re)mote sockets + lo: (lo)cal sockets (being redirected to) + co: connections +*/ + +#define bufferSpace 1024 + +void readConfiguration(); + +/* Signal handlers */ +void plumber(int s); +void hup(int s); +void term(int s); + +void initArrays(void); +void RegisterPID(void); + +void selectLoop(void); + +void log(int i, int coSe, int result); + +int getAddress(char *host, struct in_addr *iaddr); + +char *logMessages[] = { + "done-local-closed", + "done-remote-closed", + "accept-failed -", + 0, + "local-socket-failed -", + 0, + "local-bind-failed -", + 0, + "local-connect-failed -", + 0, + "not-allowed", + 0, + "denied", + 0 +}; + +#define logDone 0 +#define logAcceptFailed 2 +#define logLocalSocketFailed 4 +#define logLocalBindFailed 6 +#define logLocalConnectFailed 8 +#define logNotAllowed 10 +#define logDenied 12 + +#define logLocalClosedFirst 0 +#define logRemoteClosedFirst 1 + +/* Option parsing */ + +typedef struct _rinetd_options RinetdOptions; +struct _rinetd_options +{ + char *conf_file; +}; + +RinetdOptions options = { + "/etc/rinetd.conf" +}; + +int readArgs (int argc, + char **argv, + RinetdOptions *options); + +int main(int argc, char *argv[]) +{ + WSADATA wsaData; + int result = WSAStartup(MAKEWORD(1, 1), &wsaData); + if (result != 0) { + fprintf(stderr, "Your computer was not connected " + "to the Internet at the time that " + "this program was launched, or you " + "do not have a 32-bit " + "connection to the Internet."); + exit(1); + } + readArgs(argc, argv, &options); +#ifndef WIN32 +#ifndef DEBUG + if (!fork()) { + if (!fork()) { +#endif /* DEBUG */ + signal(SIGPIPE, plumber); + signal(SIGHUP, hup); +#endif /* WIN32 */ + signal(SIGTERM, term); + initArrays(); + readConfiguration(); + RegisterPID(); + selectLoop(); +#ifndef WIN32 +#ifndef DEBUG + } else { + exit(0); + } + } else { + exit(0); + } +#endif /* DEBUG */ +#endif /* WIN32 */ + return 0; +} + +int getConfLine(FILE *in, char *line, int space, int *lnum); + +int patternBad(char *pattern); + +void readConfiguration(void) +{ + FILE *in; + char line[16384]; + int lnum = 0; + int i; + int ai; + int di; + if (seFds) { + /* Close existing server sockets. */ + for (i = 0; (i < seTotal); i++) { + if (seFds[i] != -1) { + closesocket(seFds[i]); + free(seFromHosts[i]); + free(seToHosts[i]); + } + } + /* Free memory associated with previous set. */ + free(seFds); + free(seLocalAddrs); + free(seLocalPorts); + free(seFromHosts); + free(seFromPorts); + free(seToHosts); + free(seToPorts); + free(seAllowRules); + free(seDenyRules); + free(seAllowRulesTotal); + free(seDenyRulesTotal); + } + seTotal = 0; + if (allowRules) { + /* Forget existing allow rules. */ + for (i = 0; (i < allowRulesTotal); i++) { + free(allowRules[i]); + } + /* Free memory associated with previous set. */ + free(allowRules); + globalAllowRules = 0; + } + allowRulesTotal = 0; + if (denyRules) { + /* Forget existing deny rules. */ + for (i = 0; (i < denyRulesTotal); i++) { + free(denyRules[i]); + } + /* Free memory associated with previous set. */ + free(denyRules); + globalDenyRules = 0; + } + denyRulesTotal = 0; + if (logFileName) { + free(logFileName); + logFileName = 0; + } + if (pidLogFileName) { + free(pidLogFileName); + pidLogFileName = 0; + } + /* 1. Count the non-comment lines of each type and + allocate space for the data. */ + in = fopen(options.conf_file, "r"); + if (!in) { + fprintf(stderr, "rinetd: can't open %s\n", options.conf_file); + exit(1); + } + while (1) { + char *t = 0; + if (!getConfLine(in, line, sizeof(line), &lnum)) { + break; + } + t = strtok(line, " \t\r\n"); + if (!strcmp(t, "logfile")) { + continue; + } else if (!strcmp(t, "pidlogfile")) { + continue; + } else if (!strcmp(t, "logcommon")) { + continue; + } else if (!strcmp(t, "allow")) { + allowRulesTotal++; + } else if (!strcmp(t, "deny")) { + denyRulesTotal++; + } else { + /* A regular forwarding rule */ + seTotal++; + } + } + fclose(in); + seFds = (SOCKET *) malloc(sizeof(int) * seTotal); + if (!seFds) { + goto lowMemory; + } + seLocalAddrs = (struct in_addr *) malloc(sizeof(struct in_addr) * + seTotal); + if (!seLocalAddrs) { + goto lowMemory; + } + seLocalPorts = (unsigned short *) + malloc(sizeof(unsigned short) * seTotal); + if (!seLocalPorts) { + goto lowMemory; + } + seFromHosts = (char **) + malloc(sizeof(char *) * seTotal); + if (!seFromHosts) { + goto lowMemory; + } + seFromPorts = (int *) + malloc(sizeof(int) * seTotal); + if (!seFromPorts) { + goto lowMemory; + } + seToHosts = (char **) + malloc(sizeof(char *) * seTotal); + if (!seToHosts) { + goto lowMemory; + } + seToPorts = (int *) + malloc(sizeof(int) * seTotal); + if (!seToPorts) { + goto lowMemory; + } + allowRules = (char **) + malloc(sizeof(char *) * allowRulesTotal); + if (!allowRules) { + goto lowMemory; + } + denyRules = (char **) + malloc(sizeof(char *) * denyRulesTotal); + if (!denyRules) { + goto lowMemory; + } + seAllowRules = (int *) + malloc(sizeof(int) * seTotal); + if (!seAllowRules) { + goto lowMemory; + } + seAllowRulesTotal = (int *) + malloc(sizeof(int) * seTotal); + if (!seAllowRulesTotal) { + goto lowMemory; + } + seDenyRules = (int *) + malloc(sizeof(int) * seTotal); + if (!seDenyRules) { + goto lowMemory; + } + seDenyRulesTotal = (int *) + malloc(sizeof(int) * seTotal); + if (!seDenyRulesTotal) { + goto lowMemory; + } + /* 2. Make a second pass to configure them. */ + i = 0; + ai = 0; + di = 0; + lnum = 0; + in = fopen(options.conf_file, "r"); + if (!in) { + goto lowMemory; + } + if (seTotal > 0) { + seAllowRulesTotal[i] = 0; + seDenyRulesTotal[i] = 0; + } + while (1) { + char *bindAddress; + unsigned short bindPort; + char *connectAddress; + char *bindPortS; + char *connectPortS; + unsigned short connectPort; + struct in_addr iaddr; + struct sockaddr_in saddr; + struct servent *service; + int j; + if (!getConfLine(in, line, sizeof(line), &lnum)) { + break; + } + bindAddress = strtok(line, " \t\r\n"); + if (!bindAddress) { + fprintf(stderr, "rinetd: no bind address specified " + "on line %d.\n", lnum); + continue; + } + if (!strcmp(bindAddress, "allow")) { + char *pattern = strtok(0, " \t\r\n"); + if (!pattern) { + fprintf(stderr, "rinetd: nothing to allow " + "specified on line %d.\n", lnum); + continue; + } + if (patternBad(pattern)) { + fprintf(stderr, "rinetd: illegal allow or " + "deny pattern. Only digits, ., and\n" + "the ? and * wild cards are allowed. " + "For performance reasons, rinetd\n" + "does not look up complete " + "host names.\n"); + continue; + } + + allowRules[ai] = malloc(strlen(pattern) + 1); + if (!allowRules[ai]) { + goto lowMemory; + } + strcpy(allowRules[ai], pattern); + if (i > 0) { + if (seAllowRulesTotal[i - 1] == 0) { + seAllowRules[i - 1] = ai; + } + seAllowRulesTotal[i - 1]++; + } else { + globalAllowRules++; + } + ai++; + } else if (!strcmp(bindAddress, "deny")) { + char *pattern = strtok(0, " \t\r\n"); + if (!pattern) { + fprintf(stderr, "rinetd: nothing to deny " + "specified on line %d.\n", lnum); + continue; + } + denyRules[di] = malloc(strlen(pattern) + 1); + if (!denyRules[di]) { + goto lowMemory; + } + strcpy(denyRules[di], pattern); + if (i > 0) { + if (seDenyRulesTotal[i - 1] == 0) { + seDenyRules[i - 1] = di; + } + seDenyRulesTotal[i - 1]++; + } else { + globalDenyRules++; + } + di++; + } else if (!strcmp(bindAddress, "logfile")) { + char *nt = strtok(0, " \t\r\n"); + if (!nt) { + fprintf(stderr, "rinetd: no log file name " + "specified on line %d.\n", lnum); + continue; + } + logFileName = malloc(strlen(nt) + 1); + if (!logFileName) { + goto lowMemory; + } + strcpy(logFileName, nt); + } else if (!strcmp(bindAddress, "pidlogfile")) { + char *nt = strtok(0, " \t\r\n"); + if (!nt) { + fprintf(stderr, "rinetd: no PID log file name " + "specified on line %d.\n", lnum); + continue; + } + pidLogFileName = malloc(strlen(nt) + 1); + if (!pidLogFileName) { + goto lowMemory; + } + strcpy(pidLogFileName, nt); + } else if (!strcmp(bindAddress, "logcommon")) { + logFormatCommon = 1; + } else { + /* A regular forwarding rule. */ + bindPortS = strtok(0, " \t\r\n"); + if (!bindPortS) { + fprintf(stderr, "rinetd: no bind port " + "specified on line %d.\n", lnum); + continue; + } + service = getservbyname(bindPortS, "tcp"); + if (service) { + bindPort = ntohs(service->s_port); + } else { + bindPort = atoi(bindPortS); + } + if ((bindPort == 0) || (bindPort >= 65536)) { + fprintf(stderr, "rinetd: bind port missing " + "or out of range on line %d.\n", lnum); + continue; + } + connectAddress = strtok(0, " \t\r\n"); + if (!connectAddress) { + fprintf(stderr, "rinetd: no connect address " + "specified on line %d.\n", lnum); + continue; + } + connectPortS = strtok(0, " \t\r\n"); + if (!connectPortS) { + fprintf(stderr, "rinetd: no connect port " + "specified on line %d.\n", lnum); + continue; + } + service = getservbyname(connectPortS, "tcp"); + if (service) { + connectPort = ntohs(service->s_port); + } else { + connectPort = atoi(connectPortS); + } + if ((connectPort == 0) || (connectPort >= 65536)) { + fprintf(stderr, "rinetd: bind port missing " + "or out of range on line %d.\n", lnum); + continue; + } + /* Turn all of this stuff into reasonable addresses */ + if (!getAddress(bindAddress, &iaddr)) { + fprintf(stderr, "rinetd: host %s could not be " + "resolved on line %d.\n", + bindAddress, lnum); + continue; + } + /* Make a server socket */ + seFds[i] = socket(PF_INET, SOCK_STREAM, 0); + if (seFds[i] == INVALID_SOCKET) { + fprintf(stderr, "rinetd: couldn't create " + "server socket!\n"); + seFds[i] = -1; + continue; + } +#ifndef WIN32 + if (seFds[i] > maxfd) { + maxfd = seFds[i]; + } +#endif + saddr.sin_family = AF_INET; + memcpy(&saddr.sin_addr, &iaddr, sizeof(iaddr)); + saddr.sin_port = htons(bindPort); + j = 1; + setsockopt(seFds[i], SOL_SOCKET, SO_REUSEADDR, + (const char *) &j, sizeof(j)); + if (bind(seFds[i], (struct sockaddr *) + &saddr, sizeof(saddr)) == SOCKET_ERROR) + { + /* Warn -- don't exit. */ + fprintf(stderr, "rinetd: couldn't bind to " + "address %s port %d\n", + bindAddress, bindPort); + closesocket(seFds[i]); + seFds[i] = INVALID_SOCKET; + continue; + } + if (listen(seFds[i], 5) == SOCKET_ERROR) { + /* Warn -- don't exit. */ + fprintf(stderr, "rinetd: couldn't listen to " + "address %s port %d\n", + bindAddress, bindPort); + closesocket(seFds[i]); + seFds[i] = INVALID_SOCKET; + continue; + } + ioctlsocket(seFds[i], FIONBIO, &j); + if (!getAddress(connectAddress, &iaddr)) { + /* Warn -- don't exit. */ + fprintf(stderr, "rinetd: host %s could not be " + "resolved on line %d.\n", + bindAddress, lnum); + closesocket(seFds[i]); + seFds[i] = INVALID_SOCKET; + continue; + } + seLocalAddrs[i] = iaddr; + seLocalPorts[i] = htons(connectPort); + seFromHosts[i] = malloc(strlen(bindAddress) + 1); + if (!seFromHosts[i]) { + goto lowMemory; + } + strcpy(seFromHosts[i], bindAddress); + seFromPorts[i] = bindPort; + seToHosts[i] = malloc(strlen(connectAddress) + 1); + if (!seToHosts[i]) { + goto lowMemory; + } + strcpy(seToHosts[i], connectAddress); + seToPorts[i] = connectPort; + i++; + if (i < seTotal) { + seAllowRulesTotal[i] = 0; + seDenyRulesTotal[i] = 0; + } + } + } + /* Open the log file */ + if (logFile) { + fclose(logFile); + logFile = 0; + } + if (logFileName) { + logFile = fopen(logFileName, "a"); + if (!logFile) { + fprintf(stderr, "rinetd: could not open %s to append.\n", + logFileName); + } + } + return; +lowMemory: + fprintf(stderr, "rinetd: not enough memory to start rinetd.\n"); + exit(1); +} + +int getConfLine(FILE *in, char *line, int space, int *lnum) +{ + char *p; + while (1) { + if (!fgets(line, space, in)) { + return 0; + } + p = line; + while (isspace(*p)) { + p++; + } + if (!(*p)) { + /* Blank lines are OK */ + continue; + } + if (*p == '#') { + /* Comment lines are also OK */ + continue; + } + (*lnum)++; + return 1; + } +} + +void initArrays(void) +{ + int j; + coTotal = 64; + reFds = (SOCKET *) malloc(sizeof(int) * coTotal); + loFds = (SOCKET *) malloc(sizeof(int) * coTotal); + coInputRPos = (int *) malloc(sizeof(int) * coTotal); + coInputWPos = (int *) malloc(sizeof(int) * coTotal); + coOutputRPos = (int *) malloc(sizeof(int) * coTotal); + coOutputWPos = (int *) malloc(sizeof(int) * coTotal); + coClosed = (int *) malloc(sizeof(int) * coTotal); + coClosing = (int *) malloc(sizeof(int) * coTotal); + reClosed = (int *) malloc(sizeof(int) * coTotal); + loClosed = (int *) malloc(sizeof(int) * coTotal); + coInput = (char **) malloc(sizeof(char *) * coTotal); + coOutput = (char **) malloc(sizeof(char *) * coTotal); + coBytesInput = (int *) malloc(sizeof(int) * coTotal); + coBytesOutput = (int *) malloc(sizeof(int) * coTotal); + reAddresses = (unsigned char *) malloc(coTotal * 4); + coLog = (int *) malloc(sizeof(int) * coTotal); + coSe = (int *) malloc(sizeof(int) * coTotal); + if ((!reFds) || (!loFds) || (!coInputRPos) || (!coInputWPos) || + (!coOutputRPos) || (!coOutputWPos) || + (!coClosed) || (!coClosing) || + (!reClosed) || (!loClosed) || + (!coInput) || (!coOutput) || + (!coBytesInput) || (!coBytesOutput) || + (!coLog) || (!coSe) || (!reAddresses)) + { + fprintf(stderr, "rinetd: not enough memory to start rinetd.\n"); + exit(1); + } + for (j = 0; (j < coTotal); j++) { + coClosed[j] = 1; + coInput[j] = (char *) malloc(sizeof(char) * bufferSpace); + coOutput[j] = (char *) malloc(sizeof(char) * bufferSpace); + if ((!coInput[j]) || (!coOutput[j])) { + fprintf(stderr, "rinetd: not enough memory to start " + "rinetd.\n"); + exit(1); + } + } +} + +void selectPass(void); + +void selectLoop(void) { + while (1) { + selectPass(); + } +} + +void handleRemoteWrite(int i); +void handleRemoteRead(int i); +void handleLocalWrite(int i); +void handleLocalRead(int i); +void handleCloseFromLocal(int i); +void handleCloseFromRemote(int i); +void handleAccept(int i); +void openLocalFd(int se, int i); +int getAddress(char *host, struct in_addr *iaddr); + +void selectPass(void) { + int i; + fd_set readfds, writefds; + FD_ZERO(&readfds); + FD_ZERO(&writefds); + /* Server sockets */ + for (i = 0; (i < seTotal); i++) { + if (seFds[i] != INVALID_SOCKET) { + FD_SET(seFds[i], &readfds); + } + } + /* Connection sockets */ + for (i = 0; (i < coTotal); i++) { + if (coClosed[i]) { + continue; + } + if (coClosing[i]) { + if (!reClosed[i]) { + FD_SET(reFds[i], &writefds); + } + if (!loClosed[i]) { + FD_SET(loFds[i], &writefds); + } + } + /* Get more input if we have room for it */ + if ((!reClosed[i]) && (coInputRPos[i] < bufferSpace)) { + FD_SET(reFds[i], &readfds); + } + /* Send more output if we have any */ + if ((!reClosed[i]) && (coOutputWPos[i] < coOutputRPos[i])) { + FD_SET(reFds[i], &writefds); + } + /* Accept more output from the local + server if there's room */ + if ((!loClosed[i]) && (coOutputRPos[i] < bufferSpace)) { + FD_SET(loFds[i], &readfds); + } + /* Send more input to the local server + if we have any */ + if ((!loClosed[i]) && (coInputWPos[i] < coInputRPos[i])) { + FD_SET(loFds[i], &writefds); + } + } + select(maxfd + 1, &readfds, &writefds, 0, 0); + for (i = 0; (i < seTotal); i++) { + if (seFds[i] != -1) { + if (FD_ISSET(seFds[i], &readfds)) { + handleAccept(i); + } + } + } + for (i = 0; (i < coTotal); i++) { + if (coClosed[i]) { + continue; + } + if (!reClosed[i]) { + if (FD_ISSET(reFds[i], &readfds)) { + handleRemoteRead(i); + } + } + if (!reClosed[i]) { + if (FD_ISSET(reFds[i], &writefds)) { + handleRemoteWrite(i); + } + } + if (!loClosed[i]) { + if (FD_ISSET(loFds[i], &readfds)) { + handleLocalRead(i); + } + } + if (!loClosed[i]) { + if (FD_ISSET(loFds[i], &writefds)) { + handleLocalWrite(i); + } + } + if (loClosed[i] && reClosed[i]) { + coClosed[i] = 1; + } + } +} + +void handleRemoteRead(int i) +{ + int got; + if (bufferSpace == coInputRPos[i]) { + return; + } + got = recv(reFds[i], coInput[i] + coInputRPos[i], + bufferSpace - coInputRPos[i], 0); + if (got == 0) { + /* Prepare for closing */ + handleCloseFromRemote(i); + return; + } + if (got < 0) { + if (GetLastError() == WSAEWOULDBLOCK) { + return; + } + if (GetLastError() == WSAEINPROGRESS) { + return; + } + handleCloseFromRemote(i); + return; + } + coBytesInput[i] += got; + coInputRPos[i] += got; +} + +void handleRemoteWrite(int i) +{ + int got; + if (coClosing[i] && (coOutputWPos[i] == coOutputRPos[i])) { + reClosed[i] = 1; + coClosed[i] = 1; + PERROR("rinetd: local closed and no more output"); + log(i, coSe[i], logDone | coLog[i]); + closesocket(reFds[i]); + return; + } + got = send(reFds[i], coOutput[i] + coOutputWPos[i], + coOutputRPos[i] - coOutputWPos[i], 0); + if (got < 0) { + if (GetLastError() == WSAEWOULDBLOCK) { + return; + } + if (GetLastError() == WSAEINPROGRESS) { + return; + } + handleCloseFromRemote(i); + return; + } + coOutputWPos[i] += got; + if (coOutputWPos[i] == coOutputRPos[i]) { + coOutputWPos[i] = 0; + coOutputRPos[i] = 0; + } + coBytesOutput[i] += got; +} + +void handleLocalRead(int i) +{ + int got; + if (bufferSpace == coOutputRPos[i]) { + return; + } + got = recv(loFds[i], coOutput[i] + coOutputRPos[i], + bufferSpace - coOutputRPos[i], 0); + if (got == 0) { + handleCloseFromLocal(i); + return; + } + if (got < 0) { + if (GetLastError() == WSAEWOULDBLOCK) { + return; + } + if (GetLastError() == WSAEINPROGRESS) { + return; + } + handleCloseFromLocal(i); + return; + } + coOutputRPos[i] += got; +} + +void handleLocalWrite(int i) +{ + int got; + if (coClosing[i] && (coInputWPos[i] == coInputRPos[i])) { + loClosed[i] = 1; + coClosed[i] = 1; + PERROR("remote closed and no more input"); + log(i, coSe[i], logDone | coLog[i]); + closesocket(loFds[i]); + return; + } + got = send(loFds[i], coInput[i] + coInputWPos[i], + coInputRPos[i] - coInputWPos[i], 0); + if (got < 0) { + if (GetLastError() == WSAEWOULDBLOCK) { + return; + } + if (GetLastError() == WSAEINPROGRESS) { + return; + } + handleCloseFromLocal(i); + return; + } + coInputWPos[i] += got; + if (coInputWPos[i] == coInputRPos[i]) { + coInputWPos[i] = 0; + coInputRPos[i] = 0; + } +} + +void handleCloseFromLocal(int i) +{ + int arg; + coClosing[i] = 1; + /* The local end fizzled out, so make sure + we're all done with that */ + PERROR("close from local"); + closesocket(loFds[i]); + loClosed[i] = 1; + if (!reClosed[i]) { +#ifndef LINUX +#ifndef WIN32 + /* Now set up the remote end for a polite closing */ + + /* Request a low-water mark equal to the entire + output buffer, so the next write notification + tells us for sure that we can close the socket. */ + arg = 1024; + setsockopt(reFds[i], SOL_SOCKET, SO_SNDLOWAT, + &arg, sizeof(arg)); +#endif /* WIN32 */ +#endif /* LINUX */ + coLog[i] = logLocalClosedFirst; + } +} + +void handleCloseFromRemote(int i) +{ + int arg; + coClosing[i] = 1; + /* The remote end fizzled out, so make sure + we're all done with that */ + PERROR("close from remote"); + closesocket(reFds[i]); + reClosed[i] = 1; + if (!loClosed[i]) { +#ifndef LINUX +#ifndef WIN32 + /* Now set up the local end for a polite closing */ + + /* Request a low-water mark equal to the entire + output buffer, so the next write notification + tells us for sure that we can close the socket. */ + arg = 1024; + setsockopt(loFds[i], SOL_SOCKET, SO_SNDLOWAT, + &arg, sizeof(arg)); +#endif /* WIN32 */ +#endif /* LINUX */ + loClosed[i] = 0; + coLog[i] = logRemoteClosedFirst; + } +} + +void refuse(int index, int logCode); + +void handleAccept(int i) +{ + struct sockaddr addr; + struct sockaddr_in *sin; + unsigned char address[4]; + char addressText[64]; + int j; + int addrlen; + int index = -1; + int o; + SOCKET nfd; + addrlen = sizeof(addr); + nfd = accept(seFds[i], &addr, &addrlen); + if (nfd == INVALID_SOCKET) { + log(-1, i, logAcceptFailed); + return; + } +#ifndef WIN32 + if (nfd > maxfd) { + maxfd = nfd; + } +#endif /* WIN32 */ + j = 1; + ioctlsocket(nfd, FIONBIO, &j); + j = 0; +#ifndef WIN32 + setsockopt(nfd, SOL_SOCKET, SO_LINGER, &j, sizeof(j)); +#endif + for (j = 0; (j < coTotal); j++) { + if (coClosed[j]) { + index = j; + break; + } + } + if (index == -1) { + o = coTotal; + coTotal *= 2; + if (!SAFE_REALLOC(&reFds, sizeof(int) * o, + sizeof(SOCKET) * coTotal)) + { + goto shortage; + } + if (!SAFE_REALLOC(&loFds, sizeof(int) * o, + sizeof(SOCKET) * coTotal)) + { + goto shortage; + } + if (!SAFE_REALLOC(&coInputRPos, + sizeof(int) * o, sizeof(int) * coTotal)) + { + goto shortage; + } + if (!SAFE_REALLOC(&coInputWPos, + sizeof(int) * o, sizeof(int) * coTotal)) + { + goto shortage; + } + if (!SAFE_REALLOC(&coOutputRPos, + sizeof(int) * o, sizeof(int) * coTotal)) + { + goto shortage; + } + if (!SAFE_REALLOC(&coOutputWPos, sizeof(int) * o, + sizeof(int) * coTotal)) + { + goto shortage; + } + if (!SAFE_REALLOC(&coClosed, sizeof(int) * o, + sizeof(int) * coTotal)) + { + goto shortage; + } + if (!SAFE_REALLOC(&coClosing, sizeof(int) * o, + sizeof(int) * coTotal)) + { + goto shortage; + } + if (!SAFE_REALLOC(&reClosed, sizeof(int) * o, + sizeof(int) * coTotal)) + { + goto shortage; + } + if (!SAFE_REALLOC(&loClosed, sizeof(int) * o, + sizeof(int) * coTotal)) + { + goto shortage; + } + if (!SAFE_REALLOC(&coLog, sizeof(int) * o, + sizeof(int) * coTotal)) + { + goto shortage; + } + if (!SAFE_REALLOC(&coSe, sizeof(int) * o, + sizeof(int) * coTotal)) + { + goto shortage; + } + if (!SAFE_REALLOC(&coBytesInput, sizeof(int) * o, + sizeof(int) * coTotal)) + { + goto shortage; + } + if (!SAFE_REALLOC(&reAddresses, 4 * o, + 4 * coTotal)) + { + goto shortage; + } + if (!SAFE_REALLOC(&coBytesOutput, sizeof(int) * o, + sizeof(int) * coTotal)) + { + goto shortage; + } + if (!SAFE_REALLOC(&coInput, sizeof(char *) * o, + sizeof(char *) * coTotal)) + { + goto shortage; + } + if (!SAFE_REALLOC(&coOutput, sizeof(char *) * o, + sizeof(char *) * coTotal)) + { + goto shortage; + } + for (j = o; (j < coTotal); j++) { + coClosed[j] = 1; + coInput[j] = (char *) + malloc(sizeof(char) * bufferSpace); + if (!coInput[j]) { + int k; + for (k = o; (k < j); k++) { + free(coInput[k]); + free(coOutput[k]); + } + goto shortage; + } + coOutput[j] = (char *) + malloc(sizeof(char) * bufferSpace); + if (!coOutput[j]) { + int k; + free(coInput[j]); + for (k = o; (k < j); k++) { + free(coInput[k]); + free(coOutput[k]); + } + goto shortage; + } + } + index = o; + } + coInputRPos[index] = 0; + coInputWPos[index] = 0; + coOutputRPos[index] = 0; + coOutputWPos[index] = 0; + coClosed[index] = 0; + coClosing[index] = 0; + reClosed[index] = 0; + loClosed[index] = 0; + reFds[index] = nfd; + coBytesInput[index] = 0; + coBytesOutput[index] = 0; + coLog[index] = 0; + coSe[index] = i; + sin = (struct sockaddr_in *) &addr; + memcpy(address, &(sin->sin_addr.s_addr), 4); + memcpy(reAddresses + index * 4, address, 4); + /* Now, do we want to accept this connection? + Format it for comparison to a pattern. */ + sprintf(addressText, "%d.%d.%d.%d", + address[0], address[1], address[2], address[3]); + /* 1. Check global allow rules. If there are no + global allow rules, it's presumed OK at + this step. If there are any, and it doesn't + match at least one, kick it out. */ + if (globalAllowRules) { + int good = 0; + for (j = 0; (j < globalAllowRules); j++) { + if (match(addressText, allowRules[j])) { + good = 1; + break; + } + } + if (!good) { + refuse(index, logNotAllowed); + return; + } + } + /* 2. Check global deny rules. If it matches + any of the global deny rules, kick it out. */ + if (globalDenyRules) { + for (j = 0; (j < globalDenyRules); j++) { + if (match(addressText, denyRules[j])) { + refuse(index, logDenied); + } + } + } + /* 3. Check allow rules specific to this forwarding rule. + If there are none, it's OK. If there are any, + it must match at least one. */ + if (seAllowRulesTotal[i]) { + int good = 0; + for (j = 0; (j < seAllowRulesTotal[i]); j++) { + if (match(addressText, + allowRules[seAllowRules[i] + j])) { + good = 1; + break; + } + } + if (!good) { + refuse(index, logNotAllowed); + return; + } + } + /* 2. Check deny rules specific to this forwarding rule. If + it matches any of the deny rules, kick it out. */ + if (seDenyRulesTotal[i]) { + for (j = 0; (j < seDenyRulesTotal[i]); j++) { + if (match(addressText, + denyRules[seDenyRules[i] + j])) { + refuse(index, logDenied); + } + } + } + /* Now open a connection to the local server. + This, too, is nonblocking. Why wait + for anything when you don't have to? */ + openLocalFd(i, index); + return; +shortage: + fprintf(stderr, "rinetd: not enough memory to " + "add slots. Currently %d slots.\n", o); + /* Go back to the previous total number of slots */ + coTotal = o; +} + +void openLocalFd(int se, int i) +{ + int j; + struct sockaddr_in saddr; + loFds[i] = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (loFds[i] == INVALID_SOCKET) { + closesocket(reFds[i]); + reClosed[i] = 1; + loClosed[i] = 1; + coClosed[i] = 1; + log(i, coSe[i], logLocalSocketFailed); + return; + } +#ifndef WIN32 + if (loFds[i] > maxfd) { + maxfd = loFds[i]; + } +#endif /* WIN32 */ + /* Bind the local socket */ + saddr.sin_family = AF_INET; + saddr.sin_port = INADDR_ANY; + saddr.sin_addr.s_addr = 0; + if (bind(loFds[i], (struct sockaddr *) &saddr, sizeof(saddr)) == SOCKET_ERROR) { + closesocket(loFds[i]); + closesocket(reFds[i]); + reClosed[i] = 1; + loClosed[i] = 1; + coClosed[i] = 1; + log(i, coSe[i], logLocalBindFailed); + return; + } + memset(&saddr, 0, sizeof(struct sockaddr_in)); + saddr.sin_family = AF_INET; + memcpy(&saddr.sin_addr, &seLocalAddrs[se], sizeof(struct in_addr)); + saddr.sin_port = seLocalPorts[se]; +#ifndef WIN32 +#ifdef LINUX + j = 0; + setsockopt(loFds[i], SOL_SOCKET, SO_LINGER, &j, sizeof(j)); +#else + j = 1024; + setsockopt(loFds[i], SOL_SOCKET, SO_SNDBUF, &j, sizeof(j)); +#endif /* LINUX */ +#endif /* WIN32 */ + j = 1; + ioctlsocket(loFds[i], FIONBIO, &j); + if (connect(loFds[i], (struct sockaddr *)&saddr, + sizeof(struct sockaddr_in)) == INVALID_SOCKET) + { + if ((GetLastError() != WSAEINPROGRESS) && + (GetLastError() != WSAEWOULDBLOCK)) + { + PERROR("rinetd: connect"); + closesocket(loFds[i]); + closesocket(reFds[i]); + reClosed[i] = 1; + loClosed[i] = 1; + coClosed[i] = 1; + log(i, coSe[i], logLocalConnectFailed); + return; + } + } +} + +int getAddress(char *host, struct in_addr *iaddr) +{ + char *p = host; + int ishost = 0; + while (*p) { + if (!(isdigit(*p) || ((*p) == '.'))) { + ishost = 1; + break; + } + p++; + } + if (ishost) { + struct hostent *h; + h = gethostbyname(host); + if (!h) { + return 0; + } + memcpy( + (void *) &iaddr->s_addr, + (void *) h->h_addr, + 4); + return 1; + } else { + iaddr->s_addr = inet_addr(host); + return 1; + } +} + +#ifndef WIN32 +void plumber(int s) +{ + /* Just reinstall */ + signal(SIGPIPE, plumber); +} + +void hup(int s) +{ + /* Learn the new rules */ + readConfiguration(); + /* And reinstall the signal handler */ + signal(SIGHUP, hup); +} +#endif /* WIN32 */ + +int safeRealloc(void **data, int oldsize, int newsize) +{ + void *newData = malloc(newsize + 1); + if (!newData) { + return 0; + } + if (newsize < oldsize) { + memcpy(newData, *data, newsize); + } else { + memcpy(newData, *data, oldsize); + } + *data = newData; + return 1; +} + +void RegisterPID(void) +{ + FILE *pid_file; + char *pid_file_name = "/var/run/rinetd.pid"; + if (pidLogFileName) { + pid_file_name = pidLogFileName; + } +/* add other systems with wherever they register processes */ +#if defined(LINUX) + pid_file = fopen(pid_file_name, "w"); + if (pid_file == NULL) { + /* non-fatal, non-Linux may lack /var/run... */ + fprintf(stderr, "rinetd: Couldn't write to " + "%s. PID was not logged.\n", pid_file_name); + } else { + /* error checking deliberately omitted */ + fprintf(pid_file, "%d\n", getpid()); + fclose(pid_file); + } +#endif /* LINUX */ +} + +unsigned char nullAddress[4] = { 0, 0, 0, 0 }; + +struct tm *get_gmtoff(int *tz); + +void log(int i, int coSe, int result) +{ + unsigned char *reAddress; + int bytesOutput; + int bytesInput; + /* Bit of borrowing from Apache logging module here, + thanks folks */ + int timz; + struct tm *t; + char tstr[1024]; + char sign; + if (!log) { + return; + } + t = get_gmtoff(&timz); + sign = (timz < 0 ? '-' : '+'); + if (timz < 0) { + timz = -timz; + } + strftime(tstr, sizeof(tstr), "%d/%b/%Y:%H:%M:%S ", t); + + if (i != -1) { + reAddress = reAddresses + i * 4; + bytesOutput = coBytesOutput[i]; + bytesInput = coBytesInput[i]; + } else { + reAddress = nullAddress; + bytesOutput = 0; + bytesInput = 0; + } + if (logFile) { + if (logFormatCommon) { + /* Fake a common log format log file in a way that + most web analyzers can do something interesting with. + We lie and say the protocol is HTTP because we don't + want the web analyzer to reject the line. We also + lie and claim success (code 200) because we don't + want the web analyzer to ignore the line as an + error and not analyze the "URL." We put a result + message into our "URL" instead. The last field + is an extra, giving the number of input bytes, + after several placeholders meant to fill the + positions frequently occupied by user agent, + referrer, and server name information. */ + fprintf(logFile, "%d.%d.%d.%d - - " + "[%s %c%.2d%.2d] " + "\"GET /rinetd-services/%s/%d/%s/%d/%s HTTP/1.0\" " + "200 %d - - - %d\n", + reAddress[0], + reAddress[1], + reAddress[2], + reAddress[3], + tstr, + sign, + timz / 60, + timz % 60, + seFromHosts[coSe], seFromPorts[coSe], + seToHosts[coSe], seToPorts[coSe], + logMessages[result], + bytesOutput, + bytesInput); + } else { + /* Write an rinetd-specific log entry with a + less goofy format. */ + fprintf(logFile, "%s\t%d.%d.%d.%d\t%s\t%d\t%s\t%d\t%d" + "\t%d\t%s\n", + tstr, + reAddress[0], + reAddress[1], + reAddress[2], + reAddress[3], + seFromHosts[coSe], seFromPorts[coSe], + seToHosts[coSe], seToPorts[coSe], + bytesInput, + bytesOutput, + logMessages[result]); + } + } +} + +int readArgs (int argc, + char **argv, + RinetdOptions *options) +{ + int c; + + while (1) { + int option_index = 0; + static struct option long_options[] = { + {"conf-file", 1, 0, 'c'}, + {"help", 0, 0, 'h'}, + {"version", 0, 0, 'v'}, + {0, 0, 0, 0} + }; + c = getopt_long (argc, argv, "c:shv", + long_options, &option_index); + if (c == -1) { + break; + } + switch (c) { + case 'c': + options->conf_file = malloc(strlen(optarg) + 1); + if (!options->conf_file) { + fprintf(stderr, "Not enough memory to " + "launch rinetd.\n"); + exit(1); + } + strcpy(options->conf_file, optarg); + break; + case 'h': + printf("Usage: rinetd [OPTION]\n" + " -c, --conf-file FILE read configuration " + "from FILE\n" + " -h, --help display this help\n" + " -v, --version display version " + "number\n\n"); + printf("Most options are controlled through the\n" + "configuration file. See the rinetd(8)\n" + "manpage for more information.\n"); + exit (0); + case 'v': + printf ("rinetd %s\n", VERSION); + exit (0); + case '?': + default: + exit (1); + } + } + return 0; +} + +/* get_gmtoff was borrowed from Apache. Thanks folks. */ + +struct tm *get_gmtoff(int *tz) { + time_t tt = time(NULL); + struct tm gmt; + struct tm *t; + int days, hours, minutes; + + /* Assume we are never more than 24 hours away. */ + gmt = *gmtime(&tt); /* remember gmtime/localtime return ptr to static */ + t = localtime(&tt); /* buffer... so be careful */ + days = t->tm_yday - gmt.tm_yday; + hours = ((days < -1 ? 24 : 1 < days ? -24 : days * 24) + + t->tm_hour - gmt.tm_hour); + minutes = hours * 60 + t->tm_min - gmt.tm_min; + *tz = minutes; + return t; +} + +int patternBad(char *pattern) +{ + char *p = pattern; + while (*p) { + if (isdigit(*p) || ((*p) == '?') || ((*p) == '*') || + ((*p) == '.')) + { + p++; + } + return 0; + } + return 1; +} + +void refuse(int index, int logCode) +{ + closesocket(reFds[index]); + reClosed[index] = 1; + loClosed[index] = 1; + coClosed[index] = 1; + log(index, coSe[index], logCode); +} + +void term(int s) +{ + /* Obey the request, but first flush the log */ + if (logFile) { + fclose(logFile); + } + exit(0); +} + diff --git a/rinetd.dsp b/rinetd.dsp new file mode 100755 index 0000000..d45592b --- /dev/null +++ b/rinetd.dsp @@ -0,0 +1,97 @@ +# Microsoft Developer Studio Project File - Name="rinetd" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=rinetd - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "rinetd.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "rinetd.mak" CFG="rinetd - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "rinetd - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "rinetd - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "rinetd - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "rinetd - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "rinetd - Win32 Release" +# Name "rinetd - Win32 Debug" +# Begin Source File + +SOURCE=.\getopt.c +# End Source File +# Begin Source File + +SOURCE=.\match.c +# End Source File +# Begin Source File + +SOURCE=.\rinetd.c +# End Source File +# End Target +# End Project diff --git a/rinetd.dsw b/rinetd.dsw new file mode 100755 index 0000000..61dc1d1 --- /dev/null +++ b/rinetd.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 5.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "rinetd"=.\rinetd.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/rinetd.exe b/rinetd.exe new file mode 100755 index 0000000..fd580dc Binary files /dev/null and b/rinetd.exe differ diff --git a/rinetd.ncb b/rinetd.ncb new file mode 100755 index 0000000..4e9c87e Binary files /dev/null and b/rinetd.ncb differ diff --git a/rinetd.opt b/rinetd.opt new file mode 100755 index 0000000..1ea2aa1 Binary files /dev/null and b/rinetd.opt differ diff --git a/rinetd.plg b/rinetd.plg new file mode 100755 index 0000000..0f064d8 --- /dev/null +++ b/rinetd.plg @@ -0,0 +1,32 @@ +--------------------Configuration: rinetd - Win32 Debug-------------------- +Begining build with project "g:\rinetd\rinetd.dsp", at root. +Active configuration is Win32 (x86) Console Application (based on Win32 (x86) Console Application) + +Project's tools are: + "32-bit C/C++ Compiler for 80x86" with flags "/nologo /MLd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Fp"Debug/rinetd.pch" /YX /Fo"Debug/" /Fd"Debug/" /FD /c " + "Win32 Resource Compiler" with flags "/l 0x409 /d "_DEBUG" " + "Browser Database Maker" with flags "/nologo /o"Debug/rinetd.bsc" " + "COFF Linker for 80x86" with flags "kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /incremental:yes /pdb:"Debug/rinetd.pdb" /debug /machine:I386 /out:"Debug/rinetd.exe" /pdbtype:sept " + "Custom Build" with flags "" + "" with flags "" + +Creating temp file "c:\windows\TEMP\RSP10D4.TMP" with contents +Creating command line "cl.exe @c:\windows\TEMP\RSP10D4.TMP" +Creating temp file "c:\windows\TEMP\RSP10D5.TMP" with contents +Creating command line "link.exe @c:\windows\TEMP\RSP10D5.TMP" +Compiling... +rinetd.c +G:\rinetd\rinetd.c(965) : warning C4101: 'arg' : unreferenced local variable +G:\rinetd\rinetd.c(992) : warning C4101: 'arg' : unreferenced local variable +G:\rinetd\rinetd.c(1367) : warning C4101: 'pid_file' : unreferenced local variable +Linking... +LINK : LNK6004: Debug/rinetd.exe not found or not built by the last incremental link; performing full link + + + +rinetd.exe - 0 error(s), 3 warning(s)