Skip to content

Instantly share code, notes, and snippets.

@tsing
Created January 23, 2013 07:56
Show Gist options
  • Save tsing/4602949 to your computer and use it in GitHub Desktop.
Save tsing/4602949 to your computer and use it in GitHub Desktop.
create tunnel through http proxy
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import SocketServer
import socket
import logging
import select
import threading
import signal
import argparse
import sys
def create_tunnel(proxy_host, proxy_port, host, port, listen_port, listen_host=None):
class Handler(TunnelHandler):
def get_config(self):
return (proxy_host, proxy_port, host, port)
if listen_host is None:
listen_host = 'localhost'
server = ThreadingTCPServer((listen_host, listen_port), Handler)
return server
class ThreadingTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
allow_reuse_address = True
is_quiting = False
def quit(self):
self.is_quiting = True
self.shutdown()
class TunnelHandler(SocketServer.StreamRequestHandler):
def proxy_connect(self):
(self.proxy_host, self.proxy_port, self.host, self.port) = self.get_config()
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((self.proxy_host, self.proxy_port))
sock.send("CONNECT %s:%d HTTP/1.1\r\nHost:%s:%d\r\n\r\n" % (self.host, self.port,
self.host, self.port))
resp = sock.recv(4096)
parts = resp.split()
if parts[1] != "200":
raise Exception("Error response from Proxy server : %s" % resp)
return sock
def handle(self):
try:
proxy = self.proxy_connect()
self.handle_tcp(self.request, proxy)
except socket.error, e:
logging.warn(e)
def handle_tcp(self, request, proxy):
try:
fdset = [ request, proxy ]
while not self.server.is_quiting:
r, w, e = select.select(fdset, [], [], 0.5)
if request in r:
data = request.recv(4096)
if len(data) <= 0:
break
proxy.send(data)
if proxy in r:
data = proxy.recv(4096)
if len(data) <= 0:
break
request.send(data)
finally:
self.server.shutdown_request(request)
proxy.close()
def validate_args(proxy_str, tunnel_strs):
proxy_host, proxy_port = proxy_str.strip().split(":")
proxy_port = int(proxy_port)
if proxy_port <= 0:
sys.stderr.write("Bad proxy specification: %s" % args.proxy)
sys.exit(1)
tunnels = []
for tunnel_str in tunnel_strs:
parts = tunnel_str.strip().split(':')
num = len(parts)
if num == 3:
listen_host = None
listen_port, remote_host, remote_port = parts
elif num == 4:
listen_host, listen_port, remote_host, remote_port = parts
else:
sys.stderr.write("Bad tunnel specification: %s" % tunnel_str)
sys.exit(1)
listen_port = int(listen_port)
remote_port = int(remote_port)
if listen_port <= 0 or remote_port <= 0:
sys.stderr.write("Bad tunnel specification: %s" % tunnel_str)
tunnels.append([proxy_host, proxy_port, remote_host, remote_port,
listen_port, listen_host])
return tunnels
def main():
parser = argparse.ArgumentParser()
parser.add_argument("proxy", help="proxy_host:proxy_port")
parser.add_argument("tunnel", nargs='+', help="[bind_address:]bind_port:remote_host:remote_port")
args = parser.parse_args()
tunnels = validate_args(args.proxy, args.tunnel)
threads = []
for config in tunnels:
server = create_tunnel(*config)
t = threading.Thread(target=server.serve_forever)
t.setDaemon(True)
t.start()
threads.append((t, server, config))
def signal_handler(signum, frame):
for _, server, _ in threads:
server.quit()
signal.signal(signal.SIGINT, signal_handler)
main_thread = threading.currentThread()
while threading.activeCount() > 1:
for t in threading.enumerate():
if t is main_thread:
continue
t.join(timeout=0.1)
if __name__ == "__main__":
main()
@hlmn
Copy link

hlmn commented Oct 19, 2019

how can i connect to a proxy with basic auth?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment