From e9b7f4d7bf624ccb0ed503f20b85288a575d020d Mon Sep 17 00:00:00 2001 From: jahoti Date: Tue, 17 Aug 2021 00:00:00 +0000 Subject: Enable the hijacking proxy in the test suite to serve responses --- test/proxy_core.py | 26 +------------ test/server.py | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 24 deletions(-) create mode 100644 test/server.py (limited to 'test') diff --git a/test/proxy_core.py b/test/proxy_core.py index 6b214cf..5a23278 100644 --- a/test/proxy_core.py +++ b/test/proxy_core.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 -# # Copyright (c) 2015, inaz2 # Copyright (C) 2021 jahoti # Licensing information is collated in the `copyright` file @@ -38,7 +36,7 @@ class ProxyRequestHandler(BaseHTTPRequestHandler): p1 = Popen((gen_cert_req % hostname).split(' '), stdout=PIPE).stdout Popen((sign_cert_req % (time.time() * 1000, certpath)).split(' '), stdin=p1, stderr=PIPE).communicate() - self.wfile.write('HTTP/1.1 200 Connection Established\r\n') + self.send_response(200) self.end_headers() self.connection = ssl.wrap_socket(self.connection, keyfile='cert.key', certfile=certpath, server_side=True) @@ -48,18 +46,6 @@ class ProxyRequestHandler(BaseHTTPRequestHandler): self.close_connection = int(self.headers.get('Proxy-Connection', '').lower() == 'close') def proxy(self): - if self.path == 'http://hachette.test/': - with open('ca.crt', 'rb') as f: - data = f.read() - - self.wfile.write('HTTP/1.1 200 OK\r\n') - self.send_header('Content-Type', 'application/x-x509-ca-cert') - self.send_header('Content-Length', len(data)) - self.send_header('Connection', 'close') - self.end_headers() - self.wfile.write(data) - return - content_length = int(self.headers.get('Content-Length', 0)) req_body = self.rfile.read(content_length) if content_length else None @@ -79,18 +65,10 @@ class ProxyRequestHandler(BaseHTTPRequestHandler): class ThreadingHTTPServer(ThreadingMixIn, HTTPServer): """The actual proxy server""" - address_family, daemon_threads, handler = socket.AF_INET6, True, ProxyRequestHandler + address_family, daemon_threads = socket.AF_INET6, True def handle_error(self, request, client_address): # suppress socket/ssl related errors cls, e = sys.exc_info()[:2] if not (cls is socket.error or cls is ssl.SSLError): return HTTPServer.handle_error(self, request, client_address) - - -def start(server_class, port=1337): - """Start up the proxy/server""" - - print('Serving HTTP Proxy') - httpd = server_class(('::1', port), ProxyRequestHandler) - httpd.serve_forever() diff --git a/test/server.py b/test/server.py new file mode 100644 index 0000000..d396495 --- /dev/null +++ b/test/server.py @@ -0,0 +1,106 @@ +# Copyright (C) 2021 jahoti +# Licensing information is collated in the `copyright` file + +""" +A modular "virtual network" proxy, +wrapping the classes in proxy_core.py +""" + +from proxy_core import * +from urllib.parse import parse_qs + +internet = {} # Add info here later +mime_types = { + "7z": "application/x-7z-compressed", "oga": "audio/ogg", + "abw": "application/x-abiword", "ogv": "video/ogg", + "arc": "application/x-freearc", "ogx": "application/ogg", + "bin": "application/octet-stream", "opus": "audio/opus", + "bz": "application/x-bzip", "otf": "font/otf", + "bz2": "application/x-bzip2", "pdf": "application/pdf", + "css": "text/css", "png": "image/png", + "csv": "text/csv", "sh": "application/x-sh", + "gif": "image/gif", "svg": "image/svg+xml", + "gz": "application/gzip", "tar": "application/x-tar", + "htm": "text/html", "ts": "video/mp2t", + "html": "text/html", "ttf": "font/ttf", + "ico": "image/vnd.microsoft.icon", "txt": "text/plain", + "js": "text/javascript", "wav": "audio/wav", + "jpeg": "image/jpeg", "weba": "audio/webm", + "jpg": "image/jpeg", "webm": "video/webm", + "json": "application/json", "woff": "font/woff", + "mjs": "text/javascript", "woff2": "font/woff2", + "mp3": "audio/mpeg", "xhtml": "application/xhtml+xml", + "mp4": "video/mp4", "zip": "application/zip", + "mpeg": "video/mpeg", + "odp": "application/vnd.oasis.opendocument.presentation", + "ods": "application/vnd.oasis.opendocument.spreadsheet", + "odt": "application/vnd.oasis.opendocument.text", + "xml": "application/xml" # text/xml if readable from casual users +} + +class RequestHijacker(ProxyRequestHandler): + def handle_request(self, req_body): + path_components = self.path.split('?', maxsplit=1) + path = path_components[0] + try: + # Response format: (status_code, headers (dict. of strings), + # body as bytes or filename containing body as string) + if path in internet: + info = internet[path] + if type(info) == tuple: + status_code, headers, body_file = info + if type(body_file) == str: + if 'Content-Type' not in headers and '.' in body_file: + ext = body_file.rsplit('.', maxsplit=1)[-1] + if ext in mime_types: + headers['Content-Type'] = mime_types[ext] + + with open(body_file, mode='rb') as f: + body_file = f.read() + + else: + # A function to evaluate to get the response + get_params, post_params = {}, {} + if len(path_components) == 2: + get_params = parse_qs(path_components[1]) + + # Parse POST parameters; currently only supports + # application/x-www-form-urlencoded + if req_body: + post_params = parse_qs(req_body.encode()) + + status_code, headers, body_file = info(self.command, get_params, post_params) + if type(body_file) == str: + body_file = body_file.encode() + + if type(status_code) != int or status_code <= 0: + raise Exception('Invalid status code %r' % status_code) + + for header, header_value in headers.items(): + if type(header) != str: + raise Exception('Invalid header key %r' % header) + + elif type(header_value) != str: + raise Exception('Invalid header value %r' % header_value) + else: + status_code, headers = 404, {'Content-Type': 'text/plain'} + body_file = b'Handler for this URL not found.' + + except Exception as e: + status_code, headers, body_file = 500, {'Content-Type': 'text/plain'}, b'Internal Error:\n' + repr(e).encode() + + headers['Content-Length'] = str(len(body_file)) + self.send_response(status_code) + for header, header_value in headers.items(): + self.send_header(header, header_value) + + self.end_headers() + self.wfile.write(body_file) + + + +def do_an_internet(port=1337): + """Start up the proxy/server""" + + httpd = ThreadingHTTPServer(('', port), RequestHijacker) + httpd.serve_forever() -- cgit v1.2.3