X-Git-Url: https://git.distorted.org.uk/~mdw/userv-utils/blobdiff_plain/46ab1c83722a65a7fb6400ac1c72e1c06990c98a..8d9c58663412f651132b7f75ca3a49514cb1fd4d:/ipif/udptunnel diff --git a/ipif/udptunnel b/ipif/udptunnel index 1c022ba..1b5031c 100755 --- a/ipif/udptunnel +++ b/ipif/udptunnel @@ -3,6 +3,7 @@ # # usage: # udptunnel +# [ -l[] ... . ] # , # , # ,,, @@ -18,11 +19,14 @@ # ,print # , # ,,, -# +# , +# # # udptunnel will userv ipif locally, as -# userv ipif ,,, -# +# userv root ipif ,,, +# +# or, if -lc was given, userv root ipif is replaced with the argument(s) to +# successive -lc options. use Socket; use POSIX; @@ -51,7 +55,7 @@ sub conv_host_addr ($) { defined($r= inet_aton($s)) or quit("$s: cannot convert to address"); return $r; } -sub conv_port_addr ($) { +sub conv_port_number ($) { my ($s,$r) = @_; if ($s =~ m/\d/) { $r= $s+0; @@ -67,6 +71,19 @@ sub show_addr_port ($) { return inet_ntoa($s[1]).','.$s[0]; } +@lcmd= (); + +while ($ARGV[0] =~ m/^-/) { + $_= shift @ARGV; + last if $_ eq '--'; + if (s/^-l//) { + push @lcmd,$_ if length; + while (@ARGV && ($_= shift @ARGV) ne '-') { push @lcmd, $_; } + } else { + quit("unknown option \`$_'"); + } +} + ($las,$lps)= eat_addr_port('print|silent'); $la= conv_host_addr($las); $lp= conv_port_number($lps); @@ -79,11 +96,11 @@ $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; +($lva,$rva,$mtu,$proto) = ($1,$2,$3,$4); $_= shift @ARGV; -m/^(\d+),(\d+)$/ or quit("timeout missing or bad syntax"); -($keepalive,$timeout)= $1,$2; +m/^(\d+),(\d+)$/ or quit("keepalive,timeout missing or bad syntax"); +($keepalive,$timeout)= ($1,$2); $keepalive && ($timeout > $keepalive*2) or quit("timeout must be < 2*keepalive") if $timeout; @@ -100,13 +117,13 @@ 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; + @rcmd= (@ARGV, "$ras,print", "$lsp", "$rva,$lva,$mtu,$proto", + "$keepalive,$timeout", $repn, $lepn); + debug("remote command @rcmd"); 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"; + exec @rcmd; die "$progname: error: failed to execute $rcmd[0]: $!\n"; } $_= ; if (!length) { @@ -117,10 +134,10 @@ if ($rps eq 'command') { 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); + $rp= conv_port_number($rps); defined($c2= fork) or fail("fork for cat"); if (!$c2) { - open STDIN,">&C" or fail("redirect remote pipe to stdin"); + open(STDIN,"<&C") or fail("redirect remote pipe to stdin"); close C; exec "cat"; fail("execute cat"); } @@ -131,11 +148,13 @@ if ($rps eq 'command') { $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: $!"); } -if ($lps eq 'print') { print $lsp,"\n" or quit("write port to stdout: $!"); } +@lcmd= qw(userv root ipif) unless @lcmd; -debug("public address $lsp"); +debug("using remote $rsp local $lsp"); +push @lcmd, ("$lva,$rva,$mtu,$proto",$lepn); +debug("local command @lcmd"); pipe(UR,UW) or fail("up pipe"); pipe(DR,DW) or fail("down pipe"); @@ -143,10 +162,10 @@ 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: $!"); + open(STDIN,"<&DR") or fail("reopen stdin for packets"); + open(STDOUT,">&UW") or fail("reopen stdout for packets"); + exec @lcmd; + quit("cannot execute $lcmd[0]: $!"); } close UW; close DR; @@ -155,8 +174,8 @@ $upyet= 0; $downyet= 0; $wantreadfds=''; -vec($wantreadfds,fileno(UR))= 1; -vec($wantreadfds,fileno(L))= 1; +vec($wantreadfds,fileno(UR),1)= 1; +vec($wantreadfds,fileno(L),1)= 1; sub nonblock ($) { my ($fh,$fl) = @_; @@ -170,8 +189,23 @@ nonblock('L'); $upbuf= ''; +sub now () { my ($v); defined($v= time) or fail("get time"); return $v; } +if ($keepalive) { $nextsendka= now(); } + for (;;) { - select($readfds=$wantreadfds,'','',undef); + if ($keepalive) { + $now= now(); + $thistimeout= $nextsendka-$now; + if ($thistimeout < 0) { + defined(send L,"\300",0,$rs) + or warning("transmit keepalive error: $!"); + $nextsendka= $now+$keepalive; + $thistimeout= $keepalive; + } + } else { + $thistimeout= undef; + } + select($readfds=$wantreadfds,'','',$thistimeout); for (;;) { if (!defined($r= sysread(UR,$upbuf,$mtu*2+3,length($upbuf)))) { $! == EAGAIN || warning("tunnel endpoint read error: $!"); @@ -181,29 +215,37 @@ for (;;) { 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 { + if ($p && !defined(send L,substr($upbuf,0,$p),0,$rs)) { warning("transmit error: $!"); + } else { + if (!$upyet) { + $upyet= 1; + debug($downyet ? "tunnel open at this end" : "transmitting"); + } + if ($keepalive) { $nextsendka= now()+$keepalive; } } - $upbuf= substr($upbuf+1,$p); + $upbuf= substr($upbuf,$p+1); } } 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; + next; } + $downbuf= "\300".$downbuf."\300"; if (!defined($r= syswrite(DW,$downbuf,length $downbuf))) { warning("tunnel endpoint write error: $!"); - } else if ($r != length $downbuf) { + } elsif ($r != length $downbuf) { warning("tunnel endpoint wrong write length"); } else { - $downyet= 1; - debug($upyet ? "tunnel open" : "receive channel open"); + if (!$downyet) { + $downyet= 1; + debug($upyet ? "tunnel open at this end" : "receiving"); + } + alarm($timeout) if $timeout; } } + if ($! == ECONNREFUSED) { quit("tunnel closed at remote end"); } $! == EAGAIN || warning("receive error: $!"); }