167 lines
5.9 KiB
Python
167 lines
5.9 KiB
Python
|
import os
|
||
|
import re
|
||
|
import time
|
||
|
import requests
|
||
|
from urllib.parse import urlparse, urlsplit
|
||
|
|
||
|
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||
|
from subprocess import Popen, PIPE
|
||
|
import ssl
|
||
|
import threading
|
||
|
|
||
|
class CFProxy(BaseHTTPRequestHandler):
|
||
|
TOKEN_HEADER = 'H-Token'
|
||
|
TOKEN_VALUE = 'agfjkewjkfvasfhgkzuc'
|
||
|
HOST_HEADER = 'H-Host'
|
||
|
IP_HEADER = 'H-IP'
|
||
|
|
||
|
PROXY_HOST = "blue-wind-f1c2.grogu.workers.dev" # "proxy.grogu.ml"
|
||
|
UA = "User Agent"
|
||
|
FAKE_IP = "1.1.1.1"
|
||
|
PROXY = None
|
||
|
|
||
|
cakey = 'ca.key'
|
||
|
cacert = 'ca.crt'
|
||
|
certkey = 'cert.key'
|
||
|
certdir = 'certs/'
|
||
|
|
||
|
lock = threading.Lock()
|
||
|
|
||
|
def __init__(self, *args):
|
||
|
self.session = requests.Session()
|
||
|
self.session.proxies.update({'https': self.PROXY,
|
||
|
'http': self.PROXY})
|
||
|
self.session.headers.update({'User-Agent': self.UA,
|
||
|
self.TOKEN_HEADER: self.TOKEN_VALUE})
|
||
|
self.proxy_host = self.PROXY_HOST
|
||
|
self.fake_ip = self.FAKE_IP
|
||
|
BaseHTTPRequestHandler.__init__(self, *args)
|
||
|
|
||
|
def do_GET_old(self):
|
||
|
url = self.path
|
||
|
print("[GET] "+url)
|
||
|
req = self.get(url)
|
||
|
self.send_response(req.status_code)
|
||
|
self.end_headers()
|
||
|
self.wfile.write(req.text.encode())
|
||
|
|
||
|
def do_CONNECT(self):
|
||
|
hostname = self.path.split(':')[0]
|
||
|
certpath = "%s/%s.crt" % (self.certdir.rstrip('/'), hostname)
|
||
|
|
||
|
with self.lock:
|
||
|
if not os.path.isfile(certpath):
|
||
|
epoch = "%d" % (time.time() * 1000)
|
||
|
p1 = Popen(["openssl", "req", "-new", "-key", self.certkey, "-subj", "/CN=%s" % hostname], stdout=PIPE)
|
||
|
p2 = Popen(["openssl", "x509", "-req", "-days", "3650", "-CA", self.cacert, "-CAkey", self.cakey, "-set_serial", epoch, "-out", certpath], stdin=p1.stdout, stderr=PIPE)
|
||
|
p2.communicate()
|
||
|
|
||
|
#self.wfile.write(("%s %d %s\r\n" % (self.protocol_version, 200, 'Connection Established')).encode())
|
||
|
self.send_response(200, 'Connection Established')
|
||
|
self.end_headers()
|
||
|
|
||
|
self.connection = ssl.wrap_socket(self.connection, keyfile=self.certkey, certfile=certpath, server_side=True)
|
||
|
self.rfile = self.connection.makefile("rb", self.rbufsize)
|
||
|
self.wfile = self.connection.makefile("wb", self.wbufsize)
|
||
|
|
||
|
conntype = self.headers.get('Proxy-Connection', '')
|
||
|
if self.protocol_version == "HTTP/1.1" and conntype.lower() != 'close':
|
||
|
self.close_connection = 0
|
||
|
else:
|
||
|
self.close_connection = 1
|
||
|
|
||
|
print("[CON OK]")
|
||
|
|
||
|
def request_handler(self, req, req_body):
|
||
|
pass
|
||
|
|
||
|
def response_handler(self, req, req_body, res, res_body):
|
||
|
pass
|
||
|
|
||
|
def filter_headers(self, headers):
|
||
|
# http://tools.ietf.org/html/rfc2616#section-13.5.1
|
||
|
hop_by_hop = ('connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization', 'te', 'trailers', 'transfer-encoding', 'upgrade')
|
||
|
for k in hop_by_hop:
|
||
|
del headers[k]
|
||
|
|
||
|
# accept only supported encodings
|
||
|
if 'Accept-Encoding' in headers:
|
||
|
ae = headers['Accept-Encoding']
|
||
|
filtered_encodings = [x for x in re.split(r',\s*', ae) if x in ('identity', 'gzip', 'x-gzip', 'deflate')]
|
||
|
headers['Accept-Encoding'] = ', '.join(filtered_encodings)
|
||
|
|
||
|
return headers
|
||
|
|
||
|
def do_ALL(self):
|
||
|
if self.path == 'http://127.0.0.1/':
|
||
|
self.send_cacert()
|
||
|
return
|
||
|
|
||
|
req = self
|
||
|
content_length = int(req.headers.get('Content-Length', 0))
|
||
|
req_body = self.rfile.read(content_length) if content_length else None
|
||
|
|
||
|
if req.path[0] == '/':
|
||
|
if isinstance(self.connection, ssl.SSLSocket):
|
||
|
req.path = "https://%s%s" % (req.headers['Host'], req.path)
|
||
|
else:
|
||
|
req.path = "http://%s%s" % (req.headers['Host'], req.path)
|
||
|
|
||
|
req_body_modified = self.request_handler(req, req_body)
|
||
|
if req_body_modified is False:
|
||
|
self.send_error(403)
|
||
|
return
|
||
|
elif req_body_modified is not None:
|
||
|
req_body = req_body_modified
|
||
|
req.headers['Content-length'] = str(len(req_body))
|
||
|
|
||
|
u = urlsplit(req.path)
|
||
|
scheme, netloc, path = u.scheme, u.netloc, (u.path + '?' + u.query if u.query else u.path)
|
||
|
assert scheme in ('http', 'https')
|
||
|
if netloc:
|
||
|
req.headers['Host'] = netloc
|
||
|
setattr(req, 'headers', self.filter_headers(req.headers))
|
||
|
|
||
|
res = self.get(req.path)
|
||
|
self.send_response(res.status_code)
|
||
|
self.end_headers()
|
||
|
self.wfile.write(res.text.encode())
|
||
|
|
||
|
do_GET = do_ALL
|
||
|
do_POST = do_ALL # wont work, because we drop the body...
|
||
|
|
||
|
def send_cacert(self):
|
||
|
with open(self.cacert, 'rb') as f:
|
||
|
data = f.read()
|
||
|
|
||
|
self.wfile.write("%s %d %s\r\n" % (self.protocol_version, 200, 'OK'))
|
||
|
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)
|
||
|
|
||
|
def get(self, url, **kwargs):
|
||
|
return self.handleReq('GET', url, **kwargs)
|
||
|
|
||
|
def post(self, url, **kwargs):
|
||
|
return self.handleReq('POST', url, **kwargs)
|
||
|
|
||
|
def handleReq(self, method, url, **kwargs):
|
||
|
# Gestion des headers
|
||
|
if 'headers' in kwargs:
|
||
|
for k, v in kwargs.get('headers'):
|
||
|
self.session.headers.update({k: v})
|
||
|
kwargs.pop('headers')
|
||
|
parsed_uri = urlparse(url)
|
||
|
self.session.headers.update({self.HOST_HEADER: parsed_uri.hostname})
|
||
|
self.session.headers.update({self.IP_HEADER: self.fake_ip})
|
||
|
proxyfied_url = '{0}://{1}{2}'.format(parsed_uri.scheme, self.proxy_host, parsed_uri.path)
|
||
|
return self.session.request(method, proxyfied_url, **kwargs)
|
||
|
|
||
|
PORT = 9097
|
||
|
httpd = HTTPServer(('', PORT), CFProxy)
|
||
|
print ("[i] Now serving at " + str(PORT))
|
||
|
httpd.serve_forever()
|
||
|
|