Modified the FTP server to use the new 'sockfilt' program to do all the socket

level stuff. The FTP server communicates with sockfilt using perl's open2().
This enables easier IPv6 support and hopefully FTP-SSL support in the future.
Added four test cases for FTP-ipv6.
This commit is contained in:
Daniel Stenberg 2005-04-18 06:57:44 +00:00
parent 6063dff8d0
commit 14424f7058
14 changed files with 1315 additions and 151 deletions

View File

@ -26,7 +26,8 @@ PDFPAGES = testcurl.pdf runtests.pdf
EXTRA_DIST = ftpserver.pl httpserver.pl httpsserver.pl runtests.pl \
ftpsserver.pl getpart.pm FILEFORMAT README stunnel.pem memanalyze.pl \
testcurl.pl valgrind.pm testcurl.1 runtests.1 $(HTMLPAGES) $(PDFPAGES)
testcurl.pl valgrind.pm testcurl.1 runtests.1 $(HTMLPAGES) $(PDFPAGES) \
ftp.pm
SUBDIRS = data server libtest

View File

@ -34,7 +34,7 @@ EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46 \
test199 test225 test226 test227 test230 test231 test232 test228 \
test229 test233 test234 test235 test236 test520 test237 test238 \
test239 test243 test245 test246 test247 test248 test249 test250 \
test251
test251 test252 test253 test254 test255
# The following tests have been removed from the dist since they no longer
# work. We need to fix the test suite's FTPS server first, then bring them

View File

@ -26,8 +26,8 @@ ftp://%HOSTIP:%FTPPORT/a/path/103 -P -
# Verify data after the test has been "shot"
<verify>
<strippart>
s/^LPRT.*[\n]//
s/^EPRT.*[\n]//
s/^LPRT.*/LPRT/
s/^EPRT.*/EPRT/
s/^(PORT 127,0,0,1,)([0-9,]+)/$1/
</strippart>
<protocol>
@ -36,6 +36,8 @@ PASS curl_by_daniel@haxx.se
PWD
CWD a
CWD path
EPRT
LPRT
PORT 127,0,0,1,
TYPE I
SIZE 103

57
tests/data/test252 Normal file
View File

@ -0,0 +1,57 @@
<info>
<keywords>
FTP-ipv6
EPSV
</keywords>
</info>
#
# Server-side
<reply>
<data>
total 20
drwxr-xr-x 8 98 98 512 Oct 22 13:06 .
drwxr-xr-x 8 98 98 512 Oct 22 13:06 ..
drwxr-xr-x 2 98 98 512 May 2 1996 .NeXT
-r--r--r-- 1 0 1 35 Jul 16 1996 README
lrwxrwxrwx 1 0 1 7 Dec 9 1999 bin -> usr/bin
dr-xr-xr-x 2 0 1 512 Oct 1 1997 dev
drwxrwxrwx 2 98 98 512 May 29 16:04 download.html
dr-xr-xr-x 2 0 1 512 Nov 30 1995 etc
drwxrwxrwx 2 98 1 512 Oct 30 14:33 pub
dr-xr-xr-x 5 0 1 512 Oct 1 1997 usr
</data>
</reply>
#
# Client-side
<client>
<features>
ipv6
</features>
<server>
ftp-ipv6
</server>
<name>
FTP IPv6 dir list PASV
</name>
<command>
-g "ftp://%HOST6IP:%FTP6PORT/"
</command>
</client>
#
# Verify data after the test has been "shot"
<verify>
<strip>
filter off really nothing
</strip>
<protocol>
USER anonymous
PASS curl_by_daniel@haxx.se
PWD
EPSV
TYPE A
LIST
QUIT
</protocol>
</verify>

57
tests/data/test253 Normal file
View File

@ -0,0 +1,57 @@
<info>
<keywords>
FTP-ipv6
EPRT
</keywords>
</info>
#
# Server-side
<reply>
<data>
total 20
drwxr-xr-x 8 98 98 512 Oct 22 13:06 .
drwxr-xr-x 8 98 98 512 Oct 22 13:06 ..
drwxr-xr-x 2 98 98 512 May 2 1996 .NeXT
-r--r--r-- 1 0 1 35 Jul 16 1996 README
lrwxrwxrwx 1 0 1 7 Dec 9 1999 bin -> usr/bin
dr-xr-xr-x 2 0 1 512 Oct 1 1997 dev
drwxrwxrwx 2 98 98 512 May 29 16:04 download.html
dr-xr-xr-x 2 0 1 512 Nov 30 1995 etc
drwxrwxrwx 2 98 1 512 Oct 30 14:33 pub
dr-xr-xr-x 5 0 1 512 Oct 1 1997 usr
</data>
</reply>
#
# Client-side
<client>
<features>
ipv6
</features>
<server>
ftp-ipv6
</server>
<name>
FTP IPv6 dir list with EPRT
</name>
<command>
-g "ftp://%HOST6IP:%FTP6PORT/" -P -
</command>
</client>
#
# Verify data after the test has been "shot"
<verify>
<strippart>
s/^(EPRT \|2\|::1\|)(.*)/$1/
</strippart>
<protocol>
USER anonymous
PASS curl_by_daniel@haxx.se
PWD
EPRT |2|::1|
TYPE A
LIST
QUIT
</protocol>
</verify>

58
tests/data/test254 Normal file
View File

@ -0,0 +1,58 @@
<info>
<keywords>
FTP-ipv6
EPSV
--disable-epsv
</keywords>
</info>
#
# Server-side
<reply>
<data>
total 20
drwxr-xr-x 8 98 98 512 Oct 22 13:06 .
drwxr-xr-x 8 98 98 512 Oct 22 13:06 ..
drwxr-xr-x 2 98 98 512 May 2 1996 .NeXT
-r--r--r-- 1 0 1 35 Jul 16 1996 README
lrwxrwxrwx 1 0 1 7 Dec 9 1999 bin -> usr/bin
dr-xr-xr-x 2 0 1 512 Oct 1 1997 dev
drwxrwxrwx 2 98 98 512 May 29 16:04 download.html
dr-xr-xr-x 2 0 1 512 Nov 30 1995 etc
drwxrwxrwx 2 98 1 512 Oct 30 14:33 pub
dr-xr-xr-x 5 0 1 512 Oct 1 1997 usr
</data>
</reply>
#
# Client-side
<client>
<features>
ipv6
</features>
<server>
ftp-ipv6
</server>
<name>
FTP IPv6 dir list PASV and --disable-epsv
</name>
<command>
-g "ftp://%HOST6IP:%FTP6PORT/" --disable-epsv
</command>
</client>
#
# Verify data after the test has been "shot"
<verify>
<strip>
filter off really nothing
</strip>
<protocol>
USER anonymous
PASS curl_by_daniel@haxx.se
PWD
EPSV
TYPE A
LIST
QUIT
</protocol>
</verify>

58
tests/data/test255 Normal file
View File

@ -0,0 +1,58 @@
<info>
<keywords>
FTP-ipv6
EPRT
--disable-eprt
</keywords>
</info>
#
# Server-side
<reply>
<data>
total 20
drwxr-xr-x 8 98 98 512 Oct 22 13:06 .
drwxr-xr-x 8 98 98 512 Oct 22 13:06 ..
drwxr-xr-x 2 98 98 512 May 2 1996 .NeXT
-r--r--r-- 1 0 1 35 Jul 16 1996 README
lrwxrwxrwx 1 0 1 7 Dec 9 1999 bin -> usr/bin
dr-xr-xr-x 2 0 1 512 Oct 1 1997 dev
drwxrwxrwx 2 98 98 512 May 29 16:04 download.html
dr-xr-xr-x 2 0 1 512 Nov 30 1995 etc
drwxrwxrwx 2 98 1 512 Oct 30 14:33 pub
dr-xr-xr-x 5 0 1 512 Oct 1 1997 usr
</data>
</reply>
#
# Client-side
<client>
<features>
ipv6
</features>
<server>
ftp-ipv6
</server>
<name>
FTP IPv6 dir list with EPRT and --disable-eprt
</name>
<command>
-g "ftp://%HOST6IP:%FTP6PORT/" -P - --disable-eprt
</command>
</client>
#
# Verify data after the test has been "shot"
<verify>
<strippart>
s/^(EPRT \|2\|::1\|)(.*)/$1/
</strippart>
<protocol>
USER anonymous
PASS curl_by_daniel@haxx.se
PWD
EPRT |2|::1|
TYPE A
LIST
QUIT
</protocol>
</verify>

17
tests/ftp.pm Normal file
View File

@ -0,0 +1,17 @@
# make sure no leftovers are still running
sub ftpkillslaves {
for $ext (("", "ipv6")) {
for $id (("", "2")) {
for $base (('filt', 'data')) {
my $f = ".sock$base$id$ext.pid";
my $pid = checkserver($f);
if($pid > 0) {
kill (9, $pid); # die!
}
unlink($f);
}
}
}
}
1;

View File

@ -30,13 +30,14 @@
# You may optionally specify port on the command line, otherwise it'll
# default to port 8921.
#
use Socket;
use FileHandle;
# All socket/network/TCP related stuff is done by the 'sockfilt' program.
#
use strict;
use IPC::Open2;
require "getpart.pm";
require "ftp.pm";
my $ftpdnum="";
@ -70,7 +71,10 @@ 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
do {
if($ARGV[0] eq "-v") {
@ -84,41 +88,68 @@ do {
$ftpdnum=$ARGV[1];
shift @ARGV;
}
elsif($ARGV[0] =~ /^(\d+)$/) {
$port = $1;
elsif($ARGV[0] eq "--ipv6") {
$ipv6="--ipv6";
$ext="ipv6";
$grok_eprt = 1;
}
elsif($ARGV[0] eq "--port") {
$port = $ARGV[1];
shift @ARGV;
}
} while(shift @ARGV);
my $proto = getprotobyname('tcp') || 6;
my $sfpid;
socket(Server, PF_INET, SOCK_STREAM, $proto)|| die "socket: $!";
setsockopt(Server, SOL_SOCKET, SO_REUSEADDR,
pack("l", 1)) || die "setsockopt: $!";
bind(Server, sockaddr_in($port, INADDR_ANY))|| die "bind: $!";
listen(Server,SOMAXCONN) || die "listen: $!";
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);
logmsg "FTP server started on port $port\n";
print SFWRITE "PING\n";
my $pong = <SFREAD>;
if($pong !~ /^PONG/) {
die "Failed to start sockfilt!";
}
open(STDIN, "<&SFREAD") || die "can't dup client to stdin";
open(STDOUT, ">&SFWRITE") || die "can't dup client to stdout";
}
startsf();
logmsg sprintf("FTP server started on port IPv%d/$port\n",
$ipv6?6:4);
open(PID, ">.ftp$ftpdnum.pid");
print PID $$;
close(PID);
sub sockfilt {
my $l;
foreach $l (@_) {
printf "DATA\n%04x\n", length($l);
print $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
print @_;
sockfilt @_;
}
else {
my $a = join("", @_);
my @a = split("", $a);
for(@a) {
print $_;
select(undef, undef, undef, 0.02);
sockfilt $_;
select(undef, undef, undef, 0.01);
}
}
@ -127,16 +158,11 @@ sub sendcontrol {
# Send data to the client on the data stream
sub senddata {
print SOCK @_;
}
my $waitedpid = 0;
my $paddr;
sub REAPER {
$waitedpid = wait;
$SIG{CHLD} = \&REAPER; # loathe sysV
logmsg "reaped $waitedpid" . ($? ? " with exit $?\n" : "\n");
my $l;
foreach $l (@_) {
printf DWRITE "DATA\n%04x\n", length($l);
print DWRITE $l;
}
}
# USER is ok in fresh state
@ -146,6 +172,7 @@ my %commandok = (
'PASV' => 'loggedin|twosock',
'EPSV' => 'loggedin|twosock',
'PORT' => 'loggedin|twosock',
'EPRT' => 'loggedin|twosock',
'TYPE' => 'loggedin|twosock',
'LIST' => 'twosock',
'NLST' => 'twosock',
@ -171,6 +198,7 @@ my %commandok = (
my %statechange = ( 'USER' => 'passwd', # USER goes to passwd state
'PASS' => 'loggedin', # PASS goes to loggedin state
'PORT' => 'twosock', # PORT goes to twosock
'EPRT' => 'twosock', # EPRT goes to twosock
'PASV' => 'twosock', # PASV goes to twosock
'EPSV' => 'twosock', # EPSV goes to twosock
);
@ -196,6 +224,7 @@ my %displaytext = ('USER' => '331 We are happy you popped in!',
# callback functions for certain commands
my %commandfunc = ( 'PORT' => \&PORT_command,
'EPRT' => \&PORT_command,
'LIST' => \&LIST_command,
'NLST' => \&NLST_command,
'PASV' => \&PASV_command,
@ -210,8 +239,25 @@ my %commandfunc = ( 'PORT' => \&PORT_command,
sub close_dataconn {
close(SOCK);
logmsg "Closed data connection\n";
my ($closed)=@_; # non-zero if already disconnected
if(!$closed) {
logmsg "time to kill the data connection\n";
print DWRITE "DISC\n";
my $i;
sysread DREAD, $i, 5;
}
else {
logmsg "data connection already disconnected\n";
}
logmsg "time to quit sockfilt for data\n";
print DWRITE "QUIT\n";
logmsg "told data slave to die (pid $slavepid)\n";
waitpid $slavepid, 0;
$slavepid=0;
logmsg "=====> Closed data connection\n";
}
my $rest=0;
@ -240,9 +286,8 @@ my @ftpdir=("total 20\r\n",
for(@ftpdir) {
senddata $_;
}
close_dataconn();
close_dataconn(0);
logmsg "done passing data\n";
sendcontrol "226 ASCII transfer complete\r\n";
return 0;
}
@ -253,7 +298,7 @@ sub NLST_command {
for(@ftpdir) {
senddata "$_\r\n";
}
close_dataconn();
close_dataconn(0);
sendcontrol "226 ASCII transfer complete\r\n";
return 0;
}
@ -292,6 +337,14 @@ sub SIZE_command {
logmsg "SIZE file \"$testno\"\n";
if($testno eq "verifiedserver") {
my $response = "WE ROOLZ: $$\r\n";
my $size = length($response);
sendcontrol "213 $size\r\n";
logmsg "SIZE $testno returned $size\n";
return 0;
}
my @data = getpart("reply", "size");
my $size = $data[0];
@ -337,7 +390,8 @@ sub RETR_command {
sendcontrol "150 Binary junk ($len bytes).\r\n";
logmsg "pass our pid on the data connection\n";
senddata "WE ROOLZ: $$\r\n";
close_dataconn();
close_dataconn(0);
logmsg "Data sent, sending a 226-reponse now\n";
sendcontrol "226 File transfer complete\r\n";
if($verbose) {
print STDERR "FTPD: We returned proof we are the test server\n";
@ -377,7 +431,7 @@ sub RETR_command {
my $send = $_;
senddata $send;
}
close_dataconn();
close_dataconn(0);
$retrweirdo=0; # switch off the weirdo again!
}
else {
@ -394,7 +448,7 @@ sub RETR_command {
my $send = $_;
senddata $send;
}
close_dataconn();
close_dataconn(0);
sendcontrol "226 File transfer complete\r\n";
}
}
@ -421,122 +475,185 @@ sub STOR_command {
my $line;
my $ulsize=0;
while (defined($line = <SOCK>)) {
$ulsize += length($line);
print FILE $line if(!$nosave);
my $disc=0;
while (5 == (sysread DREAD, $line, 5)) {
logmsg "command from sockfilt: $line";
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!
logmsg "DISC means disconnect!\n";
$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();
close_dataconn($disc);
logmsg "received $ulsize bytes upload\n";
sendcontrol "226 File transfer complete\r\n";
return 0;
}
my $pasvport=9000;
sub PASV_command {
my ($arg, $cmd)=@_;
my $pasvport;
socket(Server2, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
setsockopt(Server2, SOL_SOCKET, SO_REUSEADDR,
pack("l", 1)) || die "setsockopt: $!";
# We fire up a new sockfilt to do the data tranfer for us.
$slavepid = open2(\*DREAD, \*DWRITE,
"./server/sockfilt --port 0 --logfile log/sockdata$ftpdnum$ext.log --pidfile .sockdata$ftpdnum$ext.pid $ipv6");
my $ok=0;
print DWRITE "PING\n";
my $pong = <DREAD>;
$pasvport++; # don't reuse the previous
for(1 .. 10) {
if($pasvport > 65535) {
$pasvport = 1025;
}
if(bind(Server2, sockaddr_in($pasvport, INADDR_ANY))) {
$ok=1;
last;
}
$pasvport+= 3; # try another port please
}
if(!$ok) {
if($pong !~ /^PONG/) {
sendcontrol "500 no free ports!\r\n";
logmsg "couldn't find free port\n";
return 0;
}
listen(Server2,SOMAXCONN) || die "listen: $!";
logmsg "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(DREAD, $i, 5) || die;
# READ the response size
sysread(DREAD, $i, 5) || die;
my $size = hex($i);
# READ the response data
sysread(DREAD, $i, $size) || die;
# 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
logmsg "replying to a $cmd command\n";
printf("227 Entering Passive Mode (127,0,0,1,%d,%d)\n",
($pasvport/256), ($pasvport%256));
logmsg "replying to a $cmd command, waiting on port $pasvport\n";
sendcontrol sprintf("227 Entering Passive Mode (127,0,0,1,%d,%d)\n",
($pasvport/256), ($pasvport%256));
}
else {
# EPSV reply
logmsg "replying to a $cmd command\n";
printf("229 Entering Passive Mode (|||%d|)\n", $pasvport);
logmsg "replying to a $cmd command, waiting on port $pasvport\n";
sendcontrol sprintf("229 Entering Passive Mode (|||%d|)\n", $pasvport);
}
my $paddr;
eval {
local $SIG{ALRM} = sub { die "alarm\n" };
alarm 2; # assume swift operations!
$paddr = accept(SOCK, Server2);
alarm 2; # assume swift operations
# Wait for 'CNCT'
my $input = <DREAD>;
if($input !~ /^CNCT/) {
# we wait for a connected client
next;
}
logmsg "====> Client DATA connect\n";
alarm 0;
};
if ($@) {
# timed out
close(Server2);
print DWRITE "QUIT\n";
waitpid $slavepid, 0;
logmsg "accept failed\n";
$slavepid=0;
return;
}
else {
logmsg "accept worked\n";
my($iport,$iaddr) = sockaddr_in($paddr);
my $name = gethostbyaddr($iaddr,AF_INET);
close(Server2); # close the listener when its served its purpose!
logmsg "data connection from $name [", inet_ntoa($iaddr),
"] at port $iport\n";
logmsg "data connection setup on port $pasvport\n";
}
return;
}
# Support both PORT and EPRT here. Consider LPRT too.
sub PORT_command {
my $arg = $_[0];
my ($arg, $cmd) = @_;
my $port;
if($arg !~ /(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)/) {
logmsg "bad PORT-line: $arg\n";
sendcontrol "500 silly you, go away\r\n";
# 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;
}
# EPRT |2|::1|49706|
elsif(($cmd eq "EPRT") && ($grok_eprt)) {
if($arg !~ /(\d+)\|([^\|]+)\|(\d+)/) {
logmsg "bad EPRT-line: $arg\n";
sendcontrol "500 silly you, go away\r\n";
return 0;
}
sendcontrol "200 Thanks for dropping by. We contact you later\r\n";
$port = $3;
}
else {
logmsg "got a $cmd line we don't like\n";
sendcontrol "500 we don't like $cmd now\r\n";
return 0;
}
#my $iaddr = inet_aton("$1.$2.$3.$4");
my $iaddr = inet_aton("127.0.0.1"); # always use localhost
my $port = ($5<<8)+$6;
if(!$port || $port > 65535) {
print STDERR "very illegal PORT number: $port\n";
return 1;
}
my $paddr = sockaddr_in($port, $iaddr);
my $proto = getprotobyname('tcp') || 6;
# We fire up a new sockfilt to do the data tranfer for us.
# FIX: make it use IPv6 if need be
$slavepid = open2(\*DREAD, \*DWRITE,
"./server/sockfilt --connect $port --logfile log/sockdata$ftpdnum$ext.log --pidfile .sockdata$ftpdnum$ext.pid $ipv6");
socket(SOCK, PF_INET, SOCK_STREAM, $proto) || die "major failure";
connect(SOCK, $paddr) || return 1;
print DWRITE "PING\n";
my $pong = <DREAD>;
return \&SOCK;
if($pong !~ /^PONG/) {
logmsg "sockfilt failed!\n";
}
logmsg "====> Client DATA connect to port $port\n";
return;
}
$SIG{CHLD} = \&REAPER;
my %customreply;
my %customcount;
my %delayreply;
@ -595,22 +712,45 @@ my @welcome=(
'220- | (__| |_| | _ <| |___ '."\r\n",
'220 \___|\___/|_| \_\_____|'."\r\n");
for ( $waitedpid = 0;
($paddr = accept(Client,Server)) || $waitedpid;
$waitedpid = 0, close Client)
{
next if $waitedpid and not $paddr;
my($port,$iaddr) = sockaddr_in($paddr);
my $name = gethostbyaddr($iaddr,AF_INET);
while(1) {
#
# We read 'sockfilt' commands.
#
my $input;
eval {
local $SIG{ALRM} = sub { die "alarm\n" };
alarm 5; # just in case things go bad
$input = <STDIN>;
alarm 0;
};
if ($@) {
# timed out
logmsg "reading stdin timed out\n";
}
if($input !~ /^CNCT/) {
# we wait for a connected client
if(!length($input)) {
# it probably died, restart it
kill(9, $sfpid);
waitpid $sfpid, 0;
startsf();
logmsg "restarted sockfilt\n";
}
else {
logmsg "sockfilt said: $input";
}
next;
}
logmsg "====> Client connect\n";
# flush data:
$| = 1;
kill(9, $slavepid) if($slavepid);
$slavepid=0;
logmsg "connection from $name [", inet_ntoa($iaddr), "] at port $port\n";
open(STDIN, "<&Client") || die "can't dup client to stdin";
open(STDOUT, ">&Client") || die "can't dup client to stdout";
&customize(); # read test control instructions
sendcontrol @welcome;
@ -622,8 +762,29 @@ for ( $waitedpid = 0;
my $state="fresh";
while(1) {
my $i;
last unless defined ($_ = <STDIN>);
# Now we expect to read DATA\n[hex size]\n[prot], where the [prot]
# part only is FTP lingo.
# COMMAND
sysread(STDIN, $i, 5) || die;
if($i !~ /^DATA/) {
logmsg "sockfilt said $i";
if($i =~ /^DISC/) {
# disconnect
last;
}
next;
}
# SIZE of data
sysread(STDIN, $i, 5) || die;
my $size = hex($i);
# data
sysread STDIN, $_, $size;
ftpmsg $_;
@ -632,7 +793,7 @@ for ( $waitedpid = 0;
unless (m/^([A-Z]{3,4})\s?(.*)/i) {
sendcontrol "500 '$_': command not understood.\r\n";
logmsg "unknown crap received, bailing out hard\n";
logmsg "unknown crap received: $_, bailing out hard\n";
last;
}
my $FTPCMD=$1;
@ -692,11 +853,14 @@ for ( $waitedpid = 0;
my $func = $commandfunc{$FTPCMD};
if($func) {
# it is!
\&$func($FTPARG, $FTPCMD);
&$func($FTPARG, $FTPCMD);
}
}
} # while(1)
logmsg "client disconnected\n";
close(Client);
logmsg "====> Client disconnected\n";
}
print SFWRITE "QUIT\n";
waitpid $sfpid, 0;
exit;

View File

@ -30,6 +30,7 @@ use strict;
require "getpart.pm"; # array functions
require "valgrind.pm"; # valgrind report parser
require "ftp.pm";
my $srcdir = $ENV{'srcdir'} || '.';
my $HOSTIP="127.0.0.1";
@ -43,6 +44,7 @@ my $HTTPSPORT; # HTTPS server port
my $FTPPORT; # FTP server port
my $FTP2PORT; # FTP server 2 port
my $FTPSPORT; # FTPS server port
my $FTP6PORT; # FTP IPv6 server port
my $CURL="../src/curl"; # what curl executable to run on the tests
my $DBGCURL=$CURL; #"../src/.libs/curl"; # alternative for debugging
@ -69,6 +71,7 @@ my $HTTPPIDFILE=".http.pid";
my $HTTP6PIDFILE=".http6.pid";
my $HTTPSPIDFILE=".https.pid";
my $FTPPIDFILE=".ftp.pid";
my $FTP6PIDFILE=".ftp6.pid";
my $FTP2PIDFILE=".ftp2.pid";
my $FTPSPIDFILE=".ftps.pid";
@ -112,6 +115,7 @@ my $ssl_version; # set if libcurl is built with SSL support
my $large_file; # set if libcurl is built with large file support
my $has_idn; # set if libcurl is built with IDN support
my $http_ipv6; # set if HTTP server has IPv6 support
my $ftp_ipv6; # set if FTP server has IPv6 support
my $has_ipv6; # set if libcurl is built with IPv6 support
my $has_libz; # set if libcurl is built with libz support
my $has_getrlimit; # set if system has getrlimit()
@ -365,7 +369,7 @@ sub runhttpserver {
$pid = checkserver($pidfile);
# verify if our/any server is running on this port
my $cmd = "$CURL -o log/verifiedserver -g \"http://$ip:$port/verifiedserver\" 2>log/verifystderr";
my $cmd = "$CURL -o log/verifiedserver -g \"http://$ip:$port/verifiedserver\" 2>log/verifyhttp";
print "CMD; $cmd\n" if ($verbose);
my $res = system($cmd);
@ -425,7 +429,7 @@ sub runhttpserver {
my $verified;
for(1 .. 30) {
# verify that our server is up and running:
my $data=`$CURL --silent -g \"$ip:$port/verifiedserver\" 2>/dev/null`;
my $data=`$CURL --silent -g \"$ip:$port/verifiedserver\" 2>>log/verifyhttp`;
if ( $data =~ /WE ROOLZ: (\d+)/ ) {
$pid = 0+$1;
@ -503,24 +507,39 @@ sub runhttpsserver {
return $pid;
}
#######################################################################
# start the ftp server if needed
#
sub runftpserver {
my ($id, $verbose) = @_;
my ($id, $verbose, $ipv6) = @_;
my $STATUS;
my $RUNNING;
my $port = $id?$FTP2PORT:$FTPPORT;
# check for pidfile
my $pid = checkserver ($id?$FTP2PIDFILE:$FTPPIDFILE);
my $pidfile = $id?$FTP2PIDFILE:$FTPPIDFILE;
my $ip=$HOSTIP;
my $nameext;
ftpkillslaves();
if($ipv6) {
# if IPv6, use a different setup
$pidfile = $FTP6PIDFILE;
$port = $FTP6PORT;
$ip = $HOST6IP;
$nameext="-ipv6";
}
my $pid = checkserver ();
if ($pid <= 0) {
print "RUN: Check port $port for the FTP$id server\n"
print "RUN: Check port $port for the FTP$id$nameext server\n"
if ($verbose);
my $time=time();
# check if this is our server running on this port:
my @data=`$CURL -m4 --silent ftp://$HOSTIP:$port/verifiedserver 2>/dev/null`;
my @data=`$CURL -m4 --silent -vg \"ftp://$ip:$port/verifiedserver\" 2>log/verifyftp`;
my $line;
# if this took more than 2 secs, we assume it "hung" on a weird server
@ -534,7 +553,7 @@ sub runftpserver {
}
if(!$pid && $data[0]) {
# this is not a known server
print "RUN: Unknown server on our favourite FTP port: $port\n";
print "RUN: Unknown server on our favourite FTP$nameext port: $port\n";
return -1;
}
}
@ -543,7 +562,7 @@ sub runftpserver {
print "RUN: Killing a previous server using pid $pid\n" if($verbose);
my $res = kill (9, $pid); # die!
if(!$res) {
print "RUN: Failed to kill FTP$id test server, do it manually and",
print "RUN: Failed to kill FTP$id$nameext test server, do it manually and",
" restart the tests.\n";
return -1;
}
@ -556,7 +575,10 @@ sub runftpserver {
if($id) {
$flag .="--id $id ";
}
my $cmd="$perl $srcdir/ftpserver.pl $flag $port &";
if($ipv6) {
$flag .="--ipv6 ";
}
my $cmd="$perl $srcdir/ftpserver.pl $flag --port $port &";
if($verbose) {
print "CMD: $cmd\n";
}
@ -567,7 +589,7 @@ sub runftpserver {
for(1 .. 30) {
# verify that our server is up and running:
my $line;
my $cmd="$CURL --silent ftp://$HOSTIP:$port/verifiedserver 2>/dev/null";
my $cmd="$CURL --silent -g \"ftp://$ip:$port/verifiedserver\" 2>>log/verifyftp";
print "$cmd\n" if($verbose);
my @data = `$cmd`;
foreach $line (@data) {
@ -579,7 +601,7 @@ sub runftpserver {
}
if(!$pid) {
if($verbose) {
print STDERR "RUN: Retrying FTP$id server existence in a sec\n";
print STDERR "RUN: Retrying FTP$id$nameext server existence in a sec\n";
}
sleep(1);
next;
@ -594,7 +616,7 @@ sub runftpserver {
}
if($verbose) {
print "RUN: FTP$id server is now verified to be our server\n";
print "RUN: FTP$id$nameext server is now verified to be our server\n";
}
return $pid;
@ -850,12 +872,21 @@ sub checkcurl {
}
if($has_ipv6) {
# client has ipv6 support, check that the HTTP server has it!
# client has ipv6 support
# check if the HTTP server has it!
my @sws = `server/sws --version`;
if($sws[0] =~ /IPv6/) {
# HTTP server has ipv6 support!
$http_ipv6 = 1;
}
# check if the FTP server has it!
my @sws = `server/sockfilt --version`;
if($sws[0] =~ /IPv6/) {
# FTP server has ipv6 support!
$ftp_ipv6 = 1;
}
}
if(!$curl_debug && $torture) {
@ -877,6 +908,7 @@ sub checkcurl {
printf("* libcurl debug: %s\n", $curl_debug?"ON":"OFF");
printf("* valgrind: %s\n", $valgrind?"ON":"OFF");
printf("* HTTP IPv6 %s\n", $http_ipv6?"ON":"OFF");
printf("* FTP IPv6 %s\n", $ftp_ipv6?"ON":"OFF");
printf("* HTTP port: %d\n", $HTTPPORT);
printf("* FTP port: %d\n", $FTPPORT);
@ -888,6 +920,9 @@ sub checkcurl {
if($http_ipv6) {
printf("* HTTP IPv6 port: %d\n", $HTTP6PORT);
}
if($ftp_ipv6) {
printf("* FTP IPv6 port: %d\n", $FTP6PORT);
}
print "***************************************** \n";
}
@ -903,6 +938,7 @@ sub subVariables {
$$thing =~ s/%HTTP6PORT/$HTTP6PORT/g;
$$thing =~ s/%HTTPSPORT/$HTTPSPORT/g;
$$thing =~ s/%FTPPORT/$FTPPORT/g;
$$thing =~ s/%FTP6PORT/$FTP6PORT/g;
$$thing =~ s/%FTP2PORT/$FTP2PORT/g;
$$thing =~ s/%FTPSPORT/$FTPSPORT/g;
$$thing =~ s/%SRCDIR/$srcdir/g;
@ -1533,6 +1569,7 @@ sub stopservers {
printf ("* kill pid for %-5s => %-5d\n", $_, $run{$_}) if($verbose);
stopserver($run{$_}); # the pid file is in the hash table
}
ftpkillslaves();
}
#######################################################################
@ -1566,6 +1603,16 @@ sub startservers {
$run{'ftp2'}=$pid;
}
}
elsif($what eq "ftp-ipv6") {
if(!$run{'ftp-ipv6'}) {
$pid = runftpserver("", $verbose, "ipv6");
if($pid <= 0) {
return "failed starting FTP-ipv6 server";
}
printf ("* pid ftp-ipv6 => %-5d\n", $pid) if($verbose);
$run{'ftp-ipv6'}=$pid;
}
}
elsif($what eq "http") {
if(!$run{'http'}) {
$pid = runhttpserver($verbose);
@ -1803,6 +1850,7 @@ $FTPSPORT = $base + 3; # FTPS server port
$HTTP6PORT = $base + 4; # HTTP IPv6 server port (different IP protocol
# but we follow the same port scheme anyway)
$FTP2PORT = $base + 5; # FTP server 2 port
$FTP6PORT = $base + 6; # FTP IPv6 port
#######################################################################
# Output curl version and host info being tested

View File

@ -25,16 +25,14 @@ AUTOMAKE_OPTIONS = foreign
INCLUDES = -I$(top_srcdir)/lib -I$(top_srcdir)/include
noinst_PROGRAMS = sws getpart
noinst_PROGRAMS = sws getpart sockfilt
sws_SOURCES= sws.c getpart.c getpart.h $(top_srcdir)/lib/strequal.c \
$(top_srcdir)/lib/base64.c $(top_srcdir)/lib/mprintf.c \
useful = getpart.c getpart.h $(top_srcdir)/lib/strequal.c \
$(top_srcdir)/lib/base64.c $(top_srcdir)/lib/mprintf.c \
$(top_srcdir)/lib/memdebug.c
sws_SOURCES= sws.c $(useful)
sockfilt_SOURCES = sockfilt.c $(useful) $(top_srcdir)/lib/inet_pton.c
getpart_SOURCES= testpart.c $(useful)
extra_DIST = base64.pl
getpart_CPPFLAGS = -DGETPART_TEST
getpart_SOURCES= getpart.c getpart.h $(top_srcdir)/lib/strequal.c \
$(top_srcdir)/lib/base64.c $(top_srcdir)/lib/mprintf.c \
$(top_srcdir)/lib/memdebug.c

View File

@ -218,19 +218,3 @@ const char *spitout(FILE *stream,
return string;
}
#ifdef GETPART_TEST
int main(int argc, char **argv)
{
if(argc< 3) {
printf("./moo main sub\n");
}
else {
size_t size;
unsigned int i;
const char *buffer = spitout(stdin, argv[1], argv[2], &size);
for(i=0; i< size; i++)
printf("%c", buffer[i]);
}
return 0;
}
#endif

668
tests/server/sockfilt.c Normal file
View File

@ -0,0 +1,668 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2005, 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$
***************************************************************************/
/* Purpose
*
* 1. Accept a TCP connection on a custom port (ipv4 or ipv6).
*
* 2. Get commands on STDIN. Pass data on to the TCP stream.
* Get data from TCP stream and pass on to STDOUT.
*
* This program is made to perform all the socket/stream/connection stuff for
* the test suite's (perl) FTP server. Previously the perl code did all of
* this by its own, but I decided to let this program do the socket layer
* because of several things:
*
* o We want the perl code to work with rather old perl installations, thus
* we cannot use recent perl modules or features.
*
* o We want IPv6 support for systems that provide it, and doing optional IPv6
* support in perl seems if not impossible so at least awkward.
*
* o We want FTP-SSL support, which means that a connection that starts with
* plain sockets needs to be able to "go SSL" in the midst. This would also
* require some nasty perl stuff I'd rather avoid.
*
* (Source originally based on sws.c)
*/
#include "setup.h" /* portability help from the lib directory */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <signal.h>
#include <time.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef _XOPEN_SOURCE_EXTENDED
/* This define is "almost" required to build on HPUX 11 */
#include <arpa/inet.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#include "curlx.h" /* from the private lib dir */
#include "getpart.h"
#include "inet_pton.h"
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#if defined(WIN32) && !defined(__CYGWIN__)
#include <windows.h>
#include <winsock2.h>
#include <process.h>
#define sleep(sec) Sleep ((sec)*1000)
#define EINPROGRESS WSAEINPROGRESS
#define EWOULDBLOCK WSAEWOULDBLOCK
#define EISCONN WSAEISCONN
#define ENOTSOCK WSAENOTSOCK
#define ECONNREFUSED WSAECONNREFUSED
static void win32_cleanup(void);
#if defined(ENABLE_IPV6) && defined(__MINGW32__)
const struct in6_addr in6addr_any = {{ IN6ADDR_ANY_INIT }};
#endif
#endif
/* include memdebug.h last */
#include "memdebug.h"
#define DEFAULT_PORT 8999
#ifndef DEFAULT_LOGFILE
#define DEFAULT_LOGFILE "log/sockfilt.log"
#endif
#ifdef SIGPIPE
static volatile int sigpipe; /* Why? It's not used */
#endif
char *socklogfile = (char *)DEFAULT_LOGFILE;
/*
* ourerrno() returns the errno (or equivalent) on this platform to
* hide platform specific for the function that calls this.
*/
static int ourerrno(void)
{
#ifdef WIN32
return (int)GetLastError();
#else
return errno;
#endif
}
static void logmsg(const char *msg, ...)
{
time_t t = time(NULL);
va_list ap;
struct tm *curr_time = localtime(&t);
char buffer[256]; /* possible overflow if you pass in a huge string */
FILE *logfp;
va_start(ap, msg);
vsprintf(buffer, msg, ap);
va_end(ap);
logfp = fopen(socklogfile, "a");
fprintf(logfp?logfp:stderr, /* write to stderr if the logfile doesn't open */
"%02d:%02d:%02d %s\n",
curr_time->tm_hour,
curr_time->tm_min,
curr_time->tm_sec, buffer);
if(logfp)
fclose(logfp);
}
static void lograw(unsigned char *buffer, int len)
{
char data[120];
int i;
unsigned char *ptr = buffer;
char *optr = data;
int width=0;
for(i=0; i<len; i++) {
sprintf(optr, "%c",
(isgraph(ptr[i]) || ptr[i]==0x20) ?ptr[i]:'.');
optr += 1;
width += 1;
if(width>60) {
logmsg("RAW: '%s'", data);
width = 0;
optr = data;
}
}
if(width)
logmsg("RAW: '%s'", data);
}
#ifdef SIGPIPE
static void sigpipe_handler(int sig)
{
(void)sig; /* prevent warning */
sigpipe = 1;
}
#endif
#if defined(WIN32) && !defined(__CYGWIN__)
#undef perror
#define perror(m) win32_perror(m)
static void win32_perror(const char *msg)
{
char buf[256];
DWORD err = WSAGetLastError();
if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
LANG_NEUTRAL, buf, sizeof(buf), NULL))
snprintf(buf, sizeof(buf), "Unknown error %lu (%#lx)", err, err);
if (msg)
fprintf(stderr, "%s: ", msg);
fprintf(stderr, "%s\n", buf);
}
#endif
#if defined(WIN32) && !defined(__CYGWIN__)
static void win32_init(void)
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 0);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
perror("Winsock init failed");
logmsg("Error initialising winsock -- aborting\n");
exit(1);
}
if ( LOBYTE( wsaData.wVersion ) != 2 ||
HIBYTE( wsaData.wVersion ) != 0 ) {
WSACleanup();
perror("Winsock init failed");
logmsg("No suitable winsock.dll found -- aborting\n");
exit(1);
}
}
static void win32_cleanup(void)
{
WSACleanup();
}
#endif
char use_ipv6=FALSE;
unsigned short port = DEFAULT_PORT;
unsigned short connectport = 0; /* if non-zero, we activate this mode */
enum sockmode {
PASSIVE_LISTEN, /* as a server waiting for connections */
PASSIVE_CONNECT, /* as a server, connected to a client */
ACTIVE /* as a client, connected to a server */
};
/*
sockfdp is a pointer to an established stream or CURL_SOCKET_BAD
if sockfd is CURL_SOCKET_BAD, listendfd is a listening socket we must
accept()
*/
static int juggle(curl_socket_t *sockfdp,
curl_socket_t listenfd,
enum sockmode *mode)
{
struct timeval timeout;
fd_set fds_read;
fd_set fds_write;
fd_set fds_err;
curl_socket_t maxfd;
int r;
unsigned char buffer[256]; /* FIX: bigger buffer */
char data[256];
int sockfd;
timeout.tv_sec = 120;
timeout.tv_usec = 0;
FD_ZERO(&fds_read);
FD_ZERO(&fds_write);
FD_ZERO(&fds_err);
FD_SET(fileno(stdin), &fds_read);
switch(*mode) {
case PASSIVE_LISTEN:
/* server mode */
sockfd = listenfd;
logmsg("waiting for a client to connect on socket %d", (int)sockfd);
/* there's always a socket to wait for */
FD_SET(sockfd, &fds_read);
maxfd = sockfd;
break;
case PASSIVE_CONNECT:
sockfd = *sockfdp;
logmsg("waiting for data from client on socket %d", (int)sockfd);
/* there's always a socket to wait for */
FD_SET(sockfd, &fds_read);
maxfd = sockfd;
break;
case ACTIVE:
sockfd = *sockfdp;
/* sockfd turns CURL_SOCKET_BAD when our connection has been closed */
if(sockfd != CURL_SOCKET_BAD) {
FD_SET(sockfd, &fds_read);
maxfd = sockfd;
logmsg("waiting for data from client on socket %d", (int)sockfd);
}
else {
logmsg("No socket to read on");
maxfd = 0;
}
break;
}
do {
r = select(maxfd + 1, &fds_read, &fds_write, &fds_err, &timeout);
} while((r == -1) && (ourerrno() == EINTR));
logmsg("select() returned %d", r);
switch(r) {
case -1:
return FALSE;
case 0: /* timeout! */
return TRUE;
}
if(FD_ISSET(fileno(stdin), &fds_read)) {
size_t nread;
logmsg("data on stdin");
/* read from stdin, commands/data to be dealt with and possibly passed on
to the socket
protocol:
4 letter command + LF [mandatory]
4-digit hexadecimal data length + LF [if the command takes data]
data [the data being as long as set above]
Commands:
DATA - plain pass-thru data
*/
nread = read(fileno(stdin), buffer, 5);
if(5 == nread) {
logmsg("Received command %c%c%c%c",
buffer[0], buffer[1], buffer[2], buffer[3] );
if(!memcmp("PING", buffer, 4)) {
/* send reply on stdout, just proving we are alive */
write(fileno(stdout), "PONG\n", 5);
}
else if(!memcmp("PORT", buffer, 4)) {
/* question asking us what PORT number we are listening to.
Replies with PORT with "IPv[num]/[port]" */
sprintf((char *)buffer, "IPv%d/%d\n", use_ipv6?6:4, port);
r = strlen((char *)buffer);
sprintf(data, "PORT\n%04x\n", r);
write(fileno(stdout), data, 10);
write(fileno(stdout), buffer, r);
}
else if(!memcmp("QUIT", buffer, 4)) {
/* just die */
logmsg("quits");
exit(0);
}
else if(!memcmp("DATA", buffer, 4)) {
/* data IN => data OUT */
long len;
if(5 != read(fileno(stdin), buffer, 5))
return FALSE;
len = strtol((char *)buffer, NULL, 16);
if(len != read(fileno(stdin), buffer, len))
return FALSE;
logmsg("> %d bytes data, server => client", len);
lograw(buffer, len);
if(*mode == PASSIVE_LISTEN) {
logmsg(".., but we are disconnected!");
write(fileno(stdout), "DISC\n", 5);
}
else
/* send away on the socket */
swrite(sockfd, buffer, len);
}
else if(!memcmp("DISC", buffer, 4)) {
/* disconnect! */
write(fileno(stdout), "DISC\n", 5);
if(sockfd != CURL_SOCKET_BAD) {
logmsg("====> Client forcibly disconnected");
sclose(sockfd);
*sockfdp = CURL_SOCKET_BAD;
}
else
logmsg("attempt to close already dead connection");
return TRUE;
}
}
else {
logmsg("read %d from stdin, exiting", (int)nread);
exit(0);
}
}
if((sockfd != CURL_SOCKET_BAD) && (FD_ISSET(sockfd, &fds_read)) ) {
logmsg("data on socket");
if(*mode == PASSIVE_LISTEN) {
/* there's no stream set up yet, this is an indication that there's a
client connecting. */
sockfd = accept(sockfd, NULL, NULL);
if(-1 == sockfd)
logmsg("accept() failed\n");
else {
logmsg("====> Client connect");
write(fileno(stdout), "CNCT\n", 5);
*sockfdp = sockfd; /* store the new socket */
*mode = PASSIVE_CONNECT; /* we have connected */
}
return TRUE;
}
/* read from socket, pass on data to stdout */
r = sread(sockfd, buffer, sizeof(buffer));
if(r <= 0) {
logmsg("====> Client disconnect");
write(fileno(stdout), "DISC\n", 5);
sclose(sockfd);
*sockfdp = CURL_SOCKET_BAD;
if(*mode == PASSIVE_CONNECT)
*mode = PASSIVE_LISTEN;
return TRUE;
}
sprintf(data, "DATA\n%04x\n", r);
write(fileno(stdout), data, 10);
write(fileno(stdout), buffer, r);
logmsg("< %d bytes data, client => server", r);
lograw(buffer, r);
}
return TRUE;
}
int main(int argc, char *argv[])
{
struct sockaddr_in me;
#ifdef ENABLE_IPV6
struct sockaddr_in6 me6;
#endif /* ENABLE_IPV6 */
int sock;
int msgsock = CURL_SOCKET_BAD; /* no stream socket yet */
int flag;
FILE *pidfile;
char *pidname= (char *)".sockfilt.pid";
int rc;
int arg=1;
bool ok = FALSE;
enum sockmode mode = PASSIVE_LISTEN; /* default */
while(argc>arg) {
if(!strcmp("--version", argv[arg])) {
printf("sockfilt IPv4%s\n",
#ifdef ENABLE_IPV6
"/IPv6"
#else
""
#endif
);
return 0;
}
else if(!strcmp("--pidfile", argv[arg])) {
arg++;
if(argc>arg)
pidname = argv[arg++];
}
else if(!strcmp("--logfile", argv[arg])) {
arg++;
if(argc>arg)
socklogfile = argv[arg++];
}
else if(!strcmp("--ipv6", argv[arg])) {
#ifdef ENABLE_IPV6
use_ipv6=TRUE;
#endif
arg++;
}
else if(!strcmp("--ipv4", argv[arg])) {
/* for completeness, we support this option as well */
use_ipv6=FALSE;
arg++;
}
else if(!strcmp("--port", argv[arg])) {
arg++;
if(argc>arg) {
port = (unsigned short)atoi(argv[arg]);
arg++;
}
}
else if(!strcmp("--connect", argv[arg])) {
/* Asked to actively connect to the specified local port instead of
doing a passive server-style listening. */
arg++;
if(argc>arg) {
connectport = (unsigned short)atoi(argv[arg]);
arg++;
}
}
else {
puts("Usage: sockfilt [option]\n"
" --version\n"
" --logfile [file]\n"
" --pidfile [file]\n"
" --ipv4\n"
" --ipv6\n"
" --port [port]");
exit(0);
}
}
#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
win32_init();
atexit(win32_cleanup);
#else
#ifdef SIGPIPE
#ifdef HAVE_SIGNAL
signal(SIGPIPE, sigpipe_handler);
#endif
#ifdef HAVE_SIGINTERRUPT
siginterrupt(SIGPIPE, 1);
#endif
#endif
#endif
#ifdef ENABLE_IPV6
if(!use_ipv6)
#endif
sock = socket(AF_INET, SOCK_STREAM, 0);
#ifdef ENABLE_IPV6
else
sock = socket(AF_INET6, SOCK_STREAM, 0);
#endif
if (sock < 0) {
perror("opening stream socket");
logmsg("Error opening socket");
exit(1);
}
if(connectport) {
/* Active mode, we should connect to the given port number */
mode = ACTIVE;
#ifdef ENABLE_IPV6
if(!use_ipv6) {
#endif
memset(&me, 0, sizeof(me));
me.sin_family = AF_INET;
me.sin_port = htons(connectport);
me.sin_addr.s_addr = INADDR_ANY;
Curl_inet_pton(AF_INET, "127.0.0.1", &me.sin_addr);
rc = connect(sock, (struct sockaddr *) &me, sizeof(me));
#ifdef ENABLE_IPV6
}
else {
memset(&me6, 0, sizeof(me6));
me6.sin6_family = AF_INET6;
me6.sin6_port = htons(connectport);
Curl_inet_pton(AF_INET, "::1", &me6.sin6_addr);
rc = connect(sock, (struct sockaddr *) &me6, sizeof(me6));
}
#endif /* ENABLE_IPV6 */
if(rc) {
perror("connecting stream socket");
logmsg("Error connecting to port %d", port);
exit(1);
}
logmsg("====> Client connect");
msgsock = sock; /* use this as stream */
}
else {
/* passive daemon style */
flag = 1;
if (setsockopt
(sock, SOL_SOCKET, SO_REUSEADDR, (const void *) &flag,
sizeof(int)) < 0) {
perror("setsockopt(SO_REUSEADDR)");
}
#ifdef ENABLE_IPV6
if(!use_ipv6) {
#endif
me.sin_family = AF_INET;
me.sin_addr.s_addr = INADDR_ANY;
me.sin_port = htons(port);
rc = bind(sock, (struct sockaddr *) &me, sizeof(me));
#ifdef ENABLE_IPV6
}
else {
memset(&me6, 0, sizeof(struct sockaddr_in6));
me6.sin6_family = AF_INET6;
me6.sin6_addr = in6addr_any;
me6.sin6_port = htons(port);
rc = bind(sock, (struct sockaddr *) &me6, sizeof(me6));
}
#endif /* ENABLE_IPV6 */
if(rc < 0) {
perror("binding stream socket");
logmsg("Error binding socket");
exit(1);
}
if(!port) {
/* The system picked a port number, now figure out which port we actually
got */
/* we succeeded to bind */
struct sockaddr_in add;
socklen_t socksize = sizeof(add);
if(getsockname(sock, (struct sockaddr *) &add,
&socksize)<0) {
fprintf(stderr, "getsockname() failed");
return 1;
}
port = ntohs(add.sin_port);
}
/* start accepting connections */
listen(sock, 0);
}
pidfile = fopen(pidname, "w");
if(pidfile) {
fprintf(pidfile, "%d\n", (int)getpid());
fclose(pidfile);
}
else
fprintf(stderr, "Couldn't write pid file\n");
logmsg("Running IPv%d version",
(use_ipv6?6:4));
if(connectport)
logmsg("Connected to port %d", connectport);
else
logmsg("Listening on port %d", port);
do {
ok = juggle(&msgsock, sock, &mode);
} while(ok);
sclose(sock);
return 0;
}

52
tests/server/testpart.c Normal file
View File

@ -0,0 +1,52 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2005, 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$
***************************************************************************/
#include "setup.h"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "getpart.h"
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
/* include memdebug.h last */
#include "memdebug.h"
int main(int argc, char **argv)
{
if(argc< 3) {
printf("./testpart main sub\n");
}
else {
size_t size;
unsigned int i;
const char *buffer = spitout(stdin, argv[1], argv[2], &size);
for(i=0; i< size; i++)
printf("%c", buffer[i]);
}
return 0;
}