Actual forwarder program compiles apparently ok.
[userv-utils] / ipif / udptunnel
CommitLineData
46ab1c83 1#!/usr/bin/perl
2# Simple tunnel for userv-ipif tunnels.
3#
4# usage:
5# udptunnel
1fb3cba0 6# [ -l[<local-command/arg>] ... .
7# -e<encryption-mech>[/<encryption-parameter>...]
8# ...
9# ]
10# <public-local-addr>,<public-local-port>
11# <public-remote-addr>,<public-remote-port>
46ab1c83 12# <private-local-addr>,<private-remote-addr>,<mtu>,<proto>
13# <keepalive>,<timeout>
14# <extra-local-nets> <extra-remote-nets>
15# [ <remote-command> [<remote-args> ...] ]
16#
1fb3cba0 17#
18# <..-addr> may also be hostname
19#
46ab1c83 20# <local-public-port> may be number or `print' or `silent'
21#
22# <remote-public-port> may number or `command', in which case
23# <remote-command> must be specified and should run udptunnel at the
24# remote end; it will be invoked as
1fb3cba0 25# <remote-command> <public-remote-addr>,print
46ab1c83 26# <public-local-addr>,<public-local-port>
27# <private-remote-addr>,<private-local-addr>,<mtu>,<proto>
217afbe6 28# <keepalive>,<timeout>
29# <extra-remote-nets> <extra-local-nets>
46ab1c83 30#
31# udptunnel will userv ipif locally, as
217afbe6 32# userv root ipif <private-local-addr>,<private-remote-addr>,<mtu>,<proto>
33# <extra-local-nets>
049f3484 34# or, if -l was given, userv root ipif is replaced with the argument(s) to
35# successive -l options.
46ab1c83 36
caa68336 37# Copyright (C) 1999 Ian Jackson
38#
39# This is free software; you can redistribute it and/or modify it
40# under the terms of the GNU General Public License as published by
41# the Free Software Foundation; either version 2 of the License, or
42# (at your option) any later version.
43#
44# This program is distributed in the hope that it will be useful, but
45# WITHOUT ANY WARRANTY; without even the implied warranty of
46# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
47# General Public License for more details.
48#
49# You should have received a copy of the GNU General Public License
50# along with userv-utils; if not, write to the Free Software
51# Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
52#
53# $Id$
54
46ab1c83 55use Socket;
56use POSIX;
57use Fcntl;
58
59$progname= $0; $progname =~ s,.*/,,;
60$|=1;
61
62chomp($hostname= `uname -n`);
63$? and die "$progname: cannot get hostname (uname failed with code $?)\n";
64
65sub quit ($) { die "$progname - $hostname: fatal error: $_[0]\n"; }
66sub debug ($) { print "$progname - $hostname: debug: $_[0]\n"; }
67sub fail ($) { quit("unexpected system call failure: $_[0]: $!\n"); }
68sub warning ($) { warn "$progname - $hostname: $_[0]\n"; }
69
70sub eat_addr_port ($) {
71 my ($x) = @_;
1fb3cba0 72 @ARGV or quit("<addr>,<port> missing");
46ab1c83 73 $_= shift(@ARGV);
74 $_ =~ m/^([^,]+)\,(\d+|$x)$/ or quit("$_: <host/addr>,<port> bad syntax");
75 return ($1,$2);
76}
77sub conv_host_addr ($) {
78 my ($s,$r) = @_;
79 defined($r= inet_aton($s)) or quit("$s: cannot convert to address");
80 return $r;
81}
217afbe6 82sub conv_port_number ($) {
46ab1c83 83 my ($s,$r) = @_;
84 if ($s =~ m/\d/) {
85 $r= $s+0;
86 $r>0 && $r<65536 or quit("$s: port out of range");
87 } else {
88 $r= 0;
89 }
90 return $r;
91}
92sub show_addr_port ($) {
93 my ($s,@s) = @_;
94 @s= unpack_sockaddr_in($s);
95 return inet_ntoa($s[1]).','.$s[0];
96}
97
abe75cda 98@lcmd= ();
99
100while ($ARGV[0] =~ m/^-/) {
101 $_= shift @ARGV;
102 last if $_ eq '--';
103 if (s/^-l//) {
104 push @lcmd,$_ if length;
1fb3cba0 105 while (@ARGV && ($_= shift @ARGV) ne '.') { push @lcmd, $_; }
abe75cda 106 } else {
107 quit("unknown option \`$_'");
108 }
109}
110
46ab1c83 111($las,$lps)= eat_addr_port('print|silent');
112$la= conv_host_addr($las);
113$lp= conv_port_number($lps);
114$ls= pack_sockaddr_in $lp,$la;
115
116($ras,$rps)= eat_addr_port('command');
117$rp= conv_port_number($rps);
118$ra= $rps eq 'command' ? '' : conv_host_addr($ras);
119
120$_= shift @ARGV;
121m/^([.0-9]+),([.0-9]+),(\d+),(slip|cslip)$/
122 or quit("lvaddr,rvaddr,mtu,proto missing or bad syntax or proto not [c]slip");
217afbe6 123($lva,$rva,$mtu,$proto) = ($1,$2,$3,$4);
46ab1c83 124
125$_= shift @ARGV;
217afbe6 126m/^(\d+),(\d+)$/ or quit("keepalive,timeout missing or bad syntax");
127($keepalive,$timeout)= ($1,$2);
46ab1c83 128$keepalive && ($timeout > $keepalive*2) or quit("timeout must be < 2*keepalive")
129 if $timeout;
130
131$lepn= shift @ARGV;
132$repn= shift @ARGV;
133
134alarm($timeout);
135
136defined($udp= getprotobyname('udp')) or fail("getprotobyname udp");
137
138socket(L,PF_INET,SOCK_DGRAM,$udp) or fail("socket");
139bind(L,$ls) or quit("bind failed: $!");
140defined($ls= getsockname(L)) or fail("getsockname");
141$lsp= show_addr_port($ls);
142
143if ($rps eq 'command') {
46ab1c83 144 quit("when using ,command for remote, must supply command") unless @ARGV;
217afbe6 145 @rcmd= (@ARGV, "$ras,print", "$lsp", "$rva,$lva,$mtu,$proto",
146 "$keepalive,$timeout", $repn, $lepn);
147 debug("remote command @rcmd");
46ab1c83 148 defined($c= open C,"-|") or fail("fork for remote");
149 if (!$c) {
217afbe6 150 exec @rcmd; die "$progname: error: failed to execute $rcmd[0]: $!\n";
46ab1c83 151 }
152 $_= <C>;
153 if (!length) {
154 close C;
155 quit($? ? "remote command failed (code $?)" : "no details received from remote");
156 }
157 chomp;
158 m/^([.0-9]+)\,(\d+)$/ or quit("invalid details from remote end ($_)");
159 ($ras,$rps) = ($1,$2);
160 $ra= conv_host_addr($ras);
217afbe6 161 $rp= conv_port_number($rps);
46ab1c83 162 defined($c2= fork) or fail("fork for cat");
163 if (!$c2) {
217afbe6 164 open(STDIN,"<&C") or fail("redirect remote pipe to stdin");
46ab1c83 165 close C;
166 exec "cat"; fail("execute cat");
167 }
168} else {
169 quit("when not using ,command for remote, must not supply command") if @ARGV;
170}
171
172$rs= pack_sockaddr_in $rp,$ra;
173$rsp= show_addr_port($rs);
174
217afbe6 175if ($lps eq 'print') { print($lsp,"\n") or quit("write port to stdout: $!"); }
46ab1c83 176
abe75cda 177@lcmd= qw(userv root ipif) unless @lcmd;
178
217afbe6 179debug("using remote $rsp local $lsp");
abe75cda 180push @lcmd, ("$lva,$rva,$mtu,$proto",$lepn);
181debug("local command @lcmd");
46ab1c83 182
183pipe(UR,UW) or fail("up pipe");
184pipe(DR,DW) or fail("down pipe");
185
186defined($c3= fork) or fail("fork for ipif");
187if (!$c3) {
188 close UR; close DW;
217afbe6 189 open(STDIN,"<&DR") or fail("reopen stdin for packets");
190 open(STDOUT,">&UW") or fail("reopen stdout for packets");
abe75cda 191 exec @lcmd;
192 quit("cannot execute $lcmd[0]: $!");
46ab1c83 193}
194close UW;
195close DR;
196
197$upyet= 0;
198$downyet= 0;
199
200$wantreadfds='';
217afbe6 201vec($wantreadfds,fileno(UR),1)= 1;
202vec($wantreadfds,fileno(L),1)= 1;
46ab1c83 203
204sub nonblock ($) {
205 my ($fh,$fl) = @_;
206 ($fl= fcntl($fh,F_GETFL,0)) or fail("nonblock F_GETFL");
207 $fl |= O_NONBLOCK;
208 fcntl($fh, F_SETFL, $fl) or fail("nonblock F_SETFL");
209}
210
211nonblock('UR');
212nonblock('L');
213
214$upbuf= '';
215
217afbe6 216sub now () { my ($v); defined($v= time) or fail("get time"); return $v; }
217if ($keepalive) { $nextsendka= now(); }
218
46ab1c83 219for (;;) {
217afbe6 220 if ($keepalive) {
221 $now= now();
198680ad 222 $thistimeout= $nextsendka-$now;
223 if ($thistimeout < 0) {
217afbe6 224 defined(send L,"\300",0,$rs)
225 or warning("transmit keepalive error: $!");
226 $nextsendka= $now+$keepalive;
198680ad 227 $thistimeout= $keepalive;
217afbe6 228 }
229 } else {
198680ad 230 $thistimeout= undef;
217afbe6 231 }
198680ad 232 select($readfds=$wantreadfds,'','',$thistimeout);
46ab1c83 233 for (;;) {
234 if (!defined($r= sysread(UR,$upbuf,$mtu*2+3,length($upbuf)))) {
235 $! == EAGAIN || warning("tunnel endpoint read error: $!");
236 last;
237 }
238 if (!$r) {
239 quit "tunnel endpoint closed by system";
240 }
241 while (($p= index($upbuf,"\300")) >= 0) {
1d1209a4 242 if ($p && !defined(send L,substr($upbuf,0,$p),0,$rs)) {
217afbe6 243 warning("transmit error: $!");
198680ad 244 } else {
245 if (!$upyet) {
246 $upyet= 1;
247 debug($downyet ? "tunnel open at this end" : "transmitting");
248 }
249 if ($keepalive) { $nextsendka= now()+$keepalive; }
46ab1c83 250 }
198680ad 251 $upbuf= substr($upbuf,$p+1);
46ab1c83 252 }
253 }
254 while (defined($rs_from= recv L,$downbuf,$mtu*2+3,0)) {
255 $rsp_from= show_addr_port($rs_from);
256 if ($rsp_from ne $rsp) {
257 warning("got packet from incorrect peer $rsp_from");
217afbe6 258 next;
46ab1c83 259 }
1d1209a4 260 $downbuf= "\300".$downbuf."\300";
46ab1c83 261 if (!defined($r= syswrite(DW,$downbuf,length $downbuf))) {
262 warning("tunnel endpoint write error: $!");
217afbe6 263 } elsif ($r != length $downbuf) {
46ab1c83 264 warning("tunnel endpoint wrong write length");
265 } else {
217afbe6 266 if (!$downyet) {
267 $downyet= 1;
198680ad 268 debug($upyet ? "tunnel open at this end" : "receiving");
217afbe6 269 }
270 alarm($timeout) if $timeout;
46ab1c83 271 }
272 }
40f75550 273 if ($! == ECONNREFUSED) { quit("tunnel closed at remote end"); }
46ab1c83 274 $! == EAGAIN || warning("receive error: $!");
275}