#
# usage:
# udptunnel
+# [ -l[<local-command/arg>] ... . ]
# <public-local-host/addr>,<public-local-port>
# <public-remote-host/addr>,<public-remote-port>
# <private-local-addr>,<private-remote-addr>,<mtu>,<proto>
# udptunnel will userv ipif locally, as
# userv root ipif <private-local-addr>,<private-remote-addr>,<mtu>,<proto>
# <extra-local-nets>
+# or, if -lc was given, userv root ipif is replaced with the argument(s) to
+# successive -lc options.
use Socket;
use POSIX;
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);
if ($lps eq 'print') { print($lsp,"\n") or quit("write port to stdout: $!"); }
+@lcmd= qw(userv root ipif) unless @lcmd;
+
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");
close UR; close DW;
open(STDIN,"<&DR") or fail("reopen stdin for packets");
open(STDOUT,">&UW") or fail("reopen stdout for packets");
- exec "userv","root","ipif","$lva,$rva,$mtu,$proto",$lepn;
- quit("cannot execute userv ipif: $!");
+ exec @lcmd;
+ quit("cannot execute $lcmd[0]: $!");
}
close UW;
close DR;
for (;;) {
if ($keepalive) {
$now= now();
- $timeout= $nextsendka-$now;
- if ($timeout < 0) {
+ $thistimeout= $nextsendka-$now;
+ if ($thistimeout < 0) {
defined(send L,"\300",0,$rs)
or warning("transmit keepalive error: $!");
$nextsendka= $now+$keepalive;
- $timeout= $keepalive;
+ $thistimeout= $keepalive;
}
} else {
- $timeout= undef;
+ $thistimeout= undef;
}
- select($readfds=$wantreadfds,'','',$timeout);
+ 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)) {
+ if ($p && !defined(send L,substr($upbuf,0,$p),0,$rs)) {
warning("transmit error: $!");
- } elsif (!$upyet) {
- $upyet= 1;
- debug($downyet ? "tunnel open at this end" : "transmit channel open");
+ } 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)) {
warning("got packet from incorrect peer $rsp_from");
next;
}
+ $downbuf= "\300".$downbuf."\300";
if (!defined($r= syswrite(DW,$downbuf,length $downbuf))) {
warning("tunnel endpoint write error: $!");
} elsif ($r != length $downbuf) {
} else {
if (!$downyet) {
$downyet= 1;
- debug($upyet ? "tunnel open at this end" : "receive channel open");
+ debug($upyet ? "tunnel open at this end" : "receiving");
}
alarm($timeout) if $timeout;
}
}
+ if ($! == ECONNREFUSED) { quit("tunnel closed at remote end"); }
$! == EAGAIN || warning("receive error: $!");
}