/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* tcpproxy [-/+options] host port

* This is a little program that makes a connection to the given  host
* and  then acts as a pipe to send data from stdin to the connection,
* and from the connection to stdout. We can also produce a transcript
* of the session in a logfile if desired.  Thus, this program is just
* a variant of the tee(1) command, but with a TCP  connection  rather
* than a local pipe.

* Options may start with '+' or '-' to enable/disable features;  with
* the  options for which this doesn't make sense, either may be used.
* An option letter may be upper or lower case.

* The options recognized are:

* -t<t>
*   timeout: waits <t> seconds for replies; the default is -1 (none).

* You'd  think  that  the  telnet(1) command would do the job, but it
* doesn't.  The problem  is  that  with  telnet  you  get  everything
* (including  passwords)  echoed  by  someone.  If the other end is a
* login session, the result is double echoing of input,  which  users
* find confusing and exasperating.  And the echoing of passwords is a
* showstopper, of course.

* To set this program up as a proxy server, you can create entries in
* /etc/services and /etc/inetd.conf as follows:

* AUTHOR:
*   John Chambers <jc@trillian.mit.edu>
*/


:1#include "V.h"
#include "sys_socket.h"
#include "sys_in.h"
#include "sys_netdb.h"

#define BUF 50		/* Enough to hold longest message */

char  buf[BUF];
char *dfl_host = "localhost";
char *host = 0;
char  ifmt[] = "%d";
TIMV  tmout = {1};	/* How long to wait in select() */
TIMV* tmop = 0;		/* Null for no timeout */
char *portname = 0;
int   port = 0;
U32   now, then;	/* Timestamps */
U32   rmask = 0;	/* Select read mask */
int   rsleep = -1;	/* Seconds to wait for supplies */
int   wsleep = -1;	/* Seconds between writes */
byte  TNwont[]    = {0xFF,0xFC,0};	/* Generic telnet refusal */
byte  TNwontx18[] = {0xFF,0xFC,0x18};	/* Refusal to do 0x18 */
byte  TNwontx01[] = {0xFF,0xFC,0x01};	/* Refusal to do 0x01 */
byte  TNwontx03[] = {0xFF,0xFC,0x03};	/* Refusal to do 0x03 */

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
main(ac,av)
	int   ac;
	char**av;
{	int   r=0;	/* Result (exit status) */
	char *p;
	int   c0, c1;
	int   a, i, fdcount, m, n, sock, sockmask;
	SKin addr;
	HOST *hp, *gethostbyname();
	int   x0,x1,x2,x3;

	printf("tcpproxy\n");
:1	ac = Vinit(ac,av);


	for (a=1; a<ac; a++) {
:3		V3 "Arg %2d: \"%s\"",a,av[a] D;
		switch (c0 = av[a][0]) {
		case '-':
		case '+':
:2			V3 "Opt %2d: \"%s\"",a,av[a] D;
			switch (c1 = av[a][1]) {
			case 'r':
			case 'R':
				if (sscanf(av[a]+2,ifmt,&rsleep) < 1) ++n;
:2				V2 "Wait %d seconds for replies.",rsleep D;
				break;
			case 's':
			case 'S':
				if (sscanf(av[a]+2,ifmt,&wsleep) < 1) ++n;
:2				V2 "Wait %d seconds between messages.",wsleep D;
				break;
:1			default:
:1				V1 "Arg %2d: \"%s\" ignored.",a,av[a] D;
:1				break;
			}
			break;
		default:
			if (!host) {
				host = av[a];
:2				V2 "Host: \"%s\"",host D;
			} else
			if (!portname && (sscanf(av[a],ifmt,&port) > 0)) {
				portname = av[a];
:2				V2 "Port: \"%s\"",portname D;
:3				V3 "port: %d.",port D;
:1			} else {
:1				V1 "Arg %2d: \"%s\" ignored.",a,av[a] D;
			}
			break;
		}
	}
	if (!port) port = 23;	/* Default is telnet port */

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Create socket on which to read.                                      *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
:6	V6 "before socket(AF_INET=%d, SOCK_STREAM=%d, 0)",AF_INET,SOCK_STREAM D;
	sock = Socket(AF_INET, SOCK_STREAM, 0);
	if (sock < 0) {
:1		V1 "### Can't get datagram socket [Err %d=%s=%s]",Errinfo D;
		r = 2;
		Fail;
	}
:3	V3 "Got TCP stream socket %d.",sock D;

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Construct address of socket to send to.   Gethostbyname()  retuns  a *
* structure  including the network address of the specified host.  The *
* port number is taken from the command line.                          *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
	if (!host) {
		host = dfl_host;
:3		V3 "No host; using \"%s\"",host D;
	}
	if (sscanf(host,"%d.%d.%d.%d",&x0,&x1,&x2,&x3) > 3) {
		byte p[4];
:2		V2 "%s is IP address %d.%d.%d.%d",host,x0,x1,x2,x3 D;
		p[0] = x0;
		p[1] = x1;
		p[2] = x2;
		p[3] = x3;
:5		H5(p,4,"h-addr");
		BCopy(p,&(addr.sin_addr.s_addr),hp->h_length);
	} else {
:2		V2 "\"%s\" is host name to look up.",host D;
		hp = Gethostbyn(host);
		BCopy(hp->h_addr,&(addr.sin_addr.s_addr),hp->h_length);
		addr.sin_family = AF_INET;
		addr.sin_port = htons(port);
:6		H6(&addr,sizeof(addr),"addr");
	}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Try to make a connection to the server.                              *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
	if (Connect(sock,&addr,sizeof(addr)) < 0) {
:2		V1 "Can't connect to %s.%u.",
:2			ipdot(&addr.sin_addr),
:2			ntohs( addr.sin_port) D;
		Fail;
	}
:3	V3 "Connected to %s:%u.",
:3		ipdot(&addr.sin_addr),
:3		ntohs( addr.sin_port) D;
	if (port == 23) {
:3		V3 "Send TNwont messages ..." D;
:3		V3 "Send TNwontx18 ..." D;
		Write(sock,TNwontx18,3);
:3		V3 "Send TNwontx01 ..." D;
		Write(sock,TNwontx01,3);
:3		V3 "Send TNwontx03 ..." D;
		Write(sock,TNwontx03,3);
	}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Now go into a read/write loop.  We hang looking for input from stdin
* or the socket, and input from either is sent to the other.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
	fprintf(stderr,"Wait %d seconds for replies ...\n",rsleep);
	then = UnixTime(0);
	fdcount = sock + 1;
	sockmask = 1 << sock;
	MakeRaw(0,"stdin");
	nbio(0);	/* Make sure stdin is nonblocking */
loop:
	now = UnixTime(0);
	if (rsleep >= 0) {
		tmop = &tmout;
		if ((tmout.tv_sec = rsleep - (now - then)) < 0) {
:2			V2 "Out of time." D;
			Done;
		}
	} else {
		tmop = 0;
	}
	rmask = sockmask | 1 ;
	errno = 0;
	if ((i = Select(fdcount, &rmask,0,0,tmop)) > 0) {
		char* nowp = Vtime();
:3		V3 "Got input, rmask=%08X at %s",rmask,nowp D;
		if (rmask & 1) {
			n = Read(0, buf, BUF);
			if (n > 0) {
:3				V3 "<stdin %s",Dsps(buf,n) D;
:2				P2 "%s > %s",nowp,Dsps(buf,n) D;
				Write(sock,buf,n);
			} elsif (n == 0) {
:2				V2 "EOF on stdin." D;
				MakeSane(0,"stdin");
				exit(0);
			} else {
:1				V1 "Error on stdin: %d=%s=%s.",Errinfo D;
				MakeSane(0,"stdin");
				exit(errno);
			}
			Loop;
		} elsif (rmask & sockmask) {
			n = Read(sock, buf, BUF);
:2			V5 "Got from %s.%d %s\n",
:2				ipdot(&addr.sin_addr),
:2				ntohs( addr.sin_port),
:2				Dsps(buf,n) D;
:2			P2 "%s < %s",nowp,Dsps(buf,n) D;
			if (n > 0) {
:3				V3 "<sock: %s",Dsps(buf,n) D;
				Write(1,buf,n);
			} elsif (n == 0) {
:2				V2 "EOF from %s.%d.\n",
:2					ipdot(&addr.sin_addr),
:2					ntohs( addr.sin_port) D;
				MakeSane(0,"stdin");
				exit(0);
			} else {
:2				V2 "ERR %d=%s=%s from %s.%d.\n",Errinfo,
:2					ipdot(&addr.sin_addr),
:2					ntohs( addr.sin_port) D;
				MakeSane(0,"stdin");
				exit(errno);
			}
			Loop;
		}
	}
	if (i == 0) {
:2		V2 "Timeout." D;
		Done;
	}
:2	if (errno) {
:2		V2 "Can't read from socket [Err %d=%s=%s]",Errinfo D;
:2	} else {
:2		V2 "EOF from socket." D;
:2	}
fail:
done:
	Close(sock);	/* For debug messages */
	MakeSane(0,"stdin");
	Exit(r);
}
usage()
{
	printf("Usage: %s [-/+options] [host] [port]\n", pname);
}

