From 46ab1c83722a65a7fb6400ac1c72e1c06990c98a Mon Sep 17 00:00:00 2001 From: ian Date: Fri, 24 Sep 1999 16:05:21 +0000 Subject: [PATCH] Initial draft of udptunnel; coding finished, not yet compiled/tested. --- ipif/udptunnel | 209 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100755 ipif/udptunnel diff --git a/ipif/udptunnel b/ipif/udptunnel new file mode 100755 index 0000000..1c022ba --- /dev/null +++ b/ipif/udptunnel @@ -0,0 +1,209 @@ +#!/usr/bin/perl +# Simple tunnel for userv-ipif tunnels. +# +# usage: +# udptunnel +# , +# , +# ,,, +# , +# +# [ [ ...] ] +# +# may be number or `print' or `silent' +# +# may number or `command', in which case +# must be specified and should run udptunnel at the +# remote end; it will be invoked as +# ,print +# , +# ,,, +# +# +# udptunnel will userv ipif locally, as +# userv ipif ,,, +# + +use Socket; +use POSIX; +use Fcntl; + +$progname= $0; $progname =~ s,.*/,,; +$|=1; + +chomp($hostname= `uname -n`); +$? and die "$progname: cannot get hostname (uname failed with code $?)\n"; + +sub quit ($) { die "$progname - $hostname: fatal error: $_[0]\n"; } +sub debug ($) { print "$progname - $hostname: debug: $_[0]\n"; } +sub fail ($) { quit("unexpected system call failure: $_[0]: $!\n"); } +sub warning ($) { warn "$progname - $hostname: $_[0]\n"; } + +sub eat_addr_port ($) { + my ($x) = @_; + @ARGV or quit(", missing"); + $_= shift(@ARGV); + $_ =~ m/^([^,]+)\,(\d+|$x)$/ or quit("$_: , bad syntax"); + return ($1,$2); +} +sub conv_host_addr ($) { + my ($s,$r) = @_; + defined($r= inet_aton($s)) or quit("$s: cannot convert to address"); + return $r; +} +sub conv_port_addr ($) { + my ($s,$r) = @_; + if ($s =~ m/\d/) { + $r= $s+0; + $r>0 && $r<65536 or quit("$s: port out of range"); + } else { + $r= 0; + } + return $r; +} +sub show_addr_port ($) { + my ($s,@s) = @_; + @s= unpack_sockaddr_in($s); + return inet_ntoa($s[1]).','.$s[0]; +} + +($las,$lps)= eat_addr_port('print|silent'); +$la= conv_host_addr($las); +$lp= conv_port_number($lps); +$ls= pack_sockaddr_in $lp,$la; + +($ras,$rps)= eat_addr_port('command'); +$rp= conv_port_number($rps); +$ra= $rps eq 'command' ? '' : conv_host_addr($ras); + +$_= shift @ARGV; +m/^([.0-9]+),([.0-9]+),(\d+),(slip|cslip)$/ + or quit("lvaddr,rvaddr,mtu,proto missing or bad syntax or proto not [c]slip"); +($lva,$rva,$mtu,$proto= $1,$2; + +$_= shift @ARGV; +m/^(\d+),(\d+)$/ or quit("timeout missing or bad syntax"); +($keepalive,$timeout)= $1,$2; +$keepalive && ($timeout > $keepalive*2) or quit("timeout must be < 2*keepalive") + if $timeout; + +$lepn= shift @ARGV; +$repn= shift @ARGV; + +alarm($timeout); + +defined($udp= getprotobyname('udp')) or fail("getprotobyname udp"); + +socket(L,PF_INET,SOCK_DGRAM,$udp) or fail("socket"); +bind(L,$ls) or quit("bind failed: $!"); +defined($ls= getsockname(L)) or fail("getsockname"); +$lsp= show_addr_port($ls); + +if ($rps eq 'command') { + $lepn= shift @ARGV; + quit("when using ,command for remote, must supply command") unless @ARGV; + defined($c= open C,"-|") or fail("fork for remote"); + if (!$c) { + push @ARGV, "$ras,print", "$lsp", "$rva,$lva,$mtu,$proto", $lepn; + debug("remote command @ARGV"); + exec @ARGV; die "$progname: error: failed to execute $ARGV[0]: $!\n"; + } + $_= ; + if (!length) { + close C; + quit($? ? "remote command failed (code $?)" : "no details received from remote"); + } + chomp; + m/^([.0-9]+)\,(\d+)$/ or quit("invalid details from remote end ($_)"); + ($ras,$rps) = ($1,$2); + $ra= conv_host_addr($ras); + $rp= conv_port_addr($rps); + defined($c2= fork) or fail("fork for cat"); + if (!$c2) { + open STDIN,">&C" or fail("redirect remote pipe to stdin"); + close C; + exec "cat"; fail("execute cat"); + } +} else { + quit("when not using ,command for remote, must not supply command") if @ARGV; +} + +$rs= pack_sockaddr_in $rp,$ra; +$rsp= show_addr_port($rs); + +debug("using remote $rsp"); + +if ($lps eq 'print') { print $lsp,"\n" or quit("write port to stdout: $!"); } + +debug("public address $lsp"); + +pipe(UR,UW) or fail("up pipe"); +pipe(DR,DW) or fail("down pipe"); + +defined($c3= fork) or fail("fork for ipif"); +if (!$c3) { + close UR; close DW; + open STDIN,"<&DR" or fail("reopen stdin for packets"); + open STDOUT,"<&UW" or fail("reopen stdout for packets"); + exec "userv","ipif","$lva,$rva,$mtu,$proto",$lepn; + quit("cannot execute userv ipif: $!"); +} +close UW; +close DR; + +$upyet= 0; +$downyet= 0; + +$wantreadfds=''; +vec($wantreadfds,fileno(UR))= 1; +vec($wantreadfds,fileno(L))= 1; + +sub nonblock ($) { + my ($fh,$fl) = @_; + ($fl= fcntl($fh,F_GETFL,0)) or fail("nonblock F_GETFL"); + $fl |= O_NONBLOCK; + fcntl($fh, F_SETFL, $fl) or fail("nonblock F_SETFL"); +} + +nonblock('UR'); +nonblock('L'); + +$upbuf= ''; + +for (;;) { + select($readfds=$wantreadfds,'','',undef); + for (;;) { + if (!defined($r= sysread(UR,$upbuf,$mtu*2+3,length($upbuf)))) { + $! == EAGAIN || warning("tunnel endpoint read error: $!"); + last; + } + if (!$r) { + quit "tunnel endpoint closed by system"; + } + while (($p= index($upbuf,"\300")) >= 0) { + if (defined(send L,substr($upbuf,0,$p),0,$rs)) { + $upyet= 1; + debug($downyet ? "tunnel open" : "transmit channel open"); + } else { + warning("transmit error: $!"); + } + $upbuf= substr($upbuf+1,$p); + } + } + while (defined($rs_from= recv L,$downbuf,$mtu*2+3,0)) { + $rsp_from= show_addr_port($rs_from); + if ($rsp_from ne $rsp) { + warning("got packet from incorrect peer $rsp_from"); + continue; + } + if (!defined($r= syswrite(DW,$downbuf,length $downbuf))) { + warning("tunnel endpoint write error: $!"); + } else if ($r != length $downbuf) { + warning("tunnel endpoint wrong write length"); + } else { + $downyet= 1; + debug($upyet ? "tunnel open" : "receive channel open"); + } + } + $! == EAGAIN || warning("receive error: $!"); +} -- 2.30.2