1#!/usr/bin/env perl
2#***************************************************************************
3#                                  _   _ ____  _
4#  Project                     ___| | | |  _ \| |
5#                             / __| | | | |_) | |
6#                            | (__| |_| |  _ <| |___
7#                             \___|\___/|_| \_\_____|
8#
9# Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
10#
11# This software is licensed as described in the file COPYING, which
12# you should have received as part of this distribution. The terms
13# are also available at https://curl.haxx.se/docs/copyright.html.
14#
15# You may opt to use, copy, modify, merge, publish, distribute and/or sell
16# copies of the Software, and permit persons to whom the Software is
17# furnished to do so, under the terms of the COPYING file.
18#
19# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20# KIND, either express or implied.
21#
22###########################################################################
23
24# Experimental hooks are available to run tests remotely on machines that
25# are able to run curl but are unable to run the test harness.
26# The following sections need to be modified:
27#
28#  $HOSTIP, $HOST6IP - Set to the address of the host running the test suite
29#  $CLIENTIP, $CLIENT6IP - Set to the address of the host running curl
30#  runclient, runclientoutput - Modify to copy all the files in the log/
31#    directory to the system running curl, run the given command remotely
32#    and save the return code or returned stdout (respectively), then
33#    copy all the files from the remote system's log/ directory back to
34#    the host running the test suite.  This can be done a few ways, such
35#    as using scp & ssh, rsync & telnet, or using a NFS shared directory
36#    and ssh.
37#
38# 'make && make test' needs to be done on both machines before making the
39# above changes and running runtests.pl manually.  In the shared NFS case,
40# the contents of the tests/server/ directory must be from the host
41# running the test suite, while the rest must be from the host running curl.
42#
43# Note that even with these changes a number of tests will still fail (mainly
44# to do with cookies, those that set environment variables, or those that
45# do more than touch the file system in a <precheck> or <postcheck>
46# section). These can be added to the $TESTCASES line below,
47# e.g. $TESTCASES="!8 !31 !63 !cookies..."
48#
49# Finally, to properly support -g and -n, checktestcmd needs to change
50# to check the remote system's PATH, and the places in the code where
51# the curl binary is read directly to determine its type also need to be
52# fixed. As long as the -g option is never given, and the -n is always
53# given, this won't be a problem.
54
55
56# These should be the only variables that might be needed to get edited:
57
58BEGIN {
59    push(@INC, $ENV{'srcdir'}) if(defined $ENV{'srcdir'});
60    push(@INC, ".");
61    # run time statistics needs Time::HiRes
62    eval {
63        no warnings "all";
64        require Time::HiRes;
65        import  Time::HiRes qw( time );
66    }
67}
68
69use strict;
70use warnings;
71use Cwd;
72
73# Subs imported from serverhelp module
74use serverhelp qw(
75    serverfactors
76    servername_id
77    servername_str
78    servername_canon
79    server_pidfilename
80    server_logfilename
81    );
82
83# Variables and subs imported from sshhelp module
84use sshhelp qw(
85    $sshdexe
86    $sshexe
87    $sftpexe
88    $sshconfig
89    $sftpconfig
90    $sshdlog
91    $sshlog
92    $sftplog
93    $sftpcmds
94    display_sshdconfig
95    display_sshconfig
96    display_sftpconfig
97    display_sshdlog
98    display_sshlog
99    display_sftplog
100    exe_ext
101    find_sshd
102    find_ssh
103    find_sftp
104    find_httptlssrv
105    sshversioninfo
106    );
107
108use pathhelp;
109
110require "getpart.pm"; # array functions
111require "valgrind.pm"; # valgrind report parser
112require "ftp.pm";
113
114my $HOSTIP="127.0.0.1";   # address on which the test server listens
115my $HOST6IP="[::1]";      # address on which the test server listens
116my $CLIENTIP="127.0.0.1"; # address which curl uses for incoming connections
117my $CLIENT6IP="[::1]";    # address which curl uses for incoming connections
118
119my $base = 8990; # base port number
120
121my $HTTPPORT;            # HTTP server port
122my $HTTP6PORT;           # HTTP IPv6 server port
123my $HTTPSPORT;           # HTTPS (stunnel) server port
124my $FTPPORT;             # FTP server port
125my $FTP2PORT;            # FTP server 2 port
126my $FTPSPORT;            # FTPS (stunnel) server port
127my $FTP6PORT;            # FTP IPv6 server port
128my $TFTPPORT;            # TFTP
129my $TFTP6PORT;           # TFTP
130my $SSHPORT;             # SCP/SFTP
131my $SOCKSPORT;           # SOCKS4/5 port
132my $POP3PORT;            # POP3
133my $POP36PORT;           # POP3 IPv6 server port
134my $IMAPPORT;            # IMAP
135my $IMAP6PORT;           # IMAP IPv6 server port
136my $SMTPPORT;            # SMTP
137my $SMTP6PORT;           # SMTP IPv6 server port
138my $RTSPPORT;            # RTSP
139my $RTSP6PORT;           # RTSP IPv6 server port
140my $GOPHERPORT;          # Gopher
141my $GOPHER6PORT;         # Gopher IPv6 server port
142my $HTTPTLSPORT;         # HTTP TLS (non-stunnel) server port
143my $HTTPTLS6PORT;        # HTTP TLS (non-stunnel) IPv6 server port
144my $HTTPPROXYPORT;       # HTTP proxy port, when using CONNECT
145my $HTTPPIPEPORT;        # HTTP pipelining port
146my $HTTPUNIXPATH;        # HTTP server Unix domain socket path
147my $HTTP2PORT;           # HTTP/2 server port
148
149my $srcdir = $ENV{'srcdir'} || '.';
150my $CURL="../src/curl".exe_ext(); # what curl executable to run on the tests
151my $VCURL=$CURL;   # what curl binary to use to verify the servers with
152                   # VCURL is handy to set to the system one when the one you
153                   # just built hangs or crashes and thus prevent verification
154my $DBGCURL=$CURL; #"../src/.libs/curl";  # alternative for debugging
155my $LOGDIR="log";
156my $TESTDIR="$srcdir/data";
157my $LIBDIR="./libtest";
158my $UNITDIR="./unit";
159# TODO: change this to use server_inputfilename()
160my $SERVERIN="$LOGDIR/server.input"; # what curl sent the server
161my $SERVER2IN="$LOGDIR/server2.input"; # what curl sent the second server
162my $PROXYIN="$LOGDIR/proxy.input"; # what curl sent the proxy
163my $CURLLOG="$LOGDIR/curl.log"; # all command lines run
164my $FTPDCMD="$LOGDIR/ftpserver.cmd"; # copy ftp server instructions here
165my $SERVERLOGS_LOCK="$LOGDIR/serverlogs.lock"; # server logs advisor read lock
166my $CURLCONFIG="../curl-config"; # curl-config from current build
167
168# Normally, all test cases should be run, but at times it is handy to
169# simply run a particular one:
170my $TESTCASES="all";
171
172# To run specific test cases, set them like:
173# $TESTCASES="1 2 3 7 8";
174
175#######################################################################
176# No variables below this point should need to be modified
177#
178
179# invoke perl like this:
180my $perl="perl -I$srcdir";
181my $server_response_maxtime=13;
182
183my $debug_build=0;          # built debug enabled (--enable-debug)
184my $has_memory_tracking=0;  # built with memory tracking (--enable-curldebug)
185my $libtool;
186
187# name of the file that the memory debugging creates:
188my $memdump="$LOGDIR/memdump";
189
190# the path to the script that analyzes the memory debug output file:
191my $memanalyze="$perl $srcdir/memanalyze.pl";
192
193my $pwd = getcwd();          # current working directory
194
195my $start;
196my $ftpchecktime=1; # time it took to verify our test FTP server
197
198my $stunnel = checkcmd("stunnel4") || checkcmd("tstunnel") || checkcmd("stunnel");
199my $valgrind = checktestcmd("valgrind");
200my $valgrind_logfile="--logfile";
201my $valgrind_tool;
202my $gdb = checktestcmd("gdb");
203my $httptlssrv = find_httptlssrv();
204
205my $has_ssl;        # set if libcurl is built with SSL support
206my $has_largefile;  # set if libcurl is built with large file support
207my $has_idn;        # set if libcurl is built with IDN support
208my $http_ipv6;      # set if HTTP server has IPv6 support
209my $http_unix;      # set if HTTP server has Unix sockets support
210my $ftp_ipv6;       # set if FTP server has IPv6 support
211my $tftp_ipv6;      # set if TFTP server has IPv6 support
212my $gopher_ipv6;    # set if Gopher server has IPv6 support
213my $has_ipv6;       # set if libcurl is built with IPv6 support
214my $has_unix;       # set if libcurl is built with Unix sockets support
215my $has_libz;       # set if libcurl is built with libz support
216my $has_getrlimit;  # set if system has getrlimit()
217my $has_ntlm;       # set if libcurl is built with NTLM support
218my $has_ntlm_wb;    # set if libcurl is built with NTLM delegation to winbind
219my $has_sspi;       # set if libcurl is built with Windows SSPI
220my $has_gssapi;     # set if libcurl is built with a GSS-API library
221my $has_kerberos;   # set if libcurl is built with Kerberos support
222my $has_spnego;     # set if libcurl is built with SPNEGO support
223my $has_charconv;   # set if libcurl is built with CharConv support
224my $has_tls_srp;    # set if libcurl is built with TLS-SRP support
225my $has_metalink;   # set if curl is built with Metalink support
226my $has_http2;      # set if libcurl is built with HTTP2 support
227my $has_crypto;     # set if libcurl is built with cryptographic support
228my $has_cares;      # set if built with c-ares
229my $has_threadedres;# set if built with threaded resolver
230my $has_psl;        # set if libcurl is built with PSL support
231
232# this version is decided by the particular nghttp2 library that is being used
233my $h2cver = "h2c";
234
235my $has_openssl;    # built with a lib using an OpenSSL-like API
236my $has_gnutls;     # built with GnuTLS
237my $has_nss;        # built with NSS
238my $has_yassl;      # built with yassl
239my $has_polarssl;   # built with polarssl
240my $has_axtls;      # built with axTLS
241my $has_winssl;     # built with WinSSL    (Secure Channel aka Schannel)
242my $has_darwinssl;  # built with DarwinSSL (Secure Transport)
243my $has_boringssl;  # built with BoringSSL
244my $has_libressl;   # built with libressl
245my $has_mbedtls;    # built with mbedTLS
246
247my $has_sslpinning; # built with a TLS backend that supports pinning
248
249my $has_shared = "unknown";  # built shared
250
251my $resolver;       # name of the resolver backend (for human presentation)
252my $ssllib;         # name of the SSL library we use (for human presentation)
253
254my $has_textaware;  # set if running on a system that has a text mode concept
255                    # on files. Windows for example
256
257my @protocols;   # array of lowercase supported protocol servers
258
259my $skipped=0;  # number of tests skipped; reported in main loop
260my %skipped;    # skipped{reason}=counter, reasons for skip
261my @teststat;   # teststat[testnum]=reason, reasons for skip
262my %disabled_keywords;  # key words of tests to skip
263my %enabled_keywords;   # key words of tests to run
264my %disabled;           # disabled test cases
265
266my $sshdid;      # for socks server, ssh daemon version id
267my $sshdvernum;  # for socks server, ssh daemon version number
268my $sshdverstr;  # for socks server, ssh daemon version string
269my $sshderror;   # for socks server, ssh daemon version error
270
271my $defserverlogslocktimeout = 20; # timeout to await server logs lock removal
272my $defpostcommanddelay = 0; # delay between command and postcheck sections
273
274my $timestats;   # time stamping and stats generation
275my $fullstats;   # show time stats for every single test
276my %timeprepini; # timestamp for each test preparation start
277my %timesrvrini; # timestamp for each test required servers verification start
278my %timesrvrend; # timestamp for each test required servers verification end
279my %timetoolini; # timestamp for each test command run starting
280my %timetoolend; # timestamp for each test command run stopping
281my %timesrvrlog; # timestamp for each test server logs lock removal
282my %timevrfyend; # timestamp for each test result verification end
283
284my $testnumcheck; # test number, set in singletest sub.
285my %oldenv;
286
287#######################################################################
288# variables that command line options may set
289#
290
291my $short;
292my $automakestyle;
293my $verbose;
294my $debugprotocol;
295my $anyway;
296my $gdbthis;      # run test case with gdb debugger
297my $gdbxwin;      # use windowed gdb when using gdb
298my $keepoutfiles; # keep stdout and stderr files after tests
299my $listonly;     # only list the tests
300my $postmortem;   # display detailed info about failed tests
301my $run_event_based; # run curl with --test-event to test the event API
302
303my %run;          # running server
304my %doesntrun;    # servers that don't work, identified by pidfile
305my %serverpidfile;# all server pid file names, identified by server id
306my %runcert;      # cert file currently in use by an ssl running server
307
308# torture test variables
309my $torture;
310my $tortnum;
311my $tortalloc;
312
313#######################################################################
314# logmsg is our general message logging subroutine.
315#
316sub logmsg {
317    for(@_) {
318        print "$_";
319    }
320}
321
322# get the name of the current user
323my $USER = $ENV{USER};          # Linux
324if (!$USER) {
325    $USER = $ENV{USERNAME};     # Windows
326    if (!$USER) {
327        $USER = $ENV{LOGNAME};  # Some Unix (I think)
328    }
329}
330
331# enable memory debugging if curl is compiled with it
332$ENV{'CURL_MEMDEBUG'} = $memdump;
333$ENV{'CURL_ENTROPY'}="12345678";
334$ENV{'CURL_FORCETIME'}=1; # for debug NTLM magic
335$ENV{'HOME'}=$pwd;
336
337sub catch_zap {
338    my $signame = shift;
339    logmsg "runtests.pl received SIG$signame, exiting\n";
340    stopservers($verbose);
341    die "Somebody sent me a SIG$signame";
342}
343$SIG{INT} = \&catch_zap;
344$SIG{TERM} = \&catch_zap;
345
346##########################################################################
347# Clear all possible '*_proxy' environment variables for various protocols
348# to prevent them to interfere with our testing!
349
350my $protocol;
351foreach $protocol (('ftp', 'http', 'ftps', 'https', 'no', 'all')) {
352    my $proxy = "${protocol}_proxy";
353    # clear lowercase version
354    delete $ENV{$proxy} if($ENV{$proxy});
355    # clear uppercase version
356    delete $ENV{uc($proxy)} if($ENV{uc($proxy)});
357}
358
359# make sure we don't get affected by other variables that control our
360# behaviour
361
362delete $ENV{'SSL_CERT_DIR'} if($ENV{'SSL_CERT_DIR'});
363delete $ENV{'SSL_CERT_PATH'} if($ENV{'SSL_CERT_PATH'});
364delete $ENV{'CURL_CA_BUNDLE'} if($ENV{'CURL_CA_BUNDLE'});
365
366#######################################################################
367# Load serverpidfile hash with pidfile names for all possible servers.
368#
369sub init_serverpidfile_hash {
370  for my $proto (('ftp', 'http', 'imap', 'pop3', 'smtp', 'http/2')) {
371    for my $ssl (('', 's')) {
372      for my $ipvnum ((4, 6)) {
373        for my $idnum ((1, 2, 3)) {
374          my $serv = servername_id("$proto$ssl", $ipvnum, $idnum);
375          my $pidf = server_pidfilename("$proto$ssl", $ipvnum, $idnum);
376          $serverpidfile{$serv} = $pidf;
377        }
378      }
379    }
380  }
381  for my $proto (('tftp', 'sftp', 'socks', 'ssh', 'rtsp', 'gopher', 'httptls')) {
382    for my $ipvnum ((4, 6)) {
383      for my $idnum ((1, 2)) {
384        my $serv = servername_id($proto, $ipvnum, $idnum);
385        my $pidf = server_pidfilename($proto, $ipvnum, $idnum);
386        $serverpidfile{$serv} = $pidf;
387      }
388    }
389  }
390  for my $proto (('http', 'imap', 'pop3', 'smtp', 'http/2')) {
391    for my $ssl (('', 's')) {
392      my $serv = servername_id("$proto$ssl", "unix", 1);
393      my $pidf = server_pidfilename("$proto$ssl", "unix", 1);
394      $serverpidfile{$serv} = $pidf;
395    }
396  }
397}
398
399#######################################################################
400# Check if a given child process has just died. Reaps it if so.
401#
402sub checkdied {
403    use POSIX ":sys_wait_h";
404    my $pid = $_[0];
405    if((not defined $pid) || $pid <= 0) {
406        return 0;
407    }
408    my $rc = waitpid($pid, &WNOHANG);
409    return ($rc == $pid)?1:0;
410}
411
412#######################################################################
413# Start a new thread/process and run the given command line in there.
414# Return the pids (yes plural) of the new child process to the parent.
415#
416sub startnew {
417    my ($cmd, $pidfile, $timeout, $fake)=@_;
418
419    logmsg "startnew: $cmd\n" if ($verbose);
420
421    my $child = fork();
422    my $pid2 = 0;
423
424    if(not defined $child) {
425        logmsg "startnew: fork() failure detected\n";
426        return (-1,-1);
427    }
428
429    if(0 == $child) {
430        # Here we are the child. Run the given command.
431
432        # Put an "exec" in front of the command so that the child process
433        # keeps this child's process ID.
434        exec("exec $cmd") || die "Can't exec() $cmd: $!";
435
436        # exec() should never return back here to this process. We protect
437        # ourselves by calling die() just in case something goes really bad.
438        die "error: exec() has returned";
439    }
440
441    # Ugly hack but ssh client and gnutls-serv don't support pid files
442    if ($fake) {
443        if(open(OUT, ">$pidfile")) {
444            print OUT $child . "\n";
445            close(OUT);
446            logmsg "startnew: $pidfile faked with pid=$child\n" if($verbose);
447        }
448        else {
449            logmsg "startnew: failed to write fake $pidfile with pid=$child\n";
450        }
451        # could/should do a while connect fails sleep a bit and loop
452        sleep $timeout;
453        if (checkdied($child)) {
454            logmsg "startnew: child process has failed to start\n" if($verbose);
455            return (-1,-1);
456        }
457    }
458
459    my $count = $timeout;
460    while($count--) {
461        if(-f $pidfile && -s $pidfile && open(PID, "<$pidfile")) {
462            $pid2 = 0 + <PID>;
463            close(PID);
464            if(($pid2 > 0) && pidexists($pid2)) {
465                # if $pid2 is valid, then make sure this pid is alive, as
466                # otherwise it is just likely to be the _previous_ pidfile or
467                # similar!
468                last;
469            }
470            # invalidate $pid2 if not actually alive
471            $pid2 = 0;
472        }
473        if (checkdied($child)) {
474            logmsg "startnew: child process has died, server might start up\n"
475                if($verbose);
476            # We can't just abort waiting for the server with a
477            # return (-1,-1);
478            # because the server might have forked and could still start
479            # up normally. Instead, just reduce the amount of time we remain
480            # waiting.
481            $count >>= 2;
482        }
483        sleep(1);
484    }
485
486    # Return two PIDs, the one for the child process we spawned and the one
487    # reported by the server itself (in case it forked again on its own).
488    # Both (potentially) need to be killed at the end of the test.
489    return ($child, $pid2);
490}
491
492
493#######################################################################
494# Check for a command in the PATH of the test server.
495#
496sub checkcmd {
497    my ($cmd)=@_;
498    my @paths=(split(":", $ENV{'PATH'}), "/usr/sbin", "/usr/local/sbin",
499               "/sbin", "/usr/bin", "/usr/local/bin",
500               "./libtest/.libs", "./libtest");
501    for(@paths) {
502        if( -x "$_/$cmd" && ! -d "$_/$cmd") {
503            # executable bit but not a directory!
504            return "$_/$cmd";
505        }
506    }
507}
508
509#######################################################################
510# Get the list of tests that the tests/data/Makefile.am knows about!
511#
512my $disttests;
513sub get_disttests {
514    my @dist = `cd data && make show`;
515    $disttests = join("", @dist);
516}
517
518#######################################################################
519# Check for a command in the PATH of the machine running curl.
520#
521sub checktestcmd {
522    my ($cmd)=@_;
523    return checkcmd($cmd);
524}
525
526#######################################################################
527# Run the application under test and return its return code
528#
529sub runclient {
530    my ($cmd)=@_;
531    my $ret = system($cmd);
532    print "CMD ($ret): $cmd\n" if($verbose && !$torture);
533    return $ret;
534
535# This is one way to test curl on a remote machine
536#    my $out = system("ssh $CLIENTIP cd \'$pwd\' \\; \'$cmd\'");
537#    sleep 2;    # time to allow the NFS server to be updated
538#    return $out;
539}
540
541#######################################################################
542# Run the application under test and return its stdout
543#
544sub runclientoutput {
545    my ($cmd)=@_;
546    return `$cmd`;
547
548# This is one way to test curl on a remote machine
549#    my @out = `ssh $CLIENTIP cd \'$pwd\' \\; \'$cmd\'`;
550#    sleep 2;    # time to allow the NFS server to be updated
551#    return @out;
552 }
553
554#######################################################################
555# Memory allocation test and failure torture testing.
556#
557sub torture {
558    my $testcmd = shift;
559    my $gdbline = shift;
560
561    # remove memdump first to be sure we get a new nice and clean one
562    unlink($memdump);
563
564    # First get URL from test server, ignore the output/result
565    runclient($testcmd);
566
567    logmsg " CMD: $testcmd\n" if($verbose);
568
569    # memanalyze -v is our friend, get the number of allocations made
570    my $count=0;
571    my @out = `$memanalyze -v $memdump`;
572    for(@out) {
573        if(/^Allocations: (\d+)/) {
574            $count = $1;
575            last;
576        }
577    }
578    if(!$count) {
579        logmsg " found no allocs to make fail\n";
580        return 0;
581    }
582
583    logmsg " $count allocations to make fail\n";
584
585    for ( 1 .. $count ) {
586        my $limit = $_;
587        my $fail;
588        my $dumped_core;
589
590        if($tortalloc && ($tortalloc != $limit)) {
591            next;
592        }
593
594        if($verbose) {
595            my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
596                localtime(time());
597            my $now = sprintf("%02d:%02d:%02d ", $hour, $min, $sec);
598            logmsg "Fail alloc no: $limit at $now\r";
599        }
600
601        # make the memory allocation function number $limit return failure
602        $ENV{'CURL_MEMLIMIT'} = $limit;
603
604        # remove memdump first to be sure we get a new nice and clean one
605        unlink($memdump);
606
607        logmsg "*** Alloc number $limit is now set to fail ***\n" if($gdbthis);
608
609        my $ret = 0;
610        if($gdbthis) {
611            runclient($gdbline);
612        }
613        else {
614            $ret = runclient($testcmd);
615        }
616        #logmsg "$_ Returned " . ($ret >> 8) . "\n";
617
618        # Now clear the variable again
619        delete $ENV{'CURL_MEMLIMIT'} if($ENV{'CURL_MEMLIMIT'});
620
621        if(-r "core") {
622            # there's core file present now!
623            logmsg " core dumped\n";
624            $dumped_core = 1;
625            $fail = 2;
626        }
627
628        # verify that it returns a proper error code, doesn't leak memory
629        # and doesn't core dump
630        if(($ret & 255) || ($ret >> 8) >= 128) {
631            logmsg " system() returned $ret\n";
632            $fail=1;
633        }
634        else {
635            my @memdata=`$memanalyze $memdump`;
636            my $leak=0;
637            for(@memdata) {
638                if($_ ne "") {
639                    # well it could be other memory problems as well, but
640                    # we call it leak for short here
641                    $leak=1;
642                }
643            }
644            if($leak) {
645                logmsg "** MEMORY FAILURE\n";
646                logmsg @memdata;
647                logmsg `$memanalyze -l $memdump`;
648                $fail = 1;
649            }
650        }
651        if($fail) {
652            logmsg " Failed on alloc number $limit in test.\n",
653            " invoke with \"-t$limit\" to repeat this single case.\n";
654            stopservers($verbose);
655            return 1;
656        }
657    }
658
659    logmsg "torture OK\n";
660    return 0;
661}
662
663#######################################################################
664# Stop a test server along with pids which aren't in the %run hash yet.
665# This also stops all servers which are relative to the given one.
666#
667sub stopserver {
668    my ($server, $pidlist) = @_;
669    #
670    # kill sockfilter processes for pingpong relative server
671    #
672    if($server =~ /^(ftp|imap|pop3|smtp)s?(\d*)(-ipv6|)$/) {
673        my $proto  = $1;
674        my $idnum  = ($2 && ($2 > 1)) ? $2 : 1;
675        my $ipvnum = ($3 && ($3 =~ /6$/)) ? 6 : 4;
676        killsockfilters($proto, $ipvnum, $idnum, $verbose);
677    }
678    #
679    # All servers relative to the given one must be stopped also
680    #
681    my @killservers;
682    if($server =~ /^(ftp|http|imap|pop3|smtp|httppipe)s((\d*)(-ipv6|-unix|))$/) {
683        # given a stunnel based ssl server, also kill non-ssl underlying one
684        push @killservers, "${1}${2}";
685    }
686    elsif($server =~ /^(ftp|http|imap|pop3|smtp|httppipe)((\d*)(-ipv6|-unix|))$/) {
687        # given a non-ssl server, also kill stunnel based ssl piggybacking one
688        push @killservers, "${1}s${2}";
689    }
690    elsif($server =~ /^(socks)((\d*)(-ipv6|))$/) {
691        # given a socks server, also kill ssh underlying one
692        push @killservers, "ssh${2}";
693    }
694    elsif($server =~ /^(ssh)((\d*)(-ipv6|))$/) {
695        # given a ssh server, also kill socks piggybacking one
696        push @killservers, "socks${2}";
697    }
698    push @killservers, $server;
699    #
700    # kill given pids and server relative ones clearing them in %run hash
701    #
702    foreach my $server (@killservers) {
703        if($run{$server}) {
704            # we must prepend a space since $pidlist may already contain a pid
705            $pidlist .= " $run{$server}";
706            $run{$server} = 0;
707        }
708        $runcert{$server} = 0 if($runcert{$server});
709    }
710    killpid($verbose, $pidlist);
711    #
712    # cleanup server pid files
713    #
714    foreach my $server (@killservers) {
715        my $pidfile = $serverpidfile{$server};
716        my $pid = processexists($pidfile);
717        if($pid > 0) {
718            logmsg "Warning: $server server unexpectedly alive\n";
719            killpid($verbose, $pid);
720        }
721        unlink($pidfile) if(-f $pidfile);
722    }
723}
724
725#######################################################################
726# Verify that the server that runs on $ip, $port is our server.  This also
727# implies that we can speak with it, as there might be occasions when the
728# server runs fine but we cannot talk to it ("Failed to connect to ::1: Can't
729# assign requested address")
730#
731sub verifyhttp {
732    my ($proto, $ipvnum, $idnum, $ip, $port_or_path) = @_;
733    my $server = servername_id($proto, $ipvnum, $idnum);
734    my $pid = 0;
735    my $bonus="";
736    # $port_or_path contains a path for Unix sockets, sws ignores the port
737    my $port = ($ipvnum eq "unix") ? 80 : $port_or_path;
738
739    my $verifyout = "$LOGDIR/".
740        servername_canon($proto, $ipvnum, $idnum) .'_verify.out';
741    unlink($verifyout) if(-f $verifyout);
742
743    my $verifylog = "$LOGDIR/".
744        servername_canon($proto, $ipvnum, $idnum) .'_verify.log';
745    unlink($verifylog) if(-f $verifylog);
746
747    if($proto eq "gopher") {
748        # gopher is funny
749        $bonus="1/";
750    }
751
752    my $flags = "--max-time $server_response_maxtime ";
753    $flags .= "--output $verifyout ";
754    $flags .= "--silent ";
755    $flags .= "--verbose ";
756    $flags .= "--globoff ";
757    $flags .= "--unix-socket '$port_or_path' " if $ipvnum eq "unix";
758    $flags .= "-1 "         if($has_axtls);
759    $flags .= "--insecure " if($proto eq 'https');
760    $flags .= "\"$proto://$ip:$port/${bonus}verifiedserver\"";
761
762    my $cmd = "$VCURL $flags 2>$verifylog";
763
764    # verify if our/any server is running on this port
765    logmsg "RUN: $cmd\n" if($verbose);
766    my $res = runclient($cmd);
767
768    $res >>= 8; # rotate the result
769    if($res & 128) {
770        logmsg "RUN: curl command died with a coredump\n";
771        return -1;
772    }
773
774    if($res && $verbose) {
775        logmsg "RUN: curl command returned $res\n";
776        if(open(FILE, "<$verifylog")) {
777            while(my $string = <FILE>) {
778                logmsg "RUN: $string" if($string !~ /^([ \t]*)$/);
779            }
780            close(FILE);
781        }
782    }
783
784    my $data;
785    if(open(FILE, "<$verifyout")) {
786        while(my $string = <FILE>) {
787            $data = $string;
788            last; # only want first line
789        }
790        close(FILE);
791    }
792
793    if($data && ($data =~ /WE ROOLZ: (\d+)/)) {
794        $pid = 0+$1;
795    }
796    elsif($res == 6) {
797        # curl: (6) Couldn't resolve host '::1'
798        logmsg "RUN: failed to resolve host ($proto://$ip:$port/verifiedserver)\n";
799        return -1;
800    }
801    elsif($data || ($res && ($res != 7))) {
802        logmsg "RUN: Unknown server on our $server port: $port ($res)\n";
803        return -1;
804    }
805    return $pid;
806}
807
808#######################################################################
809# Verify that the server that runs on $ip, $port is our server.  This also
810# implies that we can speak with it, as there might be occasions when the
811# server runs fine but we cannot talk to it ("Failed to connect to ::1: Can't
812# assign requested address")
813#
814sub verifyftp {
815    my ($proto, $ipvnum, $idnum, $ip, $port) = @_;
816    my $server = servername_id($proto, $ipvnum, $idnum);
817    my $pid = 0;
818    my $time=time();
819    my $extra="";
820
821    my $verifylog = "$LOGDIR/".
822        servername_canon($proto, $ipvnum, $idnum) .'_verify.log';
823    unlink($verifylog) if(-f $verifylog);
824
825    if($proto eq "ftps") {
826        $extra .= "--insecure --ftp-ssl-control ";
827    }
828
829    my $flags = "--max-time $server_response_maxtime ";
830    $flags .= "--silent ";
831    $flags .= "--verbose ";
832    $flags .= "--globoff ";
833    $flags .= $extra;
834    $flags .= "\"$proto://$ip:$port/verifiedserver\"";
835
836    my $cmd = "$VCURL $flags 2>$verifylog";
837
838    # check if this is our server running on this port:
839    logmsg "RUN: $cmd\n" if($verbose);
840    my @data = runclientoutput($cmd);
841
842    my $res = $? >> 8; # rotate the result
843    if($res & 128) {
844        logmsg "RUN: curl command died with a coredump\n";
845        return -1;
846    }
847
848    foreach my $line (@data) {
849        if($line =~ /WE ROOLZ: (\d+)/) {
850            # this is our test server with a known pid!
851            $pid = 0+$1;
852            last;
853        }
854    }
855    if($pid <= 0 && @data && $data[0]) {
856        # this is not a known server
857        logmsg "RUN: Unknown server on our $server port: $port\n";
858        return 0;
859    }
860    # we can/should use the time it took to verify the FTP server as a measure
861    # on how fast/slow this host/FTP is.
862    my $took = int(0.5+time()-$time);
863
864    if($verbose) {
865        logmsg "RUN: Verifying our test $server server took $took seconds\n";
866    }
867    $ftpchecktime = $took>=1?$took:1; # make sure it never is below 1
868
869    return $pid;
870}
871
872#######################################################################
873# Verify that the server that runs on $ip, $port is our server.  This also
874# implies that we can speak with it, as there might be occasions when the
875# server runs fine but we cannot talk to it ("Failed to connect to ::1: Can't
876# assign requested address")
877#
878sub verifyrtsp {
879    my ($proto, $ipvnum, $idnum, $ip, $port) = @_;
880    my $server = servername_id($proto, $ipvnum, $idnum);
881    my $pid = 0;
882
883    my $verifyout = "$LOGDIR/".
884        servername_canon($proto, $ipvnum, $idnum) .'_verify.out';
885    unlink($verifyout) if(-f $verifyout);
886
887    my $verifylog = "$LOGDIR/".
888        servername_canon($proto, $ipvnum, $idnum) .'_verify.log';
889    unlink($verifylog) if(-f $verifylog);
890
891    my $flags = "--max-time $server_response_maxtime ";
892    $flags .= "--output $verifyout ";
893    $flags .= "--silent ";
894    $flags .= "--verbose ";
895    $flags .= "--globoff ";
896    # currently verification is done using http
897    $flags .= "\"http://$ip:$port/verifiedserver\"";
898
899    my $cmd = "$VCURL $flags 2>$verifylog";
900
901    # verify if our/any server is running on this port
902    logmsg "RUN: $cmd\n" if($verbose);
903    my $res = runclient($cmd);
904
905    $res >>= 8; # rotate the result
906    if($res & 128) {
907        logmsg "RUN: curl command died with a coredump\n";
908        return -1;
909    }
910
911    if($res && $verbose) {
912        logmsg "RUN: curl command returned $res\n";
913        if(open(FILE, "<$verifylog")) {
914            while(my $string = <FILE>) {
915                logmsg "RUN: $string" if($string !~ /^([ \t]*)$/);
916            }
917            close(FILE);
918        }
919    }
920
921    my $data;
922    if(open(FILE, "<$verifyout")) {
923        while(my $string = <FILE>) {
924            $data = $string;
925            last; # only want first line
926        }
927        close(FILE);
928    }
929
930    if($data && ($data =~ /RTSP_SERVER WE ROOLZ: (\d+)/)) {
931        $pid = 0+$1;
932    }
933    elsif($res == 6) {
934        # curl: (6) Couldn't resolve host '::1'
935        logmsg "RUN: failed to resolve host ($proto://$ip:$port/verifiedserver)\n";
936        return -1;
937    }
938    elsif($data || ($res != 7)) {
939        logmsg "RUN: Unknown server on our $server port: $port\n";
940        return -1;
941    }
942    return $pid;
943}
944
945#######################################################################
946# Verify that the ssh server has written out its pidfile, recovering
947# the pid from the file and returning it if a process with that pid is
948# actually alive.
949#
950sub verifyssh {
951    my ($proto, $ipvnum, $idnum, $ip, $port) = @_;
952    my $server = servername_id($proto, $ipvnum, $idnum);
953    my $pidfile = server_pidfilename($proto, $ipvnum, $idnum);
954    my $pid = 0;
955    if(open(FILE, "<$pidfile")) {
956        $pid=0+<FILE>;
957        close(FILE);
958    }
959    if($pid > 0) {
960        # if we have a pid it is actually our ssh server,
961        # since runsshserver() unlinks previous pidfile
962        if(!pidexists($pid)) {
963            logmsg "RUN: SSH server has died after starting up\n";
964            checkdied($pid);
965            unlink($pidfile);
966            $pid = -1;
967        }
968    }
969    return $pid;
970}
971
972#######################################################################
973# Verify that we can connect to the sftp server, properly authenticate
974# with generated config and key files and run a simple remote pwd.
975#
976sub verifysftp {
977    my ($proto, $ipvnum, $idnum, $ip, $port) = @_;
978    my $server = servername_id($proto, $ipvnum, $idnum);
979    my $verified = 0;
980    # Find out sftp client canonical file name
981    my $sftp = find_sftp();
982    if(!$sftp) {
983        logmsg "RUN: SFTP server cannot find $sftpexe\n";
984        return -1;
985    }
986    # Find out ssh client canonical file name
987    my $ssh = find_ssh();
988    if(!$ssh) {
989        logmsg "RUN: SFTP server cannot find $sshexe\n";
990        return -1;
991    }
992    # Connect to sftp server, authenticate and run a remote pwd
993    # command using our generated configuration and key files
994    my $cmd = "\"$sftp\" -b $sftpcmds -F $sftpconfig -S \"$ssh\" $ip > $sftplog 2>&1";
995    my $res = runclient($cmd);
996    # Search for pwd command response in log file
997    if(open(SFTPLOGFILE, "<$sftplog")) {
998        while(<SFTPLOGFILE>) {
999            if(/^Remote working directory: /) {
1000                $verified = 1;
1001                last;
1002            }
1003        }
1004        close(SFTPLOGFILE);
1005    }
1006    return $verified;
1007}
1008
1009#######################################################################
1010# Verify that the non-stunnel HTTP TLS extensions capable server that runs
1011# on $ip, $port is our server.  This also implies that we can speak with it,
1012# as there might be occasions when the server runs fine but we cannot talk
1013# to it ("Failed to connect to ::1: Can't assign requested address")
1014#
1015sub verifyhttptls {
1016    my ($proto, $ipvnum, $idnum, $ip, $port) = @_;
1017    my $server = servername_id($proto, $ipvnum, $idnum);
1018    my $pidfile = server_pidfilename($proto, $ipvnum, $idnum);
1019    my $pid = 0;
1020
1021    my $verifyout = "$LOGDIR/".
1022        servername_canon($proto, $ipvnum, $idnum) .'_verify.out';
1023    unlink($verifyout) if(-f $verifyout);
1024
1025    my $verifylog = "$LOGDIR/".
1026        servername_canon($proto, $ipvnum, $idnum) .'_verify.log';
1027    unlink($verifylog) if(-f $verifylog);
1028
1029    my $flags = "--max-time $server_response_maxtime ";
1030    $flags .= "--output $verifyout ";
1031    $flags .= "--verbose ";
1032    $flags .= "--globoff ";
1033    $flags .= "--insecure ";
1034    $flags .= "--tlsauthtype SRP ";
1035    $flags .= "--tlsuser jsmith ";
1036    $flags .= "--tlspassword abc ";
1037    $flags .= "\"https://$ip:$port/verifiedserver\"";
1038
1039    my $cmd = "$VCURL $flags 2>$verifylog";
1040
1041    # verify if our/any server is running on this port
1042    logmsg "RUN: $cmd\n" if($verbose);
1043    my $res = runclient($cmd);
1044
1045    $res >>= 8; # rotate the result
1046    if($res & 128) {
1047        logmsg "RUN: curl command died with a coredump\n";
1048        return -1;
1049    }
1050
1051    if($res && $verbose) {
1052        logmsg "RUN: curl command returned $res\n";
1053        if(open(FILE, "<$verifylog")) {
1054            while(my $string = <FILE>) {
1055                logmsg "RUN: $string" if($string !~ /^([ \t]*)$/);
1056            }
1057            close(FILE);
1058        }
1059    }
1060
1061    my $data;
1062    if(open(FILE, "<$verifyout")) {
1063        while(my $string = <FILE>) {
1064            $data .= $string;
1065        }
1066        close(FILE);
1067    }
1068
1069    if($data && ($data =~ /(GNUTLS|GnuTLS)/) && open(FILE, "<$pidfile")) {
1070        $pid=0+<FILE>;
1071        close(FILE);
1072        if($pid > 0) {
1073            # if we have a pid it is actually our httptls server,
1074            # since runhttptlsserver() unlinks previous pidfile
1075            if(!pidexists($pid)) {
1076                logmsg "RUN: $server server has died after starting up\n";
1077                checkdied($pid);
1078                unlink($pidfile);
1079                $pid = -1;
1080            }
1081        }
1082        return $pid;
1083    }
1084    elsif($res == 6) {
1085        # curl: (6) Couldn't resolve host '::1'
1086        logmsg "RUN: failed to resolve host (https://$ip:$port/verifiedserver)\n";
1087        return -1;
1088    }
1089    elsif($data || ($res && ($res != 7))) {
1090        logmsg "RUN: Unknown server on our $server port: $port ($res)\n";
1091        return -1;
1092    }
1093    return $pid;
1094}
1095
1096#######################################################################
1097# STUB for verifying socks
1098#
1099sub verifysocks {
1100    my ($proto, $ipvnum, $idnum, $ip, $port) = @_;
1101    my $server = servername_id($proto, $ipvnum, $idnum);
1102    my $pidfile = server_pidfilename($proto, $ipvnum, $idnum);
1103    my $pid = 0;
1104    if(open(FILE, "<$pidfile")) {
1105        $pid=0+<FILE>;
1106        close(FILE);
1107    }
1108    if($pid > 0) {
1109        # if we have a pid it is actually our socks server,
1110        # since runsocksserver() unlinks previous pidfile
1111        if(!pidexists($pid)) {
1112            logmsg "RUN: SOCKS server has died after starting up\n";
1113            checkdied($pid);
1114            unlink($pidfile);
1115            $pid = -1;
1116        }
1117    }
1118    return $pid;
1119}
1120
1121#######################################################################
1122# Verify that the server that runs on $ip, $port is our server.
1123# Retry over several seconds before giving up.  The ssh server in
1124# particular can take a long time to start if it needs to generate
1125# keys on a slow or loaded host.
1126#
1127# Just for convenience, test harness uses 'https' and 'httptls' literals
1128# as values for 'proto' variable in order to differentiate different
1129# servers. 'https' literal is used for stunnel based https test servers,
1130# and 'httptls' is used for non-stunnel https test servers.
1131#
1132
1133my %protofunc = ('http' => \&verifyhttp,
1134                 'https' => \&verifyhttp,
1135                 'rtsp' => \&verifyrtsp,
1136                 'ftp' => \&verifyftp,
1137                 'pop3' => \&verifyftp,
1138                 'imap' => \&verifyftp,
1139                 'smtp' => \&verifyftp,
1140                 'httppipe' => \&verifyhttp,
1141                 'ftps' => \&verifyftp,
1142                 'tftp' => \&verifyftp,
1143                 'ssh' => \&verifyssh,
1144                 'socks' => \&verifysocks,
1145                 'gopher' => \&verifyhttp,
1146                 'httptls' => \&verifyhttptls);
1147
1148sub verifyserver {
1149    my ($proto, $ipvnum, $idnum, $ip, $port) = @_;
1150
1151    my $count = 30; # try for this many seconds
1152    my $pid;
1153
1154    while($count--) {
1155        my $fun = $protofunc{$proto};
1156
1157        $pid = &$fun($proto, $ipvnum, $idnum, $ip, $port);
1158
1159        if($pid > 0) {
1160            last;
1161        }
1162        elsif($pid < 0) {
1163            # a real failure, stop trying and bail out
1164            return 0;
1165        }
1166        sleep(1);
1167    }
1168    return $pid;
1169}
1170
1171#######################################################################
1172# Single shot server responsiveness test. This should only be used
1173# to verify that a server present in %run hash is still functional
1174#
1175sub responsiveserver {
1176    my ($proto, $ipvnum, $idnum, $ip, $port) = @_;
1177    my $prev_verbose = $verbose;
1178
1179    $verbose = 0;
1180    my $fun = $protofunc{$proto};
1181    my $pid = &$fun($proto, $ipvnum, $idnum, $ip, $port);
1182    $verbose = $prev_verbose;
1183
1184    if($pid > 0) {
1185        return 1; # responsive
1186    }
1187
1188    my $srvrname = servername_str($proto, $ipvnum, $idnum);
1189    logmsg " server precheck FAILED (unresponsive $srvrname server)\n";
1190    return 0;
1191}
1192
1193#######################################################################
1194# start the http2 server
1195#
1196sub runhttp2server {
1197    my ($verbose, $port) = @_;
1198    my $server;
1199    my $srvrname;
1200    my $pidfile;
1201    my $logfile;
1202    my $flags = "";
1203    my $proto="http/2";
1204    my $ipvnum = 4;
1205    my $idnum = 0;
1206    my $exe = "$perl $srcdir/http2-server.pl";
1207    my $verbose_flag = "--verbose ";
1208
1209    $server = servername_id($proto, $ipvnum, $idnum);
1210
1211    $pidfile = $serverpidfile{$server};
1212
1213    # don't retry if the server doesn't work
1214    if ($doesntrun{$pidfile}) {
1215        return (0,0);
1216    }
1217
1218    my $pid = processexists($pidfile);
1219    if($pid > 0) {
1220        stopserver($server, "$pid");
1221    }
1222    unlink($pidfile) if(-f $pidfile);
1223
1224    $srvrname = servername_str($proto, $ipvnum, $idnum);
1225
1226    $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum);
1227
1228    $flags .= "--pidfile \"$pidfile\" --logfile \"$logfile\" ";
1229    $flags .= "--port $HTTP2PORT ";
1230    $flags .= $verbose_flag if($debugprotocol);
1231
1232    my $cmd = "$exe $flags";
1233    my ($http2pid, $pid2) = startnew($cmd, $pidfile, 15, 0);
1234
1235    if($http2pid <= 0 || !pidexists($http2pid)) {
1236        # it is NOT alive
1237        logmsg "RUN: failed to start the $srvrname server\n";
1238        stopserver($server, "$pid2");
1239        $doesntrun{$pidfile} = 1;
1240        return (0,0);
1241    }
1242
1243    if($verbose) {
1244        logmsg "RUN: $srvrname server is now running PID $http2pid\n";
1245    }
1246
1247    return ($http2pid, $pid2);
1248}
1249
1250#######################################################################
1251# start the http server
1252#
1253sub runhttpserver {
1254    my ($proto, $verbose, $alt, $port_or_path) = @_;
1255    my $ip = $HOSTIP;
1256    my $ipvnum = 4;
1257    my $idnum = 1;
1258    my $server;
1259    my $srvrname;
1260    my $pidfile;
1261    my $logfile;
1262    my $flags = "";
1263    my $exe = "$perl $srcdir/httpserver.pl";
1264    my $verbose_flag = "--verbose ";
1265
1266    if($alt eq "ipv6") {
1267        # if IPv6, use a different setup
1268        $ipvnum = 6;
1269        $ip = $HOST6IP;
1270    }
1271    elsif($alt eq "proxy") {
1272        # basically the same, but another ID
1273        $idnum = 2;
1274    }
1275    elsif($alt eq "pipe") {
1276        # basically the same, but another ID
1277        $idnum = 3;
1278        $exe = "python $srcdir/http_pipe.py";
1279        $verbose_flag .= "1 ";
1280    }
1281    elsif($alt eq "unix") {
1282        # IP (protocol) is mutually exclusive with Unix sockets
1283        $ipvnum = "unix";
1284    }
1285
1286    $server = servername_id($proto, $ipvnum, $idnum);
1287
1288    $pidfile = $serverpidfile{$server};
1289
1290    # don't retry if the server doesn't work
1291    if ($doesntrun{$pidfile}) {
1292        return (0,0);
1293    }
1294
1295    my $pid = processexists($pidfile);
1296    if($pid > 0) {
1297        stopserver($server, "$pid");
1298    }
1299    unlink($pidfile) if(-f $pidfile);
1300
1301    $srvrname = servername_str($proto, $ipvnum, $idnum);
1302
1303    $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum);
1304
1305    $flags .= "--gopher " if($proto eq "gopher");
1306    $flags .= "--connect $HOSTIP " if($alt eq "proxy");
1307    $flags .= $verbose_flag if($debugprotocol);
1308    $flags .= "--pidfile \"$pidfile\" --logfile \"$logfile\" ";
1309    $flags .= "--id $idnum " if($idnum > 1);
1310    if($ipvnum eq "unix") {
1311        $flags .= "--unix-socket '$port_or_path' ";
1312    } else {
1313        $flags .= "--ipv$ipvnum --port $port_or_path ";
1314    }
1315    $flags .= "--srcdir \"$srcdir\"";
1316
1317    my $cmd = "$exe $flags";
1318    my ($httppid, $pid2) = startnew($cmd, $pidfile, 15, 0);
1319
1320    if($httppid <= 0 || !pidexists($httppid)) {
1321        # it is NOT alive
1322        logmsg "RUN: failed to start the $srvrname server\n";
1323        stopserver($server, "$pid2");
1324        displaylogs($testnumcheck);
1325        $doesntrun{$pidfile} = 1;
1326        return (0,0);
1327    }
1328
1329    # Server is up. Verify that we can speak to it.
1330    my $pid3 = verifyserver($proto, $ipvnum, $idnum, $ip, $port_or_path);
1331    if(!$pid3) {
1332        logmsg "RUN: $srvrname server failed verification\n";
1333        # failed to talk to it properly. Kill the server and return failure
1334        stopserver($server, "$httppid $pid2");
1335        displaylogs($testnumcheck);
1336        $doesntrun{$pidfile} = 1;
1337        return (0,0);
1338    }
1339    $pid2 = $pid3;
1340
1341    if($verbose) {
1342        logmsg "RUN: $srvrname server is now running PID $httppid\n";
1343    }
1344
1345    sleep(1);
1346
1347    return ($httppid, $pid2);
1348}
1349
1350#######################################################################
1351# start the http server
1352#
1353sub runhttp_pipeserver {
1354    my ($proto, $verbose, $alt, $port) = @_;
1355    my $ip = $HOSTIP;
1356    my $ipvnum = 4;
1357    my $idnum = 1;
1358    my $server;
1359    my $srvrname;
1360    my $pidfile;
1361    my $logfile;
1362    my $flags = "";
1363
1364    if($alt eq "ipv6") {
1365        # No IPv6
1366    }
1367
1368    $server = servername_id($proto, $ipvnum, $idnum);
1369
1370    $pidfile = $serverpidfile{$server};
1371
1372    # don't retry if the server doesn't work
1373    if ($doesntrun{$pidfile}) {
1374        return (0,0);
1375    }
1376
1377    my $pid = processexists($pidfile);
1378    if($pid > 0) {
1379        stopserver($server, "$pid");
1380    }
1381    unlink($pidfile) if(-f $pidfile);
1382
1383    $srvrname = servername_str($proto, $ipvnum, $idnum);
1384
1385    $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum);
1386
1387    $flags .= "--verbose 1 " if($debugprotocol);
1388    $flags .= "--pidfile \"$pidfile\" --logfile \"$logfile\" ";
1389    $flags .= "--id $idnum " if($idnum > 1);
1390    $flags .= "--port $port --srcdir \"$srcdir\"";
1391
1392    my $cmd = "$srcdir/http_pipe.py $flags";
1393    my ($httppid, $pid2) = startnew($cmd, $pidfile, 15, 0);
1394
1395    if($httppid <= 0 || !pidexists($httppid)) {
1396        # it is NOT alive
1397        logmsg "RUN: failed to start the $srvrname server\n";
1398        stopserver($server, "$pid2");
1399        displaylogs($testnumcheck);
1400        $doesntrun{$pidfile} = 1;
1401        return (0,0);
1402    }
1403
1404    # Server is up. Verify that we can speak to it.
1405    my $pid3 = verifyserver($proto, $ipvnum, $idnum, $ip, $port);
1406    if(!$pid3) {
1407        logmsg "RUN: $srvrname server failed verification\n";
1408        # failed to talk to it properly. Kill the server and return failure
1409        stopserver($server, "$httppid $pid2");
1410        displaylogs($testnumcheck);
1411        $doesntrun{$pidfile} = 1;
1412        return (0,0);
1413    }
1414    $pid2 = $pid3;
1415
1416    if($verbose) {
1417        logmsg "RUN: $srvrname server is now running PID $httppid\n";
1418    }
1419
1420    sleep(1);
1421
1422    return ($httppid, $pid2);
1423}
1424
1425#######################################################################
1426# start the https stunnel based server
1427#
1428sub runhttpsserver {
1429    my ($verbose, $ipv6, $certfile) = @_;
1430    my $proto = 'https';
1431    my $ip = ($ipv6 && ($ipv6 =~ /6$/)) ? "$HOST6IP" : "$HOSTIP";
1432    my $ipvnum = ($ipv6 && ($ipv6 =~ /6$/)) ? 6 : 4;
1433    my $idnum = 1;
1434    my $server;
1435    my $srvrname;
1436    my $pidfile;
1437    my $logfile;
1438    my $flags = "";
1439
1440    if(!$stunnel) {
1441        return (0,0);
1442    }
1443
1444    $server = servername_id($proto, $ipvnum, $idnum);
1445
1446    $pidfile = $serverpidfile{$server};
1447
1448    # don't retry if the server doesn't work
1449    if ($doesntrun{$pidfile}) {
1450        return (0,0);
1451    }
1452
1453    my $pid = processexists($pidfile);
1454    if($pid > 0) {
1455        stopserver($server, "$pid");
1456    }
1457    unlink($pidfile) if(-f $pidfile);
1458
1459    $srvrname = servername_str($proto, $ipvnum, $idnum);
1460
1461    $certfile = 'stunnel.pem' unless($certfile);
1462
1463    $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum);
1464
1465    $flags .= "--verbose " if($debugprotocol);
1466    $flags .= "--pidfile \"$pidfile\" --logfile \"$logfile\" ";
1467    $flags .= "--id $idnum " if($idnum > 1);
1468    $flags .= "--ipv$ipvnum --proto $proto ";
1469    $flags .= "--certfile \"$certfile\" " if($certfile ne 'stunnel.pem');
1470    $flags .= "--stunnel \"$stunnel\" --srcdir \"$srcdir\" ";
1471    $flags .= "--connect $HTTPPORT --accept $HTTPSPORT";
1472
1473    my $cmd = "$perl $srcdir/secureserver.pl $flags";
1474    my ($httpspid, $pid2) = startnew($cmd, $pidfile, 15, 0);
1475
1476    if($httpspid <= 0 || !pidexists($httpspid)) {
1477        # it is NOT alive
1478        logmsg "RUN: failed to start the $srvrname server\n";
1479        stopserver($server, "$pid2");
1480        displaylogs($testnumcheck);
1481        $doesntrun{$pidfile} = 1;
1482        return(0,0);
1483    }
1484
1485    # Server is up. Verify that we can speak to it.
1486    my $pid3 = verifyserver($proto, $ipvnum, $idnum, $ip, $HTTPSPORT);
1487    if(!$pid3) {
1488        logmsg "RUN: $srvrname server failed verification\n";
1489        # failed to talk to it properly. Kill the server and return failure
1490        stopserver($server, "$httpspid $pid2");
1491        displaylogs($testnumcheck);
1492        $doesntrun{$pidfile} = 1;
1493        return (0,0);
1494    }
1495    # Here pid3 is actually the pid returned by the unsecure-http server.
1496
1497    $runcert{$server} = $certfile;
1498
1499    if($verbose) {
1500        logmsg "RUN: $srvrname server is now running PID $httpspid\n";
1501    }
1502
1503    sleep(1);
1504
1505    return ($httpspid, $pid2);
1506}
1507
1508#######################################################################
1509# start the non-stunnel HTTP TLS extensions capable server
1510#
1511sub runhttptlsserver {
1512    my ($verbose, $ipv6) = @_;
1513    my $proto = "httptls";
1514    my $port = ($ipv6 && ($ipv6 =~ /6$/)) ? $HTTPTLS6PORT : $HTTPTLSPORT;
1515    my $ip = ($ipv6 && ($ipv6 =~ /6$/)) ? "$HOST6IP" : "$HOSTIP";
1516    my $ipvnum = ($ipv6 && ($ipv6 =~ /6$/)) ? 6 : 4;
1517    my $idnum = 1;
1518    my $server;
1519    my $srvrname;
1520    my $pidfile;
1521    my $logfile;
1522    my $flags = "";
1523
1524    if(!$httptlssrv) {
1525        return (0,0);
1526    }
1527
1528    $server = servername_id($proto, $ipvnum, $idnum);
1529
1530    $pidfile = $serverpidfile{$server};
1531
1532    # don't retry if the server doesn't work
1533    if ($doesntrun{$pidfile}) {
1534        return (0,0);
1535    }
1536
1537    my $pid = processexists($pidfile);
1538    if($pid > 0) {
1539        stopserver($server, "$pid");
1540    }
1541    unlink($pidfile) if(-f $pidfile);
1542
1543    $srvrname = servername_str($proto, $ipvnum, $idnum);
1544
1545    $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum);
1546
1547    $flags .= "--http ";
1548    $flags .= "--debug 1 " if($debugprotocol);
1549    $flags .= "--port $port ";
1550    $flags .= "--priority NORMAL:+SRP ";
1551    $flags .= "--srppasswd $srcdir/certs/srp-verifier-db ";
1552    $flags .= "--srppasswdconf $srcdir/certs/srp-verifier-conf";
1553
1554    my $cmd = "$httptlssrv $flags > $logfile 2>&1";
1555    my ($httptlspid, $pid2) = startnew($cmd, $pidfile, 10, 1); # fake pidfile
1556
1557    if($httptlspid <= 0 || !pidexists($httptlspid)) {
1558        # it is NOT alive
1559        logmsg "RUN: failed to start the $srvrname server\n";
1560        stopserver($server, "$pid2");
1561        displaylogs($testnumcheck);
1562        $doesntrun{$pidfile} = 1;
1563        return (0,0);
1564    }
1565
1566    # Server is up. Verify that we can speak to it. PID is from fake pidfile
1567    my $pid3 = verifyserver($proto, $ipvnum, $idnum, $ip, $port);
1568    if(!$pid3) {
1569        logmsg "RUN: $srvrname server failed verification\n";
1570        # failed to talk to it properly. Kill the server and return failure
1571        stopserver($server, "$httptlspid $pid2");
1572        displaylogs($testnumcheck);
1573        $doesntrun{$pidfile} = 1;
1574        return (0,0);
1575    }
1576    $pid2 = $pid3;
1577
1578    if($verbose) {
1579        logmsg "RUN: $srvrname server is now running PID $httptlspid\n";
1580    }
1581
1582    sleep(1);
1583
1584    return ($httptlspid, $pid2);
1585}
1586
1587#######################################################################
1588# start the pingpong server (FTP, POP3, IMAP, SMTP)
1589#
1590sub runpingpongserver {
1591    my ($proto, $id, $verbose, $ipv6) = @_;
1592    my $port;
1593    my $ip = ($ipv6 && ($ipv6 =~ /6$/)) ? "$HOST6IP" : "$HOSTIP";
1594    my $ipvnum = ($ipv6 && ($ipv6 =~ /6$/)) ? 6 : 4;
1595    my $idnum = ($id && ($id =~ /^(\d+)$/) && ($id > 1)) ? $id : 1;
1596    my $server;
1597    my $srvrname;
1598    my $pidfile;
1599    my $logfile;
1600    my $flags = "";
1601
1602    if($proto eq "ftp") {
1603        $port = ($idnum>1)?$FTP2PORT:$FTPPORT;
1604
1605        if($ipvnum==6) {
1606            # if IPv6, use a different setup
1607            $port = $FTP6PORT;
1608        }
1609    }
1610    elsif($proto eq "pop3") {
1611        $port = ($ipvnum==6) ? $POP36PORT : $POP3PORT;
1612    }
1613    elsif($proto eq "imap") {
1614        $port = ($ipvnum==6) ? $IMAP6PORT : $IMAPPORT;
1615    }
1616    elsif($proto eq "smtp") {
1617        $port = ($ipvnum==6) ? $SMTP6PORT : $SMTPPORT;
1618    }
1619    else {
1620        print STDERR "Unsupported protocol $proto!!\n";
1621        return 0;
1622    }
1623
1624    $server = servername_id($proto, $ipvnum, $idnum);
1625
1626    $pidfile = $serverpidfile{$server};
1627
1628    # don't retry if the server doesn't work
1629    if ($doesntrun{$pidfile}) {
1630        return (0,0);
1631    }
1632
1633    my $pid = processexists($pidfile);
1634    if($pid > 0) {
1635        stopserver($server, "$pid");
1636    }
1637    unlink($pidfile) if(-f $pidfile);
1638
1639    $srvrname = servername_str($proto, $ipvnum, $idnum);
1640
1641    $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum);
1642
1643    $flags .= "--verbose " if($debugprotocol);
1644    $flags .= "--pidfile \"$pidfile\" --logfile \"$logfile\" ";
1645    $flags .= "--srcdir \"$srcdir\" --proto $proto ";
1646    $flags .= "--id $idnum " if($idnum > 1);
1647    $flags .= "--ipv$ipvnum --port $port --addr \"$ip\"";
1648
1649    my $cmd = "$perl $srcdir/ftpserver.pl $flags";
1650    my ($ftppid, $pid2) = startnew($cmd, $pidfile, 15, 0);
1651
1652    if($ftppid <= 0 || !pidexists($ftppid)) {
1653        # it is NOT alive
1654        logmsg "RUN: failed to start the $srvrname server\n";
1655        stopserver($server, "$pid2");
1656        displaylogs($testnumcheck);
1657        $doesntrun{$pidfile} = 1;
1658        return (0,0);
1659    }
1660
1661    # Server is up. Verify that we can speak to it.
1662    my $pid3 = verifyserver($proto, $ipvnum, $idnum, $ip, $port);
1663    if(!$pid3) {
1664        logmsg "RUN: $srvrname server failed verification\n";
1665        # failed to talk to it properly. Kill the server and return failure
1666        stopserver($server, "$ftppid $pid2");
1667        displaylogs($testnumcheck);
1668        $doesntrun{$pidfile} = 1;
1669        return (0,0);
1670    }
1671
1672    $pid2 = $pid3;
1673
1674    if($verbose) {
1675        logmsg "RUN: $srvrname server is now running PID $ftppid\n";
1676    }
1677
1678    sleep(1);
1679
1680    return ($pid2, $ftppid);
1681}
1682
1683#######################################################################
1684# start the ftps server (or rather, tunnel)
1685#
1686sub runftpsserver {
1687    my ($verbose, $ipv6, $certfile) = @_;
1688    my $proto = 'ftps';
1689    my $ip = ($ipv6 && ($ipv6 =~ /6$/)) ? "$HOST6IP" : "$HOSTIP";
1690    my $ipvnum = ($ipv6 && ($ipv6 =~ /6$/)) ? 6 : 4;
1691    my $idnum = 1;
1692    my $server;
1693    my $srvrname;
1694    my $pidfile;
1695    my $logfile;
1696    my $flags = "";
1697
1698    if(!$stunnel) {
1699        return (0,0);
1700    }
1701
1702    $server = servername_id($proto, $ipvnum, $idnum);
1703
1704    $pidfile = $serverpidfile{$server};
1705
1706    # don't retry if the server doesn't work
1707    if ($doesntrun{$pidfile}) {
1708        return (0,0);
1709    }
1710
1711    my $pid = processexists($pidfile);
1712    if($pid > 0) {
1713        stopserver($server, "$pid");
1714    }
1715    unlink($pidfile) if(-f $pidfile);
1716
1717    $srvrname = servername_str($proto, $ipvnum, $idnum);
1718
1719    $certfile = 'stunnel.pem' unless($certfile);
1720
1721    $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum);
1722
1723    $flags .= "--verbose " if($debugprotocol);
1724    $flags .= "--pidfile \"$pidfile\" --logfile \"$logfile\" ";
1725    $flags .= "--id $idnum " if($idnum > 1);
1726    $flags .= "--ipv$ipvnum --proto $proto ";
1727    $flags .= "--certfile \"$certfile\" " if($certfile ne 'stunnel.pem');
1728    $flags .= "--stunnel \"$stunnel\" --srcdir \"$srcdir\" ";
1729    $flags .= "--connect $FTPPORT --accept $FTPSPORT";
1730
1731    my $cmd = "$perl $srcdir/secureserver.pl $flags";
1732    my ($ftpspid, $pid2) = startnew($cmd, $pidfile, 15, 0);
1733
1734    if($ftpspid <= 0 || !pidexists($ftpspid)) {
1735        # it is NOT alive
1736        logmsg "RUN: failed to start the $srvrname server\n";
1737        stopserver($server, "$pid2");
1738        displaylogs($testnumcheck);
1739        $doesntrun{$pidfile} = 1;
1740        return(0,0);
1741    }
1742
1743    # Server is up. Verify that we can speak to it.
1744    my $pid3 = verifyserver($proto, $ipvnum, $idnum, $ip, $FTPSPORT);
1745    if(!$pid3) {
1746        logmsg "RUN: $srvrname server failed verification\n";
1747        # failed to talk to it properly. Kill the server and return failure
1748        stopserver($server, "$ftpspid $pid2");
1749        displaylogs($testnumcheck);
1750        $doesntrun{$pidfile} = 1;
1751        return (0,0);
1752    }
1753    # Here pid3 is actually the pid returned by the unsecure-ftp server.
1754
1755    $runcert{$server} = $certfile;
1756
1757    if($verbose) {
1758        logmsg "RUN: $srvrname server is now running PID $ftpspid\n";
1759    }
1760
1761    sleep(1);
1762
1763    return ($ftpspid, $pid2);
1764}
1765
1766#######################################################################
1767# start the tftp server
1768#
1769sub runtftpserver {
1770    my ($id, $verbose, $ipv6) = @_;
1771    my $port = $TFTPPORT;
1772    my $ip = $HOSTIP;
1773    my $proto = 'tftp';
1774    my $ipvnum = 4;
1775    my $idnum = ($id && ($id =~ /^(\d+)$/) && ($id > 1)) ? $id : 1;
1776    my $server;
1777    my $srvrname;
1778    my $pidfile;
1779    my $logfile;
1780    my $flags = "";
1781
1782    if($ipv6) {
1783        # if IPv6, use a different setup
1784        $ipvnum = 6;
1785        $port = $TFTP6PORT;
1786        $ip = $HOST6IP;
1787    }
1788
1789    $server = servername_id($proto, $ipvnum, $idnum);
1790
1791    $pidfile = $serverpidfile{$server};
1792
1793    # don't retry if the server doesn't work
1794    if ($doesntrun{$pidfile}) {
1795        return (0,0);
1796    }
1797
1798    my $pid = processexists($pidfile);
1799    if($pid > 0) {
1800        stopserver($server, "$pid");
1801    }
1802    unlink($pidfile) if(-f $pidfile);
1803
1804    $srvrname = servername_str($proto, $ipvnum, $idnum);
1805
1806    $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum);
1807
1808    $flags .= "--verbose " if($debugprotocol);
1809    $flags .= "--pidfile \"$pidfile\" --logfile \"$logfile\" ";
1810    $flags .= "--id $idnum " if($idnum > 1);
1811    $flags .= "--ipv$ipvnum --port $port --srcdir \"$srcdir\"";
1812
1813    my $cmd = "$perl $srcdir/tftpserver.pl $flags";
1814    my ($tftppid, $pid2) = startnew($cmd, $pidfile, 15, 0);
1815
1816    if($tftppid <= 0 || !pidexists($tftppid)) {
1817        # it is NOT alive
1818        logmsg "RUN: failed to start the $srvrname server\n";
1819        stopserver($server, "$pid2");
1820        displaylogs($testnumcheck);
1821        $doesntrun{$pidfile} = 1;
1822        return (0,0);
1823    }
1824
1825    # Server is up. Verify that we can speak to it.
1826    my $pid3 = verifyserver($proto, $ipvnum, $idnum, $ip, $port);
1827    if(!$pid3) {
1828        logmsg "RUN: $srvrname server failed verification\n";
1829        # failed to talk to it properly. Kill the server and return failure
1830        stopserver($server, "$tftppid $pid2");
1831        displaylogs($testnumcheck);
1832        $doesntrun{$pidfile} = 1;
1833        return (0,0);
1834    }
1835    $pid2 = $pid3;
1836
1837    if($verbose) {
1838        logmsg "RUN: $srvrname server is now running PID $tftppid\n";
1839    }
1840
1841    sleep(1);
1842
1843    return ($pid2, $tftppid);
1844}
1845
1846
1847#######################################################################
1848# start the rtsp server
1849#
1850sub runrtspserver {
1851    my ($verbose, $ipv6) = @_;
1852    my $port = $RTSPPORT;
1853    my $ip = $HOSTIP;
1854    my $proto = 'rtsp';
1855    my $ipvnum = 4;
1856    my $idnum = 1;
1857    my $server;
1858    my $srvrname;
1859    my $pidfile;
1860    my $logfile;
1861    my $flags = "";
1862
1863    if($ipv6) {
1864        # if IPv6, use a different setup
1865        $ipvnum = 6;
1866        $port = $RTSP6PORT;
1867        $ip = $HOST6IP;
1868    }
1869
1870    $server = servername_id($proto, $ipvnum, $idnum);
1871
1872    $pidfile = $serverpidfile{$server};
1873
1874    # don't retry if the server doesn't work
1875    if ($doesntrun{$pidfile}) {
1876        return (0,0);
1877    }
1878
1879    my $pid = processexists($pidfile);
1880    if($pid > 0) {
1881        stopserver($server, "$pid");
1882    }
1883    unlink($pidfile) if(-f $pidfile);
1884
1885    $srvrname = servername_str($proto, $ipvnum, $idnum);
1886
1887    $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum);
1888
1889    $flags .= "--verbose " if($debugprotocol);
1890    $flags .= "--pidfile \"$pidfile\" --logfile \"$logfile\" ";
1891    $flags .= "--id $idnum " if($idnum > 1);
1892    $flags .= "--ipv$ipvnum --port $port --srcdir \"$srcdir\"";
1893
1894    my $cmd = "$perl $srcdir/rtspserver.pl $flags";
1895    my ($rtsppid, $pid2) = startnew($cmd, $pidfile, 15, 0);
1896
1897    if($rtsppid <= 0 || !pidexists($rtsppid)) {
1898        # it is NOT alive
1899        logmsg "RUN: failed to start the $srvrname server\n";
1900        stopserver($server, "$pid2");
1901        displaylogs($testnumcheck);
1902        $doesntrun{$pidfile} = 1;
1903        return (0,0);
1904    }
1905
1906    # Server is up. Verify that we can speak to it.
1907    my $pid3 = verifyserver($proto, $ipvnum, $idnum, $ip, $port);
1908    if(!$pid3) {
1909        logmsg "RUN: $srvrname server failed verification\n";
1910        # failed to talk to it properly. Kill the server and return failure
1911        stopserver($server, "$rtsppid $pid2");
1912        displaylogs($testnumcheck);
1913        $doesntrun{$pidfile} = 1;
1914        return (0,0);
1915    }
1916    $pid2 = $pid3;
1917
1918    if($verbose) {
1919        logmsg "RUN: $srvrname server is now running PID $rtsppid\n";
1920    }
1921
1922    sleep(1);
1923
1924    return ($rtsppid, $pid2);
1925}
1926
1927
1928#######################################################################
1929# Start the ssh (scp/sftp) server
1930#
1931sub runsshserver {
1932    my ($id, $verbose, $ipv6) = @_;
1933    my $ip=$HOSTIP;
1934    my $port = $SSHPORT;
1935    my $socksport = $SOCKSPORT;
1936    my $proto = 'ssh';
1937    my $ipvnum = 4;
1938    my $idnum = ($id && ($id =~ /^(\d+)$/) && ($id > 1)) ? $id : 1;
1939    my $server;
1940    my $srvrname;
1941    my $pidfile;
1942    my $logfile;
1943    my $flags = "";
1944
1945    $server = servername_id($proto, $ipvnum, $idnum);
1946
1947    $pidfile = $serverpidfile{$server};
1948
1949    # don't retry if the server doesn't work
1950    if ($doesntrun{$pidfile}) {
1951        return (0,0);
1952    }
1953
1954    my $pid = processexists($pidfile);
1955    if($pid > 0) {
1956        stopserver($server, "$pid");
1957    }
1958    unlink($pidfile) if(-f $pidfile);
1959
1960    $srvrname = servername_str($proto, $ipvnum, $idnum);
1961
1962    $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum);
1963
1964    $flags .= "--verbose " if($verbose);
1965    $flags .= "--debugprotocol " if($debugprotocol);
1966    $flags .= "--pidfile \"$pidfile\" ";
1967    $flags .= "--id $idnum " if($idnum > 1);
1968    $flags .= "--ipv$ipvnum --addr \"$ip\" ";
1969    $flags .= "--sshport $port --socksport $socksport ";
1970    $flags .= "--user \"$USER\"";
1971
1972    my $cmd = "$perl $srcdir/sshserver.pl $flags";
1973    my ($sshpid, $pid2) = startnew($cmd, $pidfile, 60, 0);
1974
1975    # on loaded systems sshserver start up can take longer than the timeout
1976    # passed to startnew, when this happens startnew completes without being
1977    # able to read the pidfile and consequently returns a zero pid2 above.
1978
1979    if($sshpid <= 0 || !pidexists($sshpid)) {
1980        # it is NOT alive
1981        logmsg "RUN: failed to start the $srvrname server\n";
1982        stopserver($server, "$pid2");
1983        $doesntrun{$pidfile} = 1;
1984        return (0,0);
1985    }
1986
1987    # ssh server verification allows some extra time for the server to start up
1988    # and gives us the opportunity of recovering the pid from the pidfile, when
1989    # this verification succeeds the recovered pid is assigned to pid2.
1990
1991    my $pid3 = verifyserver($proto, $ipvnum, $idnum, $ip, $port);
1992    if(!$pid3) {
1993        logmsg "RUN: $srvrname server failed verification\n";
1994        # failed to fetch server pid. Kill the server and return failure
1995        stopserver($server, "$sshpid $pid2");
1996        $doesntrun{$pidfile} = 1;
1997        return (0,0);
1998    }
1999    $pid2 = $pid3;
2000
2001    # once it is known that the ssh server is alive, sftp server verification
2002    # is performed actually connecting to it, authenticating and performing a
2003    # very simple remote command.  This verification is tried only one time.
2004
2005    $sshdlog = server_logfilename($LOGDIR, 'ssh', $ipvnum, $idnum);
2006    $sftplog = server_logfilename($LOGDIR, 'sftp', $ipvnum, $idnum);
2007
2008    if(verifysftp('sftp', $ipvnum, $idnum, $ip, $port) < 1) {
2009        logmsg "RUN: SFTP server failed verification\n";
2010        # failed to talk to it properly. Kill the server and return failure
2011        display_sftplog();
2012        display_sftpconfig();
2013        display_sshdlog();
2014        display_sshdconfig();
2015        stopserver($server, "$sshpid $pid2");
2016        $doesntrun{$pidfile} = 1;
2017        return (0,0);
2018    }
2019
2020    if($verbose) {
2021        logmsg "RUN: $srvrname server is now running PID $pid2\n";
2022    }
2023
2024    return ($pid2, $sshpid);
2025}
2026
2027#######################################################################
2028# Start the socks server
2029#
2030sub runsocksserver {
2031    my ($id, $verbose, $ipv6) = @_;
2032    my $ip=$HOSTIP;
2033    my $port = $SOCKSPORT;
2034    my $proto = 'socks';
2035    my $ipvnum = 4;
2036    my $idnum = ($id && ($id =~ /^(\d+)$/) && ($id > 1)) ? $id : 1;
2037    my $server;
2038    my $srvrname;
2039    my $pidfile;
2040    my $logfile;
2041    my $flags = "";
2042
2043    $server = servername_id($proto, $ipvnum, $idnum);
2044
2045    $pidfile = $serverpidfile{$server};
2046
2047    # don't retry if the server doesn't work
2048    if ($doesntrun{$pidfile}) {
2049        return (0,0);
2050    }
2051
2052    my $pid = processexists($pidfile);
2053    if($pid > 0) {
2054        stopserver($server, "$pid");
2055    }
2056    unlink($pidfile) if(-f $pidfile);
2057
2058    $srvrname = servername_str($proto, $ipvnum, $idnum);
2059
2060    $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum);
2061
2062    # The ssh server must be already running
2063    if(!$run{'ssh'}) {
2064        logmsg "RUN: SOCKS server cannot find running SSH server\n";
2065        $doesntrun{$pidfile} = 1;
2066        return (0,0);
2067    }
2068
2069    # Find out ssh daemon canonical file name
2070    my $sshd = find_sshd();
2071    if(!$sshd) {
2072        logmsg "RUN: SOCKS server cannot find $sshdexe\n";
2073        $doesntrun{$pidfile} = 1;
2074        return (0,0);
2075    }
2076
2077    # Find out ssh daemon version info
2078    ($sshdid, $sshdvernum, $sshdverstr, $sshderror) = sshversioninfo($sshd);
2079    if(!$sshdid) {
2080        # Not an OpenSSH or SunSSH ssh daemon
2081        logmsg "$sshderror\n" if($verbose);
2082        logmsg "SCP, SFTP and SOCKS tests require OpenSSH 2.9.9 or later\n";
2083        $doesntrun{$pidfile} = 1;
2084        return (0,0);
2085    }
2086    logmsg "ssh server found $sshd is $sshdverstr\n" if($verbose);
2087
2088    # Find out ssh client canonical file name
2089    my $ssh = find_ssh();
2090    if(!$ssh) {
2091        logmsg "RUN: SOCKS server cannot find $sshexe\n";
2092        $doesntrun{$pidfile} = 1;
2093        return (0,0);
2094    }
2095
2096    # Find out ssh client version info
2097    my ($sshid, $sshvernum, $sshverstr, $ssherror) = sshversioninfo($ssh);
2098    if(!$sshid) {
2099        # Not an OpenSSH or SunSSH ssh client
2100        logmsg "$ssherror\n" if($verbose);
2101        logmsg "SCP, SFTP and SOCKS tests require OpenSSH 2.9.9 or later\n";
2102        $doesntrun{$pidfile} = 1;
2103        return (0,0);
2104    }
2105
2106    # Verify minimum ssh client version
2107    if((($sshid =~ /OpenSSH/) && ($sshvernum < 299)) ||
2108       (($sshid =~ /SunSSH/)  && ($sshvernum < 100))) {
2109        logmsg "ssh client found $ssh is $sshverstr\n";
2110        logmsg "SCP, SFTP and SOCKS tests require OpenSSH 2.9.9 or later\n";
2111        $doesntrun{$pidfile} = 1;
2112        return (0,0);
2113    }
2114    logmsg "ssh client found $ssh is $sshverstr\n" if($verbose);
2115
2116    # Verify if ssh client and ssh daemon versions match
2117    if(($sshdid ne $sshid) || ($sshdvernum != $sshvernum)) {
2118        # Our test harness might work with slightly mismatched versions
2119        logmsg "Warning: version mismatch: sshd $sshdverstr - ssh $sshverstr\n"
2120            if($verbose);
2121    }
2122
2123    # Config file options for ssh client are previously set from sshserver.pl
2124    if(! -e $sshconfig) {
2125        logmsg "RUN: SOCKS server cannot find $sshconfig\n";
2126        $doesntrun{$pidfile} = 1;
2127        return (0,0);
2128    }
2129
2130    $sshlog  = server_logfilename($LOGDIR, 'socks', $ipvnum, $idnum);
2131
2132    # start our socks server
2133    my $cmd="\"$ssh\" -N -F $sshconfig $ip > $sshlog 2>&1";
2134    my ($sshpid, $pid2) = startnew($cmd, $pidfile, 30, 1); # fake pidfile
2135
2136    if($sshpid <= 0 || !pidexists($sshpid)) {
2137        # it is NOT alive
2138        logmsg "RUN: failed to start the $srvrname server\n";
2139        display_sshlog();
2140        display_sshconfig();
2141        display_sshdlog();
2142        display_sshdconfig();
2143        stopserver($server, "$pid2");
2144        $doesntrun{$pidfile} = 1;
2145        return (0,0);
2146    }
2147
2148    # Ugly hack but ssh doesn't support pid files. PID is from fake pidfile.
2149    my $pid3 = verifyserver($proto, $ipvnum, $idnum, $ip, $port);
2150    if(!$pid3) {
2151        logmsg "RUN: $srvrname server failed verification\n";
2152        # failed to talk to it properly. Kill the server and return failure
2153        stopserver($server, "$sshpid $pid2");
2154        $doesntrun{$pidfile} = 1;
2155        return (0,0);
2156    }
2157    $pid2 = $pid3;
2158
2159    if($verbose) {
2160        logmsg "RUN: $srvrname server is now running PID $pid2\n";
2161    }
2162
2163    return ($pid2, $sshpid);
2164}
2165
2166#######################################################################
2167# Single shot http and gopher server responsiveness test. This should only
2168# be used to verify that a server present in %run hash is still functional
2169#
2170sub responsive_http_server {
2171    my ($proto, $verbose, $alt, $port_or_path) = @_;
2172    my $ip = $HOSTIP;
2173    my $ipvnum = 4;
2174    my $idnum = 1;
2175
2176    if($alt eq "ipv6") {
2177        # if IPv6, use a different setup
2178        $ipvnum = 6;
2179        $ip = $HOST6IP;
2180    }
2181    elsif($alt eq "proxy") {
2182        $idnum = 2;
2183    }
2184    elsif($alt eq "unix") {
2185        # IP (protocol) is mutually exclusive with Unix sockets
2186        $ipvnum = "unix";
2187    }
2188
2189    return &responsiveserver($proto, $ipvnum, $idnum, $ip, $port_or_path);
2190}
2191
2192#######################################################################
2193# Single shot pingpong server responsiveness test. This should only be
2194# used to verify that a server present in %run hash is still functional
2195#
2196sub responsive_pingpong_server {
2197    my ($proto, $id, $verbose, $ipv6) = @_;
2198    my $port;
2199    my $ip = ($ipv6 && ($ipv6 =~ /6$/)) ? "$HOST6IP" : "$HOSTIP";
2200    my $ipvnum = ($ipv6 && ($ipv6 =~ /6$/)) ? 6 : 4;
2201    my $idnum = ($id && ($id =~ /^(\d+)$/) && ($id > 1)) ? $id : 1;
2202
2203    if($proto eq "ftp") {
2204        $port = ($idnum>1)?$FTP2PORT:$FTPPORT;
2205
2206        if($ipvnum==6) {
2207            # if IPv6, use a different setup
2208            $port = $FTP6PORT;
2209        }
2210    }
2211    elsif($proto eq "pop3") {
2212        $port = ($ipvnum==6) ? $POP36PORT : $POP3PORT;
2213    }
2214    elsif($proto eq "imap") {
2215        $port = ($ipvnum==6) ? $IMAP6PORT : $IMAPPORT;
2216    }
2217    elsif($proto eq "smtp") {
2218        $port = ($ipvnum==6) ? $SMTP6PORT : $SMTPPORT;
2219    }
2220    else {
2221        print STDERR "Unsupported protocol $proto!!\n";
2222        return 0;
2223    }
2224
2225    return &responsiveserver($proto, $ipvnum, $idnum, $ip, $port);
2226}
2227
2228#######################################################################
2229# Single shot rtsp server responsiveness test. This should only be
2230# used to verify that a server present in %run hash is still functional
2231#
2232sub responsive_rtsp_server {
2233    my ($verbose, $ipv6) = @_;
2234    my $port = $RTSPPORT;
2235    my $ip = $HOSTIP;
2236    my $proto = 'rtsp';
2237    my $ipvnum = 4;
2238    my $idnum = 1;
2239
2240    if($ipv6) {
2241        # if IPv6, use a different setup
2242        $ipvnum = 6;
2243        $port = $RTSP6PORT;
2244        $ip = $HOST6IP;
2245    }
2246
2247    return &responsiveserver($proto, $ipvnum, $idnum, $ip, $port);
2248}
2249
2250#######################################################################
2251# Single shot tftp server responsiveness test. This should only be
2252# used to verify that a server present in %run hash is still functional
2253#
2254sub responsive_tftp_server {
2255    my ($id, $verbose, $ipv6) = @_;
2256    my $port = $TFTPPORT;
2257    my $ip = $HOSTIP;
2258    my $proto = 'tftp';
2259    my $ipvnum = 4;
2260    my $idnum = ($id && ($id =~ /^(\d+)$/) && ($id > 1)) ? $id : 1;
2261
2262    if($ipv6) {
2263        # if IPv6, use a different setup
2264        $ipvnum = 6;
2265        $port = $TFTP6PORT;
2266        $ip = $HOST6IP;
2267    }
2268
2269    return &responsiveserver($proto, $ipvnum, $idnum, $ip, $port);
2270}
2271
2272#######################################################################
2273# Single shot non-stunnel HTTP TLS extensions capable server
2274# responsiveness test. This should only be used to verify that a
2275# server present in %run hash is still functional
2276#
2277sub responsive_httptls_server {
2278    my ($verbose, $ipv6) = @_;
2279    my $proto = "httptls";
2280    my $port = ($ipv6 && ($ipv6 =~ /6$/)) ? $HTTPTLS6PORT : $HTTPTLSPORT;
2281    my $ip = ($ipv6 && ($ipv6 =~ /6$/)) ? "$HOST6IP" : "$HOSTIP";
2282    my $ipvnum = ($ipv6 && ($ipv6 =~ /6$/)) ? 6 : 4;
2283    my $idnum = 1;
2284
2285    return &responsiveserver($proto, $ipvnum, $idnum, $ip, $port);
2286}
2287
2288#######################################################################
2289# Remove all files in the specified directory
2290#
2291sub cleardir {
2292    my $dir = $_[0];
2293    my $count;
2294    my $file;
2295
2296    # Get all files
2297    opendir(DIR, $dir) ||
2298        return 0; # can't open dir
2299    while($file = readdir(DIR)) {
2300        if($file !~ /^\./) {
2301            unlink("$dir/$file");
2302            $count++;
2303        }
2304    }
2305    closedir DIR;
2306    return $count;
2307}
2308
2309#######################################################################
2310# compare test results with the expected output, we might filter off
2311# some pattern that is allowed to differ, output test results
2312#
2313sub compare {
2314    my ($testnum, $testname, $subject, $firstref, $secondref)=@_;
2315
2316    my $result = compareparts($firstref, $secondref);
2317
2318    if($result) {
2319        # timestamp test result verification end
2320        $timevrfyend{$testnum} = Time::HiRes::time() if($timestats);
2321
2322        if(!$short) {
2323            logmsg "\n $testnum: $subject FAILED:\n";
2324            logmsg showdiff($LOGDIR, $firstref, $secondref);
2325        }
2326        elsif(!$automakestyle) {
2327            logmsg "FAILED\n";
2328        }
2329        else {
2330            # automakestyle
2331            logmsg "FAIL: $testnum - $testname - $subject\n";
2332        }
2333    }
2334    return $result;
2335}
2336
2337#######################################################################
2338# display information about curl and the host the test suite runs on
2339#
2340sub checksystem {
2341
2342    unlink($memdump); # remove this if there was one left
2343
2344    my $feat;
2345    my $curl;
2346    my $libcurl;
2347    my $versretval;
2348    my $versnoexec;
2349    my @version=();
2350
2351    my $curlverout="$LOGDIR/curlverout.log";
2352    my $curlvererr="$LOGDIR/curlvererr.log";
2353    my $versioncmd="$CURL --version 1>$curlverout 2>$curlvererr";
2354
2355    unlink($curlverout);
2356    unlink($curlvererr);
2357
2358    $versretval = runclient($versioncmd);
2359    $versnoexec = $!;
2360
2361    open(VERSOUT, "<$curlverout");
2362    @version = <VERSOUT>;
2363    close(VERSOUT);
2364
2365    $resolver="stock";
2366    for(@version) {
2367        chomp;
2368
2369        if($_ =~ /^curl/) {
2370            $curl = $_;
2371            $curl =~ s/^(.*)(libcurl.*)/$1/g;
2372
2373            $libcurl = $2;
2374            if($curl =~ /win32|mingw(32|64)/) {
2375                # This is a Windows MinGW build or native build, we need to use
2376                # Win32-style path.
2377                $pwd = pathhelp::sys_native_current_path();
2378            }
2379           if ($libcurl =~ /winssl/i) {
2380               $has_winssl=1;
2381               $ssllib="WinSSL";
2382           }
2383           elsif ($libcurl =~ /openssl/i) {
2384               $has_openssl=1;
2385               $has_sslpinning=1;
2386               $ssllib="OpenSSL";
2387           }
2388           elsif ($libcurl =~ /gnutls/i) {
2389               $has_gnutls=1;
2390               $has_sslpinning=1;
2391               $ssllib="GnuTLS";
2392           }
2393           elsif ($libcurl =~ /nss/i) {
2394               $has_nss=1;
2395               $has_sslpinning=1;
2396               $ssllib="NSS";
2397           }
2398           elsif ($libcurl =~ /(yassl|wolfssl)/i) {
2399               $has_yassl=1;
2400               $has_sslpinning=1;
2401               $ssllib="yassl";
2402           }
2403           elsif ($libcurl =~ /polarssl/i) {
2404               $has_polarssl=1;
2405               $has_sslpinning=1;
2406               $ssllib="polarssl";
2407           }
2408           elsif ($libcurl =~ /axtls/i) {
2409               $has_axtls=1;
2410               $ssllib="axTLS";
2411           }
2412           elsif ($libcurl =~ /securetransport/i) {
2413               $has_darwinssl=1;
2414               $ssllib="DarwinSSL";
2415           }
2416           elsif ($libcurl =~ /BoringSSL/i) {
2417               $has_boringssl=1;
2418               $has_sslpinning=1;
2419               $ssllib="BoringSSL";
2420           }
2421           elsif ($libcurl =~ /libressl/i) {
2422               $has_libressl=1;
2423               $has_sslpinning=1;
2424               $ssllib="libressl";
2425           }
2426           elsif ($libcurl =~ /mbedTLS/i) {
2427               $has_mbedtls=1;
2428               $has_sslpinning=1;
2429               $ssllib="mbedTLS";
2430           }
2431           if ($libcurl =~ /ares/i) {
2432               $has_cares=1;
2433               $resolver="c-ares";
2434           }
2435        }
2436        elsif($_ =~ /^Protocols: (.*)/i) {
2437            # these are the protocols compiled in to this libcurl
2438            @protocols = split(' ', lc($1));
2439
2440            # Generate a "proto-ipv6" version of each protocol to match the
2441            # IPv6 <server> name and a "proto-unix" to match the variant which
2442            # uses Unix domain sockets. This works even if support isn't
2443            # compiled in because the <features> test will fail.
2444            push @protocols, map(("$_-ipv6", "$_-unix"), @protocols);
2445
2446            # 'http-proxy' is used in test cases to do CONNECT through
2447            push @protocols, 'http-proxy';
2448
2449            # 'http-pipe' is the special server for testing pipelining
2450            push @protocols, 'http-pipe';
2451
2452            # 'none' is used in test cases to mean no server
2453            push @protocols, 'none';
2454        }
2455        elsif($_ =~ /^Features: (.*)/i) {
2456            $feat = $1;
2457            if($feat =~ /TrackMemory/i) {
2458                # built with memory tracking support (--enable-curldebug)
2459                $has_memory_tracking = 1;
2460            }
2461            if($feat =~ /debug/i) {
2462                # curl was built with --enable-debug
2463                $debug_build = 1;
2464            }
2465            if($feat =~ /SSL/i) {
2466                # ssl enabled
2467                $has_ssl=1;
2468            }
2469            if($feat =~ /Largefile/i) {
2470                # large file support
2471                $has_largefile=1;
2472            }
2473            if($feat =~ /IDN/i) {
2474                # IDN support
2475                $has_idn=1;
2476            }
2477            if($feat =~ /IPv6/i) {
2478                $has_ipv6 = 1;
2479            }
2480            if($feat =~ /UnixSockets/i) {
2481                $has_unix = 1;
2482            }
2483            if($feat =~ /libz/i) {
2484                $has_libz = 1;
2485            }
2486            if($feat =~ /NTLM/i) {
2487                # NTLM enabled
2488                $has_ntlm=1;
2489
2490                # Use this as a proxy for any cryptographic authentication
2491                $has_crypto=1;
2492            }
2493            if($feat =~ /NTLM_WB/i) {
2494                # NTLM delegation to winbind daemon ntlm_auth helper enabled
2495                $has_ntlm_wb=1;
2496            }
2497            if($feat =~ /SSPI/i) {
2498                # SSPI enabled
2499                $has_sspi=1;
2500            }
2501            if($feat =~ /GSS-API/i) {
2502                # GSS-API enabled
2503                $has_gssapi=1;
2504            }
2505            if($feat =~ /Kerberos/i) {
2506                # Kerberos enabled
2507                $has_kerberos=1;
2508
2509                # Use this as a proxy for any cryptographic authentication
2510                $has_crypto=1;
2511            }
2512            if($feat =~ /SPNEGO/i) {
2513                # SPNEGO enabled
2514                $has_spnego=1;
2515
2516                # Use this as a proxy for any cryptographic authentication
2517                $has_crypto=1;
2518            }
2519            if($feat =~ /CharConv/i) {
2520                # CharConv enabled
2521                $has_charconv=1;
2522            }
2523            if($feat =~ /TLS-SRP/i) {
2524                # TLS-SRP enabled
2525                $has_tls_srp=1;
2526            }
2527            if($feat =~ /Metalink/i) {
2528                # Metalink enabled
2529                $has_metalink=1;
2530            }
2531            if($feat =~ /PSL/i) {
2532                # PSL enabled
2533                $has_psl=1;
2534            }
2535            if($feat =~ /AsynchDNS/i) {
2536                if(!$has_cares) {
2537                    # this means threaded resolver
2538                    $has_threadedres=1;
2539                    $resolver="threaded";
2540                }
2541            }
2542            if($feat =~ /HTTP2/) {
2543                # http2 enabled
2544                $has_http2=1;
2545
2546                push @protocols, 'http/2';
2547            }
2548        }
2549        #
2550        # Test harness currently uses a non-stunnel server in order to
2551        # run HTTP TLS-SRP tests required when curl is built with https
2552        # protocol support and TLS-SRP feature enabled. For convenience
2553        # 'httptls' may be included in the test harness protocols array
2554        # to differentiate this from classic stunnel based 'https' test
2555        # harness server.
2556        #
2557        if($has_tls_srp) {
2558            my $add_httptls;
2559            for(@protocols) {
2560                if($_ =~ /^https(-ipv6|)$/) {
2561                    $add_httptls=1;
2562                    last;
2563                }
2564            }
2565            if($add_httptls && (! grep /^httptls$/, @protocols)) {
2566                push @protocols, 'httptls';
2567                push @protocols, 'httptls-ipv6';
2568            }
2569        }
2570    }
2571    if(!$curl) {
2572        logmsg "unable to get curl's version, further details are:\n";
2573        logmsg "issued command: \n";
2574        logmsg "$versioncmd \n";
2575        if ($versretval == -1) {
2576            logmsg "command failed with: \n";
2577            logmsg "$versnoexec \n";
2578        }
2579        elsif ($versretval & 127) {
2580            logmsg sprintf("command died with signal %d, and %s coredump.\n",
2581                           ($versretval & 127), ($versretval & 128)?"a":"no");
2582        }
2583        else {
2584            logmsg sprintf("command exited with value %d \n", $versretval >> 8);
2585        }
2586        logmsg "contents of $curlverout: \n";
2587        displaylogcontent("$curlverout");
2588        logmsg "contents of $curlvererr: \n";
2589        displaylogcontent("$curlvererr");
2590        die "couldn't get curl's version";
2591    }
2592
2593    if(-r "../lib/curl_config.h") {
2594        open(CONF, "<../lib/curl_config.h");
2595        while(<CONF>) {
2596            if($_ =~ /^\#define HAVE_GETRLIMIT/) {
2597                $has_getrlimit = 1;
2598            }
2599        }
2600        close(CONF);
2601    }
2602
2603    if($has_ipv6) {
2604        # client has IPv6 support
2605
2606        # check if the HTTP server has it!
2607        my @sws = `server/sws --version`;
2608        if($sws[0] =~ /IPv6/) {
2609            # HTTP server has IPv6 support!
2610            $http_ipv6 = 1;
2611            $gopher_ipv6 = 1;
2612        }
2613
2614        # check if the FTP server has it!
2615        @sws = `server/sockfilt --version`;
2616        if($sws[0] =~ /IPv6/) {
2617            # FTP server has IPv6 support!
2618            $ftp_ipv6 = 1;
2619        }
2620    }
2621
2622    if($has_unix) {
2623        # client has Unix sockets support, check whether the HTTP server has it
2624        my @sws = `server/sws --version`;
2625        $http_unix = 1 if($sws[0] =~ /unix/);
2626    }
2627
2628    if(!$has_memory_tracking && $torture) {
2629        die "can't run torture tests since curl was built without ".
2630            "TrackMemory feature (--enable-curldebug)";
2631    }
2632
2633    $has_shared = `sh $CURLCONFIG --built-shared`;
2634    chomp $has_shared;
2635
2636    my $hostname=join(' ', runclientoutput("hostname"));
2637    my $hosttype=join(' ', runclientoutput("uname -a"));
2638
2639    logmsg ("********* System characteristics ******** \n",
2640    "* $curl\n",
2641    "* $libcurl\n",
2642    "* Features: $feat\n",
2643    "* Host: $hostname",
2644    "* System: $hosttype");
2645
2646    if($has_memory_tracking && $has_threadedres) {
2647        $has_memory_tracking = 0;
2648        logmsg("*\n",
2649               "*** DISABLES memory tracking when using threaded resolver\n",
2650               "*\n");
2651    }
2652
2653    logmsg sprintf("* Servers: %s", $stunnel?"SSL ":"");
2654    logmsg sprintf("%s", $http_ipv6?"HTTP-IPv6 ":"");
2655    logmsg sprintf("%s", $http_unix?"HTTP-unix ":"");
2656    logmsg sprintf("%s\n", $ftp_ipv6?"FTP-IPv6 ":"OFF");
2657
2658    logmsg sprintf("* Env: %s%s", $valgrind?"Valgrind ":"",
2659                   $run_event_based?"event-based ":"");
2660    logmsg sprintf("%s\n", $libtool?"Libtool ":"");
2661
2662    if($verbose) {
2663        logmsg "* Ports:\n";
2664
2665        logmsg sprintf("*   HTTP/%d ", $HTTPPORT);
2666        logmsg sprintf("FTP/%d ", $FTPPORT);
2667        logmsg sprintf("FTP2/%d ", $FTP2PORT);
2668        logmsg sprintf("RTSP/%d ", $RTSPPORT);
2669        if($stunnel) {
2670            logmsg sprintf("FTPS/%d ", $FTPSPORT);
2671            logmsg sprintf("HTTPS/%d ", $HTTPSPORT);
2672        }
2673        logmsg sprintf("\n*   TFTP/%d ", $TFTPPORT);
2674        if($http_ipv6) {
2675            logmsg sprintf("HTTP-IPv6/%d ", $HTTP6PORT);
2676            logmsg sprintf("RTSP-IPv6/%d ", $RTSP6PORT);
2677        }
2678        if($ftp_ipv6) {
2679            logmsg sprintf("FTP-IPv6/%d ", $FTP6PORT);
2680        }
2681        if($tftp_ipv6) {
2682            logmsg sprintf("TFTP-IPv6/%d ", $TFTP6PORT);
2683        }
2684        logmsg sprintf("\n*   GOPHER/%d ", $GOPHERPORT);
2685        if($gopher_ipv6) {
2686            logmsg sprintf("GOPHER-IPv6/%d", $GOPHERPORT);
2687        }
2688        logmsg sprintf("\n*   SSH/%d ", $SSHPORT);
2689        logmsg sprintf("SOCKS/%d ", $SOCKSPORT);
2690        logmsg sprintf("POP3/%d ", $POP3PORT);
2691        logmsg sprintf("IMAP/%d ", $IMAPPORT);
2692        logmsg sprintf("SMTP/%d\n", $SMTPPORT);
2693        if($ftp_ipv6) {
2694            logmsg sprintf("*   POP3-IPv6/%d ", $POP36PORT);
2695            logmsg sprintf("IMAP-IPv6/%d ", $IMAP6PORT);
2696            logmsg sprintf("SMTP-IPv6/%d\n", $SMTP6PORT);
2697        }
2698        if($httptlssrv) {
2699            logmsg sprintf("*   HTTPTLS/%d ", $HTTPTLSPORT);
2700            if($has_ipv6) {
2701                logmsg sprintf("HTTPTLS-IPv6/%d ", $HTTPTLS6PORT);
2702            }
2703            logmsg "\n";
2704        }
2705        logmsg sprintf("*   HTTP-PIPE/%d \n", $HTTPPIPEPORT);
2706
2707        if($has_unix) {
2708            logmsg "* Unix socket paths:\n";
2709            if($http_unix) {
2710                logmsg sprintf("*   HTTP-Unix:%s\n", $HTTPUNIXPATH);
2711            }
2712        }
2713    }
2714    $has_textaware = ($^O eq 'MSWin32') || ($^O eq 'msys');
2715
2716    logmsg "***************************************** \n";
2717}
2718
2719#######################################################################
2720# substitute the variable stuff into either a joined up file or
2721# a command, in either case passed by reference
2722#
2723sub subVariables {
2724  my ($thing) = @_;
2725
2726  # ports
2727
2728  $$thing =~ s/%FTP6PORT/$FTP6PORT/g;
2729  $$thing =~ s/%FTP2PORT/$FTP2PORT/g;
2730  $$thing =~ s/%FTPSPORT/$FTPSPORT/g;
2731  $$thing =~ s/%FTPPORT/$FTPPORT/g;
2732
2733  $$thing =~ s/%GOPHER6PORT/$GOPHER6PORT/g;
2734  $$thing =~ s/%GOPHERPORT/$GOPHERPORT/g;
2735
2736  $$thing =~ s/%HTTPTLS6PORT/$HTTPTLS6PORT/g;
2737  $$thing =~ s/%HTTPTLSPORT/$HTTPTLSPORT/g;
2738  $$thing =~ s/%HTTP6PORT/$HTTP6PORT/g;
2739  $$thing =~ s/%HTTPSPORT/$HTTPSPORT/g;
2740  $$thing =~ s/%HTTP2PORT/$HTTP2PORT/g;
2741  $$thing =~ s/%HTTPPORT/$HTTPPORT/g;
2742  $$thing =~ s/%HTTPPIPEPORT/$HTTPPIPEPORT/g;
2743  $$thing =~ s/%PROXYPORT/$HTTPPROXYPORT/g;
2744
2745  $$thing =~ s/%IMAP6PORT/$IMAP6PORT/g;
2746  $$thing =~ s/%IMAPPORT/$IMAPPORT/g;
2747
2748  $$thing =~ s/%POP36PORT/$POP36PORT/g;
2749  $$thing =~ s/%POP3PORT/$POP3PORT/g;
2750
2751  $$thing =~ s/%RTSP6PORT/$RTSP6PORT/g;
2752  $$thing =~ s/%RTSPPORT/$RTSPPORT/g;
2753
2754  $$thing =~ s/%SMTP6PORT/$SMTP6PORT/g;
2755  $$thing =~ s/%SMTPPORT/$SMTPPORT/g;
2756
2757  $$thing =~ s/%SOCKSPORT/$SOCKSPORT/g;
2758  $$thing =~ s/%SSHPORT/$SSHPORT/g;
2759
2760  $$thing =~ s/%TFTP6PORT/$TFTP6PORT/g;
2761  $$thing =~ s/%TFTPPORT/$TFTPPORT/g;
2762
2763  # server Unix domain socket paths
2764
2765  $$thing =~ s/%HTTPUNIXPATH/$HTTPUNIXPATH/g;
2766
2767  # client IP addresses
2768
2769  $$thing =~ s/%CLIENT6IP/$CLIENT6IP/g;
2770  $$thing =~ s/%CLIENTIP/$CLIENTIP/g;
2771
2772  # server IP addresses
2773
2774  $$thing =~ s/%HOST6IP/$HOST6IP/g;
2775  $$thing =~ s/%HOSTIP/$HOSTIP/g;
2776
2777  # misc
2778
2779  $$thing =~ s/%CURL/$CURL/g;
2780  $$thing =~ s/%PWD/$pwd/g;
2781  $$thing =~ s/%SRCDIR/$srcdir/g;
2782  $$thing =~ s/%USER/$USER/g;
2783
2784  # The purpose of FTPTIME2 and FTPTIME3 is to provide times that can be
2785  # used for time-out tests and that whould work on most hosts as these
2786  # adjust for the startup/check time for this particular host. We needed
2787  # to do this to make the test suite run better on very slow hosts.
2788
2789  my $ftp2 = $ftpchecktime * 2;
2790  my $ftp3 = $ftpchecktime * 3;
2791
2792  $$thing =~ s/%FTPTIME2/$ftp2/g;
2793  $$thing =~ s/%FTPTIME3/$ftp3/g;
2794
2795  # HTTP2
2796
2797  $$thing =~ s/%H2CVER/$h2cver/g;
2798}
2799
2800sub fixarray {
2801    my @in = @_;
2802
2803    for(@in) {
2804        subVariables \$_;
2805    }
2806    return @in;
2807}
2808
2809#######################################################################
2810# Provide time stamps for single test skipped events
2811#
2812sub timestampskippedevents {
2813    my $testnum = $_[0];
2814
2815    return if((not defined($testnum)) || ($testnum < 1));
2816
2817    if($timestats) {
2818
2819        if($timevrfyend{$testnum}) {
2820            return;
2821        }
2822        elsif($timesrvrlog{$testnum}) {
2823            $timevrfyend{$testnum} = $timesrvrlog{$testnum};
2824            return;
2825        }
2826        elsif($timetoolend{$testnum}) {
2827            $timevrfyend{$testnum} = $timetoolend{$testnum};
2828            $timesrvrlog{$testnum} = $timetoolend{$testnum};
2829        }
2830        elsif($timetoolini{$testnum}) {
2831            $timevrfyend{$testnum} = $timetoolini{$testnum};
2832            $timesrvrlog{$testnum} = $timetoolini{$testnum};
2833            $timetoolend{$testnum} = $timetoolini{$testnum};
2834        }
2835        elsif($timesrvrend{$testnum}) {
2836            $timevrfyend{$testnum} = $timesrvrend{$testnum};
2837            $timesrvrlog{$testnum} = $timesrvrend{$testnum};
2838            $timetoolend{$testnum} = $timesrvrend{$testnum};
2839            $timetoolini{$testnum} = $timesrvrend{$testnum};
2840        }
2841        elsif($timesrvrini{$testnum}) {
2842            $timevrfyend{$testnum} = $timesrvrini{$testnum};
2843            $timesrvrlog{$testnum} = $timesrvrini{$testnum};
2844            $timetoolend{$testnum} = $timesrvrini{$testnum};
2845            $timetoolini{$testnum} = $timesrvrini{$testnum};
2846            $timesrvrend{$testnum} = $timesrvrini{$testnum};
2847        }
2848        elsif($timeprepini{$testnum}) {
2849            $timevrfyend{$testnum} = $timeprepini{$testnum};
2850            $timesrvrlog{$testnum} = $timeprepini{$testnum};
2851            $timetoolend{$testnum} = $timeprepini{$testnum};
2852            $timetoolini{$testnum} = $timeprepini{$testnum};
2853            $timesrvrend{$testnum} = $timeprepini{$testnum};
2854            $timesrvrini{$testnum} = $timeprepini{$testnum};
2855        }
2856    }
2857}
2858
2859#######################################################################
2860# Run a single specified test case
2861#
2862sub singletest {
2863    my ($evbased, # 1 means switch on if possible (and "curl" is tested)
2864                  # returns "not a test" if it can't be used for this test
2865        $testnum,
2866        $count,
2867        $total)=@_;
2868
2869    my @what;
2870    my $why;
2871    my %feature;
2872    my $cmd;
2873    my $disablevalgrind;
2874
2875    # copy test number to a global scope var, this allows
2876    # testnum checking when starting test harness servers.
2877    $testnumcheck = $testnum;
2878
2879    # timestamp test preparation start
2880    $timeprepini{$testnum} = Time::HiRes::time() if($timestats);
2881
2882    if($disttests !~ /test$testnum\W/ ) {
2883        logmsg "Warning: test$testnum not present in tests/data/Makefile.inc\n";
2884    }
2885    if($disabled{$testnum}) {
2886        logmsg "Warning: test$testnum is explicitly disabled\n";
2887    }
2888
2889    # load the test case file definition
2890    if(loadtest("${TESTDIR}/test${testnum}")) {
2891        if($verbose) {
2892            # this is not a test
2893            logmsg "RUN: $testnum doesn't look like a test case\n";
2894        }
2895        $why = "no test";
2896    }
2897    else {
2898        @what = getpart("client", "features");
2899    }
2900
2901    # We require a feature to be present
2902    for(@what) {
2903        my $f = $_;
2904        $f =~ s/\s//g;
2905
2906        if($f =~ /^([^!].*)$/) {
2907            # Store the feature for later
2908            $feature{$1} = $1;
2909
2910            if($1 eq "SSL") {
2911                if($has_ssl) {
2912                    next;
2913                }
2914            }
2915            elsif($1 eq "SSLpinning") {
2916                if($has_sslpinning) {
2917                    next;
2918                }
2919            }
2920            elsif($1 eq "OpenSSL") {
2921                if($has_openssl) {
2922                    next;
2923                }
2924            }
2925            elsif($1 eq "GnuTLS") {
2926                if($has_gnutls) {
2927                    next;
2928                }
2929            }
2930            elsif($1 eq "NSS") {
2931                if($has_nss) {
2932                    next;
2933                }
2934            }
2935            elsif($1 eq "axTLS") {
2936                if($has_axtls) {
2937                    next;
2938                }
2939            }
2940            elsif($1 eq "WinSSL") {
2941                if($has_winssl) {
2942                    next;
2943                }
2944            }
2945            elsif($1 eq "DarwinSSL") {
2946                if($has_darwinssl) {
2947                    next;
2948                }
2949            }
2950            elsif($1 eq "unittest") {
2951                if($debug_build) {
2952                    next;
2953                }
2954            }
2955            elsif($1 eq "debug") {
2956                if($debug_build) {
2957                    next;
2958                }
2959            }
2960            elsif($1 eq "TrackMemory") {
2961                if($has_memory_tracking) {
2962                    next;
2963                }
2964            }
2965            elsif($1 eq "large_file") {
2966                if($has_largefile) {
2967                    next;
2968                }
2969            }
2970            elsif($1 eq "idn") {
2971                if($has_idn) {
2972                    next;
2973                }
2974            }
2975            elsif($1 eq "ipv6") {
2976                if($has_ipv6) {
2977                    next;
2978                }
2979            }
2980            elsif($1 eq "libz") {
2981                if($has_libz) {
2982                    next;
2983                }
2984            }
2985            elsif($1 eq "NTLM") {
2986                if($has_ntlm) {
2987                    next;
2988                }
2989            }
2990            elsif($1 eq "NTLM_WB") {
2991                if($has_ntlm_wb) {
2992                    next;
2993                }
2994            }
2995            elsif($1 eq "SSPI") {
2996                if($has_sspi) {
2997                    next;
2998                }
2999            }
3000            elsif($1 eq "GSS-API") {
3001                if($has_gssapi) {
3002                    next;
3003                }
3004            }
3005            elsif($1 eq "Kerberos") {
3006                if($has_kerberos) {
3007                    next;
3008                }
3009            }
3010            elsif($1 eq "SPNEGO") {
3011                if($has_spnego) {
3012                    next;
3013                }
3014            }
3015            elsif($1 eq "getrlimit") {
3016                if($has_getrlimit) {
3017                    next;
3018                }
3019            }
3020            elsif($1 eq "crypto") {
3021                if($has_crypto) {
3022                    next;
3023                }
3024            }
3025            elsif($1 eq "TLS-SRP") {
3026                if($has_tls_srp) {
3027                    next;
3028                }
3029            }
3030            elsif($1 eq "Metalink") {
3031                if($has_metalink) {
3032                    next;
3033                }
3034            }
3035            elsif($1 eq "http/2") {
3036                if($has_http2) {
3037                    next;
3038                }
3039            }
3040            elsif($1 eq "PSL") {
3041                if($has_psl) {
3042                    next;
3043                }
3044            }
3045            elsif($1 eq "socks") {
3046                next;
3047            }
3048            elsif($1 eq "unix-sockets") {
3049                next if $has_unix;
3050            }
3051            # See if this "feature" is in the list of supported protocols
3052            elsif (grep /^\Q$1\E$/i, @protocols) {
3053                next;
3054            }
3055
3056            $why = "curl lacks $1 support";
3057            last;
3058        }
3059    }
3060
3061    # We require a feature to not be present
3062    if(!$why) {
3063        for(@what) {
3064            my $f = $_;
3065            $f =~ s/\s//g;
3066
3067            if($f =~ /^!(.*)$/) {
3068                if($1 eq "SSL") {
3069                    if(!$has_ssl) {
3070                        next;
3071                    }
3072                }
3073                elsif($1 eq "OpenSSL") {
3074                    if(!$has_openssl) {
3075                        next;
3076                    }
3077                }
3078                elsif($1 eq "GnuTLS") {
3079                    if(!$has_gnutls) {
3080                        next;
3081                    }
3082                }
3083                elsif($1 eq "NSS") {
3084                    if(!$has_nss) {
3085                        next;
3086                    }
3087                }
3088                elsif($1 eq "axTLS") {
3089                    if(!$has_axtls) {
3090                        next;
3091                    }
3092                }
3093                elsif($1 eq "WinSSL") {
3094                    if(!$has_winssl) {
3095                        next;
3096                    }
3097                }
3098                elsif($1 eq "DarwinSSL") {
3099                    if(!$has_darwinssl) {
3100                        next;
3101                    }
3102                }
3103                elsif($1 eq "TrackMemory") {
3104                    if(!$has_memory_tracking) {
3105                        next;
3106                    }
3107                }
3108                elsif($1 eq "large_file") {
3109                    if(!$has_largefile) {
3110                        next;
3111                    }
3112                }
3113                elsif($1 eq "idn") {
3114                    if(!$has_idn) {
3115                        next;
3116                    }
3117                }
3118                elsif($1 eq "ipv6") {
3119                    if(!$has_ipv6) {
3120                        next;
3121                    }
3122                }
3123                elsif($1 eq "unix-sockets") {
3124                    next if !$has_unix;
3125                }
3126                elsif($1 eq "libz") {
3127                    if(!$has_libz) {
3128                        next;
3129                    }
3130                }
3131                elsif($1 eq "NTLM") {
3132                    if(!$has_ntlm) {
3133                        next;
3134                    }
3135                }
3136                elsif($1 eq "NTLM_WB") {
3137                    if(!$has_ntlm_wb) {
3138                        next;
3139                    }
3140                }
3141                elsif($1 eq "SSPI") {
3142                    if(!$has_sspi) {
3143                        next;
3144                    }
3145                }
3146                elsif($1 eq "GSS-API") {
3147                    if(!$has_gssapi) {
3148                        next;
3149                    }
3150                }
3151                elsif($1 eq "Kerberos") {
3152                    if(!$has_kerberos) {
3153                        next;
3154                    }
3155                }
3156                elsif($1 eq "SPNEGO") {
3157                    if(!$has_spnego) {
3158                        next;
3159                    }
3160                }
3161                elsif($1 eq "getrlimit") {
3162                    if(!$has_getrlimit) {
3163                        next;
3164                    }
3165                }
3166                elsif($1 eq "crypto") {
3167                    if(!$has_crypto) {
3168                        next;
3169                    }
3170                }
3171                elsif($1 eq "TLS-SRP") {
3172                    if(!$has_tls_srp) {
3173                        next;
3174                    }
3175                }
3176                elsif($1 eq "Metalink") {
3177                    if(!$has_metalink) {
3178                        next;
3179                    }
3180                }
3181                elsif($1 eq "PSL") {
3182                    if(!$has_psl) {
3183                        next;
3184                    }
3185                }
3186                else {
3187                    next;
3188                }
3189            }
3190            else {
3191                next;
3192            }
3193
3194            $why = "curl has $1 support";
3195            last;
3196        }
3197    }
3198
3199    if(!$why) {
3200        my @keywords = getpart("info", "keywords");
3201        my $match;
3202        my $k;
3203
3204        if(!$keywords[0]) {
3205            $why = "missing the <keywords> section!";
3206        }
3207
3208        for $k (@keywords) {
3209            chomp $k;
3210            if ($disabled_keywords{$k}) {
3211                $why = "disabled by keyword";
3212            } elsif ($enabled_keywords{$k}) {
3213                $match = 1;
3214            }
3215        }
3216
3217        if(!$why && !$match && %enabled_keywords) {
3218            $why = "disabled by missing keyword";
3219        }
3220    }
3221
3222    # test definition may instruct to (un)set environment vars
3223    # this is done this early, so that the precheck can use environment
3224    # variables and still bail out fine on errors
3225
3226    # restore environment variables that were modified in a previous run
3227    foreach my $var (keys %oldenv) {
3228        if($oldenv{$var} eq 'notset') {
3229            delete $ENV{$var} if($ENV{$var});
3230        }
3231        else {
3232            $ENV{$var} = $oldenv{$var};
3233        }
3234        delete $oldenv{$var};
3235    }
3236
3237    # remove test server commands file before servers are started/verified
3238    unlink($FTPDCMD) if(-f $FTPDCMD);
3239
3240    # timestamp required servers verification start
3241    $timesrvrini{$testnum} = Time::HiRes::time() if($timestats);
3242
3243    if(!$why) {
3244        $why = serverfortest($testnum);
3245    }
3246
3247    # timestamp required servers verification end
3248    $timesrvrend{$testnum} = Time::HiRes::time() if($timestats);
3249
3250    my @setenv = getpart("client", "setenv");
3251    if(@setenv) {
3252        foreach my $s (@setenv) {
3253            chomp $s;
3254            subVariables \$s;
3255            if($s =~ /([^=]*)=(.*)/) {
3256                my ($var, $content) = ($1, $2);
3257                # remember current setting, to restore it once test runs
3258                $oldenv{$var} = ($ENV{$var})?"$ENV{$var}":'notset';
3259                # set new value
3260                if(!$content) {
3261                    delete $ENV{$var} if($ENV{$var});
3262                }
3263                else {
3264                    if($var =~ /^LD_PRELOAD/) {
3265                        if(exe_ext() && (exe_ext() eq '.exe')) {
3266                            # print "Skipping LD_PRELOAD due to lack of OS support\n";
3267                            next;
3268                        }
3269                        if($debug_build || ($has_shared ne "yes")) {
3270                            # print "Skipping LD_PRELOAD due to no release shared build\n";
3271                            next;
3272                        }
3273                    }
3274                    $ENV{$var} = "$content";
3275                }
3276            }
3277        }
3278    }
3279
3280    if(!$why) {
3281        # TODO:
3282        # Add a precheck cache. If a precheck command was already invoked
3283        # exactly like this, then use the previous result to speed up
3284        # successive test invokes!
3285
3286        my @precheck = getpart("client", "precheck");
3287        if(@precheck) {
3288            $cmd = $precheck[0];
3289            chomp $cmd;
3290            subVariables \$cmd;
3291            if($cmd) {
3292                my @p = split(/ /, $cmd);
3293                if($p[0] !~ /\//) {
3294                    # the first word, the command, does not contain a slash so
3295                    # we will scan the "improved" PATH to find the command to
3296                    # be able to run it
3297                    my $fullp = checktestcmd($p[0]);
3298
3299                    if($fullp) {
3300                        $p[0] = $fullp;
3301                    }
3302                    $cmd = join(" ", @p);
3303                }
3304
3305                my @o = `$cmd 2>/dev/null`;
3306                if($o[0]) {
3307                    $why = $o[0];
3308                    chomp $why;
3309                } elsif($?) {
3310                    $why = "precheck command error";
3311                }
3312                logmsg "prechecked $cmd\n" if($verbose);
3313            }
3314        }
3315    }
3316
3317    if($why && !$listonly) {
3318        # there's a problem, count it as "skipped"
3319        $skipped++;
3320        $skipped{$why}++;
3321        $teststat[$testnum]=$why; # store reason for this test case
3322
3323        if(!$short) {
3324            if($skipped{$why} <= 3) {
3325                # show only the first three skips for each reason
3326                logmsg sprintf("test %04d SKIPPED: $why\n", $testnum);
3327            }
3328        }
3329
3330        timestampskippedevents($testnum);
3331        return -1;
3332    }
3333    logmsg sprintf("test %04d...", $testnum) if(!$automakestyle);
3334
3335    # extract the reply data
3336    my @reply = getpart("reply", "data");
3337    my @replycheck = getpart("reply", "datacheck");
3338
3339    my %replyattr = getpartattr("reply", "data");
3340    my %replycheckattr = getpartattr("reply", "datacheck");
3341
3342    if (@replycheck) {
3343        # we use this file instead to check the final output against
3344        # get the mode attribute
3345        my $filemode=$replycheckattr{'mode'};
3346        if($filemode && ($filemode eq "text") && $has_textaware) {
3347            # text mode when running on windows: fix line endings
3348            map s/\r\n/\n/g, @replycheck;
3349            map s/\n/\r\n/g, @replycheck;
3350        }
3351        if($replycheckattr{'nonewline'}) {
3352            # Yes, we must cut off the final newline from the final line
3353            # of the datacheck
3354            chomp($replycheck[$#replycheck]);
3355        }
3356
3357        for my $partsuffix (('1', '2', '3', '4')) {
3358            my @replycheckpart = getpart("reply", "datacheck".$partsuffix);
3359            if(@replycheckpart || partexists("reply", "datacheck".$partsuffix) ) {
3360                my %replycheckpartattr = getpartattr("reply", "datacheck".$partsuffix);
3361                # get the mode attribute
3362                my $filemode=$replycheckpartattr{'mode'};
3363                if($filemode && ($filemode eq "text") && $has_textaware) {
3364                    # text mode when running on windows: fix line endings
3365                    map s/\r\n/\n/g, @replycheckpart;
3366                    map s/\n/\r\n/g, @replycheckpart;
3367                }
3368                if($replycheckpartattr{'nonewline'}) {
3369                    # Yes, we must cut off the final newline from the final line
3370                    # of the datacheck
3371                    chomp($replycheckpart[$#replycheckpart]);
3372                }
3373                push(@replycheck, @replycheckpart);
3374            }
3375        }
3376
3377        @reply=@replycheck;
3378    }
3379    else {
3380        # get the mode attribute
3381        my $filemode=$replyattr{'mode'};
3382        if($filemode && ($filemode eq "text") && $has_textaware) {
3383            # text mode when running on windows: fix line endings
3384            map s/\r\n/\n/g, @reply;
3385            map s/\n/\r\n/g, @reply;
3386        }
3387    }
3388
3389    # this is the valid protocol blurb curl should generate
3390    my @protocol= fixarray ( getpart("verify", "protocol") );
3391
3392    # this is the valid protocol blurb curl should generate to a proxy
3393    my @proxyprot = fixarray ( getpart("verify", "proxy") );
3394
3395    # redirected stdout/stderr to these files
3396    $STDOUT="$LOGDIR/stdout$testnum";
3397    $STDERR="$LOGDIR/stderr$testnum";
3398
3399    # if this section exists, we verify that the stdout contained this:
3400    my @validstdout = fixarray ( getpart("verify", "stdout") );
3401
3402    # if this section exists, we verify upload
3403    my @upload = getpart("verify", "upload");
3404
3405    # if this section exists, it might be FTP server instructions:
3406    my @ftpservercmd = getpart("reply", "servercmd");
3407
3408    my $CURLOUT="$LOGDIR/curl$testnum.out"; # curl output if not stdout
3409
3410    # name of the test
3411    my @testname= getpart("client", "name");
3412    my $testname = $testname[0];
3413    $testname =~ s/\n//g;
3414    logmsg "[$testname]\n" if(!$short);
3415
3416    if($listonly) {
3417        timestampskippedevents($testnum);
3418        return 0; # look successful
3419    }
3420
3421    my @codepieces = getpart("client", "tool");
3422
3423    my $tool="";
3424    if(@codepieces) {
3425        $tool = $codepieces[0];
3426        chomp $tool;
3427    }
3428
3429    # remove server output logfile
3430    unlink($SERVERIN);
3431    unlink($SERVER2IN);
3432    unlink($PROXYIN);
3433
3434    if(@ftpservercmd) {
3435        # write the instructions to file
3436        writearray($FTPDCMD, \@ftpservercmd);
3437    }
3438
3439    # get the command line options to use
3440    my @blaha;
3441    ($cmd, @blaha)= getpart("client", "command");
3442
3443    if($cmd) {
3444        # make some nice replace operations
3445        $cmd =~ s/\n//g; # no newlines please
3446        # substitute variables in the command line
3447        subVariables \$cmd;
3448    }
3449    else {
3450        # there was no command given, use something silly
3451        $cmd="-";
3452    }
3453    if($has_memory_tracking) {
3454        unlink($memdump);
3455    }
3456
3457    # create a (possibly-empty) file before starting the test
3458    my @inputfile=getpart("client", "file");
3459    my %fileattr = getpartattr("client", "file");
3460    my $filename=$fileattr{'name'};
3461    if(@inputfile || $filename) {
3462        if(!$filename) {
3463            logmsg "ERROR: section client=>file has no name attribute\n";
3464            timestampskippedevents($testnum);
3465            return -1;
3466        }
3467        my $fileContent = join('', @inputfile);
3468        subVariables \$fileContent;
3469#        logmsg "DEBUG: writing file " . $filename . "\n";
3470        open(OUTFILE, ">$filename");
3471        binmode OUTFILE; # for crapage systems, use binary
3472        print OUTFILE $fileContent;
3473        close(OUTFILE);
3474    }
3475
3476    my %cmdhash = getpartattr("client", "command");
3477
3478    my $out="";
3479
3480    if((!$cmdhash{'option'}) || ($cmdhash{'option'} !~ /no-output/)) {
3481        #We may slap on --output!
3482        if (!@validstdout) {
3483            $out=" --output $CURLOUT ";
3484        }
3485    }
3486
3487    my $serverlogslocktimeout = $defserverlogslocktimeout;
3488    if($cmdhash{'timeout'}) {
3489        # test is allowed to override default server logs lock timeout
3490        if($cmdhash{'timeout'} =~ /(\d+)/) {
3491            $serverlogslocktimeout = $1 if($1 >= 0);
3492        }
3493    }
3494
3495    my $postcommanddelay = $defpostcommanddelay;
3496    if($cmdhash{'delay'}) {
3497        # test is allowed to specify a delay after command is executed
3498        if($cmdhash{'delay'} =~ /(\d+)/) {
3499            $postcommanddelay = $1 if($1 > 0);
3500        }
3501    }
3502
3503    my $CMDLINE;
3504    my $cmdargs;
3505    my $cmdtype = $cmdhash{'type'} || "default";
3506    my $fail_due_event_based = $evbased;
3507    if($cmdtype eq "perl") {
3508        # run the command line prepended with "perl"
3509        $cmdargs ="$cmd";
3510        $CMDLINE = "perl ";
3511        $tool=$CMDLINE;
3512        $disablevalgrind=1;
3513    }
3514    elsif($cmdtype eq "shell") {
3515        # run the command line prepended with "/bin/sh"
3516        $cmdargs ="$cmd";
3517        $CMDLINE = "/bin/sh ";
3518        $tool=$CMDLINE;
3519        $disablevalgrind=1;
3520    }
3521    elsif(!$tool) {
3522        # run curl, add suitable command line options
3523        $cmd = "-1 ".$cmd if(exists $feature{"SSL"} && ($has_axtls));
3524
3525        my $inc="";
3526        if((!$cmdhash{'option'}) || ($cmdhash{'option'} !~ /no-include/)) {
3527            $inc = " --include";
3528        }
3529
3530        $cmdargs = "$out$inc ";
3531        $cmdargs .= "--trace-ascii log/trace$testnum ";
3532        $cmdargs .= "--trace-time ";
3533        if($evbased) {
3534            $cmdargs .= "--test-event ";
3535            $fail_due_event_based--;
3536        }
3537        $cmdargs .= $cmd;
3538    }
3539    else {
3540        $cmdargs = " $cmd"; # $cmd is the command line for the test file
3541        $CURLOUT = $STDOUT; # sends received data to stdout
3542
3543        if($tool =~ /^lib/) {
3544            $CMDLINE="$LIBDIR/$tool";
3545        }
3546        elsif($tool =~ /^unit/) {
3547            $CMDLINE="$UNITDIR/$tool";
3548        }
3549
3550        if(! -f $CMDLINE) {
3551            logmsg "The tool set in the test case for this: '$tool' does not exist\n";
3552            timestampskippedevents($testnum);
3553            return -1;
3554        }
3555        $DBGCURL=$CMDLINE;
3556    }
3557
3558    if($gdbthis) {
3559        # gdb is incompatible with valgrind, so disable it when debugging
3560        # Perhaps a better approach would be to run it under valgrind anyway
3561        # with --db-attach=yes or --vgdb=yes.
3562        $disablevalgrind=1;
3563    }
3564
3565    if($fail_due_event_based) {
3566        logmsg "This test cannot run event based\n";
3567        return -1;
3568    }
3569
3570    my @stdintest = getpart("client", "stdin");
3571
3572    if(@stdintest) {
3573        my $stdinfile="$LOGDIR/stdin-for-$testnum";
3574
3575        my %hash = getpartattr("client", "stdin");
3576        if($hash{'nonewline'}) {
3577            # cut off the final newline from the final line of the stdin data
3578            chomp($stdintest[$#stdintest]);
3579        }
3580
3581        writearray($stdinfile, \@stdintest);
3582
3583        $cmdargs .= " <$stdinfile";
3584    }
3585
3586    if(!$tool) {
3587        $CMDLINE="$CURL";
3588    }
3589
3590    my $usevalgrind;
3591    if($valgrind && !$disablevalgrind) {
3592        my @valgrindoption = getpart("verify", "valgrind");
3593        if((!@valgrindoption) || ($valgrindoption[0] !~ /disable/)) {
3594            $usevalgrind = 1;
3595            my $valgrindcmd = "$valgrind ";
3596            $valgrindcmd .= "$valgrind_tool " if($valgrind_tool);
3597            $valgrindcmd .= "--leak-check=yes ";
3598            $valgrindcmd .= "--suppressions=$srcdir/valgrind.supp ";
3599           # $valgrindcmd .= "--gen-suppressions=all ";
3600            $valgrindcmd .= "--num-callers=16 ";
3601            $valgrindcmd .= "${valgrind_logfile}=$LOGDIR/valgrind$testnum";
3602            $CMDLINE = "$valgrindcmd $CMDLINE";
3603        }
3604    }
3605
3606    $CMDLINE .= "$cmdargs >$STDOUT 2>$STDERR";
3607
3608    if($verbose) {
3609        logmsg "$CMDLINE\n";
3610    }
3611
3612    print CMDLOG "$CMDLINE\n";
3613
3614    unlink("core");
3615
3616    my $dumped_core;
3617    my $cmdres;
3618
3619    # Apr 2007: precommand isn't being used and could be removed
3620    my @precommand= getpart("client", "precommand");
3621    if($precommand[0]) {
3622        # this is pure perl to eval!
3623        my $code = join("", @precommand);
3624        eval $code;
3625        if($@) {
3626            logmsg "perl: $code\n";
3627            logmsg "precommand: $@";
3628            stopservers($verbose);
3629            timestampskippedevents($testnum);
3630            return -1;
3631        }
3632    }
3633
3634    if($gdbthis) {
3635        my $gdbinit = "$TESTDIR/gdbinit$testnum";
3636        open(GDBCMD, ">$LOGDIR/gdbcmd");
3637        print GDBCMD "set args $cmdargs\n";
3638        print GDBCMD "show args\n";
3639        print GDBCMD "source $gdbinit\n" if -e $gdbinit;
3640        close(GDBCMD);
3641    }
3642
3643    # timestamp starting of test command
3644    $timetoolini{$testnum} = Time::HiRes::time() if($timestats);
3645
3646    # run the command line we built
3647    if ($torture) {
3648        $cmdres = torture($CMDLINE,
3649                       "$gdb --directory libtest $DBGCURL -x $LOGDIR/gdbcmd");
3650    }
3651    elsif($gdbthis) {
3652        my $GDBW = ($gdbxwin) ? "-w" : "";
3653        runclient("$gdb --directory libtest $DBGCURL $GDBW -x $LOGDIR/gdbcmd");
3654        $cmdres=0; # makes it always continue after a debugged run
3655    }
3656    else {
3657        $cmdres = runclient("$CMDLINE");
3658        my $signal_num  = $cmdres & 127;
3659        $dumped_core = $cmdres & 128;
3660
3661        if(!$anyway && ($signal_num || $dumped_core)) {
3662            $cmdres = 1000;
3663        }
3664        else {
3665            $cmdres >>= 8;
3666            $cmdres = (2000 + $signal_num) if($signal_num && !$cmdres);
3667        }
3668    }
3669
3670    # timestamp finishing of test command
3671    $timetoolend{$testnum} = Time::HiRes::time() if($timestats);
3672
3673    if(!$dumped_core) {
3674        if(-r "core") {
3675            # there's core file present now!
3676            $dumped_core = 1;
3677        }
3678    }
3679
3680    if($dumped_core) {
3681        logmsg "core dumped\n";
3682        if(0 && $gdb) {
3683            logmsg "running gdb for post-mortem analysis:\n";
3684            open(GDBCMD, ">$LOGDIR/gdbcmd2");
3685            print GDBCMD "bt\n";
3686            close(GDBCMD);
3687            runclient("$gdb --directory libtest -x $LOGDIR/gdbcmd2 -batch $DBGCURL core ");
3688     #       unlink("$LOGDIR/gdbcmd2");
3689        }
3690    }
3691
3692    # If a server logs advisor read lock file exists, it is an indication
3693    # that the server has not yet finished writing out all its log files,
3694    # including server request log files used for protocol verification.
3695    # So, if the lock file exists the script waits here a certain amount
3696    # of time until the server removes it, or the given time expires.
3697
3698    if($serverlogslocktimeout) {
3699        my $lockretry = $serverlogslocktimeout * 20;
3700        while((-f $SERVERLOGS_LOCK) && $lockretry--) {
3701            select(undef, undef, undef, 0.05);
3702        }
3703        if(($lockretry < 0) &&
3704           ($serverlogslocktimeout >= $defserverlogslocktimeout)) {
3705            logmsg "Warning: server logs lock timeout ",
3706                   "($serverlogslocktimeout seconds) expired\n";
3707        }
3708    }
3709
3710    # Test harness ssh server does not have this synchronization mechanism,
3711    # this implies that some ssh server based tests might need a small delay
3712    # once that the client command has run to avoid false test failures.
3713    #
3714    # gnutls-serv also lacks this synchronization mechanism, so gnutls-serv
3715    # based tests might need a small delay once that the client command has
3716    # run to avoid false test failures.
3717
3718    sleep($postcommanddelay) if($postcommanddelay);
3719
3720    # timestamp removal of server logs advisor read lock
3721    $timesrvrlog{$testnum} = Time::HiRes::time() if($timestats);
3722
3723    # test definition might instruct to stop some servers
3724    # stop also all servers relative to the given one
3725
3726    my @killtestservers = getpart("client", "killserver");
3727    if(@killtestservers) {
3728        #
3729        # All servers relative to the given one must be stopped also
3730        #
3731        my @killservers;
3732        foreach my $server (@killtestservers) {
3733            chomp $server;
3734            if($server =~ /^(ftp|http|imap|pop3|smtp)s((\d*)(-ipv6|-unix|))$/) {
3735                # given a stunnel ssl server, also kill non-ssl underlying one
3736                push @killservers, "${1}${2}";
3737            }
3738            elsif($server =~ /^(ftp|http|imap|pop3|smtp)((\d*)(-ipv6|-unix|))$/) {
3739                # given a non-ssl server, also kill stunnel piggybacking one
3740                push @killservers, "${1}s${2}";
3741            }
3742            elsif($server =~ /^(socks)((\d*)(-ipv6|))$/) {
3743                # given a socks server, also kill ssh underlying one
3744                push @killservers, "ssh${2}";
3745            }
3746            elsif($server =~ /^(ssh)((\d*)(-ipv6|))$/) {
3747                # given a ssh server, also kill socks piggybacking one
3748                push @killservers, "socks${2}";
3749            }
3750            push @killservers, $server;
3751        }
3752        #
3753        # kill sockfilter processes for pingpong relative servers
3754        #
3755        foreach my $server (@killservers) {
3756            if($server =~ /^(ftp|imap|pop3|smtp)s?(\d*)(-ipv6|)$/) {
3757                my $proto  = $1;
3758                my $idnum  = ($2 && ($2 > 1)) ? $2 : 1;
3759                my $ipvnum = ($3 && ($3 =~ /6$/)) ? 6 : 4;
3760                killsockfilters($proto, $ipvnum, $idnum, $verbose);
3761            }
3762        }
3763        #
3764        # kill server relative pids clearing them in %run hash
3765        #
3766        my $pidlist;
3767        foreach my $server (@killservers) {
3768            if($run{$server}) {
3769                $pidlist .= "$run{$server} ";
3770                $run{$server} = 0;
3771            }
3772            $runcert{$server} = 0 if($runcert{$server});
3773        }
3774        killpid($verbose, $pidlist);
3775        #
3776        # cleanup server pid files
3777        #
3778        foreach my $server (@killservers) {
3779            my $pidfile = $serverpidfile{$server};
3780            my $pid = processexists($pidfile);
3781            if($pid > 0) {
3782                logmsg "Warning: $server server unexpectedly alive\n";
3783                killpid($verbose, $pid);
3784            }
3785            unlink($pidfile) if(-f $pidfile);
3786        }
3787    }
3788
3789    # remove the test server commands file after each test
3790    unlink($FTPDCMD) if(-f $FTPDCMD);
3791
3792    # run the postcheck command
3793    my @postcheck= getpart("client", "postcheck");
3794    if(@postcheck) {
3795        $cmd = $postcheck[0];
3796        chomp $cmd;
3797        subVariables \$cmd;
3798        if($cmd) {
3799            logmsg "postcheck $cmd\n" if($verbose);
3800            my $rc = runclient("$cmd");
3801            # Must run the postcheck command in torture mode in order
3802            # to clean up, but the result can't be relied upon.
3803            if($rc != 0 && !$torture) {
3804                logmsg " postcheck FAILED\n";
3805                # timestamp test result verification end
3806                $timevrfyend{$testnum} = Time::HiRes::time() if($timestats);
3807                return 1;
3808            }
3809        }
3810    }
3811
3812    # restore environment variables that were modified
3813    if(%oldenv) {
3814        foreach my $var (keys %oldenv) {
3815            if($oldenv{$var} eq 'notset') {
3816                delete $ENV{$var} if($ENV{$var});
3817            }
3818            else {
3819                $ENV{$var} = "$oldenv{$var}";
3820            }
3821        }
3822    }
3823
3824    # Skip all the verification on torture tests
3825    if ($torture) {
3826        if(!$cmdres && !$keepoutfiles) {
3827            cleardir($LOGDIR);
3828        }
3829        # timestamp test result verification end
3830        $timevrfyend{$testnum} = Time::HiRes::time() if($timestats);
3831        return $cmdres;
3832    }
3833
3834    my @err = getpart("verify", "errorcode");
3835    my $errorcode = $err[0] || "0";
3836    my $ok="";
3837    my $res;
3838    chomp $errorcode;
3839    if (@validstdout) {
3840        # verify redirected stdout
3841        my @actual = loadarray($STDOUT);
3842
3843        # what parts to cut off from stdout
3844        my @stripfile = getpart("verify", "stripfile");
3845
3846        foreach my $strip (@stripfile) {
3847            chomp $strip;
3848            my @newgen;
3849            for(@actual) {
3850                eval $strip;
3851                if($_) {
3852                    push @newgen, $_;
3853                }
3854            }
3855            # this is to get rid of array entries that vanished (zero
3856            # length) because of replacements
3857            @actual = @newgen;
3858        }
3859
3860        # variable-replace in the stdout we have from the test case file
3861        @validstdout = fixarray(@validstdout);
3862
3863        # get all attributes
3864        my %hash = getpartattr("verify", "stdout");
3865
3866        # get the mode attribute
3867        my $filemode=$hash{'mode'};
3868        if($filemode && ($filemode eq "text") && $has_textaware) {
3869            # text mode when running on windows: fix line endings
3870            map s/\r\n/\n/g, @validstdout;
3871            map s/\n/\r\n/g, @validstdout;
3872        }
3873
3874        if($hash{'nonewline'}) {
3875            # Yes, we must cut off the final newline from the final line
3876            # of the protocol data
3877            chomp($validstdout[$#validstdout]);
3878        }
3879
3880        $res = compare($testnum, $testname, "stdout", \@actual, \@validstdout);
3881        if($res) {
3882            return 1;
3883        }
3884        $ok .= "s";
3885    }
3886    else {
3887        $ok .= "-"; # stdout not checked
3888    }
3889
3890    if(@protocol) {
3891        # Verify the sent request
3892        my @out = loadarray($SERVERIN);
3893
3894        # what to cut off from the live protocol sent by curl
3895        my @strip = getpart("verify", "strip");
3896
3897        my @protstrip=@protocol;
3898
3899        # check if there's any attributes on the verify/protocol section
3900        my %hash = getpartattr("verify", "protocol");
3901
3902        if($hash{'nonewline'}) {
3903            # Yes, we must cut off the final newline from the final line
3904            # of the protocol data
3905            chomp($protstrip[$#protstrip]);
3906        }
3907
3908        for(@strip) {
3909            # strip off all lines that match the patterns from both arrays
3910            chomp $_;
3911            @out = striparray( $_, \@out);
3912            @protstrip= striparray( $_, \@protstrip);
3913        }
3914
3915        # what parts to cut off from the protocol
3916        my @strippart = getpart("verify", "strippart");
3917        my $strip;
3918        for $strip (@strippart) {
3919            chomp $strip;
3920            for(@out) {
3921                eval $strip;
3922            }
3923        }
3924
3925        $res = compare($testnum, $testname, "protocol", \@out, \@protstrip);
3926        if($res) {
3927            return 1;
3928        }
3929
3930        $ok .= "p";
3931
3932    }
3933    else {
3934        $ok .= "-"; # protocol not checked
3935    }
3936
3937    if(!$replyattr{'nocheck'} && (@reply || $replyattr{'sendzero'})) {
3938        # verify the received data
3939        my @out = loadarray($CURLOUT);
3940        $res = compare($testnum, $testname, "data", \@out, \@reply);
3941        if ($res) {
3942            return 1;
3943        }
3944        $ok .= "d";
3945    }
3946    else {
3947        $ok .= "-"; # data not checked
3948    }
3949
3950    if(@upload) {
3951        # verify uploaded data
3952        my @out = loadarray("$LOGDIR/upload.$testnum");
3953        $res = compare($testnum, $testname, "upload", \@out, \@upload);
3954        if ($res) {
3955            return 1;
3956        }
3957        $ok .= "u";
3958    }
3959    else {
3960        $ok .= "-"; # upload not checked
3961    }
3962
3963    if(@proxyprot) {
3964        # Verify the sent proxy request
3965        my @out = loadarray($PROXYIN);
3966
3967        # what to cut off from the live protocol sent by curl, we use the
3968        # same rules as for <protocol>
3969        my @strip = getpart("verify", "strip");
3970
3971        my @protstrip=@proxyprot;
3972
3973        # check if there's any attributes on the verify/protocol section
3974        my %hash = getpartattr("verify", "proxy");
3975
3976        if($hash{'nonewline'}) {
3977            # Yes, we must cut off the final newline from the final line
3978            # of the protocol data
3979            chomp($protstrip[$#protstrip]);
3980        }
3981
3982        for(@strip) {
3983            # strip off all lines that match the patterns from both arrays
3984            chomp $_;
3985            @out = striparray( $_, \@out);
3986            @protstrip= striparray( $_, \@protstrip);
3987        }
3988
3989        # what parts to cut off from the protocol
3990        my @strippart = getpart("verify", "strippart");
3991        my $strip;
3992        for $strip (@strippart) {
3993            chomp $strip;
3994            for(@out) {
3995                eval $strip;
3996            }
3997        }
3998
3999        $res = compare($testnum, $testname, "proxy", \@out, \@protstrip);
4000        if($res) {
4001            return 1;
4002        }
4003
4004        $ok .= "P";
4005
4006    }
4007    else {
4008        $ok .= "-"; # protocol not checked
4009    }
4010
4011    my $outputok;
4012    for my $partsuffix (('', '1', '2', '3', '4')) {
4013        my @outfile=getpart("verify", "file".$partsuffix);
4014        if(@outfile || partexists("verify", "file".$partsuffix) ) {
4015            # we're supposed to verify a dynamically generated file!
4016            my %hash = getpartattr("verify", "file".$partsuffix);
4017
4018            my $filename=$hash{'name'};
4019            if(!$filename) {
4020                logmsg "ERROR: section verify=>file$partsuffix ".
4021                       "has no name attribute\n";
4022                stopservers($verbose);
4023                # timestamp test result verification end
4024                $timevrfyend{$testnum} = Time::HiRes::time() if($timestats);
4025                return -1;
4026            }
4027            my @generated=loadarray($filename);
4028
4029            # what parts to cut off from the file
4030            my @stripfile = getpart("verify", "stripfile".$partsuffix);
4031
4032            my $filemode=$hash{'mode'};
4033            if($filemode && ($filemode eq "text") && $has_textaware) {
4034                # text mode when running on windows: fix line endings
4035                map s/\r\n/\n/g, @outfile;
4036                map s/\n/\r\n/g, @outfile;
4037            }
4038
4039            my $strip;
4040            for $strip (@stripfile) {
4041                chomp $strip;
4042                my @newgen;
4043                for(@generated) {
4044                    eval $strip;
4045                    if($_) {
4046                        push @newgen, $_;
4047                    }
4048                }
4049                # this is to get rid of array entries that vanished (zero
4050                # length) because of replacements
4051                @generated = @newgen;
4052            }
4053
4054            @outfile = fixarray(@outfile);
4055
4056            $res = compare($testnum, $testname, "output ($filename)",
4057                           \@generated, \@outfile);
4058            if($res) {
4059                return 1;
4060            }
4061
4062            $outputok = 1; # output checked
4063        }
4064    }
4065    $ok .= ($outputok) ? "o" : "-"; # output checked or not
4066
4067    # accept multiple comma-separated error codes
4068    my @splerr = split(/ *, */, $errorcode);
4069    my $errok;
4070    foreach my $e (@splerr) {
4071        if($e == $cmdres) {
4072            # a fine error code
4073            $errok = 1;
4074            last;
4075        }
4076    }
4077
4078    if($errok) {
4079        $ok .= "e";
4080    }
4081    else {
4082        if(!$short) {
4083            logmsg sprintf("\n%s returned $cmdres, when expecting %s\n",
4084                           (!$tool)?"curl":$tool, $errorcode);
4085        }
4086        logmsg " exit FAILED\n";
4087        # timestamp test result verification end
4088        $timevrfyend{$testnum} = Time::HiRes::time() if($timestats);
4089        return 1;
4090    }
4091
4092    if($has_memory_tracking) {
4093        if(! -f $memdump) {
4094            logmsg "\n** ALERT! memory tracking with no output file?\n"
4095                if(!$cmdtype eq "perl");
4096        }
4097        else {
4098            my @memdata=`$memanalyze $memdump`;
4099            my $leak=0;
4100            for(@memdata) {
4101                if($_ ne "") {
4102                    # well it could be other memory problems as well, but
4103                    # we call it leak for short here
4104                    $leak=1;
4105                }
4106            }
4107            if($leak) {
4108                logmsg "\n** MEMORY FAILURE\n";
4109                logmsg @memdata;
4110                # timestamp test result verification end
4111                $timevrfyend{$testnum} = Time::HiRes::time() if($timestats);
4112                return 1;
4113            }
4114            else {
4115                $ok .= "m";
4116            }
4117        }
4118    }
4119    else {
4120        $ok .= "-"; # memory not checked
4121    }
4122
4123    if($valgrind) {
4124        if($usevalgrind) {
4125            unless(opendir(DIR, "$LOGDIR")) {
4126                logmsg "ERROR: unable to read $LOGDIR\n";
4127                # timestamp test result verification end
4128                $timevrfyend{$testnum} = Time::HiRes::time() if($timestats);
4129                return 1;
4130            }
4131            my @files = readdir(DIR);
4132            closedir(DIR);
4133            my $vgfile;
4134            foreach my $file (@files) {
4135                if($file =~ /^valgrind$testnum(\..*|)$/) {
4136                    $vgfile = $file;
4137                    last;
4138                }
4139            }
4140            if(!$vgfile) {
4141                logmsg "ERROR: valgrind log file missing for test $testnum\n";
4142                # timestamp test result verification end
4143                $timevrfyend{$testnum} = Time::HiRes::time() if($timestats);
4144                return 1;
4145            }
4146            my @e = valgrindparse($srcdir, $feature{'SSL'}, "$LOGDIR/$vgfile");
4147            if(@e && $e[0]) {
4148                if($automakestyle) {
4149                    logmsg "FAIL: $testnum - $testname - valgrind\n";
4150                }
4151                else {
4152                    logmsg " valgrind ERROR ";
4153                    logmsg @e;
4154                }
4155                # timestamp test result verification end
4156                $timevrfyend{$testnum} = Time::HiRes::time() if($timestats);
4157                return 1;
4158            }
4159            $ok .= "v";
4160        }
4161        else {
4162            if(!$short && !$disablevalgrind) {
4163                logmsg " valgrind SKIPPED\n";
4164            }
4165            $ok .= "-"; # skipped
4166        }
4167    }
4168    else {
4169        $ok .= "-"; # valgrind not checked
4170    }
4171    # add 'E' for event-based
4172    $ok .= $evbased ? "E" : "-";
4173
4174    logmsg "$ok " if(!$short);
4175
4176    my $sofar= time()-$start;
4177    my $esttotal = $sofar/$count * $total;
4178    my $estleft = $esttotal - $sofar;
4179    my $left=sprintf("remaining: %02d:%02d",
4180                     $estleft/60,
4181                     $estleft%60);
4182
4183    if(!$automakestyle) {
4184        logmsg sprintf("OK (%-3d out of %-3d, %s)\n", $count, $total, $left);
4185    }
4186    else {
4187        logmsg "PASS: $testnum - $testname\n";
4188    }
4189
4190    # the test succeeded, remove all log files
4191    if(!$keepoutfiles) {
4192        cleardir($LOGDIR);
4193    }
4194
4195    # timestamp test result verification end
4196    $timevrfyend{$testnum} = Time::HiRes::time() if($timestats);
4197
4198    return 0;
4199}
4200
4201#######################################################################
4202# Stop all running test servers
4203#
4204sub stopservers {
4205    my $verbose = $_[0];
4206    #
4207    # kill sockfilter processes for all pingpong servers
4208    #
4209    killallsockfilters($verbose);
4210    #
4211    # kill all server pids from %run hash clearing them
4212    #
4213    my $pidlist;
4214    foreach my $server (keys %run) {
4215        if($run{$server}) {
4216            if($verbose) {
4217                my $prev = 0;
4218                my $pids = $run{$server};
4219                foreach my $pid (split(' ', $pids)) {
4220                    if($pid != $prev) {
4221                        logmsg sprintf("* kill pid for %s => %d\n",
4222                            $server, $pid);
4223                        $prev = $pid;
4224                    }
4225                }
4226            }
4227            $pidlist .= "$run{$server} ";
4228            $run{$server} = 0;
4229        }
4230        $runcert{$server} = 0 if($runcert{$server});
4231    }
4232    killpid($verbose, $pidlist);
4233    #
4234    # cleanup all server pid files
4235    #
4236    foreach my $server (keys %serverpidfile) {
4237        my $pidfile = $serverpidfile{$server};
4238        my $pid = processexists($pidfile);
4239        if($pid > 0) {
4240            logmsg "Warning: $server server unexpectedly alive\n";
4241            killpid($verbose, $pid);
4242        }
4243        unlink($pidfile) if(-f $pidfile);
4244    }
4245}
4246
4247#######################################################################
4248# startservers() starts all the named servers
4249#
4250# Returns: string with error reason or blank for success
4251#
4252sub startservers {
4253    my @what = @_;
4254    my ($pid, $pid2);
4255    for(@what) {
4256        my (@whatlist) = split(/\s+/,$_);
4257        my $what = lc($whatlist[0]);
4258        $what =~ s/[^a-z0-9\/-]//g;
4259
4260        my $certfile;
4261        if($what =~ /^(ftp|http|imap|pop3|smtp)s((\d*)(-ipv6|-unix|))$/) {
4262            $certfile = ($whatlist[1]) ? $whatlist[1] : 'stunnel.pem';
4263        }
4264
4265        if(($what eq "pop3") ||
4266           ($what eq "ftp") ||
4267           ($what eq "imap") ||
4268           ($what eq "smtp")) {
4269            if($torture && $run{$what} &&
4270               !responsive_pingpong_server($what, "", $verbose)) {
4271                stopserver($what);
4272            }
4273            if(!$run{$what}) {
4274                ($pid, $pid2) = runpingpongserver($what, "", $verbose);
4275                if($pid <= 0) {
4276                    return "failed starting ". uc($what) ." server";
4277                }
4278                printf ("* pid $what => %d %d\n", $pid, $pid2) if($verbose);
4279                $run{$what}="$pid $pid2";
4280            }
4281        }
4282        elsif($what eq "ftp2") {
4283            if($torture && $run{'ftp2'} &&
4284               !responsive_pingpong_server("ftp", "2", $verbose)) {
4285                stopserver('ftp2');
4286            }
4287            if(!$run{'ftp2'}) {
4288                ($pid, $pid2) = runpingpongserver("ftp", "2", $verbose);
4289                if($pid <= 0) {
4290                    return "failed starting FTP2 server";
4291                }
4292                printf ("* pid ftp2 => %d %d\n", $pid, $pid2) if($verbose);
4293                $run{'ftp2'}="$pid $pid2";
4294            }
4295        }
4296        elsif($what eq "ftp-ipv6") {
4297            if($torture && $run{'ftp-ipv6'} &&
4298               !responsive_pingpong_server("ftp", "", $verbose, "ipv6")) {
4299                stopserver('ftp-ipv6');
4300            }
4301            if(!$run{'ftp-ipv6'}) {
4302                ($pid, $pid2) = runpingpongserver("ftp", "", $verbose, "ipv6");
4303                if($pid <= 0) {
4304                    return "failed starting FTP-IPv6 server";
4305                }
4306                logmsg sprintf("* pid ftp-ipv6 => %d %d\n", $pid,
4307                       $pid2) if($verbose);
4308                $run{'ftp-ipv6'}="$pid $pid2";
4309            }
4310        }
4311        elsif($what eq "gopher") {
4312            if($torture && $run{'gopher'} &&
4313               !responsive_http_server("gopher", $verbose, 0, $GOPHERPORT)) {
4314                stopserver('gopher');
4315            }
4316            if(!$run{'gopher'}) {
4317                ($pid, $pid2) = runhttpserver("gopher", $verbose, 0,
4318                                              $GOPHERPORT);
4319                if($pid <= 0) {
4320                    return "failed starting GOPHER server";
4321                }
4322                logmsg sprintf ("* pid gopher => %d %d\n", $pid, $pid2)
4323                    if($verbose);
4324                $run{'gopher'}="$pid $pid2";
4325            }
4326        }
4327        elsif($what eq "gopher-ipv6") {
4328            if($torture && $run{'gopher-ipv6'} &&
4329               !responsive_http_server("gopher", $verbose, "ipv6",
4330                                       $GOPHER6PORT)) {
4331                stopserver('gopher-ipv6');
4332            }
4333            if(!$run{'gopher-ipv6'}) {
4334                ($pid, $pid2) = runhttpserver("gopher", $verbose, "ipv6",
4335                                              $GOPHER6PORT);
4336                if($pid <= 0) {
4337                    return "failed starting GOPHER-IPv6 server";
4338                }
4339                logmsg sprintf("* pid gopher-ipv6 => %d %d\n", $pid,
4340                               $pid2) if($verbose);
4341                $run{'gopher-ipv6'}="$pid $pid2";
4342            }
4343        }
4344        elsif($what eq "http/2") {
4345            if(!$run{'http/2'}) {
4346                ($pid, $pid2) = runhttp2server($verbose, $HTTP2PORT);
4347                if($pid <= 0) {
4348                    return "failed starting HTTP/2 server";
4349                }
4350                logmsg sprintf ("* pid http/2 => %d %d\n", $pid, $pid2)
4351                    if($verbose);
4352                $run{'http/2'}="$pid $pid2";
4353            }
4354        }
4355        elsif($what eq "http") {
4356            if($torture && $run{'http'} &&
4357               !responsive_http_server("http", $verbose, 0, $HTTPPORT)) {
4358                stopserver('http');
4359            }
4360            if(!$run{'http'}) {
4361                ($pid, $pid2) = runhttpserver("http", $verbose, 0,
4362                                              $HTTPPORT);
4363                if($pid <= 0) {
4364                    return "failed starting HTTP server";
4365                }
4366                logmsg sprintf ("* pid http => %d %d\n", $pid, $pid2)
4367                    if($verbose);
4368                $run{'http'}="$pid $pid2";
4369            }
4370        }
4371        elsif($what eq "http-proxy") {
4372            if($torture && $run{'http-proxy'} &&
4373               !responsive_http_server("http", $verbose, "proxy",
4374                                       $HTTPPROXYPORT)) {
4375                stopserver('http-proxy');
4376            }
4377            if(!$run{'http-proxy'}) {
4378                ($pid, $pid2) = runhttpserver("http", $verbose, "proxy",
4379                                              $HTTPPROXYPORT);
4380                if($pid <= 0) {
4381                    return "failed starting HTTP-proxy server";
4382                }
4383                logmsg sprintf ("* pid http-proxy => %d %d\n", $pid, $pid2)
4384                    if($verbose);
4385                $run{'http-proxy'}="$pid $pid2";
4386            }
4387        }
4388        elsif($what eq "http-ipv6") {
4389            if($torture && $run{'http-ipv6'} &&
4390               !responsive_http_server("http", $verbose, "ipv6", $HTTP6PORT)) {
4391                stopserver('http-ipv6');
4392            }
4393            if(!$run{'http-ipv6'}) {
4394                ($pid, $pid2) = runhttpserver("http", $verbose, "ipv6",
4395                                              $HTTP6PORT);
4396                if($pid <= 0) {
4397                    return "failed starting HTTP-IPv6 server";
4398                }
4399                logmsg sprintf("* pid http-ipv6 => %d %d\n", $pid, $pid2)
4400                    if($verbose);
4401                $run{'http-ipv6'}="$pid $pid2";
4402            }
4403        }
4404        elsif($what eq "http-pipe") {
4405            if($torture && $run{'http-pipe'} &&
4406               !responsive_http_server("http", $verbose, "pipe",
4407                                       $HTTPPIPEPORT)) {
4408                stopserver('http-pipe');
4409            }
4410            if(!$run{'http-pipe'}) {
4411                ($pid, $pid2) = runhttpserver("http", $verbose, "pipe",
4412                                              $HTTPPIPEPORT);
4413                if($pid <= 0) {
4414                    return "failed starting HTTP-pipe server";
4415                }
4416                logmsg sprintf ("* pid http-pipe => %d %d\n", $pid, $pid2)
4417                    if($verbose);
4418                $run{'http-pipe'}="$pid $pid2";
4419            }
4420        }
4421        elsif($what eq "rtsp") {
4422            if($torture && $run{'rtsp'} &&
4423               !responsive_rtsp_server($verbose)) {
4424                stopserver('rtsp');
4425            }
4426            if(!$run{'rtsp'}) {
4427                ($pid, $pid2) = runrtspserver($verbose);
4428                if($pid <= 0) {
4429                    return "failed starting RTSP server";
4430                }
4431                printf ("* pid rtsp => %d %d\n", $pid, $pid2) if($verbose);
4432                $run{'rtsp'}="$pid $pid2";
4433            }
4434        }
4435        elsif($what eq "rtsp-ipv6") {
4436            if($torture && $run{'rtsp-ipv6'} &&
4437               !responsive_rtsp_server($verbose, "ipv6")) {
4438                stopserver('rtsp-ipv6');
4439            }
4440            if(!$run{'rtsp-ipv6'}) {
4441                ($pid, $pid2) = runrtspserver($verbose, "ipv6");
4442                if($pid <= 0) {
4443                    return "failed starting RTSP-IPv6 server";
4444                }
4445                logmsg sprintf("* pid rtsp-ipv6 => %d %d\n", $pid, $pid2)
4446                    if($verbose);
4447                $run{'rtsp-ipv6'}="$pid $pid2";
4448            }
4449        }
4450        elsif($what eq "ftps") {
4451            if(!$stunnel) {
4452                # we can't run ftps tests without stunnel
4453                return "no stunnel";
4454            }
4455            if(!$has_ssl) {
4456                # we can't run ftps tests if libcurl is SSL-less
4457                return "curl lacks SSL support";
4458            }
4459            if($runcert{'ftps'} && ($runcert{'ftps'} ne $certfile)) {
4460                # stop server when running and using a different cert
4461                stopserver('ftps');
4462            }
4463            if($torture && $run{'ftp'} &&
4464               !responsive_pingpong_server("ftp", "", $verbose)) {
4465                stopserver('ftp');
4466            }
4467            if(!$run{'ftp'}) {
4468                ($pid, $pid2) = runpingpongserver("ftp", "", $verbose);
4469                if($pid <= 0) {
4470                    return "failed starting FTP server";
4471                }
4472                printf ("* pid ftp => %d %d\n", $pid, $pid2) if($verbose);
4473                $run{'ftp'}="$pid $pid2";
4474            }
4475            if(!$run{'ftps'}) {
4476                ($pid, $pid2) = runftpsserver($verbose, "", $certfile);
4477                if($pid <= 0) {
4478                    return "failed starting FTPS server (stunnel)";
4479                }
4480                logmsg sprintf("* pid ftps => %d %d\n", $pid, $pid2)
4481                    if($verbose);
4482                $run{'ftps'}="$pid $pid2";
4483            }
4484        }
4485        elsif($what eq "file") {
4486            # we support it but have no server!
4487        }
4488        elsif($what eq "https") {
4489            if(!$stunnel) {
4490                # we can't run https tests without stunnel
4491                return "no stunnel";
4492            }
4493            if(!$has_ssl) {
4494                # we can't run https tests if libcurl is SSL-less
4495                return "curl lacks SSL support";
4496            }
4497            if($runcert{'https'} && ($runcert{'https'} ne $certfile)) {
4498                # stop server when running and using a different cert
4499                stopserver('https');
4500            }
4501            if($torture && $run{'http'} &&
4502               !responsive_http_server("http", $verbose, 0, $HTTPPORT)) {
4503                stopserver('http');
4504            }
4505            if(!$run{'http'}) {
4506                ($pid, $pid2) = runhttpserver("http", $verbose, 0,
4507                                              $HTTPPORT);
4508                if($pid <= 0) {
4509                    return "failed starting HTTP server";
4510                }
4511                printf ("* pid http => %d %d\n", $pid, $pid2) if($verbose);
4512                $run{'http'}="$pid $pid2";
4513            }
4514            if(!$run{'https'}) {
4515                ($pid, $pid2) = runhttpsserver($verbose, "", $certfile);
4516                if($pid <= 0) {
4517                    return "failed starting HTTPS server (stunnel)";
4518                }
4519                logmsg sprintf("* pid https => %d %d\n", $pid, $pid2)
4520                    if($verbose);
4521                $run{'https'}="$pid $pid2";
4522            }
4523        }
4524        elsif($what eq "httptls") {
4525            if(!$httptlssrv) {
4526                # for now, we can't run http TLS-EXT tests without gnutls-serv
4527                return "no gnutls-serv";
4528            }
4529            if($torture && $run{'httptls'} &&
4530               !responsive_httptls_server($verbose, "IPv4")) {
4531                stopserver('httptls');
4532            }
4533            if(!$run{'httptls'}) {
4534                ($pid, $pid2) = runhttptlsserver($verbose, "IPv4");
4535                if($pid <= 0) {
4536                    return "failed starting HTTPTLS server (gnutls-serv)";
4537                }
4538                logmsg sprintf("* pid httptls => %d %d\n", $pid, $pid2)
4539                    if($verbose);
4540                $run{'httptls'}="$pid $pid2";
4541            }
4542        }
4543        elsif($what eq "httptls-ipv6") {
4544            if(!$httptlssrv) {
4545                # for now, we can't run http TLS-EXT tests without gnutls-serv
4546                return "no gnutls-serv";
4547            }
4548            if($torture && $run{'httptls-ipv6'} &&
4549               !responsive_httptls_server($verbose, "ipv6")) {
4550                stopserver('httptls-ipv6');
4551            }
4552            if(!$run{'httptls-ipv6'}) {
4553                ($pid, $pid2) = runhttptlsserver($verbose, "ipv6");
4554                if($pid <= 0) {
4555                    return "failed starting HTTPTLS-IPv6 server (gnutls-serv)";
4556                }
4557                logmsg sprintf("* pid httptls-ipv6 => %d %d\n", $pid, $pid2)
4558                    if($verbose);
4559                $run{'httptls-ipv6'}="$pid $pid2";
4560            }
4561        }
4562        elsif($what eq "tftp") {
4563            if($torture && $run{'tftp'} &&
4564               !responsive_tftp_server("", $verbose)) {
4565                stopserver('tftp');
4566            }
4567            if(!$run{'tftp'}) {
4568                ($pid, $pid2) = runtftpserver("", $verbose);
4569                if($pid <= 0) {
4570                    return "failed starting TFTP server";
4571                }
4572                printf ("* pid tftp => %d %d\n", $pid, $pid2) if($verbose);
4573                $run{'tftp'}="$pid $pid2";
4574            }
4575        }
4576        elsif($what eq "tftp-ipv6") {
4577            if($torture && $run{'tftp-ipv6'} &&
4578               !responsive_tftp_server("", $verbose, "ipv6")) {
4579                stopserver('tftp-ipv6');
4580            }
4581            if(!$run{'tftp-ipv6'}) {
4582                ($pid, $pid2) = runtftpserver("", $verbose, "ipv6");
4583                if($pid <= 0) {
4584                    return "failed starting TFTP-IPv6 server";
4585                }
4586                printf("* pid tftp-ipv6 => %d %d\n", $pid, $pid2) if($verbose);
4587                $run{'tftp-ipv6'}="$pid $pid2";
4588            }
4589        }
4590        elsif($what eq "sftp" || $what eq "scp" || $what eq "socks4" || $what eq "socks5" ) {
4591            if(!$run{'ssh'}) {
4592                ($pid, $pid2) = runsshserver("", $verbose);
4593                if($pid <= 0) {
4594                    return "failed starting SSH server";
4595                }
4596                printf ("* pid ssh => %d %d\n", $pid, $pid2) if($verbose);
4597                $run{'ssh'}="$pid $pid2";
4598            }
4599            if($what eq "socks4" || $what eq "socks5") {
4600                if(!$run{'socks'}) {
4601                    ($pid, $pid2) = runsocksserver("", $verbose);
4602                    if($pid <= 0) {
4603                        return "failed starting socks server";
4604                    }
4605                    printf ("* pid socks => %d %d\n", $pid, $pid2) if($verbose);
4606                    $run{'socks'}="$pid $pid2";
4607                }
4608            }
4609            if($what eq "socks5") {
4610                if(!$sshdid) {
4611                    # Not an OpenSSH or SunSSH ssh daemon
4612                    logmsg "Not OpenSSH or SunSSH; socks5 tests need at least OpenSSH 3.7\n";
4613                    return "failed starting socks5 server";
4614                }
4615                elsif(($sshdid =~ /OpenSSH/) && ($sshdvernum < 370)) {
4616                    # Need OpenSSH 3.7 for socks5 - http://www.openssh.com/txt/release-3.7
4617                    logmsg "$sshdverstr insufficient; socks5 tests need at least OpenSSH 3.7\n";
4618                    return "failed starting socks5 server";
4619                }
4620                elsif(($sshdid =~ /SunSSH/)  && ($sshdvernum < 100)) {
4621                    # Need SunSSH 1.0 for socks5
4622                    logmsg "$sshdverstr insufficient; socks5 tests need at least SunSSH 1.0\n";
4623                    return "failed starting socks5 server";
4624                }
4625            }
4626        }
4627        elsif($what eq "http-unix") {
4628            if($torture && $run{'http-unix'} &&
4629               !responsive_http_server("http", $verbose, "unix", $HTTPUNIXPATH)) {
4630                stopserver('http-unix');
4631            }
4632            if(!$run{'http-unix'}) {
4633                ($pid, $pid2) = runhttpserver("http", $verbose, "unix",
4634                                              $HTTPUNIXPATH);
4635                if($pid <= 0) {
4636                    return "failed starting HTTP-unix server";
4637                }
4638                logmsg sprintf("* pid http-unix => %d %d\n", $pid, $pid2)
4639                    if($verbose);
4640                $run{'http-unix'}="$pid $pid2";
4641            }
4642        }
4643        elsif($what eq "none") {
4644            logmsg "* starts no server\n" if ($verbose);
4645        }
4646        else {
4647            warn "we don't support a server for $what";
4648            return "no server for $what";
4649        }
4650    }
4651    return 0;
4652}
4653
4654##############################################################################
4655# This function makes sure the right set of server is running for the
4656# specified test case. This is a useful design when we run single tests as not
4657# all servers need to run then!
4658#
4659# Returns: a string, blank if everything is fine or a reason why it failed
4660#
4661sub serverfortest {
4662    my ($testnum)=@_;
4663
4664    my @what = getpart("client", "server");
4665
4666    if(!$what[0]) {
4667        warn "Test case $testnum has no server(s) specified";
4668        return "no server specified";
4669    }
4670
4671    for(my $i = scalar(@what) - 1; $i >= 0; $i--) {
4672        my $srvrline = $what[$i];
4673        chomp $srvrline if($srvrline);
4674        if($srvrline =~ /^(\S+)((\s*)(.*))/) {
4675            my $server = "${1}";
4676            my $lnrest = "${2}";
4677            my $tlsext;
4678            if($server =~ /^(httptls)(\+)(ext|srp)(\d*)(-ipv6|)$/) {
4679                $server = "${1}${4}${5}";
4680                $tlsext = uc("TLS-${3}");
4681            }
4682            if(! grep /^\Q$server\E$/, @protocols) {
4683                if(substr($server,0,5) ne "socks") {
4684                    if($tlsext) {
4685                        return "curl lacks $tlsext support";
4686                    }
4687                    else {
4688                        return "curl lacks $server server support";
4689                    }
4690                }
4691            }
4692            $what[$i] = "$server$lnrest" if($tlsext);
4693        }
4694    }
4695
4696    return &startservers(@what);
4697}
4698
4699#######################################################################
4700# runtimestats displays test-suite run time statistics
4701#
4702sub runtimestats {
4703    my $lasttest = $_[0];
4704
4705    return if(not $timestats);
4706
4707    logmsg "\nTest suite total running time breakdown per task...\n\n";
4708
4709    my @timesrvr;
4710    my @timeprep;
4711    my @timetool;
4712    my @timelock;
4713    my @timevrfy;
4714    my @timetest;
4715    my $timesrvrtot = 0.0;
4716    my $timepreptot = 0.0;
4717    my $timetooltot = 0.0;
4718    my $timelocktot = 0.0;
4719    my $timevrfytot = 0.0;
4720    my $timetesttot = 0.0;
4721    my $counter;
4722
4723    for my $testnum (1 .. $lasttest) {
4724        if($timesrvrini{$testnum}) {
4725            $timesrvrtot += $timesrvrend{$testnum} - $timesrvrini{$testnum};
4726            $timepreptot +=
4727                (($timetoolini{$testnum} - $timeprepini{$testnum}) -
4728                 ($timesrvrend{$testnum} - $timesrvrini{$testnum}));
4729            $timetooltot += $timetoolend{$testnum} - $timetoolini{$testnum};
4730            $timelocktot += $timesrvrlog{$testnum} - $timetoolend{$testnum};
4731            $timevrfytot += $timevrfyend{$testnum} - $timesrvrlog{$testnum};
4732            $timetesttot += $timevrfyend{$testnum} - $timeprepini{$testnum};
4733            push @timesrvr, sprintf("%06.3f  %04d",
4734                $timesrvrend{$testnum} - $timesrvrini{$testnum}, $testnum);
4735            push @timeprep, sprintf("%06.3f  %04d",
4736                ($timetoolini{$testnum} - $timeprepini{$testnum}) -
4737                ($timesrvrend{$testnum} - $timesrvrini{$testnum}), $testnum);
4738            push @timetool, sprintf("%06.3f  %04d",
4739                $timetoolend{$testnum} - $timetoolini{$testnum}, $testnum);
4740            push @timelock, sprintf("%06.3f  %04d",
4741                $timesrvrlog{$testnum} - $timetoolend{$testnum}, $testnum);
4742            push @timevrfy, sprintf("%06.3f  %04d",
4743                $timevrfyend{$testnum} - $timesrvrlog{$testnum}, $testnum);
4744            push @timetest, sprintf("%06.3f  %04d",
4745                $timevrfyend{$testnum} - $timeprepini{$testnum}, $testnum);
4746        }
4747    }
4748
4749    {
4750        no warnings 'numeric';
4751        @timesrvr = sort { $b <=> $a } @timesrvr;
4752        @timeprep = sort { $b <=> $a } @timeprep;
4753        @timetool = sort { $b <=> $a } @timetool;
4754        @timelock = sort { $b <=> $a } @timelock;
4755        @timevrfy = sort { $b <=> $a } @timevrfy;
4756        @timetest = sort { $b <=> $a } @timetest;
4757    }
4758
4759    logmsg "Spent ". sprintf("%08.3f ", $timesrvrtot) .
4760           "seconds starting and verifying test harness servers.\n";
4761    logmsg "Spent ". sprintf("%08.3f ", $timepreptot) .
4762           "seconds reading definitions and doing test preparations.\n";
4763    logmsg "Spent ". sprintf("%08.3f ", $timetooltot) .
4764           "seconds actually running test tools.\n";
4765    logmsg "Spent ". sprintf("%08.3f ", $timelocktot) .
4766           "seconds awaiting server logs lock removal.\n";
4767    logmsg "Spent ". sprintf("%08.3f ", $timevrfytot) .
4768           "seconds verifying test results.\n";
4769    logmsg "Spent ". sprintf("%08.3f ", $timetesttot) .
4770           "seconds doing all of the above.\n";
4771
4772    $counter = 25;
4773    logmsg "\nTest server starting and verification time per test ".
4774        sprintf("(%s)...\n\n", (not $fullstats)?"top $counter":"full");
4775    logmsg "-time-  test\n";
4776    logmsg "------  ----\n";
4777    foreach my $txt (@timesrvr) {
4778        last if((not $fullstats) && (not $counter--));
4779        logmsg "$txt\n";
4780    }
4781
4782    $counter = 10;
4783    logmsg "\nTest definition reading and preparation time per test ".
4784        sprintf("(%s)...\n\n", (not $fullstats)?"top $counter":"full");
4785    logmsg "-time-  test\n";
4786    logmsg "------  ----\n";
4787    foreach my $txt (@timeprep) {
4788        last if((not $fullstats) && (not $counter--));
4789        logmsg "$txt\n";
4790    }
4791
4792    $counter = 25;
4793    logmsg "\nTest tool execution time per test ".
4794        sprintf("(%s)...\n\n", (not $fullstats)?"top $counter":"full");
4795    logmsg "-time-  test\n";
4796    logmsg "------  ----\n";
4797    foreach my $txt (@timetool) {
4798        last if((not $fullstats) && (not $counter--));
4799        logmsg "$txt\n";
4800    }
4801
4802    $counter = 15;
4803    logmsg "\nTest server logs lock removal time per test ".
4804        sprintf("(%s)...\n\n", (not $fullstats)?"top $counter":"full");
4805    logmsg "-time-  test\n";
4806    logmsg "------  ----\n";
4807    foreach my $txt (@timelock) {
4808        last if((not $fullstats) && (not $counter--));
4809        logmsg "$txt\n";
4810    }
4811
4812    $counter = 10;
4813    logmsg "\nTest results verification time per test ".
4814        sprintf("(%s)...\n\n", (not $fullstats)?"top $counter":"full");
4815    logmsg "-time-  test\n";
4816    logmsg "------  ----\n";
4817    foreach my $txt (@timevrfy) {
4818        last if((not $fullstats) && (not $counter--));
4819        logmsg "$txt\n";
4820    }
4821
4822    $counter = 50;
4823    logmsg "\nTotal time per test ".
4824        sprintf("(%s)...\n\n", (not $fullstats)?"top $counter":"full");
4825    logmsg "-time-  test\n";
4826    logmsg "------  ----\n";
4827    foreach my $txt (@timetest) {
4828        last if((not $fullstats) && (not $counter--));
4829        logmsg "$txt\n";
4830    }
4831
4832    logmsg "\n";
4833}
4834
4835#######################################################################
4836# Check options to this test program
4837#
4838
4839my $number=0;
4840my $fromnum=-1;
4841my @testthis;
4842while(@ARGV) {
4843    if ($ARGV[0] eq "-v") {
4844        # verbose output
4845        $verbose=1;
4846    }
4847    elsif($ARGV[0] =~ /^-b(.*)/) {
4848        my $portno=$1;
4849        if($portno =~ s/(\d+)$//) {
4850            $base = int $1;
4851        }
4852    }
4853    elsif ($ARGV[0] eq "-c") {
4854        # use this path to curl instead of default
4855        $DBGCURL=$CURL="\"$ARGV[1]\"";
4856        shift @ARGV;
4857    }
4858    elsif ($ARGV[0] eq "-vc") {
4859        # use this path to a curl used to verify servers
4860
4861        # Particularly useful when you introduce a crashing bug somewhere in
4862        # the development version as then it won't be able to run any tests
4863        # since it can't verify the servers!
4864
4865        $VCURL="\"$ARGV[1]\"";
4866        shift @ARGV;
4867    }
4868    elsif ($ARGV[0] eq "-d") {
4869        # have the servers display protocol output
4870        $debugprotocol=1;
4871    }
4872    elsif ($ARGV[0] eq "-g") {
4873        # run this test with gdb
4874        $gdbthis=1;
4875    }
4876    elsif ($ARGV[0] eq "-gw") {
4877        # run this test with windowed gdb
4878        $gdbthis=1;
4879        $gdbxwin=1;
4880    }
4881    elsif($ARGV[0] eq "-s") {
4882        # short output
4883        $short=1;
4884    }
4885    elsif($ARGV[0] eq "-am") {
4886        # automake-style output
4887        $short=1;
4888        $automakestyle=1;
4889    }
4890    elsif($ARGV[0] eq "-n") {
4891        # no valgrind
4892        undef $valgrind;
4893    }
4894    elsif($ARGV[0] =~ /^-t(.*)/) {
4895        # torture
4896        $torture=1;
4897        my $xtra = $1;
4898
4899        if($xtra =~ s/(\d+)$//) {
4900            $tortalloc = $1;
4901        }
4902        # we undef valgrind to make this fly in comparison
4903        undef $valgrind;
4904    }
4905    elsif($ARGV[0] eq "-a") {
4906        # continue anyway, even if a test fail
4907        $anyway=1;
4908    }
4909    elsif($ARGV[0] eq "-e") {
4910        # run the tests cases event based if possible
4911        $run_event_based=1;
4912    }
4913    elsif($ARGV[0] eq "-p") {
4914        $postmortem=1;
4915    }
4916    elsif($ARGV[0] eq "-l") {
4917        # lists the test case names only
4918        $listonly=1;
4919    }
4920    elsif($ARGV[0] eq "-k") {
4921        # keep stdout and stderr files after tests
4922        $keepoutfiles=1;
4923    }
4924    elsif($ARGV[0] eq "-r") {
4925        # run time statistics needs Time::HiRes
4926        if($Time::HiRes::VERSION) {
4927            keys(%timeprepini) = 1000;
4928            keys(%timesrvrini) = 1000;
4929            keys(%timesrvrend) = 1000;
4930            keys(%timetoolini) = 1000;
4931            keys(%timetoolend) = 1000;
4932            keys(%timesrvrlog) = 1000;
4933            keys(%timevrfyend) = 1000;
4934            $timestats=1;
4935            $fullstats=0;
4936        }
4937    }
4938    elsif($ARGV[0] eq "-rf") {
4939        # run time statistics needs Time::HiRes
4940        if($Time::HiRes::VERSION) {
4941            keys(%timeprepini) = 1000;
4942            keys(%timesrvrini) = 1000;
4943            keys(%timesrvrend) = 1000;
4944            keys(%timetoolini) = 1000;
4945            keys(%timetoolend) = 1000;
4946            keys(%timesrvrlog) = 1000;
4947            keys(%timevrfyend) = 1000;
4948            $timestats=1;
4949            $fullstats=1;
4950        }
4951    }
4952    elsif(($ARGV[0] eq "-h") || ($ARGV[0] eq "--help")) {
4953        # show help text
4954        print <<EOHELP
4955Usage: runtests.pl [options] [test selection(s)]
4956  -a       continue even if a test fails
4957  -bN      use base port number N for test servers (default $base)
4958  -c path  use this curl executable
4959  -d       display server debug info
4960  -g       run the test case with gdb
4961  -gw      run the test case with gdb as a windowed application
4962  -h       this help text
4963  -k       keep stdout and stderr files present after tests
4964  -l       list all test case names/descriptions
4965  -n       no valgrind
4966  -p       print log file contents when a test fails
4967  -r       run time statistics
4968  -rf      full run time statistics
4969  -s       short output
4970  -am      automake style output PASS/FAIL: [number] [name]
4971  -t[N]    torture (simulate memory alloc failures); N means fail Nth alloc
4972  -v       verbose output
4973  -vc path use this curl only to verify the existing servers
4974  [num]    like "5 6 9" or " 5 to 22 " to run those tests only
4975  [!num]   like "!5 !6 !9" to disable those tests
4976  [keyword] like "IPv6" to select only tests containing the key word
4977  [!keyword] like "!cookies" to disable any tests containing the key word
4978EOHELP
4979    ;
4980        exit;
4981    }
4982    elsif($ARGV[0] =~ /^(\d+)/) {
4983        $number = $1;
4984        if($fromnum >= 0) {
4985            for($fromnum .. $number) {
4986                push @testthis, $_;
4987            }
4988            $fromnum = -1;
4989        }
4990        else {
4991            push @testthis, $1;
4992        }
4993    }
4994    elsif($ARGV[0] =~ /^to$/i) {
4995        $fromnum = $number+1;
4996    }
4997    elsif($ARGV[0] =~ /^!(\d+)/) {
4998        $fromnum = -1;
4999        $disabled{$1}=$1;
5000    }
5001    elsif($ARGV[0] =~ /^!(.+)/) {
5002        $disabled_keywords{$1}=$1;
5003    }
5004    elsif($ARGV[0] =~ /^([-[{a-zA-Z].*)/) {
5005        $enabled_keywords{$1}=$1;
5006    }
5007    else {
5008        print "Unknown option: $ARGV[0]\n";
5009        exit;
5010    }
5011    shift @ARGV;
5012}
5013
5014if(@testthis && ($testthis[0] ne "")) {
5015    $TESTCASES=join(" ", @testthis);
5016}
5017
5018if($valgrind) {
5019    # we have found valgrind on the host, use it
5020
5021    # verify that we can invoke it fine
5022    my $code = runclient("valgrind >/dev/null 2>&1");
5023
5024    if(($code>>8) != 1) {
5025        #logmsg "Valgrind failure, disable it\n";
5026        undef $valgrind;
5027    } else {
5028
5029        # since valgrind 2.1.x, '--tool' option is mandatory
5030        # use it, if it is supported by the version installed on the system
5031        runclient("valgrind --help 2>&1 | grep -- --tool > /dev/null 2>&1");
5032        if (($? >> 8)==0) {
5033            $valgrind_tool="--tool=memcheck";
5034        }
5035        open(C, "<$CURL");
5036        my $l = <C>;
5037        if($l =~ /^\#\!/) {
5038            # A shell script. This is typically when built with libtool,
5039            $valgrind="../libtool --mode=execute $valgrind";
5040        }
5041        close(C);
5042
5043        # valgrind 3 renamed the --logfile option to --log-file!!!
5044        my $ver=join(' ', runclientoutput("valgrind --version"));
5045        # cut off all but digits and dots
5046        $ver =~ s/[^0-9.]//g;
5047
5048        if($ver =~ /^(\d+)/) {
5049            $ver = $1;
5050            if($ver >= 3) {
5051                $valgrind_logfile="--log-file";
5052            }
5053        }
5054    }
5055}
5056
5057if ($gdbthis) {
5058    # open the executable curl and read the first 4 bytes of it
5059    open(CHECK, "<$CURL");
5060    my $c;
5061    sysread CHECK, $c, 4;
5062    close(CHECK);
5063    if($c eq "#! /") {
5064        # A shell script. This is typically when built with libtool,
5065        $libtool = 1;
5066        $gdb = "../libtool --mode=execute gdb";
5067    }
5068}
5069
5070$HTTPPORT        = $base++; # HTTP server port
5071$HTTPSPORT       = $base++; # HTTPS (stunnel) server port
5072$FTPPORT         = $base++; # FTP server port
5073$FTPSPORT        = $base++; # FTPS (stunnel) server port
5074$HTTP6PORT       = $base++; # HTTP IPv6 server port
5075$FTP2PORT        = $base++; # FTP server 2 port
5076$FTP6PORT        = $base++; # FTP IPv6 port
5077$TFTPPORT        = $base++; # TFTP (UDP) port
5078$TFTP6PORT       = $base++; # TFTP IPv6 (UDP) port
5079$SSHPORT         = $base++; # SSH (SCP/SFTP) port
5080$SOCKSPORT       = $base++; # SOCKS port
5081$POP3PORT        = $base++; # POP3 server port
5082$POP36PORT       = $base++; # POP3 IPv6 server port
5083$IMAPPORT        = $base++; # IMAP server port
5084$IMAP6PORT       = $base++; # IMAP IPv6 server port
5085$SMTPPORT        = $base++; # SMTP server port
5086$SMTP6PORT       = $base++; # SMTP IPv6 server port
5087$RTSPPORT        = $base++; # RTSP server port
5088$RTSP6PORT       = $base++; # RTSP IPv6 server port
5089$GOPHERPORT      = $base++; # Gopher IPv4 server port
5090$GOPHER6PORT     = $base++; # Gopher IPv6 server port
5091$HTTPTLSPORT     = $base++; # HTTP TLS (non-stunnel) server port
5092$HTTPTLS6PORT    = $base++; # HTTP TLS (non-stunnel) IPv6 server port
5093$HTTPPROXYPORT   = $base++; # HTTP proxy port, when using CONNECT
5094$HTTPPIPEPORT    = $base++; # HTTP pipelining port
5095$HTTP2PORT       = $base++; # HTTP/2 port
5096$HTTPUNIXPATH    = 'http.sock'; # HTTP server Unix domain socket path
5097
5098#######################################################################
5099# clear and create logging directory:
5100#
5101
5102cleardir($LOGDIR);
5103mkdir($LOGDIR, 0777);
5104
5105#######################################################################
5106# initialize some variables
5107#
5108
5109get_disttests();
5110init_serverpidfile_hash();
5111
5112#######################################################################
5113# Output curl version and host info being tested
5114#
5115
5116if(!$listonly) {
5117    checksystem();
5118}
5119
5120#######################################################################
5121# Fetch all disabled tests, if there are any
5122#
5123
5124sub disabledtests {
5125    my ($file) = @_;
5126
5127    if(open(D, "<$file")) {
5128        while(<D>) {
5129            if(/^ *\#/) {
5130                # allow comments
5131                next;
5132            }
5133            if($_ =~ /(\d+)/) {
5134                $disabled{$1}=$1; # disable this test number
5135            }
5136        }
5137        close(D);
5138    }
5139}
5140
5141# globally disabled tests
5142disabledtests("$TESTDIR/DISABLED");
5143
5144# locally disabled tests, ignored by git etc
5145disabledtests("$TESTDIR/DISABLED.local");
5146
5147#######################################################################
5148# If 'all' tests are requested, find out all test numbers
5149#
5150
5151if ( $TESTCASES eq "all") {
5152    # Get all commands and find out their test numbers
5153    opendir(DIR, $TESTDIR) || die "can't opendir $TESTDIR: $!";
5154    my @cmds = grep { /^test([0-9]+)$/ && -f "$TESTDIR/$_" } readdir(DIR);
5155    closedir(DIR);
5156
5157    $TESTCASES=""; # start with no test cases
5158
5159    # cut off everything but the digits
5160    for(@cmds) {
5161        $_ =~ s/[a-z\/\.]*//g;
5162    }
5163    # sort the numbers from low to high
5164    foreach my $n (sort { $a <=> $b } @cmds) {
5165        if($disabled{$n}) {
5166            # skip disabled test cases
5167            my $why = "configured as DISABLED";
5168            $skipped++;
5169            $skipped{$why}++;
5170            $teststat[$n]=$why; # store reason for this test case
5171            next;
5172        }
5173        $TESTCASES .= " $n";
5174    }
5175}
5176else {
5177    my $verified="";
5178    map {
5179        if (-e "$TESTDIR/test$_") {
5180            $verified.="$_ ";
5181        }
5182    } split(" ", $TESTCASES);
5183    if($verified eq "") {
5184        print "No existing test cases were specified\n";
5185        exit;
5186    }
5187    $TESTCASES = $verified;
5188}
5189
5190#######################################################################
5191# Start the command line log
5192#
5193open(CMDLOG, ">$CURLLOG") ||
5194    logmsg "can't log command lines to $CURLLOG\n";
5195
5196#######################################################################
5197
5198# Display the contents of the given file.  Line endings are canonicalized
5199# and excessively long files are elided
5200sub displaylogcontent {
5201    my ($file)=@_;
5202    if(open(SINGLE, "<$file")) {
5203        my $linecount = 0;
5204        my $truncate;
5205        my @tail;
5206        while(my $string = <SINGLE>) {
5207            $string =~ s/\r\n/\n/g;
5208            $string =~ s/[\r\f\032]/\n/g;
5209            $string .= "\n" unless ($string =~ /\n$/);
5210            $string =~ tr/\n//;
5211            for my $line (split("\n", $string)) {
5212                $line =~ s/\s*\!$//;
5213                if ($truncate) {
5214                    push @tail, " $line\n";
5215                } else {
5216                    logmsg " $line\n";
5217                }
5218                $linecount++;
5219                $truncate = $linecount > 1000;
5220            }
5221        }
5222        if(@tail) {
5223            my $tailshow = 200;
5224            my $tailskip = 0;
5225            my $tailtotal = scalar @tail;
5226            if($tailtotal > $tailshow) {
5227                $tailskip = $tailtotal - $tailshow;
5228                logmsg "=== File too long: $tailskip lines omitted here\n";
5229            }
5230            for($tailskip .. $tailtotal-1) {
5231                logmsg "$tail[$_]";
5232            }
5233        }
5234        close(SINGLE);
5235    }
5236}
5237
5238sub displaylogs {
5239    my ($testnum)=@_;
5240    opendir(DIR, "$LOGDIR") ||
5241        die "can't open dir: $!";
5242    my @logs = readdir(DIR);
5243    closedir(DIR);
5244
5245    logmsg "== Contents of files in the $LOGDIR/ dir after test $testnum\n";
5246    foreach my $log (sort @logs) {
5247        if($log =~ /\.(\.|)$/) {
5248            next; # skip "." and ".."
5249        }
5250        if($log =~ /^\.nfs/) {
5251            next; # skip ".nfs"
5252        }
5253        if(($log eq "memdump") || ($log eq "core")) {
5254            next; # skip "memdump" and  "core"
5255        }
5256        if((-d "$LOGDIR/$log") || (! -s "$LOGDIR/$log")) {
5257            next; # skip directory and empty files
5258        }
5259        if(($log =~ /^stdout\d+/) && ($log !~ /^stdout$testnum/)) {
5260            next; # skip stdoutNnn of other tests
5261        }
5262        if(($log =~ /^stderr\d+/) && ($log !~ /^stderr$testnum/)) {
5263            next; # skip stderrNnn of other tests
5264        }
5265        if(($log =~ /^upload\d+/) && ($log !~ /^upload$testnum/)) {
5266            next; # skip uploadNnn of other tests
5267        }
5268        if(($log =~ /^curl\d+\.out/) && ($log !~ /^curl$testnum\.out/)) {
5269            next; # skip curlNnn.out of other tests
5270        }
5271        if(($log =~ /^test\d+\.txt/) && ($log !~ /^test$testnum\.txt/)) {
5272            next; # skip testNnn.txt of other tests
5273        }
5274        if(($log =~ /^file\d+\.txt/) && ($log !~ /^file$testnum\.txt/)) {
5275            next; # skip fileNnn.txt of other tests
5276        }
5277        if(($log =~ /^netrc\d+/) && ($log !~ /^netrc$testnum/)) {
5278            next; # skip netrcNnn of other tests
5279        }
5280        if(($log =~ /^trace\d+/) && ($log !~ /^trace$testnum/)) {
5281            next; # skip traceNnn of other tests
5282        }
5283        if(($log =~ /^valgrind\d+/) && ($log !~ /^valgrind$testnum(\..*|)$/)) {
5284            next; # skip valgrindNnn of other tests
5285        }
5286        logmsg "=== Start of file $log\n";
5287        displaylogcontent("$LOGDIR/$log");
5288        logmsg "=== End of file $log\n";
5289    }
5290}
5291
5292#######################################################################
5293# The main test-loop
5294#
5295
5296my $failed;
5297my $testnum;
5298my $ok=0;
5299my $total=0;
5300my $lasttest=0;
5301my @at = split(" ", $TESTCASES);
5302my $count=0;
5303
5304$start = time();
5305
5306foreach $testnum (@at) {
5307
5308    $lasttest = $testnum if($testnum > $lasttest);
5309    $count++;
5310
5311    my $error = singletest($run_event_based, $testnum, $count, scalar(@at));
5312    if($error < 0) {
5313        # not a test we can run
5314        next;
5315    }
5316
5317    $total++; # number of tests we've run
5318
5319    if($error>0) {
5320        $failed.= "$testnum ";
5321        if($postmortem) {
5322            # display all files in log/ in a nice way
5323            displaylogs($testnum);
5324        }
5325        if(!$anyway) {
5326            # a test failed, abort
5327            logmsg "\n - abort tests\n";
5328            last;
5329        }
5330    }
5331    elsif(!$error) {
5332        $ok++; # successful test counter
5333    }
5334
5335    # loop for next test
5336}
5337
5338my $sofar = time() - $start;
5339
5340#######################################################################
5341# Close command log
5342#
5343close(CMDLOG);
5344
5345# Tests done, stop the servers
5346stopservers($verbose);
5347
5348my $all = $total + $skipped;
5349
5350runtimestats($lasttest);
5351
5352if($total) {
5353    logmsg sprintf("TESTDONE: $ok tests out of $total reported OK: %d%%\n",
5354                   $ok/$total*100);
5355
5356    if($ok != $total) {
5357        logmsg "TESTFAIL: These test cases failed: $failed\n";
5358    }
5359}
5360else {
5361    logmsg "TESTFAIL: No tests were performed\n";
5362}
5363
5364if($all) {
5365    logmsg "TESTDONE: $all tests were considered during ".
5366        sprintf("%.0f", $sofar) ." seconds.\n";
5367}
5368
5369if($skipped && !$short) {
5370    my $s=0;
5371    logmsg "TESTINFO: $skipped tests were skipped due to these restraints:\n";
5372
5373    for(keys %skipped) {
5374        my $r = $_;
5375        printf "TESTINFO: \"%s\" %d times (", $r, $skipped{$_};
5376
5377        # now show all test case numbers that had this reason for being
5378        # skipped
5379        my $c=0;
5380        my $max = 9;
5381        for(0 .. scalar @teststat) {
5382            my $t = $_;
5383            if($teststat[$_] && ($teststat[$_] eq $r)) {
5384                if($c < $max) {
5385                    logmsg ", " if($c);
5386                    logmsg $_;
5387                }
5388                $c++;
5389            }
5390        }
5391        if($c > $max) {
5392            logmsg " and ".($c-$max)." more";
5393        }
5394        logmsg ")\n";
5395    }
5396}
5397
5398if($total && ($ok != $total)) {
5399    exit 1;
5400}
5401