2003-06-17 01:36:47 +00:00
|
|
|
#!/usr/bin/env perl
|
|
|
|
|
|
|
|
# -T
|
|
|
|
|
|
|
|
# $Id$
|
|
|
|
|
|
|
|
# The contents of this file are subject to the Mozilla Public License
|
|
|
|
# Version 1.0 (the "License"); you may not use this file except in
|
|
|
|
# compliance with the License. You may obtain a copy of the License at
|
|
|
|
# http:#www.mozilla.org/MPL/
|
|
|
|
#
|
|
|
|
# Software distributed under the License is distributed on an "AS IS"
|
|
|
|
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
|
|
|
# License for the specific language governing rights and limitations
|
|
|
|
# under the License.
|
|
|
|
#
|
|
|
|
# The Original Code is the Berkeley Open Infrastructure for Network Computing.
|
|
|
|
#
|
|
|
|
# The Initial Developer of the Original Code is the SETI@home project.
|
|
|
|
# Portions created by the SETI@home project are Copyright (C) 2002, 2003
|
|
|
|
# University of California at Berkeley. All Rights Reserved.
|
|
|
|
#
|
|
|
|
# Contributor(s):
|
|
|
|
#
|
|
|
|
|
|
|
|
# testproxy - proxy a TCP/IP connection with configurable mid-way
|
|
|
|
# disconnections, for simulating internet transfer failures
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
use Carp;
|
|
|
|
use IO::Socket::INET;
|
|
|
|
|
|
|
|
my $listen_port = shift;
|
|
|
|
my $target_server = shift;
|
|
|
|
my $testcode = join(' ', @ARGV);
|
|
|
|
|
|
|
|
if (!$listen_port || !$target_server) {
|
|
|
|
print STDERR <<HELP;
|
|
|
|
syntax: $0 <listen_port> <target_server:port> CODE...
|
|
|
|
|
|
|
|
CODE is evaluated every 128 bytes transferred from server to client.
|
|
|
|
Some variables you can access/modify:
|
|
|
|
\$target, \$client : perl IO::Handle::INET objects
|
|
|
|
\$nconnections : number of connections so far
|
2003-06-17 02:09:25 +00:00
|
|
|
\$url : url of request (if applicable)
|
|
|
|
\$time : seconds since server started
|
2003-06-17 01:36:47 +00:00
|
|
|
\$chars, \$nchars : characters & length about to send to client.
|
|
|
|
\$bytes_transferred : characters already sent to client
|
2003-06-18 02:59:57 +00:00
|
|
|
\$start : beginning of connection
|
2003-06-17 01:36:47 +00:00
|
|
|
\$done, \$success : finished transfer; successful transfer
|
2003-06-18 02:59:57 +00:00
|
|
|
\$n, \$m : unused variables initialized to 0
|
2003-06-17 01:36:47 +00:00
|
|
|
|
|
|
|
For more, view the code.
|
|
|
|
|
2003-06-17 02:09:25 +00:00
|
|
|
Functions:
|
2003-06-18 02:59:57 +00:00
|
|
|
close_connection, kill_server, if_done_kill, if_done_ping, logmsg
|
2003-06-17 02:09:25 +00:00
|
|
|
|
2003-06-18 02:59:57 +00:00
|
|
|
You can also call standard perl functions such as print, sleep, exit. You
|
|
|
|
should return 1 to indicate success (otherwise testproxy will warn).
|
2003-06-17 01:36:47 +00:00
|
|
|
|
|
|
|
Examples:
|
|
|
|
# fail connections for first 3 connections
|
|
|
|
$0 8080 localhost:80 'exit 1 if \$nconnections < 4; 1'
|
|
|
|
|
|
|
|
# sleep 5 seconds in the middle of transfer, and print "success" if
|
|
|
|
# transfer succeeds; kill the server after the first connection
|
|
|
|
$0 8080 localhost:80 'sleep 5 if \$bytes_transferred == 256;
|
|
|
|
if (\$done) { print "success\\n" if \$success; kill_server; \$success }
|
|
|
|
else { 1 }'
|
|
|
|
|
|
|
|
# equivalent to above:
|
|
|
|
$0 8080 localhost:80 'sleep 5 if \$bytes_transferred == 256;
|
|
|
|
if_done_kill(); if_done_ping();'
|
|
|
|
|
|
|
|
HELP
|
|
|
|
;
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($target_server !~ /:/) {
|
|
|
|
$target_server .= ':http';
|
|
|
|
}
|
|
|
|
|
|
|
|
my $N = "\015\012";
|
|
|
|
|
|
|
|
sub proxy;
|
|
|
|
sub spawn;
|
2003-06-18 02:59:57 +00:00
|
|
|
use POSIX qw/strftime/;
|
|
|
|
sub logmsg { print STDERR "$0 $$ ", strftime("%Y/%m/%d %H:%M:%S", localtime), ": @_\n" }
|
2003-06-17 01:36:47 +00:00
|
|
|
|
|
|
|
my $server = IO::Socket::INET->new(Listen => 5,
|
|
|
|
LocalAddr => inet_ntoa(INADDR_ANY),
|
|
|
|
LocalPort => $listen_port,
|
|
|
|
Proto => 'tcp',
|
|
|
|
ReuseAddr => 1)
|
|
|
|
or die "$0: creating socket on port $listen_port: $!";
|
|
|
|
|
|
|
|
logmsg "server started on port $listen_port proxy to $target_server";
|
|
|
|
|
|
|
|
my $waitedpid = 0;
|
|
|
|
my $paddr;
|
|
|
|
|
|
|
|
my $server_pid = $$;
|
|
|
|
|
|
|
|
use POSIX ":sys_wait_h";
|
|
|
|
sub REAPER {
|
|
|
|
my $child;
|
|
|
|
while (($waitedpid = waitpid(-1,WNOHANG)) > 0) {
|
2003-06-18 02:59:57 +00:00
|
|
|
logmsg "reaped $waitedpid" . ($? ? " with exit $?" : '');
|
2003-06-17 01:36:47 +00:00
|
|
|
}
|
|
|
|
$SIG{CHLD} = \&REAPER; # loathe sysV
|
|
|
|
}
|
|
|
|
|
|
|
|
$SIG{CHLD} = \&REAPER;
|
|
|
|
|
2003-06-18 02:59:57 +00:00
|
|
|
my $time_started = time();
|
2003-06-17 01:36:47 +00:00
|
|
|
my $nconnections = 0;
|
2003-06-18 02:59:57 +00:00
|
|
|
my $cclient;
|
2003-06-17 01:36:47 +00:00
|
|
|
|
|
|
|
for ( $waitedpid = 0;
|
2003-06-18 02:59:57 +00:00
|
|
|
($cclient = $server->accept()) || $waitedpid;
|
2003-06-17 01:36:47 +00:00
|
|
|
$waitedpid = 0)
|
|
|
|
{
|
2003-06-18 02:59:57 +00:00
|
|
|
next if $waitedpid and not $cclient;
|
|
|
|
die unless $cclient;
|
|
|
|
my $paddr = $cclient->peername();
|
2003-06-17 01:36:47 +00:00
|
|
|
my($port,$iaddr) = sockaddr_in($paddr);
|
|
|
|
my $name = gethostbyaddr($iaddr,AF_INET);
|
|
|
|
|
2003-06-18 02:59:57 +00:00
|
|
|
logmsg "connection from $name:$port"; # [", inet_ntoa($iaddr), "]"
|
2003-06-17 01:36:47 +00:00
|
|
|
|
|
|
|
++$nconnections;
|
|
|
|
|
2003-06-18 02:59:57 +00:00
|
|
|
spawn \&proxy, $cclient;
|
2003-06-17 01:36:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sub spawn {
|
|
|
|
my $coderef = shift;
|
|
|
|
|
|
|
|
unless ($coderef && ref($coderef) eq 'CODE') {
|
|
|
|
confess "usage: spawn CODEREF";
|
|
|
|
}
|
|
|
|
|
|
|
|
my $pid;
|
|
|
|
if (!defined($pid = fork)) {
|
|
|
|
logmsg "cannot fork: $!";
|
|
|
|
return;
|
|
|
|
} elsif ($pid) {
|
2003-06-18 02:59:57 +00:00
|
|
|
logmsg "begat $pid";
|
2003-06-17 01:36:47 +00:00
|
|
|
return; # I'm the parent
|
|
|
|
}
|
|
|
|
# else I'm the child -- go spawn
|
|
|
|
|
|
|
|
exit &$coderef(@_);
|
|
|
|
}
|
|
|
|
|
|
|
|
sub kill_server()
|
|
|
|
{
|
|
|
|
kill "INT", $server_pid;
|
|
|
|
}
|
|
|
|
|
2003-06-18 02:59:57 +00:00
|
|
|
my $start = 0;
|
2003-06-17 01:36:47 +00:00
|
|
|
my $done = 0;
|
|
|
|
my $success = 0;
|
2003-06-18 02:59:57 +00:00
|
|
|
my $url = undef;
|
|
|
|
my $n = 0;
|
|
|
|
my $m = 0;
|
2003-06-17 01:36:47 +00:00
|
|
|
|
|
|
|
sub if_done_ping()
|
|
|
|
{
|
|
|
|
if ($done) {
|
|
|
|
if ($success) {
|
|
|
|
print "success\n";
|
|
|
|
} else {
|
|
|
|
print "failed\n";
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
1;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub if_done_kill()
|
|
|
|
{
|
|
|
|
if ($done) {
|
|
|
|
kill_server();
|
|
|
|
}
|
|
|
|
1;
|
|
|
|
}
|
|
|
|
|
2003-06-18 02:59:57 +00:00
|
|
|
sub eval_test_code()
|
|
|
|
{
|
|
|
|
my $time = time() - $time_started;
|
|
|
|
eval $testcode || warn "test code failed";
|
|
|
|
}
|
|
|
|
|
|
|
|
my ($client, $target);
|
|
|
|
|
|
|
|
sub close_connection() {
|
|
|
|
# $client->close(), $target->close() doesn't always work for some reason
|
|
|
|
# (maybe to do with forked processes)
|
|
|
|
$client->shutdown(2);
|
|
|
|
$target->shutdown(2);
|
|
|
|
1;
|
|
|
|
}
|
|
|
|
|
2003-06-17 01:36:47 +00:00
|
|
|
sub proxy {
|
2003-06-18 02:59:57 +00:00
|
|
|
$client = shift or die;
|
2003-06-17 01:36:47 +00:00
|
|
|
|
2003-06-18 02:59:57 +00:00
|
|
|
$target = IO::Socket::INET->new(PeerAddr => $target_server)
|
2003-06-17 01:36:47 +00:00
|
|
|
or die "$0: couldn't connect to $target_server: $!";
|
|
|
|
|
|
|
|
$client->autoflush(1);
|
|
|
|
$target->autoflush(1);
|
|
|
|
|
2003-06-18 02:59:57 +00:00
|
|
|
my $bytes_transferred = 0;
|
|
|
|
my $chars;
|
|
|
|
my $nchars;
|
|
|
|
|
|
|
|
{
|
|
|
|
$chars = undef; $nchars = 0;
|
|
|
|
$done = 0;
|
|
|
|
$success = 0;
|
|
|
|
$start = 1;
|
|
|
|
eval_test_code();
|
|
|
|
$start = 0;
|
|
|
|
}
|
|
|
|
|
2003-06-17 01:36:47 +00:00
|
|
|
# transfer lines from client -> server until we get an empty line
|
|
|
|
|
|
|
|
while (my $line = $client->getline()) {
|
2003-06-18 02:59:57 +00:00
|
|
|
if ($. == 1 && $line =~ /^(GET|PUT|POST) ([^\s]+)/) {
|
|
|
|
$url = $2;
|
|
|
|
logmsg "url = $url";
|
|
|
|
}
|
2003-06-17 01:36:47 +00:00
|
|
|
$target->print($line);
|
|
|
|
$line =~ s/[\015\012]+$//;
|
|
|
|
last unless $line;
|
|
|
|
}
|
|
|
|
|
2003-06-18 02:59:57 +00:00
|
|
|
# indicate we have stopped reading data from client and stopped writing
|
|
|
|
# data to server (not sure if this helps)
|
|
|
|
$client->shutdown(0);
|
|
|
|
$target->shutdown(1);
|
2003-06-17 01:36:47 +00:00
|
|
|
|
2003-06-18 02:59:57 +00:00
|
|
|
# transfer from server->client
|
2003-06-17 01:36:47 +00:00
|
|
|
|
|
|
|
while ($nchars = $target->read($chars, 128)) {
|
2003-06-18 02:59:57 +00:00
|
|
|
eval_test_code();
|
2003-06-17 01:36:47 +00:00
|
|
|
$bytes_transferred += $nchars;
|
|
|
|
$client->write($chars, $nchars);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
$chars = undef; $nchars = 0;
|
|
|
|
$done = 1;
|
|
|
|
$success = $client->connected() && 1;
|
2003-06-18 02:59:57 +00:00
|
|
|
eval_test_code();
|
2003-06-17 01:36:47 +00:00
|
|
|
}
|
|
|
|
|
2003-06-18 02:59:57 +00:00
|
|
|
close_connection();
|
|
|
|
return 0;
|
2003-06-17 01:36:47 +00:00
|
|
|
}
|