diff options
Diffstat (limited to '')
-rwxr-xr-x | test/functional/util/dummy_avast.py | 53 | ||||
-rwxr-xr-x | test/functional/util/dummy_clam.py | 50 | ||||
-rwxr-xr-x | test/functional/util/dummy_fprot.py | 54 | ||||
-rwxr-xr-x | test/functional/util/dummy_http.py | 145 | ||||
-rw-r--r-- | test/functional/util/dummy_killer.py | 30 | ||||
-rwxr-xr-x | test/functional/util/dummy_p0f.py | 98 | ||||
-rwxr-xr-x | test/functional/util/dummy_ssl.py | 66 | ||||
-rwxr-xr-x | test/functional/util/dummy_udp.py | 26 | ||||
-rw-r--r-- | test/functional/util/nn_unpack.lua | 16 | ||||
-rw-r--r-- | test/functional/util/server.pem | 46 |
10 files changed, 584 insertions, 0 deletions
diff --git a/test/functional/util/dummy_avast.py b/test/functional/util/dummy_avast.py new file mode 100755 index 0000000..a4a6c62 --- /dev/null +++ b/test/functional/util/dummy_avast.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +PID = "/tmp/dummy_avast.pid" + +import os +import socket +import socketserver +import sys + +import dummy_killer + +class MyTCPHandler(socketserver.BaseRequestHandler): + + def handle(self): + self.request.sendall(b"220 DAEMON\r\n") + self.data = self.request.recv(1024).strip() + self.request.sendall(b"210 SCAN DATA\r\n") + if self.server.foundvirus: + self.request.sendall(b"SCAN /some/path/malware/xpaj/00908235ee9e267fa2f4c83fb4304c63af976cbc\t[L]0.0\t0 Eicar\\ [Heur]\r\n") + else: + self.request.sendall(b"SCAN /some/path/malware/xpaj/00908235ee9e267fa2f4c83fb4304c63af976cbc\t[+]\r\n") + self.request.sendall(b"200 SCAN OK\r\n") + self.request.close() + +if __name__ == "__main__": + HOST = "localhost" + + alen = len(sys.argv) + if alen > 1: + port = int(sys.argv[1]) + if alen >= 3: + foundvirus = bool(sys.argv[2]) + else: + foundvirus = False + else: + port = 3310 + foundvirus = False + + server = socketserver.TCPServer((HOST, port), MyTCPHandler, bind_and_activate=False) + server.allow_reuse_address = True + server.foundvirus = foundvirus + server.server_bind() + server.server_activate() + + dummy_killer.setup_killer(server) + dummy_killer.write_pid(PID) + + try: + server.handle_request() + except socket.error: + print("Socket closed") + + server.server_close() diff --git a/test/functional/util/dummy_clam.py b/test/functional/util/dummy_clam.py new file mode 100755 index 0000000..1b614f5 --- /dev/null +++ b/test/functional/util/dummy_clam.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +PID = "/tmp/dummy_clamav.pid" + +import os +import socket +import socketserver +import sys + +import dummy_killer + +class MyTCPHandler(socketserver.BaseRequestHandler): + + def handle(self): + self.data = self.request.recv(1024).strip() + if self.server.foundvirus: + self.request.sendall(b"stream: Eicar-Test-Signature FOUND\0") + else: + self.request.sendall(b"stream: OK\0") + self.request.close() + +if __name__ == "__main__": + HOST = "localhost" + + alen = len(sys.argv) + if alen > 1: + port = int(sys.argv[1]) + if alen >= 3: + foundvirus = bool(sys.argv[2]) + else: + foundvirus = False + else: + port = 3310 + foundvirus = False + + server = socketserver.TCPServer((HOST, port), MyTCPHandler, bind_and_activate=False) + server.allow_reuse_address = True + server.foundvirus = foundvirus + server.server_bind() + server.server_activate() + + dummy_killer.setup_killer(server) + dummy_killer.write_pid(PID) + + try: + server.handle_request() + except socket.error: + print("Socket closed") + + server.server_close() diff --git a/test/functional/util/dummy_fprot.py b/test/functional/util/dummy_fprot.py new file mode 100755 index 0000000..31ae2c4 --- /dev/null +++ b/test/functional/util/dummy_fprot.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 + +import os +import signal +import socket +import socketserver +import sys + +import dummy_killer + +PID = "/tmp/dummy_fprot.pid" + +class MyTCPHandler(socketserver.BaseRequestHandler): + + def handle(self): + self.data = self.request.recv(1024).strip() + if self.server.foundvirus: + self.request.sendall(b"1 <infected: EICAR_Test_File> FOO->bar\n") + else: + self.request.sendall(b"0 <clean> FOO\n") + self.request.close() + +if __name__ == "__main__": + + HOST = "localhost" + + alen = len(sys.argv) + if alen > 1: + port = int(sys.argv[1]) + if alen >= 4: + PID = sys.argv[3] + foundvirus = bool(sys.argv[2]) + elif alen >= 3: + foundvirus = bool(sys.argv[2]) + else: + foundvirus = False + else: + port = 10200 + foundvirus = False + + server = socketserver.TCPServer((HOST, port), MyTCPHandler, bind_and_activate=False) + server.allow_reuse_address = True + server.foundvirus = foundvirus + server.server_bind() + server.server_activate() + + dummy_killer.setup_killer(server) + dummy_killer.write_pid(PID) + + try: + server.handle_request() + except socket.error: + print("Socket closed") + server.server_close() diff --git a/test/functional/util/dummy_http.py b/test/functional/util/dummy_http.py new file mode 100755 index 0000000..c1abf7e --- /dev/null +++ b/test/functional/util/dummy_http.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 + +import asyncio +import dummy_killer +import tornado.ioloop +import tornado.web +import tornado.httpserver +import ssl +import argparse +import os + +class MainHandler(tornado.web.RequestHandler): + @tornado.gen.coroutine + def get(self, path): + if path == '/empty': + # Return an empty reply + self.set_header("Content-Type", "text/plain") + self.write("") + elif path == '/error_403': + # Return a 403 HTTP error + raise tornado.web.HTTPError(403) + elif path == '/timeout': + # Wait for 4 seconds before returning an empty reply + yield tornado.gen.sleep(4) + self.set_header("Content-Type", "text/plain") + self.write("") + elif path == '/request': + # Return a string 'hello world' + self.set_header("Content-Type", "text/plain") + self.write("hello world") + elif path == '/map-simple': + # Return a string 'hello map' + self.set_header("Content-Type", "text/plain") + self.write("hello map") + elif path == '/map-query': + # Parse the 'key' argument from the HTTP request + key = self.get_query_argument("key", default=None) + if key == 'au': + # Return a string 'hit' if 'key' is equal to 'au' + self.set_header("Content-Type", "text/plain") + self.write("1.0") + else: + # Return a 404 HTTP error if 'key' is not equal to 'au' + raise tornado.web.HTTPError(404) + elif path == '/settings': + self.set_header("Content-Type", "application/json") + self.write("{\"actions\": { \"reject\": 1.0}, \"symbols\": { \"EXTERNAL_SETTINGS\": 1.0 }}") + else: + raise tornado.web.HTTPError(404) + + @tornado.gen.coroutine + def post(self, path): + if path == '/empty': + # Return an empty reply + self.set_header("Content-Type", "text/plain") + self.write("") + elif path == '/error_403': + # Return a 403 HTTP error + raise tornado.web.HTTPError(403) + elif path == '/request': + # Return a string 'hello post' + self.set_header("Content-Type", "text/plain") + self.write("hello post") + elif path == '/timeout': + # Wait for 4 seconds before returning an empty reply + yield tornado.gen.sleep(4) + self.set_header("Content-Type", "text/plain") + self.write("") + elif path == '/map-simple': + # Return a string 'hello map' + self.set_header("Content-Type", "text/plain") + self.write("hello map") + elif path == '/map-query': + # Parse the 'key' argument from the HTTP request + key = self.get_query_argument("key", default="") + if key == 'au': + # Return a string 'hit' if 'key' is equal to 'au' + self.set_header("Content-Type", "text/plain") + self.write("hit") + else: + # Return a 404 HTTP error if 'key' is not equal to 'au' + raise tornado.web.HTTPError(404) + elif path == '/settings': + self.set_header("Content-Type", "application/json") + self.write("{\"actions\": { \"reject\": 1.0}, \"symbols\": { \"EXTERNAL_SETTINGS\": 1.0 }}") + else: + raise tornado.web.HTTPError(404) + + def head(self, path): + self.set_header("Content-Type", "text/plain") + if path == "/redirect1": + # Send an HTTP redirect to the bind address of the server + self.redirect(f"{self.request.protocol}://{self.request.host}/hello") + elif path == "/redirect2": + # Send an HTTP redirect to the bind address of the server + self.redirect(f"{self.request.protocol}://{self.request.host}/redirect1") + elif self.path == "/redirect3": + # Send an HTTP redirect to the bind address of the server + self.redirect(f"{self.request.protocol}://{self.request.host}/redirect4") + elif self.path == "/redirect4": + # Send an HTTP redirect to the bind address of the server + self.redirect(f"{self.request.protocol}://{self.request.host}/redirect3") + else: + self.send_response(200) + self.set_header("Content-Type", "text/plain") + +def make_app(): + return tornado.web.Application([ + (r"(/[^/]+)", MainHandler), + ]) + +async def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--bind", "-b", default="localhost", help="bind address") + parser.add_argument("--port", "-p", type=int, default=18080, help="bind port") + parser.add_argument("--keyfile", "-k", help="server private key file") + parser.add_argument("--certfile", "-c", help="server certificate file") + parser.add_argument("--pidfile", "-pf", help="path to the PID file") + args = parser.parse_args() + + # Create the Tornado application + app = make_app() + + # If keyfile and certfile are provided, create an HTTPS server. + # Otherwise, create an HTTP server. + if args.keyfile and args.certfile: + ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + ssl_ctx.load_cert_chain(args.certfile, args.keyfile) + server = tornado.httpserver.HTTPServer(app, ssl_options=ssl_ctx) + else: + server = tornado.httpserver.HTTPServer(app) + + # Write the PID to the specified PID file, if provided + if args.pidfile: + dummy_killer.write_pid(args.pidfile) + + # Start the server + server.bind(args.port, args.bind) + server.start(1) + + await asyncio.Event().wait() + +if __name__ == "__main__": + loop = asyncio.get_event_loop() + loop.run_until_complete(main()) diff --git a/test/functional/util/dummy_killer.py b/test/functional/util/dummy_killer.py new file mode 100644 index 0000000..0a052fb --- /dev/null +++ b/test/functional/util/dummy_killer.py @@ -0,0 +1,30 @@ +import signal +import os +import atexit +import tempfile + +def setup_killer(server, method = None): + def default_method(): + server.server_close() + + if method is None: + method = default_method + + def alarm_handler(signum, frame): + method() + + signal.signal(signal.SIGALRM, alarm_handler) + signal.signal(signal.SIGTERM, alarm_handler) + signal.alarm(120) + + +def write_pid(path): + with tempfile.NamedTemporaryFile(mode='w', delete=False) as f: + f.write(str(os.getpid())) + f.close() + os.rename(f.name, path) + + def cleanup(): + os.remove(path) + + atexit.register(cleanup) diff --git a/test/functional/util/dummy_p0f.py b/test/functional/util/dummy_p0f.py new file mode 100755 index 0000000..1d86ba0 --- /dev/null +++ b/test/functional/util/dummy_p0f.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 + +PID = "/tmp/dummy_p0f.pid" + +import os +import sys +import struct +import socket +import socketserver + +import dummy_killer + +class MyStreamHandler(socketserver.BaseRequestHandler): + + def handle(self): + S = { + 'bad_query' : 0x0, + 'ok' : 0x10, + 'no_match' : 0x20 + } + + OS = { + 'windows' : (b'Windows', b'7 or 8'), + 'linux' : (b'Linux', b'3.11 and newer') + } + + self.data = self.request.recv(21).strip() + + if self.server.p0f_status == 'bad_response': + response = 0 + else: + response = struct.pack( + "IbIIIIIIIhbb32s32s32s32s32s32s", + 0x50304602, # magic + S[self.server.p0f_status], # status + 1568493408, # first_seen + 1568493408, # last_seen + 1, # total_conn + 1, # uptime_min + 4, # up_mod_days + 1568493408, # last_nat + 1568493408, # last_chg + 10, # distance + 0, # bad_sw + 0, # os_match_q + OS[self.server.p0f_os][0], # os_name + OS[self.server.p0f_os][1], # os_flavor + b'', # http_name + b'', # http_flavor + b'Ethernet or modem', # link_type + b'' # language + ) + + self.request.sendall(response) + self.request.close() + +def cleanup(SOCK): + if os.path.exists(SOCK): + try: + os.unlink(SOCK) + except OSError: + print("Could not unlink socket: " + SOCK) + +if __name__ == "__main__": + SOCK = '/tmp/p0f.sock' + p0f_status = 'ok' + p0f_os = 'linux' + + os.umask(0000) + + alen = len(sys.argv) + if alen > 1: + SOCK = sys.argv[1] + if alen >= 4: + p0f_os = sys.argv[2] + p0f_status = sys.argv[3] + elif alen >= 3: + p0f_os = sys.argv[2] + + cleanup(SOCK) + + server = socketserver.UnixStreamServer(SOCK, MyStreamHandler, bind_and_activate=False) + server.allow_reuse_address = True + server.p0f_status = p0f_status + server.p0f_os = p0f_os + server.server_bind() + server.server_activate() + + dummy_killer.setup_killer(server) + dummy_killer.write_pid(PID) + + try: + server.handle_request() + except socket.error: + print("Socket closed") + + server.server_close() + cleanup(SOCK) diff --git a/test/functional/util/dummy_ssl.py b/test/functional/util/dummy_ssl.py new file mode 100755 index 0000000..44b1782 --- /dev/null +++ b/test/functional/util/dummy_ssl.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +import os +import socket +import ssl +import sys +import time + +import dummy_killer +import socketserver + +PORT = 14433 +HOST_NAME = '127.0.0.1' + +PID = "/tmp/dummy_ssl.pid" + +class SSLTCPHandler(socketserver.StreamRequestHandler): + def handle(self): + time.sleep(0.5) + data = self.request.recv(6000000) + while data: + print("{} wrote:".format(self.client_address[0])) + print(data) + time.sleep(0.1) + self.request.sendall(b'hello\n') + time.sleep(0.1) + data = self.request.recv(6000000) + +class SSL_TCP_Server(socketserver.ThreadingMixIn, socketserver.TCPServer): + def __init__(self, + server_address, + RequestHandlerClass, + certfile, + keyfile, + bind_and_activate=True): + self.allow_reuse_address = True + super().__init__(server_address, RequestHandlerClass, False) + self.timeout = 1 + ctx = ssl.create_default_context() + ctx.load_cert_chain(certfile=certfile) + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE + self.socket = ctx.wrap_socket(self.socket, server_side=True) + if (bind_and_activate): + self.server_bind() + self.server_activate() + + def run(self): + dummy_killer.write_pid(PID) + try: + self.serve_forever() + except KeyboardInterrupt: + print("Interrupt") + except socket.error as e: + print("Socket closed {}".format(e)) + finally: + self.server_close() + + def stop(self): + self.keep_running = False + self.server_close() + +if __name__ == '__main__': + server = SSL_TCP_Server((HOST_NAME, PORT), SSLTCPHandler, sys.argv[1], sys.argv[1]) + dummy_killer.setup_killer(server, server.stop) + server.run() diff --git a/test/functional/util/dummy_udp.py b/test/functional/util/dummy_udp.py new file mode 100755 index 0000000..f05d6d6 --- /dev/null +++ b/test/functional/util/dummy_udp.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +import socket +import sys + +import dummy_killer + +UDP_IP = "127.0.0.1" +PID = "/tmp/dummy_udp.pid" + +if __name__ == "__main__": + alen = len(sys.argv) + if alen > 1: + port = int(sys.argv[1]) + else: + port = 5005 + sock = socket.socket(socket.AF_INET, # Internet + socket.SOCK_DGRAM) # UDP + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind((UDP_IP, port)) + dummy_killer.write_pid(PID) + + while True: + data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes + print("received message:", data) + sock.sendto(data, addr) diff --git a/test/functional/util/nn_unpack.lua b/test/functional/util/nn_unpack.lua new file mode 100644 index 0000000..fee98d5 --- /dev/null +++ b/test/functional/util/nn_unpack.lua @@ -0,0 +1,16 @@ +local ucl = require "ucl" + +local function unhex(str) + return (str:gsub('..', function (cc) + return string.char(tonumber(cc, 16)) + end)) +end + +local parser = ucl.parser() +local ok, err = parser:parse_string(unhex(arg[1]), 'msgpack') +if not ok then + io.stderr:write(err) + os.exit(1) +end + +print(ucl.to_format(parser:get_object(), 'json-compact')) diff --git a/test/functional/util/server.pem b/test/functional/util/server.pem new file mode 100644 index 0000000..b5ec4c3 --- /dev/null +++ b/test/functional/util/server.pem @@ -0,0 +1,46 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA1nROqQC9tDLdnC2o1246g1T/oDq5Szbktof7TPHm/wDmWG47 +pwD22UkyEXthlq9kT9BZEgN6E2XF9UeA/7tZw7S4nbDUY/gWoGu7KlGLIIb5YXBW +CzSHAwlJtW/Pg5Muk7vJldHOufBJXP99PjJVzZZuYvv9+JJfSWHEeuY+RysWnTX5 +saq4cwDG8fCKqKEwy8PKatr8eJFL1/cPnC/0SAB77mIEdYHV5hBDUoNTpPWQrB2s +SgTqLa2pYTCWGUAvu4TiZKx00f6Z7soLa8RXddhEj6Qu5zLl+t5Llc8iSr7MgWTZ +0PD/lChOUg7H9STM4KiQ/qtBvSyGB0Cz71aWdQIDAQABAoIBAQDVAuG+7IWBn6SP +pKq4kTx9hsgCF1JFa1jyFU9/tieD0xj5bUTDNSn72cBprOvaCIzS2lgelGWFLuna +IBP579XRlohp2WKdiaav4VIfTq+qt2atai+NIbeZRHh6R30Pa/ovs68fqS51cj7s +qLl2NfkUI/+xQiuZ44nSEdJfYKMre8d/sF2zWg5sO5DaJWYsKdAqclRR4q8ZrEQY +6AZ8hsAlsyczpvU1A7Y/XWnyg7jr2towxeAbey13phFdukrZnRq2pRaprvwA1KqC +AtO0jx7L3MP3eamwQaNyaBD/PE+wnGpbPr36oU1ewhUTSZUlx3aYzaUDPrqMWwMk +7cb/gnsBAoGBAO9OQCnVE4D8r3DCRtmFfgqasWIakEs591+lzVSnHrLDsiaPk/5Q +Rv6FAyxU8T78SJNaYgCH3XW0NHeyyNi3Z1TQ9HDZLuiAhrVptIWFSvjCGIE4ijCG +3JQLkKPs2d/5/fXcZbgNhcm0lLYtP3SOsVB2p75dER8BjVVSsl14UfUJAoGBAOVq +PNSI55/jgwaA/zvX+fsawY6V20C42KpDq7LywAk/JCf5+pHZX4szClj3CS3shdP3 +B7WRqNHfLHTF7ID+JYpkahJI/Zq6a5wR4i/zXLIQCtSIaR0zZcDBu9GZvcwQlnLR +wvkkOlHXlmkmq94FV/i/b2rmbK7RTuc2TGk5+j0NAoGAGmZpkbPeCPbXa/si0dB2 +TTkvpIEFtibY8YZbFqGxM0t/ld11GDNHAcEuzm84hhhS8V6hPSm/9sJAn4vruGzT +S3oZ3XE4SZIUSmM09R31XWgcR/Uy2ZOnNfXoqQzyJFFyAPOljR6AyfXQCiEHxRYQ +3a2ZZ9jgkKkdLHKJFuK1N/ECgYAcdoDTkaTDJpQEA48nGpWuPNSU3yzTq9tdzIWJ +7yo6O3Y963rWC5UaDRwUi5m88+JquPRg55B9cWXvmvrLyjxYHjs2x42HW+er9mAM +uPHgObNOSRpZgB34u1CVIbD1l31DA5lgFcmSi9/ibeTW5+zRNNca+Tm0us1CTG9Q +gtv0JQKBgQCdqEhX8h10N4qQQ/E0ejX5VSR6JE/EfbFO2gaL62YOhSRKaddZvG7R +npQeNY2yS1TIoU/LT5pC5ytQ6hwjWFLo98MHY6B0rUWbUl8Uzsz+mcKbVSZnEIWj +9CGVOzPlLELRJdTqVUNPdJu7NjzNKoizZjAU1SynHFu7jy98e730QA== +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIDGDCCAgACCQCoEpIa1RdPlzANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJh +YTELMAkGA1UECAwCYWExCzAJBgNVBAcMAmFhMQswCQYDVQQKDAJhYTELMAkGA1UE +CwwCYWExCzAJBgNVBAMMAmFhMB4XDTE5MDUyOTIxMTMzNFoXDTE5MDYyODIxMTMz +NFowTjELMAkGA1UEBhMCYWExCzAJBgNVBAgMAmFhMQswCQYDVQQHDAJhYTELMAkG +A1UECgwCYWExCzAJBgNVBAsMAmFhMQswCQYDVQQDDAJhYTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBANZ0TqkAvbQy3ZwtqNduOoNU/6A6uUs25LaH+0zx +5v8A5lhuO6cA9tlJMhF7YZavZE/QWRIDehNlxfVHgP+7WcO0uJ2w1GP4FqBruypR +iyCG+WFwVgs0hwMJSbVvz4OTLpO7yZXRzrnwSVz/fT4yVc2WbmL7/fiSX0lhxHrm +PkcrFp01+bGquHMAxvHwiqihMMvDymra/HiRS9f3D5wv9EgAe+5iBHWB1eYQQ1KD +U6T1kKwdrEoE6i2tqWEwlhlAL7uE4mSsdNH+me7KC2vEV3XYRI+kLucy5freS5XP +Ikq+zIFk2dDw/5QoTlIOx/UkzOCokP6rQb0shgdAs+9WlnUCAwEAATANBgkqhkiG +9w0BAQsFAAOCAQEAF0TDCTa239+aQikOsqPjhZL9+6W/2z84uzskpTs449P9sJ9a +l6NcS0RnMmrhkUS/XdesFUOqabH2hMrj7rr8ezH6Y9GMT84ncvUaZAhFy1k5wwyB +E2hUJ6rv66VNWQ4I4Uxc9xoiqkAk4waFXhFiQZfVJ91RwLzXMJPsgAV9Wspg/jk8 +dNP1MW0rGrUx8CodkT370chI7DYHFTVudeb+MLUIF1RHQ4p/ATvzWIex3sIpkllv +BjUz5pvTiSy7PZGIHZdZh5n4JfFjrUJHGlWRulPhX5sw8XPn1bMRDr/9EKJDcsWS +rx5ZUoYjrLdTA2XJMcQ2AYrIZLyyD5+ihN841g== +-----END CERTIFICATE----- |