#!/usr/local/bin/perl ;# ;# tcprelay: application level tcp bridge ;# ;# Copyright (c) 1990,1991,1992 Kazumasa Utashiro ;# Software Research Associates, Inc., Japan ;# ;# Version 1.0, Oct 29 1990 ;# Version 1.1, Jan 21 1991 ;; $rcsid = '$Id: tcprelay,v 1.2 1992/04/13 19:10:28 utashiro Exp $';#' ;# ;# Usage: ;# tcprelay [switches] servername clientname [service] ;# tcprelay [switches] -i ;# ;# Switches: ;# -fg: force foregound ;# -bg: force backgound [default] ;# -ftp: force ftp mode (automatically on when connecting to ftp port) ;# ;# Description: ;# This program relays tcp connection in application layer, which ;# is useful when connecting across the IP disjoint gateway. ;# Tcprelay connect to specified server and then makes local port ;# and listen for connection from the client. After tcprelay listen, ;# anybody can connect to that port, so client name is required ;# to avoid unexpected connect request. Local port number is not ;# explicitly defined, so you have to see message from tcprelay ;# and invoke internet command with that number on client machine. ;# ;# -------- ----|---- -------- ;# |client|---|gateway|---|server| ;# -------- ----|---- -------- ;# ;# If the session seems to be ftp, tcprelay fakes PORT command ;# in ftp interaction. It makes connection to port in CLIENT which ;# is specified in PORT command, and makes local socket to listen from ;# ftp SERVER and returns that local port number to SERVER instead of ;# the number sent from ftp CLIENT. Use -ftp option when you want to ;# connect to ftpd which doesn't have standard port number 21. ;# ;# Default service is ftp, because this program is made for doing ;# ftp originaly. ;# ;# Example: ;# 1) % tcprelay server client : on gateway ;# port=xxxx : remember port number in message ;# 2) % ftp gateway xxxx : on client ;# ;# require 'sys/socket.ph'; unless (do 'sys/socket.ph') { print "File sys/socket.ph is not found. Using default...\n"; eval 'sub SOCK_STREAM {1;} sub AF_INET {2;} sub PF_INET {2;}'; } while ($_ = $ARGV[0], /^-/) { shift; if (/-s$/) {$silent = 1; next;} if (/-i$/) {$interactive = 1; next;} if (/-ftp$/) {$ctype = 'ftp'; next;} if (/-d(\d*)$/) {$debug = $1||1; next;} if (/-(fg|bg)$/) {$fg = $1 eq 'fg'; next;} &usage; } $| = 1; if ($interactive) { print "Enter server name or address: "; chop($servername=<>); print "Enter client name or address: "; chop($clientname=<>); } else { if ($#ARGV < $[+1) {&usage;} ($servername, $clientname, $serverport, $localport) = @ARGV; } sub usage { ($myname = $0) =~ s|.*/||; print "Usage: $myname server client, or $myname -i\n"; print "$rcsid\n" if $rcsid =~ /:/; exit(1); } $sockaddr='S n a4 x8'; ($name, $aliases, $TCP) = getprotobyname('tcp'); $serverport='ftp' unless $serverport; chop($localname = `hostname`); ($name, $aliases, $type, $len, $localaddr) = gethostbyname($localname); ($serveraddr = &getaddr($servername)) || die "Unknown server $servername.\n"; ($clientaddr = &getaddr($clientname)) || die "Unknown client $clientname.\n"; ($name, $aliases, $serverport) = getservbyname($serverport, 'tcp') unless $serverport =~ /^\d+$/; if (!defined($ctype)) { if ($serverport == 21) {$ctype = 'ftp';} else {$ctype = 'something';} } $masterpid=$$; $SIG{'HUP'}=$SIG{'INT'}=$SIG{'QUIT'}=$SIG{'TERM'}='terminate'; sub terminate {kill -15, $masterpid; exit(1);} &relay($ctype, $serveraddr, $serverport); sub relay { local($type, $serveraddr, $serverport, $newport)=@_; local($toplevel) = ($$ eq $masterpid); $this = pack($sockaddr, &AF_INET, 0, $localaddr); $that = pack($sockaddr, &AF_INET, $serverport, $serveraddr); socket(S1, &PF_INET, &SOCK_STREAM, $TCP) || die "socket: $!"; bind(S1, $this) || die "bind: $!"; connect(S1, $that) || die "connect: $!"; select(S1); $| = 1; select(stdout); print "Connection established with server\n" if ($toplevel); print "Connection type is ftp\n" if ($type eq 'ftp'); $this = pack($sockaddr, &AF_INET, $localport, "\0\0\0\0"); $localport = 0; socket(A1, &PF_INET, &SOCK_STREAM, $TCP) || die "socket: $!\n"; bind(A1, $this) || die "bind: $!\n"; listen(A1, 1) || die "listen: $!\n"; $newport = (unpack($sockaddr, getsockname(A1)))[1]; if (!$toplevel && !fork) { close(S1); close(A1); return($newport); } close(S), close(C) unless $toplevel; open(S,"+>&S1"); close(S1); open(A,"+>&A1"); close(A1); printf "Please connect to port=%d\n", $newport if $toplevel; ($addr = accept(C, A)) || die "accept: $!\n"; close(A); ($af, $peerport, $peeraddr) = unpack($sockaddr, $addr); if ($toplevel && $peeraddr ne $clientaddr) { printf ("Connection from %s is not allowed!\n", join('.', unpack('C4', $peeraddr))); exit(1); } print "Connection established with client\n" if ($toplevel); select(S); $| = 1; select(C); $| = 1; select(stdout); if ($child = fork) { if ($toplevel && !$fg && ($pid = fork)) { print "Process is running in background (pid=$pid)\n"; exit(0); } &forward('data', S, C); } else { &forward($type, C, S); } print "$$: exiting\n" if $debug; exit(0); } sub forward { local($type, $from, $to) = @_; if ($type eq 'ftp') { while (<$from>) { if (/^PORT ([\d,]+)/ && (@p = split(/,/, $1))) { $p = &relay('data', pack('C4', @p), $p[4]*256 + $p[5]); $_ = sprintf("PORT %d,%d,%d,%d,%d,%d\n", unpack('C4', $localaddr), $p/256, $p%256); } print $to $_; } } else { print $to $_ while(read($from, $_, 4096)); } shutdown($from, 1); shutdown($to, 0); } sub getaddr { local($_) = @_; /^[0-9\.]+$/ ? pack("C4", split(/\./)) : (gethostbyname($_))[4]; }