1
|
# Copyright (C) 2021 jahoti <jahoti@tilde.team>
|
2
|
# Licensing information is collated in the `copyright` file
|
3
|
|
4
|
"""
|
5
|
A modular "virtual network" proxy,
|
6
|
wrapping the classes in proxy_core.py
|
7
|
"""
|
8
|
|
9
|
from proxy_core import *
|
10
|
from urllib.parse import parse_qs
|
11
|
|
12
|
internet = {} # Add info here later
|
13
|
mime_types = {
|
14
|
"7z": "application/x-7z-compressed", "oga": "audio/ogg",
|
15
|
"abw": "application/x-abiword", "ogv": "video/ogg",
|
16
|
"arc": "application/x-freearc", "ogx": "application/ogg",
|
17
|
"bin": "application/octet-stream", "opus": "audio/opus",
|
18
|
"bz": "application/x-bzip", "otf": "font/otf",
|
19
|
"bz2": "application/x-bzip2", "pdf": "application/pdf",
|
20
|
"css": "text/css", "png": "image/png",
|
21
|
"csv": "text/csv", "sh": "application/x-sh",
|
22
|
"gif": "image/gif", "svg": "image/svg+xml",
|
23
|
"gz": "application/gzip", "tar": "application/x-tar",
|
24
|
"htm": "text/html", "ts": "video/mp2t",
|
25
|
"html": "text/html", "ttf": "font/ttf",
|
26
|
"ico": "image/vnd.microsoft.icon", "txt": "text/plain",
|
27
|
"js": "text/javascript", "wav": "audio/wav",
|
28
|
"jpeg": "image/jpeg", "weba": "audio/webm",
|
29
|
"jpg": "image/jpeg", "webm": "video/webm",
|
30
|
"json": "application/json", "woff": "font/woff",
|
31
|
"mjs": "text/javascript", "woff2": "font/woff2",
|
32
|
"mp3": "audio/mpeg", "xhtml": "application/xhtml+xml",
|
33
|
"mp4": "video/mp4", "zip": "application/zip",
|
34
|
"mpeg": "video/mpeg",
|
35
|
"odp": "application/vnd.oasis.opendocument.presentation",
|
36
|
"ods": "application/vnd.oasis.opendocument.spreadsheet",
|
37
|
"odt": "application/vnd.oasis.opendocument.text",
|
38
|
"xml": "application/xml" # text/xml if readable from casual users
|
39
|
}
|
40
|
|
41
|
class RequestHijacker(ProxyRequestHandler):
|
42
|
certdir = global_certdir
|
43
|
|
44
|
def handle_request(self, req_body):
|
45
|
path_components = self.path.split('?', maxsplit=1)
|
46
|
path = path_components[0]
|
47
|
try:
|
48
|
# Response format: (status_code, headers (dict. of strings),
|
49
|
# body as bytes or filename containing body as string)
|
50
|
if path in internet:
|
51
|
info = internet[path]
|
52
|
if type(info) == tuple:
|
53
|
status_code, headers, body_file = info
|
54
|
if type(body_file) == str:
|
55
|
if 'Content-Type' not in headers and '.' in body_file:
|
56
|
ext = body_file.rsplit('.', maxsplit=1)[-1]
|
57
|
if ext in mime_types:
|
58
|
headers['Content-Type'] = mime_types[ext]
|
59
|
|
60
|
with open(body_file, mode='rb') as f:
|
61
|
body_file = f.read()
|
62
|
|
63
|
else:
|
64
|
# A function to evaluate to get the response
|
65
|
get_params, post_params = {}, {}
|
66
|
if len(path_components) == 2:
|
67
|
get_params = parse_qs(path_components[1])
|
68
|
|
69
|
# Parse POST parameters; currently only supports
|
70
|
# application/x-www-form-urlencoded
|
71
|
if req_body:
|
72
|
post_params = parse_qs(req_body.encode())
|
73
|
|
74
|
status_code, headers, body_file = info(self.command, get_params, post_params)
|
75
|
if type(body_file) == str:
|
76
|
body_file = body_file.encode()
|
77
|
|
78
|
if type(status_code) != int or status_code <= 0:
|
79
|
raise Exception('Invalid status code %r' % status_code)
|
80
|
|
81
|
for header, header_value in headers.items():
|
82
|
if type(header) != str:
|
83
|
raise Exception('Invalid header key %r' % header)
|
84
|
|
85
|
elif type(header_value) != str:
|
86
|
raise Exception('Invalid header value %r' % header_value)
|
87
|
else:
|
88
|
status_code, headers = 404, {'Content-Type': 'text/plain'}
|
89
|
body_file = b'Handler for this URL not found.'
|
90
|
|
91
|
except Exception as e:
|
92
|
status_code, headers, body_file = 500, {'Content-Type': 'text/plain'}, b'Internal Error:\n' + repr(e).encode()
|
93
|
|
94
|
headers['Content-Length'] = str(len(body_file))
|
95
|
self.send_response(status_code)
|
96
|
for header, header_value in headers.items():
|
97
|
self.send_header(header, header_value)
|
98
|
|
99
|
self.end_headers()
|
100
|
self.wfile.write(body_file)
|
101
|
|
102
|
|
103
|
|
104
|
def do_an_internet(certdir, port):
|
105
|
"""Start up the proxy/server"""
|
106
|
global global_certdir
|
107
|
global_certdir = certdir
|
108
|
|
109
|
httpd = ThreadingHTTPServer(('', port), RequestHijacker)
|
110
|
httpd.serve_forever()
|