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 */
+}