chiark / gitweb /
@@ -1,6 +1,7 @@
[userv-utils.git] / ipif / udptunnel
index 8a2dbc55394bee11fafcfdc67bb332af315c5df6..d7a00368cc96308c4dfe14a2e2f6496ac957beef 100755 (executable)
@@ -3,19 +3,26 @@
 #
 # 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>
 #                     <keepalive>,<timeout>
 # udptunnel will userv ipif locally, as
 #    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$
 
 use Socket;
 use POSIX;
@@ -42,7 +69,7 @@ sub warning ($) { warn "$progname - $hostname: $_[0]\n"; }
 
 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);
@@ -68,6 +95,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);
@@ -134,7 +174,11 @@ $rsp= show_addr_port($rs);
 
 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");
@@ -144,8 +188,8 @@ 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","root","ipif","$lva,$rva,$mtu,$proto",$lepn;
-    quit("cannot execute userv ipif: $!");
+    exec @lcmd;
+    quit("cannot execute $lcmd[0]: $!");
 }
 close UW;
 close DR;
@@ -175,17 +219,17 @@ if ($keepalive) { $nextsendka= now(); }
 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: $!");
@@ -195,13 +239,16 @@ for (;;) {
            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" : "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)) {
@@ -210,6 +257,7 @@ for (;;) {
            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) {
@@ -217,10 +265,11 @@ for (;;) {
        } else {
            if (!$downyet) {
                $downyet= 1;
-               debug($upyet ? "tunnel open" : "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: $!");
 }