我的这个 proxy 代码参考自 SUZUKI Hisao 的 Tiny HTTP Proxy。主要修改的有两点:
- 原版的 do_CONNECT 是两个套接字直接互相转发数据,我改成了 SSL 中间人代理.. 而且依赖到 python 2.6 才支持的 server-side ssl wrap
- 另外就是自己封装了 descrypto 类,完成和远程 PHP 的加密
-
# -*- coding: utf-8 -*-
-
# 建议 Python 2.6 环境,以支持 https proxy
-
# Win32 下需安装
-
# <a href="http://www.python.org/ftp/python/2.6.5/python-2.6.5.msi
-
#">http://www.python.org/ftp/python/2.6.5/python-2.6.5.msi
-
#</a> <a href="http://www.voidspace.org.uk/downloads/pycrypto-2.0.1.win32-py2.6.exe
-
#">http://www.voidspace.org.uk/downloads/pycrypto-2.0.1.win32-py2.6.exe
-
#</a> 至于 IronPython 目前还没有 server-side ssl 支持,据说 IP 2.6.1 将会有...
-
try:
-
from ipcrypto import descrypto
-
#IronPython 里的 socket 不支持 bind 到 '0.0.0.0'
-
import platform
-
bind_address = (platform.node(), 8000)
-
except:
-
from pycrypto import descrypto
-
bind_address = ('0.0.0.0', 8000)
-
-
import urllib2
-
import BaseHTTPServer, SocketServer
-
-
#REMOTEURL/PASSWORD 和国外主机配合
-
REMOTEURL = '<a href="http://www.dup2.org/blarblarblar.php'
-
PASSWORD">http://www.dup2.org/blarblarblar.php'
-
PASSWORD</a> = 'yourpasswordhere'
-
-
# KEY/CERT 的生成参考 <a href="http://docs.python.org/library/ssl.html
-
KEYFILE">http://docs.python.org/library/ssl.html
-
KEYFILE</a> = 'cert.pem'
-
CERTFILE = 'cert.pem'
-
-
#自定义允许的IP列表, 给每个IP起个名字帮助记忆
-
allow_clients = {'127.0.0.1': 'myself'}
-
-
desobj = descrypto(PASSWORD)
-
-
skip_headers = ["keep-alive", "proxy-connection", "connection", "accept-encoding"]
-
-
class pseudofile():
-
''' SSL Pseudo File Object'''
-
def __init__(self, sslobj):
-
self.sslobj = sslobj
-
self.closed = 0
-
-
def read(self, size):
-
chunks = []
-
read = 0
-
while read < size:
-
data = self.sslobj.read(size-read)
-
read += len(data)
-
chunks.append(data)
-
return ''.join(chunks)
-
-
def readline(self):
-
line = []
-
while 1:
-
char = self.sslobj.read(1)
-
line.append(char)
-
if char == "\n": return ''.join(line)
-
-
def write(self, data):
-
bytes = len(data)
-
while bytes > 0:
-
sent = self.sslobj.write(data)
-
if sent == bytes:
-
break # avoid copy
-
data = data[sent:]
-
bytes = bytes - sent
-
-
# 下面两个方法是 BaseHTTPServer 里会调用到的
-
def flush(self):
-
pass
-
close = flush
-
-
def checkip(f):
-
def new_f(_self):
-
(ip, port) = _self.client_address
-
if ip in allow_clients:
-
f(_self)
-
else:
-
_self.send_error(403)
-
return new_f
-
-
class ProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
-
-
@checkip
-
def do_GET(self):
-
content_length = 0
-
if hasattr(self, "sslhost"): self.raw_requestline = "%s https://%s%s %s\r\n" % (self.command, self.sslhost, self.path
-
, self.request_version)
-
h = [self.raw_requestline]
-
for kv in self.headers.items():
-
if kv[0] == 'content-length':
-
content_length = int(kv[1])
-
if kv[0] in skip_headers: continue
-
h.append("%s: %s" % kv)
-
h.append("connection: close")
-
req = "\r\n".join(h) + "\r\n\r\n"
-
-
if content_length:
-
req += self.rfile.read(content_length)
-
-
encreq = desobj.enc(req)
-
req = urllib2.Request(REMOTEURL, encreq)
-
f = urllib2.urlopen(req)
-
-
text_mode = f.read(1)
-
response = f.read()
-
if text_mode == "1":
-
response = desobj.dec(response)
-
self.wfile.write(response)
-
print 'REQUEST:', self.raw_requestline.strip()
-
#有时候一些看起来是 text/* 的请求也是 binary mode,通常是 304 Not Modified
-
print 'RESPONSE: %s, %d Bytes' % ('crypted mode' if text_mode == "1" else 'raw mode', len(response))
-
-
self.close_connection = 1
-
-
@checkip
-
def do_CONNECT(self):
-
# print self.raw_requestline
-
# "CONNECT twitter.com:443 HTTP/1.1"
-
self.sslhost = self.raw_requestline.split()[1]
-
self.wfile.write(self.protocol_version + " 200 Connection established\r\n")
-
self.wfile.write("Proxy-agent: QYB\r\n\r\n")
-
# TODO 浏览器端会看到一个警告,但是没有办法;避免警告是不对的,必须让使用者认识到现在是中间人模式
-
try:
-
import ssl
-
self.rfile = pseudofile(ssl.wrap_socket(self.connection, KEYFILE, CERTFILE, True))
-
self.wfile = self.rfile
-
self.handle_one_request()
-
except:
-
print 'ssl error:', self.raw_requestline
-
self.close_connection = 1
-
-
do_PUT = do_GET
-
do_POST = do_GET
-
do_HEAD = do_GET
-
do_DELETE = do_GET
-
-
class ThreadingHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer): pass
-
httpd = ThreadingHTTPServer(bind_address, ProxyHandler)
-
httpd.serve_forever()
Topic:
最新评论