commit a359399bb2484e8d222f9c58364fc0bc4ef3c228 Author: Sam Hocevar <sam@hocevar.net> Date: Sun Jan 10 20:43:31 2016 +0100 Import old rinetd 0.41. Taken from the Debian archive: http://archive.debian.org/debian/dists/Debian-2.0/main/source/net/ diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..bf00fec --- /dev/null +++ b/CHANGES @@ -0,0 +1,28 @@ +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. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..79cf791 --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +CFLAGS=-DLINUX -g + +rinetd: rinetd.o + gcc rinetd.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..486688b --- /dev/null +++ b/README @@ -0,0 +1,15 @@ +rinetd version 0.41, by Thomas Boutell. Released under +the terms of the GNU 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. + +To build, check the Makefile for platform-specific +details and then type make. To install, type +"make install" as root. + +For documentation run "make install", then type +"man rinetd" for details. + diff --git a/index.html b/index.html new file mode 100644 index 0000000..dc9fb03 --- /dev/null +++ b/index.html @@ -0,0 +1,110 @@ +<html> +<head> +<title>RINETD(8)</title> +</head> +<body bgcolor="#FFFFF0"> +<!-- + 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. +--> +<table> +<tr> +<th width=33% align=left>RINETD(8) +<th width=33% align=right>Unix System Manager's Manual +<th width=33% align=right>RINETD(8) +</tr> +</table> +<p> +<font color="#FF8888" size="4">NAME</font> +<p> +rinetd -- internet ``redirection server'' +<p> +<font color="#FF8888" size="4">SYNOPSIS</font> +<p> +<code>/usr/sbin/rinetd</code> +<p> +<font color="#FF8888" size="4">VERSION</font> +<p> +Version 0.41, 2/11/1998. +<p> +<font color="#FF8888" size="4">WHERE TO GET</font> +<p> +<a href="ftp://ftp.boutell.com/pub/boutell/rinetd/rinetd.tar.gz">By +anonymous FTP from ftp.boutell.com</a> in the subdirectory +<code>boutell/rinetd</code> as the file <code>rinetd.tar.gz</code>. +<p> +<font color="#FF8888" size="4">DESCRIPTION</font> +<p> +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 <code>/etc/rinetd.conf</code>. +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. +<p> +rinetd should be launched at boot time, using the following syntax: +<p> +<code>/usr/sbin/rinetd</code> +<p> +The format of <code>/etc/rinetd.conf</code> is as follows: +<p> +<code>bindaddress bindport connectaddress connectport</code> +<p> +For example: +<p> +<code>206.125.69.81 80 10.1.1.2 80</code> +<p> +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. +<p> +Service names can be specified instead of port numbers. On most systems, +service names are defined in the file /etc/services. +<p> +Both IP addresses and hostnames are accepted for +bindaddress and connectaddress. +<p> +rinetd redirects TCP connections only. There is +no support for UDP. +<p> +The kill -1 signal (SIGHUP) can be used to cause rinetd +to reload its configuration file <strong>without</strong> 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). +<p> +<font color="#FF8888" size="4">BUGS</font> +<p> +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 <code>SO_LINGER</code> 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. +<p> +<font color="#FF8888" size="4">LICENSE</font> +<p> +Copyright (c) 1997, 1998, +<a href="http://www.boutell.com/boutell">Thomas Boutell</a> and +<a href="http://www.boutell.com/">Boutell.Com, Inc.</a> +This software is released for free use under the terms of +the GNU Public License, version 2 or higher. +<p> +<font color="#FF8888" size="4">CONTACT INFORMATION</font> +<p> +See <a href="http://www.boutell.com/rinetd">the rinetd web page</a> +for the latest release. +Thomas Boutell can be reached by email: +<a href="mailto:boutell@boutell.com">boutell@boutell.com</a> +<p> +<font color="#FF8888" size="4">THANKS</font> +<p> +Thanks are due to Bill Davidsen. + diff --git a/rinetd.8 b/rinetd.8 new file mode 100644 index 0000000..367ca8d --- /dev/null +++ b/rinetd.8 @@ -0,0 +1,78 @@ +.\" 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. +.\" +.Dd February 11, 1998 +.Dt RINETD 8 +.Os LINUX +.Sh NAME +.Nm rinetd +.Nd internet +.Dq redirection server +.Sh SYNOPSIS +.Nm /usr/sbin/rinetd +.Sh VERSION +Version 0.41, 3/1/1998. +.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. +.Pp +rinetd should be launched at boot time, using the following syntax: +.Pp +/usr/sbin/rinetd +.Pp +The format of /etc/rinetd.conf 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 +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. +.Pp +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). +.Pp +.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. +.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. +.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. + diff --git a/rinetd.c b/rinetd.c new file mode 100644 index 0000000..c6bf148 --- /dev/null +++ b/rinetd.c @@ -0,0 +1,822 @@ +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <signal.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/time.h> + +#ifdef DEBUG +#define PERROR perror +#else +#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 maxfd = 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 createServerSockets(); + +/* Signal handlers */ +void plumber(int s); +void hup(int s); + +void initArrays(); +void RegisterPID(); + +void selectLoop(); + +int main(int argc, char *argv[]) +{ +#ifndef DEBUG + if (!fork()) { + if (!fork()) { +#endif /* DEBUG */ + signal(SIGPIPE, plumber); + signal(SIGHUP, hup); + initArrays(); + RegisterPID(); + createServerSockets(); + selectLoop(); +#ifndef DEBUG + } else { + exit(0); + } + } else { + exit(0); + } +#endif /* DEBUG */ + return 0; +} + +int getConfLine(FILE *in, char *line, int space, int *lnum); + +void createServerSockets() +{ + FILE *in; + char line[16384]; + int lnum = 0; + int i; + if (seTotal) { + /* Close existing server sockets. */ + for (i = 0; (i < seTotal); i++) { + close(seFds[i]); + } + /* Free memory associated with previous set. */ + free(seFds); + free(seLocalAddrs); + free(seLocalPorts); + } + seTotal = 0; + /* 1. Count the non-comment lines and make room + for that many server sockets. */ + in = fopen("/etc/rinetd.conf", "r"); + if (!in) { + fprintf(stderr, "Can't open rinetd.conf\n"); + exit(1); + } + while (1) { + if (!getConfLine(in, line, sizeof(line), &lnum)) { + break; + } + seTotal++; + } + fclose(in); + seFds = (int *) malloc(sizeof(int) * seTotal); + if (!seFds) { + fprintf(stderr, "Not enough memory to start rinetd.\n"); + exit(1); + } + seLocalAddrs = (struct in_addr *) malloc(sizeof(struct in_addr) * + seTotal); + if (!seLocalAddrs) { + fprintf(stderr, "Not enough memory to start rinetd.\n"); + exit(1); + } + seLocalPorts = (unsigned short *) + malloc(sizeof(unsigned short) * seTotal); + if (!seLocalPorts) { + fprintf(stderr, "Not enough memory to start rinetd.\n"); + exit(1); + } + /* 2. Make a second pass to configure them. */ + i = 0; + lnum = 0; + in = fopen("/etc/rinetd.conf", "r"); + if (!in) { + fprintf(stderr, "Can't open rinetd.conf\n"); + exit(1); + } + while (1) { + char *bindAddress; + unsigned short bindPort; + char *connectAddress; + char *tempS; + 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, "No bind address specified " + "on line %d.\n", lnum); + exit(1); + } + 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); + } else { + bindPort = atoi(tempS); + } + 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++; + } +} + +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() +{ + int j; + coTotal = 64; + reFds = (int *) malloc(sizeof(int) * coTotal); + loFds = (int *) 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); + if ((!reFds) || (!loFds) || (!coInputRPos) || (!coInputWPos) || + (!coOutputRPos) || (!coOutputWPos) || + (!coClosed) || (!coClosing) || + (!reClosed) || (!loClosed) || + (!coInput) || (!coOutput)) + { + fprintf(stderr, "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, "Not enough memory to start rinetd.\n"); + exit(1); + } + } +} + +void selectPass(); + +void selectLoop() { + 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() { + 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); + } + /* 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 (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 (errno == EWOULDBLOCK) { + return; + } + if (errno == EINPROGRESS) { + return; + } + handleCloseFromRemote(i); + return; + } + coInputRPos[i] += got; +} + +void handleRemoteWrite(int i) +{ + int got; + if (coClosing[i] && (coOutputWPos[i] == coOutputRPos[i])) { + reClosed[i] = 1; + coClosed[i] = 1; + PERROR("local closed and no more output"); + close(reFds[i]); + return; + } + got = send(reFds[i], coOutput[i] + coOutputWPos[i], + coOutputRPos[i] - coOutputWPos[i], 0); + if (got < 0) { + if (errno == EWOULDBLOCK) { + return; + } + if (errno == EINPROGRESS) { + return; + } + handleCloseFromRemote(i); + return; + } + coOutputWPos[i] += got; + if (coOutputWPos[i] == coOutputRPos[i]) { + coOutputWPos[i] = 0; + coOutputRPos[i] = 0; + } +} + +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 (errno == EWOULDBLOCK) { + return; + } + if (errno == EINPROGRESS) { + 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"); + close(loFds[i]); + return; + } + got = send(loFds[i], coInput[i] + coInputWPos[i], + coInputRPos[i] - coInputWPos[i], 0); + if (got < 0) { + if (errno == EWOULDBLOCK) { + return; + } + if (errno == EINPROGRESS) { + 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"); + close(loFds[i]); + loClosed[i] = 1; + if (!reClosed[i]) { +#ifndef LINUX + /* 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 /* LINUX */ + } +} + +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"); + close(reFds[i]); + reClosed[i] = 1; + if (!loClosed[i]) { +#ifndef LINUX + /* 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 /* LINUX */ + loClosed[i] = 0; + } +} + +void handleAccept(int i) +{ + struct sockaddr addr; + int j; + int addrlen; + int index = -1; + int o; + int nfd = accept(seFds[i], &addr, &addrlen); + if (nfd < 0) { + return; + } + if (nfd > maxfd) { + maxfd = nfd; + } + j = 1; + fcntl(nfd, F_SETFL, O_NONBLOCK); + j = 0; + setsockopt(nfd, SOL_SOCKET, SO_LINGER, &j, sizeof(j)); + 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(int) * coTotal)) + { + goto shortage; + } + if (!SAFE_REALLOC(&loFds, sizeof(int) * o, + sizeof(int) * 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(&reClosed, sizeof(int) * o, + sizeof(int) * coTotal)) + { + goto shortage; + } + if (!SAFE_REALLOC(&loClosed, 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 = coTotal; + } + 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; + /* 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] < 0) { + close(reFds[i]); + reClosed[i] = 1; + loClosed[i] = 1; + coClosed[i] = 1; + return; + } + if (loFds[i] > maxfd) { + maxfd = loFds[i]; + } + /* 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)) < 0) { + close(loFds[i]); + close(reFds[i]); + reClosed[i] = 1; + loClosed[i] = 1; + coClosed[i] = 1; + 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]; +#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 */ + j = 1; + fcntl(loFds[i], F_SETFL, O_NONBLOCK); + if (connect(loFds[i], (struct sockaddr *)&saddr, + sizeof(struct sockaddr_in)) < 0) + { + if (errno != EINPROGRESS) { + PERROR("connect"); + close(loFds[i]); + close(reFds[i]); + reClosed[i] = 1; + loClosed[i] = 1; + coClosed[i] = 1; + 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; + } +} + +void plumber(int s) +{ + /* Just reinstall */ + signal(SIGPIPE, plumber); +} + +void hup(int s) +{ + /* Recreate server sockets */ + createServerSockets(); + /* And reinstall */ + signal(SIGHUP, hup); +} + +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() +{ + FILE *pid_file; + +/* add other systems with wherever they register processes */ +#if defined(LINUX) + pid_file = fopen("/var/run/rinetd.pid", "w"); + if (pid_file == NULL) { + /* non-fatal, non-Linux may lack /var/run... */ + fprintf(stderr, "PID unregistered\n"); + } else { + /* error checking deliberately omitted */ + fprintf(pid_file, "%d\n", getpid()); + fclose(pid_file); + } +#endif /* LINUX */ +}