From 9d4e63db418172c744c7bc98a7bc21c1c876f1ef Mon Sep 17 00:00:00 2001 From: ian Date: Tue, 30 May 2000 22:49:36 +0000 Subject: [PATCH] Works at least without crypto. --- ipif/forwarder.c | 47 +++--- ipif/udptunnel | 392 ++++++++++++++++++++++++++++++----------------- 2 files changed, 283 insertions(+), 156 deletions(-) diff --git a/ipif/forwarder.c b/ipif/forwarder.c index 9b7e51e..a9e43e0 100644 --- a/ipif/forwarder.c +++ b/ipif/forwarder.c @@ -107,9 +107,12 @@ static void inbound(void) { int r, i, different, this_saddrlen; const char *emsg; + buf_in.start= buf_in.base+1; + buf_in.size= buffer_size-2; + setnonblock(public_local_fd,1); this_saddrlen= sizeof(this_saddr); - r= recvfrom(public_local_fd, buf_in.base, buffer_size-1, 0, + r= recvfrom(public_local_fd, buf_in.start, buf_in.size, 0, &this_saddr, &this_saddrlen); if (!r) { diag("empty ciphertext"); return; } @@ -118,14 +121,14 @@ static void inbound(void) { return; } if (this_saddr.sin_family != AF_INET) { - fprintf(stderr,"%s: received unknown AF %lu", + fprintf(stderr,"%s: received unknown AF %lu\n", programid, (unsigned long)this_saddr.sin_family); return; } assert(this_saddrlen == sizeof(this_saddr)); - buf_in.size= buffer_size; - buf_in.start= buf_in.base; + assert(r <= buf_in.size); + buf_in.size= r; for (i=n_mechs-1; i>=0; i--) { emsg= mechs[i]->decode(md_in[i],&buf_in); if (emsg) { @@ -136,21 +139,22 @@ static void inbound(void) { alarm(timeout); - different= !public_remote_specd || - memcmp(&this_saddr,&public_remote,sizeof(this_saddr)); + different= (!public_remote_specd || + public_remote.sin_addr.s_addr != this_saddr.sin_addr.s_addr || + public_remote.sin_port != this_saddr.sin_port); if (different) { if (public_remote_specd==2) { - fprintf(stderr, "%s: packet from unexpected sender %s:%lu", + fprintf(stderr, "%s: packet from unexpected sender %s:%lu\n", programid, inet_ntoa(this_saddr.sin_addr), - (unsigned long)this_saddr.sin_port); + (unsigned long)ntohs(this_saddr.sin_port)); return; } - fprintf(stderr, "%s: tunnel open with peer %s:%lu", + fprintf(stderr, "%s: tunnel open with peer %s:%lu\n", programid, inet_ntoa(this_saddr.sin_addr), - (unsigned long)this_saddr.sin_port); + (unsigned long)ntohs(this_saddr.sin_port)); nextsendka= now(); public_remote_specd= 1; memcpy(&public_remote,&this_saddr,sizeof(public_remote)); @@ -162,10 +166,14 @@ static void inbound(void) { } any_recvd= 1; - - buf_in.start[buf_in.size]= 0300; - *--buf_in.start= 0300; - buf_in.size+= 2; + + if (!buf_in.size || *buf_in.start != 0300) { + *--buf_in.start= 0300; + buf_in.size++; + } + if (buf_in.start[buf_in.size-1] != 0300) { + buf_in.start[buf_in.size++]= 0300; + } setnonblock(private_in_fd,0); write_must(private_in_fd, buf_in.start, buf_in.size, "write down"); @@ -213,7 +221,7 @@ static void outbound(void) { after_eaten= accum_buf; while ((delim= memchr(after_eaten, 0300, accum_used))) { this_packet= delim - after_eaten; - sendpacket(after_eaten, this_packet); + if (this_packet) sendpacket(after_eaten, this_packet); accum_used -= this_packet+1; after_eaten = delim+1; } @@ -248,8 +256,9 @@ int main(int argc, const char *const *const argv_in) { arg= getarg_string(); if (*arg) { public_remote_specd= 1; + public_remote.sin_family= AF_INET; arg_assert(inet_aton(arg,&public_remote.sin_addr)); - public_remote.sin_port= getarg_ulong(); + public_remote.sin_port= htons(getarg_ulong()); } encdec_keys_fd= getarg_ulong(); @@ -280,7 +289,7 @@ int main(int argc, const char *const *const argv_in) { if (keepalive) { tnow= now(); - if (tnow >= nextsendka) sendpacket("\300",1); + if (tnow >= nextsendka && public_remote_specd) sendpacket("\300",1); polltimeout= (nextsendka - tnow)*1000; } else { polltimeout= -1; @@ -291,7 +300,7 @@ int main(int argc, const char *const *const argv_in) { if (r==-1 && errno==EINTR) continue; if (r==-1) sysfail("poll"); - if (pollfds[0].revents & POLLIN) inbound(); - if (pollfds[1].revents & POLLOUT) outbound(); + if (pollfds[0].revents & (POLLIN|POLLERR)) inbound(); + if (pollfds[1].revents & (POLLIN|POLLERR)) outbound(); } } diff --git a/ipif/udptunnel b/ipif/udptunnel index d7a0036..c2da428 100755 --- a/ipif/udptunnel +++ b/ipif/udptunnel @@ -4,7 +4,11 @@ # usage: # udptunnel # [ -l[] ... . -# -e[/...] +# | -e [/...] +# | -m (`masquerade support': subcommand gets `Wait' instead of our addr/port) +# | -d (`dump keys': when no subcmd, spew keys rather than reading them; +# we always send keys to our subcmd if there is one) +# | -f # ... # ] # , @@ -14,27 +18,70 @@ # # [ [ ...] ] # +# proto may be slip or cslip # -# <..-addr> may also be hostname +# Any <..-addr> may also be hostname # -# may be number or `print' or `silent' +# Local addr's and ports may also be: +# `Print' choose one ourselves and print both port and addr +# `Any' choose one ourselves and do not print it +# Remote addr's and ports may also be: +# `Wait' wait to receive a packet before assigning address +# `Command' run a subcommand and wait for it to tell us the values +# When any addr or port is `Command' then must be specified. # -# may number or `command', in which case -# must be specified and should run udptunnel at the +# If is specified it should run udptunnel at the # remote end; it will be invoked as -# ,print -# , +# [ <-e arguments passed along> ] +# , +# , # ,,, # , # # + +# If it was given Print for , this command's first +# stdout output should be the real +# , pair (and of course this +# udptunnel's output will be). It may then produce more stdout which, +# if any, will be forwarded to the local end's stdout as debugging info. +# +# After this, if any encryption was specified, the encryption +# parameters will be fed into its stdin. See the documentation in the +# mech-*.c files for details of the parameters. udptunnel will +# arrange to feed the keys fd of udptunnel-forwarder into the stdin of +# the remote command. +# +# is as follows: +# +# actual addr/port that addr/port +# `Command' `Print' +# `Wait' `Any' +# +# is as follows: +# +# (-m not specified) (-m specified) +# actual addr/port that addr/port `Wait' +# `Wait' the chosen address `Wait' +# `Print' the chosen address `Wait' +# # udptunnel will userv ipif locally, as # userv root ipif ,,, # -# or, if -l was given, userv root ipif is replaced with the argument(s) to -# successive -l options. +# or, if -l was given, userv root ipif is replaced with the argument(s) +# following -l option(s) until `.'. +# +# udptunnel will also run udptunnel-forwarder with appropriate options +# +# recommended encryption parameters are: +# -e nonce (prepend 32 bit counter) +# -e timestamp// (prepend 32 bit time_t, and check on receipt) +# -e pkcs5/8 (pad as per PKCS#5 to 8-byte boundary) +# -e blowfish-cbcmac/128 (prepend CBC MAC with random IV and 128 bit key) +# -e blowfish-cbc/128 (encrypt with CBC, random IV and 128 bit key) +# where is perhaps 10 and perhaps 30. -# Copyright (C) 1999 Ian Jackson +# Copyright (C) 1999-2000 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 @@ -64,58 +111,105 @@ $? 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 fail ($) { quit("unexpected system call failure: $_[0]: $!"); } 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"); + (m/^$x,/i && m/^[a-z]/ || m/,$x$/i && m/,[a-z]/) + and warning("$_: use Mixed Case for special values"); + m/^([0-9a-z][0-9a-z-+.]+|$x)\,(\d+|$x)$/ + or quit("$_: , bad syntax". + (m/[A-Z]/ ? ' (use lowercase for hostnames)' : '')); return ($1,$2); } sub conv_host_addr ($) { - my ($s,$r) = @_; - defined($r= inet_aton($s)) or quit("$s: cannot convert to address"); + my ($s,$r,@h) = @_; + return INADDR_ANY() if $s =~ m/^[A-Z][a-z]/; + return $r if defined($r= inet_aton($s)); + @h= gethostbyname($s) or quit("$s: cannot get address"); + $h[2] eq &AF_INET or quit("$s: address is not IPv4"); + @h < 5 or quit("$s: name maps to no addresses"); + $r= $h[4]; + @h == 5 or warning("$s: name has several addresses, using ".inet_ntoa($r)); return $r; } sub conv_port_number ($) { 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 0 if $s =~ m/^[A-Z][a-z]/; + $r= $s+0; + $r>0 && $r<65536 or quit("$s: port out of range"); return $r; } -sub show_addr_port ($) { +sub show_addr ($) { + my ($s,@s) = @_; + @s= unpack_sockaddr_in($s); + return inet_ntoa($s[1]); +} +sub show_port ($) { my ($s,@s) = @_; @s= unpack_sockaddr_in($s); - return inet_ntoa($s[1]).','.$s[0]; + return $s[0]; +} +sub show_addr_port ($) { + my ($s) = @_; + return show_addr($s).','.show_port($s); } +sub arg_value ($$) { + my ($val,$opt); + $_= '-'; + return $val if length $val; + @ARGV or quit("$opt needs value"); + return shift @ARGV; +} + +$|=1; @lcmd= (); +@encryption= (); +$masq= 0; +$dump= 0; +$fcmd= 'udptunnel-forwarder'; 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 \`$_'"); + last if m/^--?$/; + while (!m/^-$/) { + if (s/^-l//) { + push @lcmd,$_ if length; + while (@ARGV && ($_= shift @ARGV) ne '.') { push @lcmd, $_; } + $_= '-' + } elsif (s/^-f//) { + $fcmd= arg_value($_,'-f'); + } elsif (s/^-e//) { + $encrarg= arg_value($_,'-e'); + push @encrargs, "-e$encrarg"; + push @encryption, split m#/#, $encrarg; + } elsif (s/^-m/-/) { + $masq= 1; + } elsif (s/^-d/-/) { + $dump= 1; + } else { + quit("unknown option \`$_'"); + } } } -($las,$lps)= eat_addr_port('print|silent'); +# Variables \$[lr]a?p?(|s|d|r) +# Local/Remote Address&/Port +# actualvalue/Specified/Displaypassdown/fromRemote/passtoForwarder +# +($las,$lps)= eat_addr_port('Print|Any'); $la= conv_host_addr($las); $lp= conv_port_number($lps); $ls= pack_sockaddr_in $lp,$la; -($ras,$rps)= eat_addr_port('command'); +($ras,$rps)= eat_addr_port('Wait|Command'); +$ra= conv_host_addr($ras); $rp= conv_port_number($rps); -$ra= $rps eq 'command' ? '' : conv_host_addr($ras); +$rs= pack_sockaddr_in $rp,$ra; $_= shift @ARGV; m/^([.0-9]+),([.0-9]+),(\d+),(slip|cslip)$/ @@ -128,63 +222,120 @@ m/^(\d+),(\d+)$/ or quit("keepalive,timeout missing or bad syntax"); $keepalive && ($timeout > $keepalive*2) or quit("timeout must be < 2*keepalive") if $timeout; -$lepn= shift @ARGV; -$repn= shift @ARGV; - -alarm($timeout); +# Variables \$[lr]exn +# Local/Remote Extra Nets +$lexn= shift @ARGV; +$rexn= shift @ARGV; 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); +$lad= show_addr($ls); +$lpd= show_port($ls); +$lapd= "$lad,$lpd"; + +print "$lapd\n" or fail("print addr/port") if ($las eq 'Print' || $lps eq 'Print'); -if ($rps eq 'command') { - quit("when using ,command for remote, must supply command") unless @ARGV; - @rcmd= (@ARGV, "$ras,print", "$lsp", "$rva,$lva,$mtu,$proto", - "$keepalive,$timeout", $repn, $lepn); +$rapcmd= ($ras eq 'Command' || $rps eq 'Command'); +quit("need remote-command if Command for remote addr/port") if $rapcmd && !@ARGV; + +sub xform_remote ($$) { + my ($showed,$spec) = @_; + return 'Print' if $spec eq 'Command'; + return 'Any' if $spec eq 'Wait'; + return $showed; +} + +if (@ARGV) { + warning("-d specified with remote command, ignoring") if $dump; + $dump= 1; + + $rad= xform_remote(show_addr($rs),$ras); + $rpd= xform_remote(show_port($rs),$rps); + @rcmd= (@ARGV, + @encrargs, + "$rad,$rpd", + $masq ? 'Wait,Wait' : $lapd, + "$rva,$lva,$mtu,$proto", + "$keepalive,$timeout", + $rexn, $lexn); debug("remote command @rcmd"); - defined($c= open C,"-|") or fail("fork for remote"); - if (!$c) { - exec @rcmd; die "$progname: error: failed to execute $rcmd[0]: $!\n"; + + if ($rapcmd) { + pipe(RAPREAD,RCMDREADSUB) or fail("pipe"); + select(RCMDREADSUB); $|=1; select(STDOUT); } - $_= ; - if (!length) { - close C; - quit($? ? "remote command failed (code $?)" : "no details received from remote"); + pipe(DUMPKEYS,RCMDWRITESUB) or fail("pipe"); + defined($c_rcmd= fork) or fail("fork for remote"); + if (!$c_rcmd) { + open STDIN, ">&RCMDWRITESUB" or fail("reopen stdin for remote command"); + open STDOUT, ">&RCMDREADSUB" or fail("reopen stdout for remote command") + if $rapcmd; + close RAPREAD if $rapcmd; + close DUMPKEYS; + close RCMDWRITESUB; + close RCMDREADSUB; + close L; + exec @rcmd; fail("failed to execute remote command $rcmd[0]"); } - chomp; - m/^([.0-9]+)\,(\d+)$/ or quit("invalid details from remote end ($_)"); - ($ras,$rps) = ($1,$2); - $ra= conv_host_addr($ras); - $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"); - close C; - exec "cat"; fail("execute cat"); + close RCMDWRITESUB; + + if ($rapcmd) { + close RCMDREADSUB if $rapcmd; + $!=0; $_= ; $e="$!"; + + defined($c_catremdebug= fork) or fail("fork for cat remote debug"); + if (!$c_catremdebug) { + open(STDIN,"<&RAPREAD") or fail("redirect remote debug"); + close RAPREAD; + close DUMPKEYS; + close L; + exec "cat"; fail("execute cat"); + } + close RAPREAD; + + if (!length) { + close DUMPKEYS; + waitpid $c_rcmd,0 or fail("wait for remote command"); + quit($? ? "remote command failed (code $?)" : + $e ? "read error from remote command: $e" : + "no details received from remote"); + } + chomp; + m/^([.0-9]+)\,(\d+)$/ or quit("invalid details from remote end: \`$_'"); + ($rar,$rpr) = ($1,$2); + $ra= conv_host_addr($rar); + $rp= conv_port_number($rpr); } +} elsif ($dump) { + open DUMPKEYS, ">&STDOUT" or fail("reopen stdout for key material"); + $dump= 1; } else { - quit("when not using ,command for remote, must not supply command") if @ARGV; + open DUMPKEYS, "<&STDIN" or fail("reopen stdout for key material"); } $rs= pack_sockaddr_in $rp,$ra; -$rsp= show_addr_port($rs); - -if ($lps eq 'print') { print($lsp,"\n") or quit("write port to stdout: $!"); } +if ($ras eq 'Wait' || $rps eq 'Wait') { + @rapf= (''); + $rapd= ('Wait,Wait'); +} else { + @rapf= (show_addr($rs), show_port($rs)); + $rapd= show_addr_port($rs); +} @lcmd= qw(userv root ipif) unless @lcmd; -debug("using remote $rsp local $lsp"); -push @lcmd, ("$lva,$rva,$mtu,$proto",$lepn); -debug("local command @lcmd"); +debug("using remote $rapd local $lapd"); +push @lcmd, ("$lva,$rva,$mtu,$proto",$lexn); +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) { +defined($c_lcmd= fork) or fail("fork for local command"); +if (!$c_lcmd) { close UR; close DW; open(STDIN,"<&DR") or fail("reopen stdin for packets"); open(STDOUT,">&UW") or fail("reopen stdout for packets"); @@ -194,82 +345,49 @@ if (!$c3) { close UW; close DR; -$upyet= 0; -$downyet= 0; - -$wantreadfds=''; -vec($wantreadfds,fileno(UR),1)= 1; -vec($wantreadfds,fileno(L),1)= 1; +@fcmd= ($fcmd, + fileno(L), fileno(DW), fileno(UR), + $mtu, $keepalive, $timeout, + @rapf, + fileno(DUMPKEYS), $dump ? 'y' : '', + @encryption); +debug("forwarding command @fcmd."); -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"); +defined($c_fwd= fork) or fail("fork for udptunnel-forwarder"); +if (!$c_fwd) { + foreach $fd (qw(L DW UR)) { + fcntl($fd, F_SETFD, 0) or fail("set no-close-on-exec $fd"); + } + exec @fcmd; fail("cannot execute $fcmd[0]"); } -nonblock('UR'); -nonblock('L'); +close L; +close DUMPKEYS; +close UR; +close DW; -$upbuf= ''; +%procs= ($c_fwd, 'forwarder', + $c_lcmd, 'local command'); +$procs{$c_rcmd}= 'remote command' if $c_rcmd; +$procs{$c_catremdebug}= 'debug cat' if $c_catremdebug; -sub now () { my ($v); defined($v= time) or fail("get time"); return $v; } -if ($keepalive) { $nextsendka= now(); } +$estatus= 0; -for (;;) { - 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: $!"); - last; - } - if (!$r) { - quit "tunnel endpoint closed by system"; - } - while (($p= index($upbuf,"\300")) >= 0) { - 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,$p+1); - } +while (keys %procs) { + ($c= wait) >0 or + fail("wait failed (expecting ". join('; ',keys %procs). ")"); + warning("unexpected child reaped: pid $c, code $?"), next + unless exists $procs{$c}; + $str= $procs{$c}; + delete $procs{$c}; + $? ? warning("subprocess $str failed with code $?") + : debug("subprocess $str finished"); + if ($c==$c_lcmd || $c==$c_fwd || $c==$c_rcmd) { + kill 15, grep (exists $procs{$_}, $c_fwd, $c_rcmd); } - 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"); - next; - } - $downbuf= "\300".$downbuf."\300"; - if (!defined($r= syswrite(DW,$downbuf,length $downbuf))) { - warning("tunnel endpoint write error: $!"); - } elsif ($r != length $downbuf) { - warning("tunnel endpoint wrong write length"); - } else { - 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: $!"); + $estatus=1 unless $c == $c_catremdebug; } + +debug("all processes terminated, exiting with status $estatus"); + +exit $estatus; -- 2.30.2