Project

General

Profile

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

haketilo / test / proxy_core.py @ 6c69435c

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 run this inside your intended certificates directory.
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
	certdir = ''
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%s.crt' % (certdir, hostname if hostname != 'ca' else 'CA')
35

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

    
41
		self.send_response(200)
42
		self.end_headers()
43

    
44
		self.connection = ssl.wrap_socket(self.connection, keyfile=certdir+'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
		content_length = int(self.headers.get('Content-Length', 0))
52
		req_body = self.rfile.read(content_length) if content_length else None
53

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

    
60
		self.handle_request(req_body)
61

    
62
	do_OPTIONS = do_DELETE = do_PUT = do_HEAD = do_POST = do_GET = proxy
63

    
64
	def handle_request(self, req_body):
65
		pass
66

    
67

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

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