#!/usr/athena/bin/perl

# Author: Ken Duda <kkkken@mit.edu>
# When:   Tue Jan 26 10:10:23 1993
#
#
# Forward a TCP connection (designed for use with X, but could be used
# with other services.)  Meant to be run on a gateway host.  Suppose
# you're at foo.com, and your gateway is called gateway.foo.com, and
# your workstation is crufty.foo.com, and you have a friend at bar.edu,
# on host sleepy.bar.edu.  If you wanted to allow him to open an X
# connection to your display, you could type (on gateway.foo.com)
# 
#    tcpforward sleepy.bar.edu 100 crufty.foo.com:0
# 
# 
# Then when a connection comes in on port 6100 on gateway.foo.com, it is
# forwarded to port 6000 on crufty.foo.com... i.e., your friend could
# type (from sleepy.bar.edu only!)
# 
#    xeyes -display gateway.foo.com:100
#
# and the connection would appear on crufty.foo.com's display 0.
# (of course, crufty.foo.com must xhost gateway.foo.com.)

require 'sys/socket.ph';
$sockaddr = 'S n a4 x8';

#
# Configuration
#

$X0_port = 6000;

#
# Cmd-line args
#

sub usage {
    print STDERR "usage: $0 from-host gateway-display-number to-display
example: $0 outside-host.way-far-away.edu 100 myhost.my-company.com:0\n";
    exit 1;
}

$source_host = shift;
$gateway_port = shift + $X0_port;
$destination_display = shift;

&usage if @ARGV;
($destination_display =~ /^([^:]+):(-?\d+)(.(\d+))?$/) || &usage;
$destination_host = $1;
$destination_dpynum = $2;

die("$source_host: no such host") unless (@source_hostdef = gethostbyname ($source_host));
die("$destination_host: no such host") unless (@destination_hostdef = gethostbyname ($destination_host));

chop ($thishostname = `hostname`);
$thishostaddr = (gethostbyname($hostname))[4];

socket (SOURCE_WAIT, &PF_INET, &SOCK_STREAM, 0) || die("socket: $!");

bind (SOURCE_WAIT, pack($sockaddr, &AF_INET, $gateway_port, $thishostaddr)) || die("bind: $!");
listen (SOURCE_WAIT, 1) || die("listen: $!");
print "Waiting for connection from $source_hostdef[0]...\n";
accept (SOURCE, SOURCE_WAIT) || die("accept: $!");
close (SOURCE_WAIT);

$connection_from = (unpack($sockaddr, getpeername(SOURCE)))[2];

if ($connection_from ne $source_hostdef[4]) {
    @wronghost = unpack($connection_from, "C4");
    printf ("Got connection from WRONG HOST: %d.%d.%d.%d\n", @wronghost);
    exit 1;
}

print "Connection from source established; connecting to $destination_host on :$destination_dpynum...\n";
socket (DESTINATION, &PF_INET, &SOCK_STREAM, 0) || die("socket: $!");
connect (DESTINATION, pack($sockaddr, &AF_INET, $X0_port + $destination_dpynum, $destination_hostdef[4])) || die("connect: $!");

print "Connection to destination established; forwarding packets...\n";

while (1) {
    vec ($rbits, fileno(DESTINATION), 1) = 1;
    vec ($rbits, fileno(SOURCE), 1) = 1;
    if (select($rbits,undef,undef,undef)) {
	if (vec($rbits, fileno(DESTINATION), 1)) {
	    $numread = sysread(DESTINATION, $packet, 4096);
	    defined($numread) || die("reading from host $destination_host: $!");
	    die ("Connection closed by host $destination_host") unless $numread;
	    select SOURCE;
	    $| = 1;
	    print $packet;
	}
	if (vec($rbits, fileno(SOURCE), 1)) {
	    $numread = sysread(SOURCE, $packet, 4096);
	    defined($numread) || die("reading from host $source_host: $!");
	    die ("Connection closed by host $source_host") unless $numread;
	    select DESTINATION;
	    $| = 1;
	    print $packet;
	}
    } else {
	print STDERR "$0: select: $!\n";
    }
}

	
