Project

General

Profile

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

haketilo / test / proxy_core.py @ b1444d9c

1
# Copyright (c) 2015, inaz2
2
# Copyright (C) 2021 jahoti <jahoti@tilde.team>
3
# Licensing information is collated in the `copyright` file
4

    
5
"""
6
The core for a "virtual network" proxy
7

    
8
Be sure to set certdir to your intended certificates directory before running.
9
"""
10

    
11
import os, socket, ssl, subprocess, sys, threading, time
12
from http.server import HTTPServer, BaseHTTPRequestHandler
13
from socketserver import ThreadingMixIn
14

    
15
gen_cert_req, lock = 'openssl req -new -key %scert.key -subj /CN=%s', threading.Lock()
16
sign_cert_req = 'openssl x509 -req -days 3650 -CA %sca.crt -CAkey %sca.key -set_serial %d -out %s'
17

    
18
def popen(command, *args, **kwargs):
19
    return subprocess.Popen((command % args).split(' '), **kwargs)
20

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

    
28
		self.log_message(format, *args)
29

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

    
34
		with lock:
35
			if not os.path.isfile(certpath):
36
				p1 = popen(gen_cert_req, certdir, hostname, stdout=subprocess.PIPE).stdout
37
				popen(sign_cert_req, certdir, certdir, time.time() * 1000, certpath, stdin=p1, stderr=subprocess.PIPE).communicate()
38

    
39
		self.send_response(200)
40
		self.end_headers()
41

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

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

    
48
	def proxy(self):
49
		content_length = int(self.headers.get('Content-Length', 0))
50
		req_body = self.rfile.read(content_length) if content_length else None
51

    
52
		if self.path[0] == '/':
53
			if isinstance(self.connection, ssl.SSLSocket):
54
				self.path = 'https://%s%s' % (self.headers['Host'], self.path)
55
			else:
56
				self.path = 'http://%s%s' % (self.headers['Host'], self.path)
57

    
58
		self.handle_request(req_body)
59

    
60
	do_OPTIONS = do_DELETE = do_PUT = do_HEAD = do_POST = do_GET = proxy
61

    
62
	def handle_request(self, req_body):
63
		pass
64

    
65

    
66
class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
67
	"""The actual proxy server"""
68
	address_family, daemon_threads = socket.AF_INET6, True
69

    
70
	def handle_error(self, request, client_address):
71
		# suppress socket/ssl related errors
72
		cls, e = sys.exc_info()[:2]
73
		if not (cls is socket.error or cls is ssl.SSLError):
74
			return HTTPServer.handle_error(self, request, client_address)
(4-4/6)