Subject: UDP forwarder

From: Thomas Koenig <[email-masked]>
Date: Sun, 2 Feb 1997 00:27:26 +0100 (MET)

Hi, gang;

for those of you who have a whole room of machines firewalled from the
outside world, here's a UDP forwarder.

It's hacked together a bit, and makes a bit of noise about undefined
variables and void usages of gethostbyname and such, but it seems to
work otherwise.

Usage (on bastion_host):

udp_fwd 7531 kom30.ethz.ch

Then, let all the clients run with
client -e bastion_host

There may be trouble because of the finite number of sockets a single
process can have.  If you start seeing messages about "failed socket",
then you need a second machine to do more forwarding.  Bletcherous, but
the only alternative would have been using raw packets, and that
requires root privilege, plus some extra hacking that I'm not prepared
to do :-)

#! /usr/bin/perl -w

require 5.002;
use Socket;
use Sys::Hostname;

$port = $ARGV[0] or die "Need local port number!\n";
$host = $ARGV[1] or die "Need remote hostname!\n";
$proto = getprotobyname('udp') or die "Can't find protocol udp\n";
$remport = $ARGV[2] or $remport = $port;
$iaddr = gethostbyname($host) or die "Cannot find $host, aborting\n";
$paddr = sockaddr_in($remport, $iaddr);

warn "Forwarding requests to local port $port to $host, port $remport\n"; 

socket(KA, PF_INET, SOCK_DGRAM, $proto)
    or die "Can't create local socket: $\n";

bind(KA, sockaddr_in($port, INADDR_ANY))
    or die "Can't bind local socket:$!\n";

$kain = $rin = '';
vec($kain,fileno(KA),1) = 1;
$rin = $kain;

$lasttime = time;
while (1) {
    $nfound = select($rout=$rin, undef, undef, 60.);
    next if $nfound == 0;
    $nowtime = time;

    if (vec($rout, fileno(KA),1)) {
	$addr = recv ( KA,$packet, 200, 0);
        ($fport, $fiaddr) = unpack_sockaddr_in($addr);
	$name = $name{$addr} = inet_ntoa($fiaddr) . "."  .$fport;
	# print "Getting packet from $name{$addr}; filehandle: ";
	# print $filehandle{$name} if $filehandle{$name};
	if (!$filehandle{$name}) {
	    if ($lasttime{$name}) {
		next;
	    }
	    $i ++;
	    $filehandle{$name} = CHFILE . $i;
	    print "Generating new socket for $name{$addr}\n";
	    if (socket($filehandle{$name}, PF_INET, SOCK_DGRAM, $proto)) {
		if (connect($filehandle{$name}, $paddr)) {
		    $fn = fileno($filehandle{$name});
		    vec($rin, $fn,1) = 1;
		    $fromaddr{$fn} = $addr;
		    $fh{$fn} = $filehandle{$name};
		}
		else {
		    warn "Connect failed for $name{$addr}\n";
		    close($filehandle{$name});
		    undef($filehandle{$name});
		    next;
		}
	    }
	    else {
		warn "Socket failed for $name{$addr}\n";
		undef $filehandle{$name};
		next;
	    }
	}

	$lasttime{$name} = $nowtime;
	send($filehandle{$name}, $packet, 0);
	vec($rout, fileno(KA), 1) = 0;
    }
    if ($rout !~ /^\000*$/) {
	foreach $fn (keys %fromaddr) {
	    if (vec($rout, $fn, 1)) {
		recv($fh{$fn}, $packet, 200, 0);
		send(KA, $packet, 0, $fromaddr{$fn});
	    }
	}
    }

    if ($nowtime - $lasttime > 30.) {
	for (keys %filehandle) {
	    if ($nowtime - $lasttime{$_} > 3600.) {
		close($filehandle{$_});
		undef $filehandle{$_};
	    }
	}
	$lasttime = $nowtime;
    }
}