remote.pl revision 1.1
1#!/usr/bin/perl 2# $OpenBSD: remote.pl,v 1.1 2012/12/28 20:36:25 bluhm Exp $ 3 4# Copyright (c) 2010-2012 Alexander Bluhm <bluhm@openbsd.org> 5# 6# Permission to use, copy, modify, and distribute this software for any 7# purpose with or without fee is hereby granted, provided that the above 8# copyright notice and this permission notice appear in all copies. 9# 10# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 18use strict; 19use warnings; 20use File::Basename; 21use File::Copy; 22use Socket; 23use Socket6; 24 25use Client; 26use Relayd; 27use Server; 28use Remote; 29require 'funcs.pl'; 30 31sub usage { 32 die <<"EOF"; 33usage: 34 remote.pl localport remoteaddr remoteport [test-args.pl] 35 Run test with local client and server. Remote relayd 36 forwarding from remoteaddr remoteport to server localport 37 has to be started manually. 38 remote.pl copy|splice listenaddr connectaddr connectport [test-args.pl] 39 Only start remote relayd. 40 remote.pl copy|splice localaddr remoteaddr remotessh [test-args.pl] 41 Run test with local client and server. Remote relayd is 42 started automatically with ssh on remotessh. 43EOF 44} 45 46my $test; 47our %args; 48if (@ARGV and -f $ARGV[-1]) { 49 $test = pop; 50 do $test 51 or die "Do test file $test failed: ", $@ || $!; 52} 53my $mode = 54 @ARGV == 3 && $ARGV[0] =~ /^\d+$/ && $ARGV[2] =~ /^\d+$/ ? "manual" : 55 @ARGV == 4 && $ARGV[1] !~ /^\d+$/ && $ARGV[3] =~ /^\d+$/ ? "relay" : 56 @ARGV == 4 && $ARGV[1] !~ /^\d+$/ && $ARGV[3] !~ /^\d+$/ ? "auto" : 57 usage(); 58 59my $r; 60if ($mode eq "relay") { 61 my($rport) = find_ports(num => 1); 62 $r = Relayd->new( 63 forward => $ARGV[0], 64 %{$args{relayd}}, 65 listendomain => AF_INET, 66 listenaddr => $ARGV[1], 67 listenport => $rport, 68 connectdomain => AF_INET, 69 connectaddr => $ARGV[2], 70 connectport => $ARGV[3], 71 logfile => dirname($0)."/remote.log", 72 conffile => dirname($0)."/relayd.conf", 73 testfile => $test, 74 ); 75 open(my $log, '<', $r->{logfile}) 76 or die "Remote log file open failed: $!"; 77 $SIG{__DIE__} = sub { 78 die @_ if $^S; 79 copy($log, \*STDERR); 80 warn @_; 81 exit 255; 82 }; 83 copy($log, \*STDERR); 84 $r->run; 85 copy($log, \*STDERR); 86 $r->up; 87 copy($log, \*STDERR); 88 print STDERR "listen sock: $ARGV[1] $rport\n"; 89 <STDIN>; 90 copy($log, \*STDERR); 91 print STDERR "stdin closed\n"; 92 $r->kill_child; 93 $r->down; 94 copy($log, \*STDERR); 95 96 exit; 97} 98 99my $s = Server->new( 100 func => \&read_char, 101 %{$args{server}}, 102 listendomain => AF_INET, 103 listenaddr => ($mode eq "auto" ? $ARGV[1] : undef), 104 listenport => ($mode eq "manual" ? $ARGV[0] : undef), 105); 106if ($mode eq "auto") { 107 $r = Remote->new( 108 forward => $ARGV[0], 109 logfile => "relayd.log", 110 testfile => $test, 111 %{$args{relay}}, 112 remotessh => $ARGV[3], 113 listenaddr => $ARGV[2], 114 connectaddr => $ARGV[1], 115 connectport => $s->{listenport}, 116 ); 117 $r->run->up; 118} 119my $c = Client->new( 120 func => \&write_char, 121 %{$args{client}}, 122 connectdomain => AF_INET, 123 connectaddr => ($mode eq "manual" ? $ARGV[1] : $r->{listenaddr}), 124 connectport => ($mode eq "manual" ? $ARGV[2] : $r->{listenport}), 125); 126 127$s->run; 128$c->run->up; 129$s->up; 130 131$c->down; 132$s->down; 133$r->close_child; 134$r->down; 135 136foreach ([ client => $c ], [ relayd => $r ], [ server => $s ]) { 137 my($name, $proc) = @$_; 138 my $pattern = $args{$name}{loggrep} or next; 139 $pattern = [ $pattern ] unless ref($pattern) eq 'ARRAY'; 140 foreach my $pat (@$pattern) { 141 if (ref($pat) eq 'HASH') { 142 while (my($re, $num) = each %$pat) { 143 my @matches = $proc->loggrep($re); 144 @matches == $num or 145 die "$name matches @matches: $re => $num"; 146 } 147 } else { 148 $proc->loggrep($pat) 149 or die "$name log missing pattern: $pat"; 150 } 151 } 152} 153 154exit if $args{nocheck}; 155 156my @clen = $c->loggrep(qr/^LEN: /) or die "no client len" 157 unless $args{client}{nocheck}; 158my @slen = $s->loggrep(qr/^LEN: /) or die "no server len" 159 unless $args{server}{nocheck}; 160!@clen || !@slen || @clen ~~ @slen 161 or die "client: @clen", "server: @slen", "len mismatch"; 162!defined($args{len}) || !$clen[0] || $clen[0] eq "LEN: $args{len}\n" 163 or die "client: $clen[0]", "len $args{len} expected"; 164!defined($args{len}) || !$slen[0] || $slen[0] eq "LEN: $args{len}\n" 165 or die "server: $slen[0]", "len $args{len} expected"; 166foreach my $len (map { ref eq 'ARRAY' ? @$_ : $_ } @{$args{lengths} || []}) { 167 my $clen = shift @clen; 168 $clen eq "LEN: $len\n" 169 or die "client: $clen", "len $len expected"; 170 my $slen = shift @slen; 171 $slen eq "LEN: $len\n" 172 or die "server: $slen", "len $len expected"; 173} 174 175my $cmd5 = $c->loggrep(qr/^MD5: /) unless $args{client}{nocheck}; 176my $smd5 = $s->loggrep(qr/^MD5: /) unless $args{server}{nocheck}; 177!$cmd5 || !$smd5 || ref($args{md5}) eq 'ARRAY' || $cmd5 eq $smd5 178 or die "client: $cmd5", "server: $smd5", "md5 mismatch"; 179my $md5 = ref($args{md5}) eq 'ARRAY' ? join('|', @{$args{md5}}) : $args{md5}; 180!$md5 || !$cmd5 || $cmd5 =~ /^MD5: ($md5)$/ 181 or die "client: $cmd5", "md5 $md5 expected"; 182!$md5 || !$smd5 || $smd5 =~ /^MD5: ($md5)$/ 183 or die "server: $smd5", "md5 $md5 expected"; 184