1*6236dae4SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*6236dae4SAndroid Build Coastguard Worker# -*- coding: utf-8 -*- 3*6236dae4SAndroid Build Coastguard Worker#*************************************************************************** 4*6236dae4SAndroid Build Coastguard Worker# _ _ ____ _ 5*6236dae4SAndroid Build Coastguard Worker# Project ___| | | | _ \| | 6*6236dae4SAndroid Build Coastguard Worker# / __| | | | |_) | | 7*6236dae4SAndroid Build Coastguard Worker# | (__| |_| | _ <| |___ 8*6236dae4SAndroid Build Coastguard Worker# \___|\___/|_| \_\_____| 9*6236dae4SAndroid Build Coastguard Worker# 10*6236dae4SAndroid Build Coastguard Worker# Copyright (C) Daniel Stenberg, <[email protected]>, et al. 11*6236dae4SAndroid Build Coastguard Worker# 12*6236dae4SAndroid Build Coastguard Worker# This software is licensed as described in the file COPYING, which 13*6236dae4SAndroid Build Coastguard Worker# you should have received as part of this distribution. The terms 14*6236dae4SAndroid Build Coastguard Worker# are also available at https://curl.se/docs/copyright.html. 15*6236dae4SAndroid Build Coastguard Worker# 16*6236dae4SAndroid Build Coastguard Worker# You may opt to use, copy, modify, merge, publish, distribute and/or sell 17*6236dae4SAndroid Build Coastguard Worker# copies of the Software, and permit persons to whom the Software is 18*6236dae4SAndroid Build Coastguard Worker# furnished to do so, under the terms of the COPYING file. 19*6236dae4SAndroid Build Coastguard Worker# 20*6236dae4SAndroid Build Coastguard Worker# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 21*6236dae4SAndroid Build Coastguard Worker# KIND, either express or implied. 22*6236dae4SAndroid Build Coastguard Worker# 23*6236dae4SAndroid Build Coastguard Worker# SPDX-License-Identifier: curl 24*6236dae4SAndroid Build Coastguard Worker# 25*6236dae4SAndroid Build Coastguard Worker########################################################################### 26*6236dae4SAndroid Build Coastguard Worker# 27*6236dae4SAndroid Build Coastguard Worker"""DICT server.""" 28*6236dae4SAndroid Build Coastguard Worker 29*6236dae4SAndroid Build Coastguard Workerfrom __future__ import (absolute_import, division, print_function, 30*6236dae4SAndroid Build Coastguard Worker unicode_literals) 31*6236dae4SAndroid Build Coastguard Worker 32*6236dae4SAndroid Build Coastguard Workerimport argparse 33*6236dae4SAndroid Build Coastguard Workerimport logging 34*6236dae4SAndroid Build Coastguard Workerimport os 35*6236dae4SAndroid Build Coastguard Workerimport sys 36*6236dae4SAndroid Build Coastguard Worker 37*6236dae4SAndroid Build Coastguard Workerfrom util import ClosingFileHandler 38*6236dae4SAndroid Build Coastguard Worker 39*6236dae4SAndroid Build Coastguard Workertry: # Python 2 40*6236dae4SAndroid Build Coastguard Worker import SocketServer as socketserver # type: ignore 41*6236dae4SAndroid Build Coastguard Workerexcept ImportError: # Python 3 42*6236dae4SAndroid Build Coastguard Worker import socketserver 43*6236dae4SAndroid Build Coastguard Worker 44*6236dae4SAndroid Build Coastguard Workerlog = logging.getLogger(__name__) 45*6236dae4SAndroid Build Coastguard WorkerHOST = "localhost" 46*6236dae4SAndroid Build Coastguard Worker 47*6236dae4SAndroid Build Coastguard Worker# The strings that indicate the test framework is checking our aliveness 48*6236dae4SAndroid Build Coastguard WorkerVERIFIED_REQ = b"verifiedserver" 49*6236dae4SAndroid Build Coastguard WorkerVERIFIED_RSP = "WE ROOLZ: {pid}" 50*6236dae4SAndroid Build Coastguard Worker 51*6236dae4SAndroid Build Coastguard Worker 52*6236dae4SAndroid Build Coastguard Workerdef dictserver(options): 53*6236dae4SAndroid Build Coastguard Worker """Start up a TCP server with a DICT handler and serve DICT requests forever.""" 54*6236dae4SAndroid Build Coastguard Worker if options.pidfile: 55*6236dae4SAndroid Build Coastguard Worker pid = os.getpid() 56*6236dae4SAndroid Build Coastguard Worker # see tests/server/util.c function write_pidfile 57*6236dae4SAndroid Build Coastguard Worker if os.name == "nt": 58*6236dae4SAndroid Build Coastguard Worker pid += 65536 59*6236dae4SAndroid Build Coastguard Worker with open(options.pidfile, "w") as f: 60*6236dae4SAndroid Build Coastguard Worker f.write(str(pid)) 61*6236dae4SAndroid Build Coastguard Worker 62*6236dae4SAndroid Build Coastguard Worker local_bind = (options.host, options.port) 63*6236dae4SAndroid Build Coastguard Worker log.info("[DICT] Listening on %s", local_bind) 64*6236dae4SAndroid Build Coastguard Worker 65*6236dae4SAndroid Build Coastguard Worker # Need to set the allow_reuse on the class, not on the instance. 66*6236dae4SAndroid Build Coastguard Worker socketserver.TCPServer.allow_reuse_address = True 67*6236dae4SAndroid Build Coastguard Worker server = socketserver.TCPServer(local_bind, DictHandler) 68*6236dae4SAndroid Build Coastguard Worker server.serve_forever() 69*6236dae4SAndroid Build Coastguard Worker 70*6236dae4SAndroid Build Coastguard Worker return ScriptRC.SUCCESS 71*6236dae4SAndroid Build Coastguard Worker 72*6236dae4SAndroid Build Coastguard Worker 73*6236dae4SAndroid Build Coastguard Workerclass DictHandler(socketserver.BaseRequestHandler): 74*6236dae4SAndroid Build Coastguard Worker """Handler class for DICT connections.""" 75*6236dae4SAndroid Build Coastguard Worker 76*6236dae4SAndroid Build Coastguard Worker def handle(self): 77*6236dae4SAndroid Build Coastguard Worker """Respond to all queries with a 552.""" 78*6236dae4SAndroid Build Coastguard Worker try: 79*6236dae4SAndroid Build Coastguard Worker # First, send a response to allow the server to continue. 80*6236dae4SAndroid Build Coastguard Worker rsp = "220 dictserver <xnooptions> <msgid@msgid>\n" 81*6236dae4SAndroid Build Coastguard Worker self.request.sendall(rsp.encode("utf-8")) 82*6236dae4SAndroid Build Coastguard Worker 83*6236dae4SAndroid Build Coastguard Worker # Receive the request. 84*6236dae4SAndroid Build Coastguard Worker data = self.request.recv(1024).strip() 85*6236dae4SAndroid Build Coastguard Worker log.debug("[DICT] Incoming data: %r", data) 86*6236dae4SAndroid Build Coastguard Worker 87*6236dae4SAndroid Build Coastguard Worker if VERIFIED_REQ in data: 88*6236dae4SAndroid Build Coastguard Worker log.debug("[DICT] Received verification request from test " 89*6236dae4SAndroid Build Coastguard Worker "framework") 90*6236dae4SAndroid Build Coastguard Worker pid = os.getpid() 91*6236dae4SAndroid Build Coastguard Worker # see tests/server/util.c function write_pidfile 92*6236dae4SAndroid Build Coastguard Worker if os.name == "nt": 93*6236dae4SAndroid Build Coastguard Worker pid += 65536 94*6236dae4SAndroid Build Coastguard Worker response_data = VERIFIED_RSP.format(pid=pid) 95*6236dae4SAndroid Build Coastguard Worker else: 96*6236dae4SAndroid Build Coastguard Worker log.debug("[DICT] Received normal request") 97*6236dae4SAndroid Build Coastguard Worker response_data = "No matches" 98*6236dae4SAndroid Build Coastguard Worker 99*6236dae4SAndroid Build Coastguard Worker # Send back a failure to find. 100*6236dae4SAndroid Build Coastguard Worker response = "552 {0}\n".format(response_data) 101*6236dae4SAndroid Build Coastguard Worker log.debug("[DICT] Responding with %r", response) 102*6236dae4SAndroid Build Coastguard Worker self.request.sendall(response.encode("utf-8")) 103*6236dae4SAndroid Build Coastguard Worker 104*6236dae4SAndroid Build Coastguard Worker except IOError: 105*6236dae4SAndroid Build Coastguard Worker log.exception("[DICT] IOError hit during request") 106*6236dae4SAndroid Build Coastguard Worker 107*6236dae4SAndroid Build Coastguard Worker 108*6236dae4SAndroid Build Coastguard Workerdef get_options(): 109*6236dae4SAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 110*6236dae4SAndroid Build Coastguard Worker 111*6236dae4SAndroid Build Coastguard Worker parser.add_argument("--port", action="store", default=9016, 112*6236dae4SAndroid Build Coastguard Worker type=int, help="port to listen on") 113*6236dae4SAndroid Build Coastguard Worker parser.add_argument("--host", action="store", default=HOST, 114*6236dae4SAndroid Build Coastguard Worker help="host to listen on") 115*6236dae4SAndroid Build Coastguard Worker parser.add_argument("--verbose", action="store", type=int, default=0, 116*6236dae4SAndroid Build Coastguard Worker help="verbose output") 117*6236dae4SAndroid Build Coastguard Worker parser.add_argument("--pidfile", action="store", 118*6236dae4SAndroid Build Coastguard Worker help="file name for the PID") 119*6236dae4SAndroid Build Coastguard Worker parser.add_argument("--logfile", action="store", 120*6236dae4SAndroid Build Coastguard Worker help="file name for the log") 121*6236dae4SAndroid Build Coastguard Worker parser.add_argument("--srcdir", action="store", help="test directory") 122*6236dae4SAndroid Build Coastguard Worker parser.add_argument("--id", action="store", help="server ID") 123*6236dae4SAndroid Build Coastguard Worker parser.add_argument("--ipv4", action="store_true", default=0, 124*6236dae4SAndroid Build Coastguard Worker help="IPv4 flag") 125*6236dae4SAndroid Build Coastguard Worker 126*6236dae4SAndroid Build Coastguard Worker return parser.parse_args() 127*6236dae4SAndroid Build Coastguard Worker 128*6236dae4SAndroid Build Coastguard Worker 129*6236dae4SAndroid Build Coastguard Workerdef setup_logging(options): 130*6236dae4SAndroid Build Coastguard Worker """Set up logging from the command line options.""" 131*6236dae4SAndroid Build Coastguard Worker root_logger = logging.getLogger() 132*6236dae4SAndroid Build Coastguard Worker add_stdout = False 133*6236dae4SAndroid Build Coastguard Worker 134*6236dae4SAndroid Build Coastguard Worker formatter = logging.Formatter("%(asctime)s %(levelname)-5.5s %(message)s") 135*6236dae4SAndroid Build Coastguard Worker 136*6236dae4SAndroid Build Coastguard Worker # Write out to a logfile 137*6236dae4SAndroid Build Coastguard Worker if options.logfile: 138*6236dae4SAndroid Build Coastguard Worker handler = ClosingFileHandler(options.logfile) 139*6236dae4SAndroid Build Coastguard Worker handler.setFormatter(formatter) 140*6236dae4SAndroid Build Coastguard Worker handler.setLevel(logging.DEBUG) 141*6236dae4SAndroid Build Coastguard Worker root_logger.addHandler(handler) 142*6236dae4SAndroid Build Coastguard Worker else: 143*6236dae4SAndroid Build Coastguard Worker # The logfile wasn't specified. Add a stdout logger. 144*6236dae4SAndroid Build Coastguard Worker add_stdout = True 145*6236dae4SAndroid Build Coastguard Worker 146*6236dae4SAndroid Build Coastguard Worker if options.verbose: 147*6236dae4SAndroid Build Coastguard Worker # Add a stdout logger as well in verbose mode 148*6236dae4SAndroid Build Coastguard Worker root_logger.setLevel(logging.DEBUG) 149*6236dae4SAndroid Build Coastguard Worker add_stdout = True 150*6236dae4SAndroid Build Coastguard Worker else: 151*6236dae4SAndroid Build Coastguard Worker root_logger.setLevel(logging.INFO) 152*6236dae4SAndroid Build Coastguard Worker 153*6236dae4SAndroid Build Coastguard Worker if add_stdout: 154*6236dae4SAndroid Build Coastguard Worker stdout_handler = logging.StreamHandler(sys.stdout) 155*6236dae4SAndroid Build Coastguard Worker stdout_handler.setFormatter(formatter) 156*6236dae4SAndroid Build Coastguard Worker stdout_handler.setLevel(logging.DEBUG) 157*6236dae4SAndroid Build Coastguard Worker root_logger.addHandler(stdout_handler) 158*6236dae4SAndroid Build Coastguard Worker 159*6236dae4SAndroid Build Coastguard Worker 160*6236dae4SAndroid Build Coastguard Workerclass ScriptRC(object): 161*6236dae4SAndroid Build Coastguard Worker """Enum for script return codes.""" 162*6236dae4SAndroid Build Coastguard Worker 163*6236dae4SAndroid Build Coastguard Worker SUCCESS = 0 164*6236dae4SAndroid Build Coastguard Worker FAILURE = 1 165*6236dae4SAndroid Build Coastguard Worker EXCEPTION = 2 166*6236dae4SAndroid Build Coastguard Worker 167*6236dae4SAndroid Build Coastguard Worker 168*6236dae4SAndroid Build Coastguard Workerif __name__ == '__main__': 169*6236dae4SAndroid Build Coastguard Worker # Get the options from the user. 170*6236dae4SAndroid Build Coastguard Worker options = get_options() 171*6236dae4SAndroid Build Coastguard Worker 172*6236dae4SAndroid Build Coastguard Worker # Setup logging using the user options 173*6236dae4SAndroid Build Coastguard Worker setup_logging(options) 174*6236dae4SAndroid Build Coastguard Worker 175*6236dae4SAndroid Build Coastguard Worker # Run main script. 176*6236dae4SAndroid Build Coastguard Worker try: 177*6236dae4SAndroid Build Coastguard Worker rc = dictserver(options) 178*6236dae4SAndroid Build Coastguard Worker except Exception: 179*6236dae4SAndroid Build Coastguard Worker log.exception('Error running server') 180*6236dae4SAndroid Build Coastguard Worker rc = ScriptRC.EXCEPTION 181*6236dae4SAndroid Build Coastguard Worker 182*6236dae4SAndroid Build Coastguard Worker if options.pidfile and os.path.isfile(options.pidfile): 183*6236dae4SAndroid Build Coastguard Worker os.unlink(options.pidfile) 184*6236dae4SAndroid Build Coastguard Worker 185*6236dae4SAndroid Build Coastguard Worker log.info("[DICT] Returning %d", rc) 186*6236dae4SAndroid Build Coastguard Worker sys.exit(rc) 187