#!/usr/bin/perl # # Relay program # Listen to a client and pass on anything between # the client and the server # # Author: Qing Gong (gong@us.ibm.com) # Major Cleanup: Tony Sanders # # Date: 09/20/96 # Date: 04/21/97 (bug fix, get rid of defunct processes) # Date: 08/18/97 (created dummy process to release the shell) # Date: 11/10/97 (many changes and fixes, port restrictions) # # CAVEAT: relay must be a hostname (cannot be IP address) # Usage: relay.pl relay-port [virtual_host] $relay_port = shift @ARGV || 9999; # This will cause the server to bind to a specific virtual host # Leave it undefined to bind to INADDR_ANY $LOCAL = shift @ARGV; ### ### SITE CONFIGURATION ### $MIN_PORT = 6660; $MAX_PORT = 6669; # Define this if you want to automatically background the process $DAEMON = 1; # Define this if you want a connection log $LOGFILE = "/tmp/relay.log"; $SOCKADDR_IN = 'S n C4 x8'; # $SOCKADDR_IN = 'x C n C4 x8'; # BSD4.4 socket structure $AF_INET = 2; $SOCKET_STREAM = 1; $CLIENT_QUEUE = 10; eval 'sub SOL_SOCKET {0xffff;}' unless defined &SOL_SOCKET; eval 'sub SO_REUSEADDR {0x04;}' unless defined &SO_REUSEADDR; eval 'sub WNOHANG {1;}' unless defined &WNOHANG; eval '&main'; die "DAEMON: $@" if $@; exit(0); ##################################################################### sub CHILD { while(waitpid(-1,&WNOHANG) > 0) { 0; } } sub main { $PROTO = (getprotobyname('tcp'))[2]; die "getprotobyname(tcp): $!" unless defined $PROTO; socket(SERVER_SOCKET, $AF_INET, $SOCKET_STREAM, $PROTO) || die "Socket $!"; setsockopt(SERVER_SOCKET, &SOL_SOCKET, &SO_REUSEADDR, pack("l", 1)); $myip = "\0\0\0\0"; $myip = (gethostbyname($LOCAL))[4] if defined $LOCAL; die "gethostbyname($LOCAL): $!\n" unless defined $myip; $server_sin = pack($SOCKADDR_IN, $AF_INET, $relay_port, unpack("C4",$myip)); bind(SERVER_SOCKET, $server_sin) || die "Bind $!"; listen(SERVER_SOCKET, $CLIENT_QUEUE) || die "Listen $!"; print "Relay started on port $relay_port\n"; # become a daemon if (defined $DAEMON && $DAEMON != 0) { exit(0) if $pid0 = fork; # parent exits die "fork: $!" unless defined $pid0; } # avoid zombie children $SIG{CHLD} = 'CHILD'; # dummy child process starts here while (1) { # accept client connection (pends until connection) unless (accept(NEW_SOCKET, SERVER_SOCKET)) { &write_log("DAMEON DIED: accept $!"); die "Accept $!" unless $! =~ /interrupted/i; } chop($relayto = ); # log the connection $relayfor = &gethostbysocket(NEW_SOCKET); &write_log("$relayfor -> $relayto"); # forward message to the target machine eval '&run_relay($relayto, $relayfor)'; if ($@) { local($msg) = chop($@); # tell the client something went wrong print NEW_SOCKET "NOTICE AUTH :$@\r\n"; # and note it in our log &write_log("$relayfor -> $relayto: $@"); } # only needed in the child created above close(NEW_SOCKET); } } # get client IP/host name by socket handle sub gethostbysocket { local($name); local($addr) = getpeername($_[0]); local($d, $d1, $p1, $p2, $p3, $p4) = unpack($SOCKADDR_IN, $addr); $addr = pack("C4", $p1, $p2, $p3, $p4); if (!($name = (gethostbyaddr($addr, AF_INET))[0])) { $name = "$p1.$p2.$p3.$p4"; } return $name; } # write a server log line sub write_log { local($sec, $min, $hr, $md, $mon, $yr, $wday, $yday, $isdat) = localtime(time); $mon++; if (defined($LOGFILE)){ open(LOGFILE, ">> $LOGFILE"); # OPTIONAL: || die "$LOGFILE: $!"; printf LOGFILE "%02d:%02d:%02d %02d/%02d/%02d > %s", $hr, $min, $sec, $mon, $md, $yr, $_[0]; close(LOGFILE); } } # relay data between client and target using two processes sub run_relay { local($target, $client) = @_; local($targetname, $targetport) = split(':', $target, 2); die "Invalid Destination Port\n" if ($targetport < $MIN_PORT || $targetport > $MAX_PORT); if (($pid = fork) == 0) { # child does the work, parent continues to accept connections socket(TARGET_SOCKET, $AF_INET, $SOCKET_STREAM, $PROTO) || die "socket: $!\n"; if (defined($LOCAL)) { local($myip) = (gethostbyname($LOCAL))[4]; die "gethostbyname($LOCAL): $!\n" unless defined $myip; local($myaddr) = pack($SOCKADDR_IN, $AF_INET, 0, unpack("C4", $myip)); bind(TARGET_SOCKET, $myaddr) || die "bind: $!\n"; } # XXX: fix this for the case when it's an IP address $targetip = (gethostbyname($targetname))[4]; $target_sin = pack($SOCKADDR_IN, $AF_INET, $targetport, unpack("C4",$targetip)); connect(TARGET_SOCKET, $target_sin) || die "connect: $!\n"; # unbuffer the sockets since we'll be reading and writing # from these filehandles using seperate processes select((select(TARGET_SOCKET), $| = 1)[0]); select((select(NEW_SOCKET), $| = 1)[0]); if ($pid1 = fork) { $SIG{CHLD} = 'CHILD'; # parent relays data from the client while (defined($buf = )) { print TARGET_SOCKET $buf; } &write_log("CLOSED PARENT: $client => $target (child: $pid1)"); close(NEW_SOCKET); close(TARGET_SOCKET); # give other side time to cleanup and then kill it sleep(1); kill(1, $pid1); sleep(1); kill(9, $pid1); exit(0); } elsif (defined $pid1) { # child relays data back from the target while(defined($buf = )) { print NEW_SOCKET $buf; } $pid1 = getppid(); &write_log("CLOSED CHILD: $client <= $target (parent: $pid1)"); close(NEW_SOCKET); close(TARGET_SOCKET); # give other side time to cleanup and then kill it sleep(1); kill(1, $pid1); sleep(1); kill(9, $pid1); exit(0); } # we can only reach this point if an error occurred die "fork1: $!\n"; } elsif (!defined $pid) { sleep(5); # slow things down a bit die "No more relay processes available at this time\n"; } } 
Make your own free website on Tripod.com