diff --git a/tests/README b/tests/README index 633eac584..4d7ce9ab7 100644 --- a/tests/README +++ b/tests/README @@ -74,6 +74,7 @@ The curl Test Suite - TCP/9013 for HTTP proxy server for CONNECT - TCP/9014 for HTTP pipelining server - TCP/9015 for HTTP/2 server + - TCP/9016 for DICT server 1.3 Test servers diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc index 3f64f86c9..1ba1c0e6f 100644 --- a/tests/data/Makefile.inc +++ b/tests/data/Makefile.inc @@ -154,7 +154,7 @@ test1416 test1417 test1418 test1419 test1420 test1421 test1422 test1423 \ test1424 test1425 test1426 \ test1428 test1429 test1430 test1431 test1432 test1433 test1434 test1435 \ test1436 test1437 test1438 test1439 test1440 test1441 test1442 test1443 \ -test1444 test1445 test1446 \ +test1444 test1445 test1446 test1450 \ \ test1500 test1501 test1502 test1503 test1504 test1505 test1506 test1507 \ test1508 test1509 test1510 test1511 test1512 test1513 test1514 test1515 \ diff --git a/tests/data/test1450 b/tests/data/test1450 new file mode 100644 index 000000000..ca3ac8849 --- /dev/null +++ b/tests/data/test1450 @@ -0,0 +1,34 @@ + + + +DICT + + + +# +# Server-side + + + +# +# Client-side + + +dict + + +dict + + +Basic DICT lookup + + +dict://%HOSTIP:%DICTPORT/d:basic + + + +# +# Verify data after the test has been "shot" + + + diff --git a/tests/dictserver.py b/tests/dictserver.py new file mode 100755 index 000000000..45cc15505 --- /dev/null +++ b/tests/dictserver.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +""" DICT server """ + +from __future__ import (absolute_import, division, print_function, + unicode_literals) +import argparse +import os +import sys +import logging +try: # Python 2 + import SocketServer as socketserver +except ImportError: # Python 3 + import socketserver + + +log = logging.getLogger(__name__) +HOST = "localhost" + +# The strings that indicate the test framework is checking our aliveness +VERIFIED_REQ = b"verifiedserver" +VERIFIED_RSP = "WE ROOLZ: {pid}" + + +def dictserver(options): + """ + Starts up a TCP server with a DICT handler and serves DICT requests + forever. + """ + if options.pidfile: + pid = os.getpid() + with open(options.pidfile, "w") as f: + f.write("{0}".format(pid)) + + local_bind = (HOST, options.port) + log.info("[DICT] Listening on %s", local_bind) + + # Need to set the allow_reuse on the class, not on the instance. + socketserver.TCPServer.allow_reuse_address = True + server = socketserver.TCPServer(local_bind, DictHandler) + server.serve_forever() + + return ScriptRC.SUCCESS + + +class DictHandler(socketserver.BaseRequestHandler): + """Handler class for DICT connections. + + """ + def handle(self): + """ + Simple function which responds to all queries with a 552. + """ + + # First, send a response to allow the server to continue. + rsp = "220 dictserver \n" + self.request.sendall(rsp.encode("utf-8")) + + # Receive the request. + data = self.request.recv(1024).strip() + log.debug("[DICT] Incoming data: %r", data) + + if VERIFIED_REQ in data: + log.debug("[DICT] Received verification request from test " + "framework") + response_data = VERIFIED_RSP.format(pid=os.getpid()) + else: + log.debug("[DICT] Received normal request") + response_data = "No matches" + + # Send back a failure to find. + response = "552 {0}\n".format(response_data) + log.debug("[DICT] Responding with %r", response) + self.request.sendall(response.encode("utf-8")) + + +def get_options(): + parser = argparse.ArgumentParser() + + parser.add_argument("--port", action="store", default=9016, + type=int, help="port to listen on") + parser.add_argument("--verbose", action="store", type=int, default=0, + help="verbose output") + parser.add_argument("--pidfile", action="store", + help="file name for the PID") + parser.add_argument("--logfile", action="store", + help="file name for the log") + parser.add_argument("--srcdir", action="store", help="test directory") + parser.add_argument("--id", action="store", help="server ID") + parser.add_argument("--ipv4", action="store_true", default=0, + help="IPv4 flag") + + return parser.parse_args() + + +def setup_logging(options): + """ + Set up logging from the command line options + """ + root_logger = logging.getLogger() + add_stdout = False + + formatter = logging.Formatter("%(asctime)s %(levelname)-5.5s %(message)s") + + # Write out to a logfile + if options.logfile: + handler = logging.FileHandler(options.logfile, mode="w") + handler.setFormatter(formatter) + handler.setLevel(logging.DEBUG) + root_logger.addHandler(handler) + else: + # The logfile wasn't specified. Add a stdout logger. + add_stdout = True + + if options.verbose: + # Add a stdout logger as well in verbose mode + root_logger.setLevel(logging.DEBUG) + add_stdout = True + else: + root_logger.setLevel(logging.INFO) + + if add_stdout: + stdout_handler = logging.StreamHandler(sys.stdout) + stdout_handler.setFormatter(formatter) + stdout_handler.setLevel(logging.DEBUG) + root_logger.addHandler(stdout_handler) + + +class ScriptRC(object): + """Enum for script return codes""" + SUCCESS = 0 + FAILURE = 1 + EXCEPTION = 2 + + +class ScriptException(Exception): + pass + + +if __name__ == '__main__': + # Get the options from the user. + options = get_options() + + # Setup logging using the user options + setup_logging(options) + + # Run main script. + try: + rc = dictserver(options) + except Exception as e: + log.exception(e) + rc = ScriptRC.EXCEPTION + + log.info("[DICT] Returning %d", rc) + sys.exit(rc) diff --git a/tests/runtests.pl b/tests/runtests.pl index 05a8bb469..1f65c42c3 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -145,6 +145,7 @@ my $HTTPPROXYPORT; # HTTP proxy port, when using CONNECT my $HTTPPIPEPORT; # HTTP pipelining port my $HTTPUNIXPATH; # HTTP server Unix domain socket path my $HTTP2PORT; # HTTP/2 server port +my $DICTPORT; # DICT server port my $srcdir = $ENV{'srcdir'} || '.'; my $CURL="../src/curl".exe_ext(); # what curl executable to run on the tests @@ -378,7 +379,8 @@ sub init_serverpidfile_hash { } } } - for my $proto (('tftp', 'sftp', 'socks', 'ssh', 'rtsp', 'gopher', 'httptls')) { + for my $proto (('tftp', 'sftp', 'socks', 'ssh', 'rtsp', 'gopher', 'httptls', + 'dict')) { for my $ipvnum ((4, 6)) { for my $idnum ((1, 2)) { my $serv = servername_id($proto, $ipvnum, $idnum); @@ -1143,7 +1145,8 @@ my %protofunc = ('http' => \&verifyhttp, 'ssh' => \&verifyssh, 'socks' => \&verifysocks, 'gopher' => \&verifyhttp, - 'httptls' => \&verifyhttptls); + 'httptls' => \&verifyhttptls, + 'dict' => \&verifyftp); sub verifyserver { my ($proto, $ipvnum, $idnum, $ip, $port) = @_; @@ -2164,6 +2167,82 @@ sub runsocksserver { return ($pid2, $sshpid); } +####################################################################### +# start the dict server +# +sub rundictserver { + my ($verbose, $alt, $port) = @_; + my $proto = "dict"; + my $ip = $HOSTIP; + my $ipvnum = 4; + my $idnum = 1; + my $server; + my $srvrname; + my $pidfile; + my $logfile; + my $flags = ""; + + if($alt eq "ipv6") { + # No IPv6 + } + + $server = servername_id($proto, $ipvnum, $idnum); + + $pidfile = $serverpidfile{$server}; + + # don't retry if the server doesn't work + if ($doesntrun{$pidfile}) { + return (0,0); + } + + my $pid = processexists($pidfile); + if($pid > 0) { + stopserver($server, "$pid"); + } + unlink($pidfile) if(-f $pidfile); + + $srvrname = servername_str($proto, $ipvnum, $idnum); + + $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum); + + $flags .= "--verbose 1 " if($debugprotocol); + $flags .= "--pidfile \"$pidfile\" --logfile \"$logfile\" "; + $flags .= "--id $idnum " if($idnum > 1); + $flags .= "--port $port --srcdir \"$srcdir\""; + + my $cmd = "$srcdir/dictserver.py $flags"; + my ($dictpid, $pid2) = startnew($cmd, $pidfile, 15, 0); + + if($dictpid <= 0 || !pidexists($dictpid)) { + # it is NOT alive + logmsg "RUN: failed to start the $srvrname server\n"; + stopserver($server, "$pid2"); + displaylogs($testnumcheck); + $doesntrun{$pidfile} = 1; + return (0,0); + } + + # Server is up. Verify that we can speak to it. + my $pid3 = verifyserver($proto, $ipvnum, $idnum, $ip, $port); + if(!$pid3) { + logmsg "RUN: $srvrname server failed verification\n"; + # failed to talk to it properly. Kill the server and return failure + stopserver($server, "$dictpid $pid2"); + displaylogs($testnumcheck); + $doesntrun{$pidfile} = 1; + return (0,0); + } + $pid2 = $pid3; + + if($verbose) { + logmsg "RUN: $srvrname server is now running PID $dictpid\n"; + } + + sleep(1); + + return ($dictpid, $pid2); +} + ####################################################################### # Single shot http and gopher server responsiveness test. This should only # be used to verify that a server present in %run hash is still functional @@ -2762,6 +2841,8 @@ sub subVariables { $$thing =~ s/%TFTP6PORT/$TFTP6PORT/g; $$thing =~ s/%TFTPPORT/$TFTPPORT/g; + $$thing =~ s/%DICTPORT/$DICTPORT/g; + # server Unix domain socket paths $$thing =~ s/%HTTPUNIXPATH/$HTTPUNIXPATH/g; @@ -2796,7 +2877,7 @@ sub subVariables { # HTTP2 - $$thing =~ s/%H2CVER/$h2cver/g; + $$thing =~ s/%H2CVER/$h2cver/g; } sub fixarray { @@ -4603,6 +4684,17 @@ sub startservers { $run{'http-unix'}="$pid $pid2"; } } + elsif($what eq "dict") { + if(!$run{'dict'}) { + ($pid, $pid2) = rundictserver($verbose, "", $DICTPORT); + if($pid <= 0) { + return "failed starting DICT server"; + } + logmsg sprintf ("* pid DICT => %d %d\n", $pid, $pid2) + if($verbose); + $run{'dict'}="$pid $pid2"; + } + } elsif($what eq "none") { logmsg "* starts no server\n" if ($verbose); } @@ -5062,6 +5154,7 @@ $HTTPTLS6PORT = $base++; # HTTP TLS (non-stunnel) IPv6 server port $HTTPPROXYPORT = $base++; # HTTP proxy port, when using CONNECT $HTTPPIPEPORT = $base++; # HTTP pipelining port $HTTP2PORT = $base++; # HTTP/2 port +$DICTPORT = $base++; # DICT port $HTTPUNIXPATH = 'http.sock'; # HTTP server Unix domain socket path ####################################################################### diff --git a/tests/serverhelp.pm b/tests/serverhelp.pm index d6a06508d..c4fd0fc61 100644 --- a/tests/serverhelp.pm +++ b/tests/serverhelp.pm @@ -105,7 +105,7 @@ sub servername_str { $proto = uc($proto) if($proto); die "unsupported protocol: '$proto'" unless($proto && - ($proto =~ /^(((FTP|HTTP|HTTP\/2|IMAP|POP3|SMTP|HTTP-PIPE)S?)|(TFTP|SFTP|SOCKS|SSH|RTSP|GOPHER|HTTPTLS))$/)); + ($proto =~ /^(((FTP|HTTP|HTTP\/2|IMAP|POP3|SMTP|HTTP-PIPE)S?)|(TFTP|SFTP|SOCKS|SSH|RTSP|GOPHER|HTTPTLS|DICT))$/)); $ipver = (not $ipver) ? 'ipv4' : lc($ipver); die "unsupported IP version: '$ipver'" unless($ipver &&