From d46367a44a404ac09e54fc7c51a0deafc11ee182 Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Sun, 10 Jan 2016 20:46:19 +0100 Subject: [PATCH] Import old rinetd 0.52. Taken from the Debian archive: http://archive.debian.org/debian-security/pool/updates/main/r/rinetd/ --- CHANGES | 12 + Makefile | 4 +- README | 2 +- index.html | 170 ++++++++-- match.c | 200 ++++++++++++ match.h | 9 + rinetd.8 | 154 +++++++-- rinetd.c | 929 ++++++++++++++++++++++++++++++++++++++++++++--------- 8 files changed, 1287 insertions(+), 193 deletions(-) create mode 100644 match.c create mode 100644 match.h diff --git a/CHANGES b/CHANGES index bf00fec..82bb24e 100644 --- a/CHANGES +++ b/CHANGES @@ -26,3 +26,15 @@ 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. diff --git a/Makefile b/Makefile index 79cf791..d598b18 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ CFLAGS=-DLINUX -g -rinetd: rinetd.o - gcc rinetd.o -o rinetd +rinetd: rinetd.o match.o + gcc rinetd.o match.o -o rinetd install: rinetd install -m 700 rinetd /usr/sbin diff --git a/README b/README index 486688b..24e4454 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -rinetd version 0.41, by Thomas Boutell. Released under +rinetd version 0.51, by Thomas Boutell. Released under the terms of the GNU Public License, version 2 or later. This program is used to efficiently redirect connections diff --git a/index.html b/index.html index dc9fb03..46747e5 100644 --- a/index.html +++ b/index.html @@ -26,7 +26,7 @@ rinetd -- internet ``redirection server''

VERSION

-Version 0.41, 2/11/1998. +Version 0.52, 8/7/1998.

WHERE TO GET

@@ -42,52 +42,171 @@ 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. +on machines inside an IP masquerading firewall. rinetd does not +redirect FTP, because FTP requires more than one socket.

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

/usr/sbin/rinetd

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

-bindaddress bindport connectaddress connectport +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 -

+

+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 80, 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.

-rinetd redirects TCP connections only. There is -no support for UDP. +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 (this was added in version 0.4). Under Linux\(tm the process id -is saved in the file \fI/var/run/rinetd.pid\fR -to facilitate the kill -HUP (added in version 0.41). +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

-rinetd does not currently produce any log information. The -server redirected to is not able to identify the host the -client really came from. 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 platforms, alternate code -which uses a different trick to work around blocking close() -is provided, but this code is untested. The manpage -is sketchy. +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. 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 +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

@@ -106,5 +225,8 @@ Thomas Boutell can be reached by email:

THANKS

-Thanks are due to Bill Davidsen. - +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 open software projects. + + diff --git a/match.c b/match.c new file mode 100644 index 0000000..f8203a2 --- /dev/null +++ b/match.c @@ -0,0 +1,200 @@ +#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); +} + +int matchBody(char *sorig, char *p, int nocase) +{ + /* 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. + + 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; + + 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 (nocase) { + if (tolower(*p) != tolower(*s)) { + goto nextPattern; + } + } else { + if (*p != *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 ((((*s) == 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 (nocase) { + if (tolower(*p) != tolower(*s)) { + goto nextPattern; + } + } else { + if (*p != *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 index 367ca8d..a15d5b6 100644 --- a/rinetd.8 +++ b/rinetd.8 @@ -12,7 +12,7 @@ .Sh SYNOPSIS .Nm /usr/sbin/rinetd .Sh VERSION -Version 0.41, 3/1/1998. +Version 0.52, 9/7/1998. .Sh DESCRIPTION .Nm rinetd redirects TCP connections from one IP address and port to another. rinetd @@ -21,13 +21,18 @@ 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. +on machines inside an IP masquerading firewall. rinetd does not +redirect FTP, because FTP requires more than one socket. .Pp -rinetd should be launched at boot time, using the following syntax: +rinetd is typically launched at boot time, using the following syntax: .Pp /usr/sbin/rinetd .Pp -The format of /etc/rinetd.conf is as follows: +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 @@ -41,38 +46,145 @@ 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 80, 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 -rinetd redirects TCP connections only. There is -no support for UDP. +.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 (this was added in version 0.4). +connections. Under Linux\(tm the process id is saved in the file \fI/var/run/rinetd.pid\fR -to facilitate the kill -HUP (added in version 0.41). -.Pp +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 -rinetd does not currently produce any log information. The -server redirected to is not able to identify the host the -client really came from. 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 platforms, alternate code -which uses a different trick to work around blocking close() -is provided, but this code is untested. The manpage -is sketchy. +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. 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 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, 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. +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. - +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 index c6bf148..a214447 100644 --- a/rinetd.c +++ b/rinetd.c @@ -9,6 +9,19 @@ #include #include #include +#include +#include +#include + +#define VERSION "0.52" + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif #ifdef DEBUG #define PERROR perror @@ -16,24 +29,55 @@ #define PERROR(x) #endif /* DEBUG */ -int *seFds; -struct in_addr *seLocalAddrs; -unsigned short *seLocalPorts; -int *reFds; -int *loFds; -int *coInputRPos; -int *coInputWPos; -int *coOutputRPos; -int *coOutputWPos; -int *coClosed; -int *coClosing; -int *reClosed; -int *loClosed; -char **coInput; -char **coOutput; -int seTotal; -int coTotal; +int *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; + +int *reFds = 0; +int *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. @@ -53,28 +97,77 @@ int safeRealloc(void **data, int oldsize, int newsize); #define bufferSpace 1024 -void createServerSockets(); +void readConfiguration(); /* Signal handlers */ void plumber(int s); void hup(int s); +void term(int s); -void initArrays(); -void RegisterPID(); +void initArrays(void); +void RegisterPID(void); -void selectLoop(); +void selectLoop(void); + +void log(int i, int coSe, int result); + +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 readAargs (int argc, + char **argv, + RinetdOptions *options); int main(int argc, char *argv[]) { + readArgs(argc, argv, &options); #ifndef DEBUG if (!fork()) { if (!fork()) { #endif /* DEBUG */ signal(SIGPIPE, plumber); signal(SIGHUP, hup); + signal(SIGTERM, term); initArrays(); + readConfiguration(); RegisterPID(); - createServerSockets(); selectLoop(); #ifndef DEBUG } else { @@ -89,67 +182,179 @@ int main(int argc, char *argv[]) int getConfLine(FILE *in, char *line, int space, int *lnum); -void createServerSockets() +int patternBad(char *pattern); + +void readConfiguration(void) { FILE *in; char line[16384]; int lnum = 0; int i; - if (seTotal) { + int ai; + int di; + if (seFds) { /* Close existing server sockets. */ for (i = 0; (i < seTotal); i++) { - close(seFds[i]); + if (seFds[i] != -1) { + close(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; - /* 1. Count the non-comment lines and make room - for that many server sockets. */ - in = fopen("/etc/rinetd.conf", "r"); + 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, "Can't open rinetd.conf\n"); + 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; } - seTotal++; + 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 = (int *) malloc(sizeof(int) * seTotal); if (!seFds) { - fprintf(stderr, "Not enough memory to start rinetd.\n"); - exit(1); + goto lowMemory; } seLocalAddrs = (struct in_addr *) malloc(sizeof(struct in_addr) * seTotal); if (!seLocalAddrs) { - fprintf(stderr, "Not enough memory to start rinetd.\n"); - exit(1); + goto lowMemory; } seLocalPorts = (unsigned short *) malloc(sizeof(unsigned short) * seTotal); if (!seLocalPorts) { - fprintf(stderr, "Not enough memory to start rinetd.\n"); - exit(1); + 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("/etc/rinetd.conf", "r"); + in = fopen(options.conf_file, "r"); if (!in) { - fprintf(stderr, "Can't open rinetd.conf\n"); - exit(1); + goto lowMemory; + } + if (seTotal > 0) { + seAllowRulesTotal[i] = 0; + seDenyRulesTotal[i] = 0; } while (1) { char *bindAddress; unsigned short bindPort; char *connectAddress; - char *tempS; + char *bindPortS; + char *connectPortS; unsigned short connectPort; struct in_addr iaddr; struct sockaddr_in saddr; @@ -160,94 +365,221 @@ void createServerSockets() } bindAddress = strtok(line, " \t\r\n"); if (!bindAddress) { - fprintf(stderr, "No bind address specified " + fprintf(stderr, "rinetd: no bind address specified " "on line %d.\n", lnum); - exit(1); + continue; } - tempS = strtok(0, " \t\r\n"); - if (!tempS) { - fprintf(stderr, "No bind port specified " - "on line %d.\n", lnum); - exit(1); - } - service = getservbyname(tempS, "tcp"); - if (service) { - bindPort = ntohs(service->s_port); + 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 { - bindPort = atoi(tempS); + /* 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] < 0) { + fprintf(stderr, "rinetd: couldn't create " + "server socket!\n"); + seFds[i] = -1; + continue; + } + if (seFds[i] > maxfd) { + maxfd = seFds[i]; + } + 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, + &j, sizeof(j)); + if (bind(seFds[i], (struct sockaddr *) + &saddr, sizeof(saddr)) < 0) + { + /* Warn -- don't exit. */ + fprintf(stderr, "rinetd: couldn't bind to " + "address %s port %d\n", + bindAddress, bindPort); + close(seFds[i]); + seFds[i] = -1; + continue; + } + if (listen(seFds[i], 5) < 0) { + /* Warn -- don't exit. */ + fprintf(stderr, "rinetd: couldn't listen to " + "address %s port %d\n", + bindAddress, bindPort); + close(seFds[i]); + seFds[i] = -1; + continue; + } + fcntl(seFds[i], F_SETFL, O_NONBLOCK); + if (!getAddress(connectAddress, &iaddr)) { + /* Warn -- don't exit. */ + fprintf(stderr, "rinetd: host %s could not be " + "resolved on line %d.\n", + bindAddress, lnum); + close(seFds[i]); + seFds[i] = -1; + 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; + } } - if ((bindPort == 0) || (bindPort >= 65536)) { - fprintf(stderr, "Bind port missing or out " - "of range on line %d.\n", lnum); - exit(1); - } - connectAddress = strtok(0, " \t\r\n"); - if (!connectAddress) { - fprintf(stderr, "No connect address specified " - "on line %d.\n", lnum); - exit(1); - } - tempS = strtok(0, " \t\r\n"); - if (!tempS) { - fprintf(stderr, "No connect port specified " - "on line %d.\n", lnum); - exit(1); - } - service = getservbyname(tempS, "tcp"); - if (service) { - connectPort = ntohs(service->s_port); - } else { - connectPort = atoi(tempS); - } - if ((connectPort == 0) || (connectPort >= 65536)) { - fprintf(stderr, "Bind port missing or out " - "of range on line %d.\n", lnum); - exit(1); - } - /* Turn all of this stuff into reasonable addresses */ - if (!getAddress(bindAddress, &iaddr)) { - fprintf(stderr, "Host %s could not be resolved " - "on line %d.\n", bindAddress, lnum); - exit(1); - } - /* Make a server socket */ - seFds[i] = socket(PF_INET, SOCK_STREAM, 0); - if (seFds[i] < 0) { - fprintf(stderr, "Couldn't create server socket!\n"); - exit(1); - } - if (seFds[i] > maxfd) { - maxfd = seFds[i]; - } - 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, - &j, sizeof(j)); - if (bind(seFds[i], (struct sockaddr *) - &saddr, sizeof(saddr)) < 0) - { - fprintf(stderr, "Couldn't bind to address %s port %d\n", - bindAddress, bindPort); - exit(1); - } - if (listen(seFds[i], 5) < 0) { - fprintf(stderr, "Couldn't listen to address %s " - "port %d\n", - bindAddress, bindPort); - exit(1); - } - fcntl(seFds[i], F_SETFL, O_NONBLOCK); - if (!getAddress(connectAddress, &iaddr)) { - fprintf(stderr, "Host %s could not be resolved " - "on line %d.\n", bindAddress, lnum); - exit(1); - } - seLocalAddrs[i] = iaddr; - seLocalPorts[i] = htons(connectPort); - i++; } + /* 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) @@ -274,7 +606,7 @@ int getConfLine(FILE *in, char *line, int space, int *lnum) } } -void initArrays() +void initArrays(void) { int j; coTotal = 64; @@ -290,13 +622,20 @@ void initArrays() 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)) + (!coInput) || (!coOutput) || + (!coBytesInput) || (!coBytesOutput) || + (!coLog) || (!coSe) || (!reAddresses)) { - fprintf(stderr, "Not enough memory to start rinetd.\n"); + fprintf(stderr, "rinetd: not enough memory to start rinetd.\n"); exit(1); } for (j = 0; (j < coTotal); j++) { @@ -304,15 +643,16 @@ void initArrays() coInput[j] = (char *) malloc(sizeof(char) * bufferSpace); coOutput[j] = (char *) malloc(sizeof(char) * bufferSpace); if ((!coInput[j]) || (!coOutput[j])) { - fprintf(stderr, "Not enough memory to start rinetd.\n"); + fprintf(stderr, "rinetd: not enough memory to start " + "rinetd.\n"); exit(1); } } } -void selectPass(); +void selectPass(void); -void selectLoop() { +void selectLoop(void) { while (1) { selectPass(); } @@ -328,14 +668,16 @@ void handleAccept(int i); void openLocalFd(int se, int i); int getAddress(char *host, struct in_addr *iaddr); -void selectPass() { +void selectPass(void) { int i; fd_set readfds, writefds; FD_ZERO(&readfds); FD_ZERO(&writefds); /* Server sockets */ for (i = 0; (i < seTotal); i++) { - FD_SET(seFds[i], &readfds); + if (seFds[i] != -1) { + FD_SET(seFds[i], &readfds); + } } /* Connection sockets */ for (i = 0; (i < coTotal); i++) { @@ -371,8 +713,10 @@ void selectPass() { } select(maxfd + 1, &readfds, &writefds, 0, 0); for (i = 0; (i < seTotal); i++) { - if (FD_ISSET(seFds[i], &readfds)) { - handleAccept(i); + if (seFds[i] != -1) { + if (FD_ISSET(seFds[i], &readfds)) { + handleAccept(i); + } } } for (i = 0; (i < coTotal); i++) { @@ -428,6 +772,7 @@ void handleRemoteRead(int i) handleCloseFromRemote(i); return; } + coBytesInput[i] += got; coInputRPos[i] += got; } @@ -437,7 +782,8 @@ void handleRemoteWrite(int i) if (coClosing[i] && (coOutputWPos[i] == coOutputRPos[i])) { reClosed[i] = 1; coClosed[i] = 1; - PERROR("local closed and no more output"); + PERROR("rinetd: local closed and no more output"); + log(i, coSe[i], logDone | coLog[i]); close(reFds[i]); return; } @@ -458,6 +804,7 @@ void handleRemoteWrite(int i) coOutputWPos[i] = 0; coOutputRPos[i] = 0; } + coBytesOutput[i] += got; } void handleLocalRead(int i) @@ -492,6 +839,7 @@ void handleLocalWrite(int i) loClosed[i] = 1; coClosed[i] = 1; PERROR("remote closed and no more input"); + log(i, coSe[i], logDone | coLog[i]); close(loFds[i]); return; } @@ -534,7 +882,8 @@ void handleCloseFromLocal(int i) setsockopt(reFds[i], SOL_SOCKET, SO_SNDLOWAT, &arg, sizeof(arg)); #endif /* LINUX */ - } + coLog[i] = logLocalClosedFirst; + } } void handleCloseFromRemote(int i) @@ -558,18 +907,27 @@ void handleCloseFromRemote(int i) &arg, sizeof(arg)); #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; - int nfd = accept(seFds[i], &addr, &addrlen); + int nfd; + addrlen = sizeof(addr); + nfd = accept(seFds[i], &addr, &addrlen); if (nfd < 0) { + log(-1, i, logAcceptFailed); return; } if (nfd > maxfd) { @@ -633,6 +991,31 @@ void handleAccept(int i) { 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)) { @@ -678,6 +1061,70 @@ void handleAccept(int i) 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? */ @@ -700,6 +1147,7 @@ void openLocalFd(int se, int i) reClosed[i] = 1; loClosed[i] = 1; coClosed[i] = 1; + log(i, coSe[i], logLocalSocketFailed); return; } if (loFds[i] > maxfd) { @@ -715,6 +1163,7 @@ void openLocalFd(int se, int i) reClosed[i] = 1; loClosed[i] = 1; coClosed[i] = 1; + log(i, coSe[i], logLocalBindFailed); return; } memset(&saddr, 0, sizeof(struct sockaddr_in)); @@ -734,12 +1183,13 @@ void openLocalFd(int se, int i) sizeof(struct sockaddr_in)) < 0) { if (errno != EINPROGRESS) { - PERROR("connect"); + PERROR("rinetd: connect"); close(loFds[i]); close(reFds[i]); reClosed[i] = 1; loClosed[i] = 1; coClosed[i] = 1; + log(i, coSe[i], logLocalConnectFailed); return; } } @@ -781,9 +1231,9 @@ void plumber(int s) void hup(int s) { - /* Recreate server sockets */ - createServerSockets(); - /* And reinstall */ + /* Learn the new rules */ + readConfiguration(); + /* And reinstall the signal handler */ signal(SIGHUP, hup); } @@ -802,17 +1252,20 @@ int safeRealloc(void **data, int oldsize, int newsize) return 1; } -void -RegisterPID() +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("/var/run/rinetd.pid", "w"); + pid_file = fopen(pid_file_name, "w"); if (pid_file == NULL) { /* non-fatal, non-Linux may lack /var/run... */ - fprintf(stderr, "PID unregistered\n"); + 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()); @@ -820,3 +1273,189 @@ RegisterPID() } #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); + } + } +} + +/* 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) +{ + close(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); +} +