#
# usage:
# udptunnel
-# <public-local-host/addr>,<public-local-port>
-# <public-remote-host/addr>,<public-remote-port>
+# [ -l[<local-command/arg>] ... .
+# -e<encryption-mech>[/<encryption-parameter>...]
+# ...
+# ]
+# <public-local-addr>,<public-local-port>
+# <public-remote-addr>,<public-remote-port>
# <private-local-addr>,<private-remote-addr>,<mtu>,<proto>
# <keepalive>,<timeout>
# <extra-local-nets> <extra-remote-nets>
# [ <remote-command> [<remote-args> ...] ]
#
+#
+# <..-addr> may also be hostname
+#
# <local-public-port> may be number or `print' or `silent'
#
# <remote-public-port> may number or `command', in which case
# <remote-command> must be specified and should run udptunnel at the
# remote end; it will be invoked as
-# <remote-command> <public-remote-host/addr>,print
+# <remote-command> <public-remote-addr>,print
# <public-local-addr>,<public-local-port>
# <private-remote-addr>,<private-local-addr>,<mtu>,<proto>
-# <extra-local-nets>
+# <keepalive>,<timeout>
+# <extra-remote-nets> <extra-local-nets>
#
# udptunnel will userv ipif locally, as
-# userv ipif <private-local-addr>,<private-remote-addr>,<mtu>,<proto>
-# <extra-local-nets>
+# userv root ipif <private-local-addr>,<private-remote-addr>,<mtu>,<proto>
+# <extra-local-nets>
+# or, if -l was given, userv root ipif is replaced with the argument(s) to
+# successive -l options.
+
+# Copyright (C) 1999 Ian Jackson
+#
+# This is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with userv-utils; if not, write to the Free Software
+# Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# $Id: udptunnel,v 1.10 2000/05/29 17:16:29 ian Exp $
use Socket;
use POSIX;
sub eat_addr_port ($) {
my ($x) = @_;
- @ARGV or quit("<host/addr>,<port> missing");
+ @ARGV or quit("<addr>,<port> missing");
$_= shift(@ARGV);
$_ =~ m/^([^,]+)\,(\d+|$x)$/ or quit("$_: <host/addr>,<port> bad syntax");
return ($1,$2);
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;
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);
$_= 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;
$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";
}
$_= <C>;
if (!length) {
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");
}
$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");
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;
$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) = @_;
$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: $!");
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: $!");
}