Project

General

Profile

Download (3.09 KB) Statistics
| Branch: | Tag: | Revision:

haketilo / test / proxy_core.py @ 7796e554

1
#!/usr/bin/env python3
2
#
3
# Copyright (c) 2015, inaz2
4
# Copyright (C) 2021 jahoti <jahoti@tilde.team>
5
# Licensing information is collated in the `copyright` file
6

    
7
"""
8
The core for a "virtual network" proxy
9

    
10
Be sure to run this inside your intended certificates directory.
11
"""
12

    
13
import os, socket, ssl, sys, threading, time
14
from http.server import HTTPServer, BaseHTTPRequestHandler
15
from socketserver import ThreadingMixIn
16
from subprocess import Popen, PIPE
17

    
18
gen_cert_req, lock = 'openssl req -new -key cert.key -subj /CN=%s', threading.Lock()
19
sign_cert_req = 'openssl x509 -req -days 3650 -CA ca.crt -CAkey ca.key -set_serial %d -out %s'
20

    
21

    
22
class ProxyRequestHandler(BaseHTTPRequestHandler):
23
	"""Handles a network request made to the proxy"""
24
	
25
	def log_error(self, format, *args):
26
		# suppress "Request timed out: timeout('timed out',)"
27
		if isinstance(args[0], socket.timeout):
28
			return
29

    
30
		self.log_message(format, *args)
31

    
32
	def do_CONNECT(self):
33
		hostname = self.path.split(':')[0]
34
		certpath = '%s.crt' % (hostname if hostname != 'ca' else 'CA')
35

    
36
		with lock:
37
			if not os.path.isfile(certpath):
38
				p1 = Popen((gen_cert_req % hostname).split(' '), stdout=PIPE).stdout
39
				Popen((sign_cert_req % (time.time() * 1000, certpath)).split(' '), stdin=p1, stderr=PIPE).communicate()
40

    
41
		self.wfile.write('HTTP/1.1 200 Connection Established\r\n')
42
		self.end_headers()
43

    
44
		self.connection = ssl.wrap_socket(self.connection, keyfile='cert.key', certfile=certpath, server_side=True)
45
		self.rfile = self.connection.makefile('rb', self.rbufsize)
46
		self.wfile = self.connection.makefile('wb', self.wbufsize)
47

    
48
		self.close_connection = int(self.headers.get('Proxy-Connection', '').lower() == 'close')
49

    
50
	def proxy(self):
51
		if self.path == 'http://hachette.test/':
52
			with open('ca.crt', 'rb') as f:
53
				data = f.read()
54

    
55
			self.wfile.write('HTTP/1.1 200 OK\r\n')
56
			self.send_header('Content-Type', 'application/x-x509-ca-cert')
57
			self.send_header('Content-Length', len(data))
58
			self.send_header('Connection', 'close')
59
			self.end_headers()
60
			self.wfile.write(data)
61
			return
62

    
63
		content_length = int(self.headers.get('Content-Length', 0))
64
		req_body = self.rfile.read(content_length) if content_length else None
65

    
66
		if self.path[0] == '/':
67
			if isinstance(self.connection, ssl.SSLSocket):
68
				self.path = 'https://%s%s' % (self.headers['Host'], self.path)
69
			else:
70
				self.path = 'http://%s%s' % (self.headers['Host'], self.path)
71

    
72
		self.handle_request(req_body)
73

    
74
	do_OPTIONS = do_DELETE = do_PUT = do_HEAD = do_POST = do_GET = proxy
75

    
76
	def handle_request(self, req_body):
77
		pass
78

    
79

    
80
class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
81
	"""The actual proxy server"""
82
	address_family, daemon_threads, handler = socket.AF_INET6, True, ProxyRequestHandler
83

    
84
	def handle_error(self, request, client_address):
85
		# suppress socket/ssl related errors
86
		cls, e = sys.exc_info()[:2]
87
		if not (cls is socket.error or cls is ssl.SSLError):
88
			return HTTPServer.handle_error(self, request, client_address)
89

    
90

    
91
def start(server_class, port=1337):
92
	"""Start up the proxy/server"""
93
	
94
	print('Serving HTTP Proxy')
95
	httpd = server_class(('::1', port), ProxyRequestHandler)
96
	httpd.serve_forever()
(2-2/2)