File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / tests / runtests.pl
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 3 10:01:16 2020 UTC (5 years ago) by misho
Branches: curl, MAIN
CVS tags: v7_70_0p4, HEAD
curl

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>