summaryrefslogtreecommitdiffstats
path: root/Utilities/cmcurl-7.19.0/tests/ftpserver.pl
diff options
context:
space:
mode:
Diffstat (limited to 'Utilities/cmcurl-7.19.0/tests/ftpserver.pl')
-rwxr-xr-xUtilities/cmcurl-7.19.0/tests/ftpserver.pl909
1 files changed, 909 insertions, 0 deletions
diff --git a/Utilities/cmcurl-7.19.0/tests/ftpserver.pl b/Utilities/cmcurl-7.19.0/tests/ftpserver.pl
new file mode 100755
index 0000000..3841e1f
--- /dev/null
+++ b/Utilities/cmcurl-7.19.0/tests/ftpserver.pl
@@ -0,0 +1,909 @@
+#!/usr/bin/env perl
+#***************************************************************************
+# _ _ ____ _
+# Project ___| | | | _ \| |
+# / __| | | | |_) | |
+# | (__| |_| | _ <| |___
+# \___|\___/|_| \_\_____|
+#
+# Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://curl.haxx.se/docs/copyright.html.
+#
+# You may opt to use, copy, modify, merge, publish, distribute and/or sell
+# copies of the Software, and permit persons to whom the Software is
+# furnished to do so, under the terms of the COPYING file.
+#
+# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+# KIND, either express or implied.
+#
+# $Id$
+###########################################################################
+
+# This is the FTP server designed for the curl test suite.
+#
+# It is meant to exercise curl, it is not meant to be a fully working
+# or even very standard compliant server.
+#
+# You may optionally specify port on the command line, otherwise it'll
+# default to port 8921.
+#
+# All socket/network/TCP related stuff is done by the 'sockfilt' program.
+#
+
+use strict;
+use IPC::Open2;
+#use Time::HiRes qw( gettimeofday ); # not available in perl 5.6
+
+require "getpart.pm";
+require "ftp.pm";
+
+
+my $ftpdnum="";
+
+# open and close each time to allow removal at any time
+sub logmsg {
+ # if later than perl 5.6 is used
+ # my ($seconds, $microseconds) = gettimeofday;
+ my $seconds = time();
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
+ localtime($seconds);
+ open(FTPLOG, ">>log/ftpd$ftpdnum.log");
+ printf FTPLOG ("%02d:%02d:%02d ", $hour, $min, $sec);
+ print FTPLOG @_;
+ close(FTPLOG);
+}
+
+sub ftpmsg {
+ # append to the server.input file
+ open(INPUT, ">>log/server$ftpdnum.input") ||
+ logmsg "failed to open log/server$ftpdnum.input\n";
+
+ print INPUT @_;
+ close(INPUT);
+
+ # use this, open->print->close system only to make the file
+ # open as little as possible, to make the test suite run
+ # better on windows/cygwin
+}
+
+my $verbose=0; # set to 1 for debugging
+my $pasvbadip=0;
+my $retrweirdo=0;
+my $retrnosize=0;
+my $srcdir=".";
+my $nosave=0;
+my $controldelay=0; # set to 1 to delay the control connect data sending to
+ # test that curl deals with that nicely
+my $slavepid; # for the DATA connection sockfilt slave process
+my $ipv6;
+my $ext; # append to log/pid file names
+my $grok_eprt;
+my $port = 8921; # just a default
+my $listenaddr = "127.0.0.1"; # just a default
+my $pidfile = ".ftpd.pid"; # a default, use --pidfile
+
+my $SERVERLOGS_LOCK="log/serverlogs.lock"; # server logs advisor read lock
+
+do {
+ if($ARGV[0] eq "-v") {
+ $verbose=1;
+ }
+ elsif($ARGV[0] eq "-s") {
+ $srcdir=$ARGV[1];
+ shift @ARGV;
+ }
+ elsif($ARGV[0] eq "--id") {
+ $ftpdnum=$ARGV[1];
+ shift @ARGV;
+ }
+ elsif($ARGV[0] eq "--pidfile") {
+ $pidfile=$ARGV[1];
+ shift @ARGV;
+ }
+ elsif($ARGV[0] eq "--ipv6") {
+ $ipv6="--ipv6";
+ $ext="ipv6";
+ $grok_eprt = 1;
+ }
+ elsif($ARGV[0] eq "--port") {
+ $port = $ARGV[1];
+ shift @ARGV;
+ }
+ elsif($ARGV[0] eq "--addr") {
+ $listenaddr = $ARGV[1];
+ $listenaddr =~ s/^\[(.*)\]$/\1/;
+ shift @ARGV;
+ }
+} while(shift @ARGV);
+
+sub catch_zap {
+ my $signame = shift;
+ print STDERR "ftpserver.pl received SIG$signame, exiting\n";
+ ftpkillslaves(1);
+ clear_advisor_read_lock($SERVERLOGS_LOCK);
+ die "Somebody sent me a SIG$signame";
+}
+$SIG{INT} = \&catch_zap;
+$SIG{KILL} = \&catch_zap;
+
+my $sfpid;
+
+local(*SFREAD, *SFWRITE);
+
+sub sysread_or_die {
+ my $FH = shift;
+ my $scalar = shift;
+ my $length = shift;
+ my $fcaller;
+ my $lcaller;
+ my $result;
+
+ $result = sysread($$FH, $$scalar, $length);
+
+ if(not defined $result) {
+ ($fcaller, $lcaller) = (caller)[1,2];
+ logmsg "Failed to read input\n";
+ logmsg "Error: ftp$ftpdnum$ext sysread error: $!\n";
+ kill(9, $sfpid);
+ waitpid($sfpid, 0);
+ clear_advisor_read_lock($SERVERLOGS_LOCK);
+ die "Died in sysread_or_die() at $fcaller " .
+ "line $lcaller. ftp$ftpdnum$ext sysread error: $!\n";
+ }
+ elsif($result == 0) {
+ ($fcaller, $lcaller) = (caller)[1,2];
+ logmsg "Failed to read input\n";
+ logmsg "Error: ftp$ftpdnum$ext read zero\n";
+ kill(9, $sfpid);
+ waitpid($sfpid, 0);
+ clear_advisor_read_lock($SERVERLOGS_LOCK);
+ die "Died in sysread_or_die() at $fcaller " .
+ "line $lcaller. ftp$ftpdnum$ext read zero\n";
+ }
+
+ return $result;
+}
+
+sub startsf {
+ my $cmd="./server/sockfilt --port $port --logfile log/sockctrl$ftpdnum$ext.log --pidfile .sockfilt$ftpdnum$ext.pid $ipv6";
+ $sfpid = open2(*SFREAD, *SFWRITE, $cmd);
+
+ print STDERR "$cmd\n" if($verbose);
+
+ print SFWRITE "PING\n";
+ my $pong;
+ sysread SFREAD, $pong, 5;
+
+ if($pong !~ /^PONG/) {
+ logmsg "Failed sockfilt command: $cmd\n";
+ kill(9, $sfpid);
+ waitpid($sfpid, 0);
+ clear_advisor_read_lock($SERVERLOGS_LOCK);
+ die "Failed to start sockfilt!";
+ }
+}
+
+# remove the file here so that if startsf() fails, it is very noticeable
+unlink($pidfile);
+
+startsf();
+
+logmsg sprintf("FTP server listens on port IPv%d/$port\n", $ipv6?6:4);
+open(PID, ">$pidfile");
+print PID $$."\n";
+close(PID);
+
+logmsg("logged pid $$ in $pidfile\n");
+
+sub sockfilt {
+ my $l;
+ foreach $l (@_) {
+ printf SFWRITE "DATA\n%04x\n", length($l);
+ print SFWRITE $l;
+ }
+}
+
+
+# Send data to the client on the control stream, which happens to be plain
+# stdout.
+
+sub sendcontrol {
+ if(!$controldelay) {
+ # spit it all out at once
+ sockfilt @_;
+ }
+ else {
+ my $a = join("", @_);
+ my @a = split("", $a);
+
+ for(@a) {
+ sockfilt $_;
+ select(undef, undef, undef, 0.01);
+ }
+ }
+ my $log;
+ foreach $log (@_) {
+ my $l = $log;
+ $l =~ s/[\r\n]//g;
+ logmsg "> \"$l\"\n";
+ }
+}
+
+# Send data to the client on the data stream
+
+sub senddata {
+ my $l;
+ foreach $l (@_) {
+ printf DWRITE "DATA\n%04x\n", length($l);
+ print DWRITE $l;
+ }
+}
+
+# this text is shown before the function specified below is run
+my %displaytext = ('USER' => '331 We are happy you popped in!',
+ 'PASS' => '230 Welcome you silly person',
+ 'PORT' => '200 You said PORT - I say FINE',
+ 'TYPE' => '200 I modify TYPE as you wanted',
+ 'LIST' => '150 here comes a directory',
+ 'NLST' => '150 here comes a directory',
+ 'CWD' => '250 CWD command successful.',
+ 'SYST' => '215 UNIX Type: L8', # just fake something
+ 'QUIT' => '221 bye bye baby', # just reply something
+ 'PWD' => '257 "/nowhere/anywhere" is current directory',
+ 'MKD' => '257 Created your requested directory',
+ 'REST' => '350 Yeah yeah we set it there for you',
+ 'DELE' => '200 OK OK OK whatever you say',
+ 'RNFR' => '350 Received your order. Please provide more',
+ 'RNTO' => '250 Ok, thanks. File renaming completed.',
+ 'NOOP' => '200 Yes, I\'m very good at doing nothing.',
+ 'PBSZ' => '500 PBSZ not implemented',
+ 'PROT' => '500 PROT not implemented',
+ );
+
+# callback functions for certain commands
+my %commandfunc = ( 'PORT' => \&PORT_command,
+ 'EPRT' => \&PORT_command,
+ 'LIST' => \&LIST_command,
+ 'NLST' => \&NLST_command,
+ 'PASV' => \&PASV_command,
+ 'EPSV' => \&PASV_command,
+ 'RETR' => \&RETR_command,
+ 'SIZE' => \&SIZE_command,
+ 'REST' => \&REST_command,
+ 'STOR' => \&STOR_command,
+ 'APPE' => \&STOR_command, # append looks like upload
+ 'MDTM' => \&MDTM_command,
+ );
+
+
+sub close_dataconn {
+ my ($closed)=@_; # non-zero if already disconnected
+
+ if(!$closed) {
+ logmsg "* disconnect data connection\n";
+ print DWRITE "DISC\n";
+ my $i;
+ sysread DREAD, $i, 5;
+ }
+ else {
+ logmsg "data connection already disconnected\n";
+ }
+ logmsg "=====> Closed data connection\n";
+
+ logmsg "* quit sockfilt for data (pid $slavepid)\n";
+ print DWRITE "QUIT\n";
+ waitpid $slavepid, 0;
+ $slavepid=0;
+}
+
+my $rest=0;
+sub REST_command {
+ $rest = $_[0];
+ logmsg "Set REST position to $rest\n"
+}
+
+sub LIST_command {
+ # print "150 ASCII data connection for /bin/ls (193.15.23.1,59196) (0 bytes)\r\n";
+
+# this is a built-in fake-dir ;-)
+my @ftpdir=("total 20\r\n",
+"drwxr-xr-x 8 98 98 512 Oct 22 13:06 .\r\n",
+"drwxr-xr-x 8 98 98 512 Oct 22 13:06 ..\r\n",
+"drwxr-xr-x 2 98 98 512 May 2 1996 .NeXT\r\n",
+"-r--r--r-- 1 0 1 35 Jul 16 1996 README\r\n",
+"lrwxrwxrwx 1 0 1 7 Dec 9 1999 bin -> usr/bin\r\n",
+"dr-xr-xr-x 2 0 1 512 Oct 1 1997 dev\r\n",
+"drwxrwxrwx 2 98 98 512 May 29 16:04 download.html\r\n",
+"dr-xr-xr-x 2 0 1 512 Nov 30 1995 etc\r\n",
+"drwxrwxrwx 2 98 1 512 Oct 30 14:33 pub\r\n",
+"dr-xr-xr-x 5 0 1 512 Oct 1 1997 usr\r\n");
+
+ logmsg "pass LIST data on data connection\n";
+ for(@ftpdir) {
+ senddata $_;
+ }
+ close_dataconn(0);
+ sendcontrol "226 ASCII transfer complete\r\n";
+ return 0;
+}
+
+sub NLST_command {
+ my @ftpdir=("file", "with space", "fake", "..", " ..", "funny", "README");
+ logmsg "pass NLST data on data connection\n";
+ for(@ftpdir) {
+ senddata "$_\r\n";
+ }
+ close_dataconn(0);
+ sendcontrol "226 ASCII transfer complete\r\n";
+ return 0;
+}
+
+sub MDTM_command {
+ my $testno = $_[0];
+ my $testpart = "";
+ if ($testno > 10000) {
+ $testpart = $testno % 10000;
+ $testno = int($testno / 10000);
+ }
+
+ loadtest("$srcdir/data/test$testno");
+
+ my @data = getpart("reply", "mdtm");
+
+ my $reply = $data[0];
+ chomp $reply;
+
+ if($reply <0) {
+ sendcontrol "550 $testno: no such file.\r\n";
+ }
+ elsif($reply) {
+ sendcontrol "$reply\r\n";
+ }
+ else {
+ sendcontrol "500 MDTM: no such command.\r\n";
+ }
+ return 0;
+}
+
+sub SIZE_command {
+ my $testno = $_[0];
+ my $testpart = "";
+ if ($testno > 10000) {
+ $testpart = $testno % 10000;
+ $testno = int($testno / 10000);
+ }
+
+ loadtest("$srcdir/data/test$testno");
+
+ if($testno eq "verifiedserver") {
+ my $response = "WE ROOLZ: $$\r\n";
+ my $size = length($response);
+ sendcontrol "213 $size\r\n";
+ return 0;
+ }
+
+ my @data = getpart("reply", "size");
+
+ my $size = $data[0];
+
+ if($size) {
+ if($size > -1) {
+ sendcontrol "213 $size\r\n";
+ }
+ else {
+ sendcontrol "550 $testno: No such file or directory.\r\n";
+ }
+ }
+ else {
+ $size=0;
+ @data = getpart("reply", "data$testpart");
+ for(@data) {
+ $size += length($_);
+ }
+ if($size) {
+ sendcontrol "213 $size\r\n";
+ }
+ else {
+ sendcontrol "550 $testno: No such file or directory.\r\n";
+ }
+ }
+ return 0;
+}
+
+sub RETR_command {
+ my ($testno) = @_;
+
+ if($testno =~ /^verifiedserver$/) {
+ # this is the secret command that verifies that this actually is
+ # the curl test server
+ my $response = "WE ROOLZ: $$\r\n";
+ my $len = length($response);
+ sendcontrol "150 Binary junk ($len bytes).\r\n";
+ senddata "WE ROOLZ: $$\r\n";
+ close_dataconn(0);
+ sendcontrol "226 File transfer complete\r\n";
+ if($verbose) {
+ print STDERR "FTPD: We returned proof we are the test server\n";
+ }
+ return 0;
+ }
+
+ $testno =~ s/^([^0-9]*)//;
+ my $testpart = "";
+ if ($testno > 10000) {
+ $testpart = $testno % 10000;
+ $testno = int($testno / 10000);
+ }
+
+ loadtest("$srcdir/data/test$testno");
+
+ my @data = getpart("reply", "data$testpart");
+
+ my $size=0;
+ for(@data) {
+ $size += length($_);
+ }
+
+ my %hash = getpartattr("reply", "data$testpart");
+
+ if($size || $hash{'sendzero'}) {
+
+ if($rest) {
+ # move read pointer forward
+ $size -= $rest;
+ logmsg "REST $rest was removed from size, makes $size left\n";
+ $rest = 0; # reset REST offset again
+ }
+ if($retrweirdo) {
+ sendcontrol "150 Binary data connection for $testno () ($size bytes).\r\n",
+ "226 File transfer complete\r\n";
+
+ for(@data) {
+ my $send = $_;
+ senddata $send;
+ }
+ close_dataconn(0);
+ $retrweirdo=0; # switch off the weirdo again!
+ }
+ else {
+ my $sz = "($size bytes)";
+ if($retrnosize) {
+ $sz = "size?";
+ }
+
+ sendcontrol "150 Binary data connection for $testno () $sz.\r\n";
+
+ for(@data) {
+ my $send = $_;
+ senddata $send;
+ }
+ close_dataconn(0);
+ sendcontrol "226 File transfer complete\r\n";
+ }
+ }
+ else {
+ sendcontrol "550 $testno: No such file or directory.\r\n";
+ }
+ return 0;
+}
+
+sub STOR_command {
+ my $testno=$_[0];
+
+ my $filename = "log/upload.$testno";
+
+ logmsg "STOR test number $testno in $filename\n";
+
+ sendcontrol "125 Gimme gimme gimme!\r\n";
+
+ open(FILE, ">$filename") ||
+ return 0; # failed to open output
+
+ my $line;
+ my $ulsize=0;
+ my $disc=0;
+ while (5 == (sysread DREAD, $line, 5)) {
+ if($line eq "DATA\n") {
+ my $i;
+ sysread DREAD, $i, 5;
+
+ #print STDERR " GOT: $i";
+
+ my $size = hex($i);
+ sysread DREAD, $line, $size;
+
+ #print STDERR " GOT: $size bytes\n";
+
+ $ulsize += $size;
+ print FILE $line if(!$nosave);
+ logmsg "> Appending $size bytes to file\n";
+ }
+ elsif($line eq "DISC\n") {
+ # disconnect!
+ $disc=1;
+ last;
+ }
+ else {
+ logmsg "No support for: $line";
+ last;
+ }
+ }
+ if($nosave) {
+ print FILE "$ulsize bytes would've been stored here\n";
+ }
+ close(FILE);
+ close_dataconn($disc);
+ logmsg "received $ulsize bytes upload\n";
+ sendcontrol "226 File transfer complete\r\n";
+ return 0;
+}
+
+sub PASV_command {
+ my ($arg, $cmd)=@_;
+ my $pasvport;
+ my $pidf=".sockdata$ftpdnum$ext.pid";
+
+ my $prev = checkserver($pidf);
+ if($prev > 0) {
+ print "kill existing server: $prev\n" if($verbose);
+ kill(9, $prev);
+ waitpid($prev, 0);
+ }
+
+ # We fire up a new sockfilt to do the data transfer for us.
+ $slavepid = open2(\*DREAD, \*DWRITE,
+ "./server/sockfilt --port 0 --logfile log/sockdata$ftpdnum$ext.log --pidfile $pidf $ipv6");
+
+ print DWRITE "PING\n";
+ my $pong;
+
+ sysread_or_die(\*DREAD, \$pong, 5);
+
+ if($pong !~ /^PONG/) {
+ kill(9, $slavepid);
+ waitpid($slavepid, 0);
+ sendcontrol "500 no free ports!\r\n";
+ logmsg "failed to run sockfilt for data connection\n";
+ return 0;
+ }
+
+ logmsg "Run sockfilt for data on pid $slavepid\n";
+
+ # Find out what port we listen on
+ my $i;
+ print DWRITE "PORT\n";
+
+ # READ the response code
+ sysread_or_die(\*DREAD, \$i, 5);
+
+ # READ the response size
+ sysread_or_die(\*DREAD, \$i, 5);
+
+ my $size = hex($i);
+
+ # READ the response data
+ sysread_or_die(\*DREAD, \$i, $size);
+
+ # The data is in the format
+ # IPvX/NNN
+
+ if($i =~ /IPv(\d)\/(\d+)/) {
+ # FIX: deal with IP protocol version
+ $pasvport = $2;
+ }
+
+ if($cmd ne "EPSV") {
+ # PASV reply
+ my $p=$listenaddr;
+ $p =~ s/\./,/g;
+ if($pasvbadip) {
+ $p="1,2,3,4";
+ }
+ sendcontrol sprintf("227 Entering Passive Mode ($p,%d,%d)\n",
+ ($pasvport/256), ($pasvport%256));
+ }
+ else {
+ # EPSV reply
+ sendcontrol sprintf("229 Entering Passive Mode (|||%d|)\n", $pasvport);
+ }
+
+ eval {
+ local $SIG{ALRM} = sub { die "alarm\n" };
+
+ # assume swift operations unless explicitly slow
+ alarm ($controldelay?20:10);
+
+ # Wait for 'CNCT'
+ my $input;
+
+ while(sysread(DREAD, $input, 5)) {
+
+ if($input !~ /^CNCT/) {
+ # we wait for a connected client
+ logmsg "Odd, we got $input from client\n";
+ next;
+ }
+ logmsg "====> Client DATA connect\n";
+ last;
+ }
+ alarm 0;
+ };
+ if ($@) {
+ # timed out
+
+ print DWRITE "QUIT\n";
+ waitpid $slavepid, 0;
+ logmsg "accept failed\n";
+ $slavepid=0;
+ return;
+ }
+ else {
+ logmsg "data connection setup on port $pasvport\n";
+ }
+
+ return;
+}
+
+# Support both PORT and EPRT here. Consider LPRT too.
+
+sub PORT_command {
+ my ($arg, $cmd) = @_;
+ my $port;
+ my $addr;
+
+ # We always ignore the given IP and use localhost.
+
+ if($cmd eq "PORT") {
+ if($arg !~ /(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)/) {
+ logmsg "bad PORT-line: $arg\n";
+ sendcontrol "500 silly you, go away\r\n";
+ return 0;
+ }
+ $port = ($5<<8)+$6;
+ $addr = "$1.$2.$3.$4";
+ }
+ # EPRT |2|::1|49706|
+ elsif(($cmd eq "EPRT") && ($grok_eprt)) {
+ if($arg !~ /(\d+)\|([^\|]+)\|(\d+)/) {
+ sendcontrol "500 silly you, go away\r\n";
+ return 0;
+ }
+ sendcontrol "200 Thanks for dropping by. We contact you later\r\n";
+ $port = $3;
+ $addr = $2;
+ }
+ else {
+ sendcontrol "500 we don't like $cmd now\r\n";
+ return 0;
+ }
+
+ if(!$port || $port > 65535) {
+ print STDERR "very illegal PORT number: $port\n";
+ return 1;
+ }
+
+ # We fire up a new sockfilt to do the data transfer for us.
+ # FIX: make it use IPv6 if need be
+ my $filtcmd="./server/sockfilt --connect $port --addr $addr --logfile log/sockdata$ftpdnum$ext.log --pidfile .sockdata$ftpdnum$ext.pid $ipv6";
+ $slavepid = open2(\*DREAD, \*DWRITE, $filtcmd);
+
+ print STDERR "$filtcmd\n" if($verbose);
+
+ print DWRITE "PING\n";
+ my $pong;
+ sysread DREAD, $pong, 5;
+
+ if($pong !~ /^PONG/) {
+ logmsg "Failed sockfilt for data connection\n";
+ kill(9, $slavepid);
+ waitpid($slavepid, 0);
+ }
+
+ logmsg "====> Client DATA connect to port $port\n";
+
+ return;
+}
+
+my %customreply;
+my %customcount;
+my %delayreply;
+sub customize {
+ $nosave = 0; # default is to save as normal
+ $controldelay = 0; # default is no delaying the responses
+ $retrweirdo = 0;
+ $retrnosize = 0;
+ $pasvbadip = 0;
+ $nosave = 0;
+ %customreply = ();
+ %customcount = ();
+ %delayreply = ();
+
+ open(CUSTOM, "<log/ftpserver.cmd") ||
+ return 1;
+
+ logmsg "FTPD: Getting commands from log/ftpserver.cmd\n";
+
+ while(<CUSTOM>) {
+ if($_ =~ /REPLY ([A-Z]+) (.*)/) {
+ $customreply{$1}=eval "qq{$2}";
+ logmsg "FTPD: set custom reply for $1\n";
+ }
+ if($_ =~ /COUNT ([A-Z]+) (.*)/) {
+ # we blank the customreply for this command when having
+ # been used this number of times
+ $customcount{$1}=$2;
+ logmsg "FTPD: blank custom reply for $1 after $2 uses\n";
+ }
+ elsif($_ =~ /DELAY ([A-Z]+) (\d*)/) {
+ $delayreply{$1}=$2;
+ logmsg "FTPD: delay reply for $1 with $2 seconds\n";
+ }
+ elsif($_ =~ /SLOWDOWN/) {
+ $controldelay=1;
+ logmsg "FTPD: send response with 0.1 sec delay between each byte\n";
+ }
+ elsif($_ =~ /RETRWEIRDO/) {
+ logmsg "FTPD: instructed to use RETRWEIRDO\n";
+ $retrweirdo=1;
+ }
+ elsif($_ =~ /RETRNOSIZE/) {
+ logmsg "FTPD: instructed to use RETRNOSIZE\n";
+ $retrnosize=1;
+ }
+ elsif($_ =~ /PASVBADIP/) {
+ logmsg "FTPD: instructed to use PASVBADIP\n";
+ $pasvbadip=1;
+ }
+ elsif($_ =~ /NOSAVE/) {
+ # don't actually store the file we upload - to be used when
+ # uploading insanely huge amounts
+ $nosave = 1;
+ logmsg "FTPD: NOSAVE prevents saving of uploaded data\n";
+ }
+ }
+ close(CUSTOM);
+}
+
+my @welcome=(
+ '220- _ _ ____ _ '."\r\n",
+ '220- ___| | | | _ \| | '."\r\n",
+ '220- / __| | | | |_) | | '."\r\n",
+ '220- | (__| |_| | _ <| |___ '."\r\n",
+ '220 \___|\___/|_| \_\_____|'."\r\n");
+
+
+while(1) {
+ #
+ # We read 'sockfilt' commands.
+ #
+ my $input;
+
+ logmsg "Awaiting input\n";
+ sysread_or_die(\*SFREAD, \$input, 5);
+
+ if($input !~ /^CNCT/) {
+ # we wait for a connected client
+ logmsg "sockfilt said: $input";
+ next;
+ }
+ logmsg "====> Client connect\n";
+
+ set_advisor_read_lock($SERVERLOGS_LOCK);
+
+ # flush data:
+ $| = 1;
+
+ kill(9, $slavepid) if($slavepid);
+ waitpid($slavepid, 0) if($slavepid);
+ $slavepid=0;
+
+ &customize(); # read test control instructions
+
+ sendcontrol @welcome;
+ if($verbose) {
+ for(@welcome) {
+ print STDERR "OUT: $_";
+ }
+ }
+
+ while(1) {
+ my $i;
+
+ # Now we expect to read DATA\n[hex size]\n[prot], where the [prot]
+ # part only is FTP lingo.
+
+ # COMMAND
+ sysread_or_die(\*SFREAD, \$i, 5);
+
+ if($i !~ /^DATA/) {
+ logmsg "sockfilt said $i";
+ if($i =~ /^DISC/) {
+ # disconnect
+ last;
+ }
+ next;
+ }
+
+ # SIZE of data
+ sysread_or_die(\*SFREAD, \$i, 5);
+
+ my $size = hex($i);
+
+ # data
+ sysread SFREAD, $_, $size;
+
+ ftpmsg $_;
+
+ # Remove trailing CRLF.
+ s/[\n\r]+$//;
+
+ unless (m/^([A-Z]{3,4})\s?(.*)/i) {
+ sendcontrol "500 '$_': command not understood.\r\n";
+ last;
+ }
+ my $FTPCMD=$1;
+ my $FTPARG=$2;
+ my $full=$_;
+
+ logmsg "< \"$full\"\n";
+
+ if($verbose) {
+ print STDERR "IN: $full\n";
+ }
+
+ my $delay = $delayreply{$FTPCMD};
+ if($delay) {
+ # just go sleep this many seconds!
+ logmsg("Sleep for $delay seconds\n");
+ sleep($delay);
+ }
+
+ my $text;
+ $text = $customreply{$FTPCMD};
+ my $fake = $text;
+ if($text eq "") {
+ $text = $displaytext{$FTPCMD};
+ }
+ else {
+ if($customcount{$FTPCMD} && (!--$customcount{$FTPCMD})) {
+ # used enough number of times, now blank the customreply
+ $customreply{$FTPCMD}="";
+ }
+ }
+ my $check;
+ if($text) {
+ sendcontrol "$text\r\n";
+ }
+ else {
+ $check=1; # no response yet
+ }
+
+ if($fake eq "") {
+ # only perform this if we're not faking a reply
+ my $func = $commandfunc{$FTPCMD};
+ if($func) {
+ &$func($FTPARG, $FTPCMD);
+ $check=0; # taken care of
+ }
+ }
+
+ if($check) {
+ logmsg "$FTPCMD wasn't handled!\n";
+ sendcontrol "500 $FTPCMD is not dealt with!\r\n";
+ }
+
+ } # while(1)
+ logmsg "====> Client disconnected\n";
+
+ clear_advisor_read_lock($SERVERLOGS_LOCK);
+}
+
+print SFWRITE "QUIT\n";
+waitpid $sfpid, 0;
+
+clear_advisor_read_lock($SERVERLOGS_LOCK);
+
+exit;