IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

代理的本地部分

BT的花 blogs 2010-04-01 08:56:08 累计浏览 3,913 次
本机暂存

我的这个 proxy 代码参考自 SUZUKI Hisao 的 Tiny HTTP Proxy。主要修改的有两点:

  1. 原版的 do_CONNECT 是两个套接字直接互相转发数据,我改成了 SSL 中间人代理.. 而且依赖到 python 2.6 才支持的 server-side ssl wrap
  2. 另外就是自己封装了 descrypto 类,完成和远程 PHP 的加密

配合其运行的代码见
代理的远程部分
代理的加密部分

# -*- coding: utf-8 -*-
# 建议 Python 2.6 环境,以支持 https proxy
# Win32 下需安装
# http://www.python.org/ftp/python/2.6.5/python-2.6.5.msi
# http://www.voidspace.org.uk/downloads/pycrypto-2.0.1.win32-py2.6.exe
# 至于 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 = 'http://www.dup2.org/blarblarblar.php'
PASSWORD = 'yourpasswordhere'

# KEY/CERT 的生成参考 http://docs.python.org/library/ssl.html
KEYFILE = '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()

同分类推荐文章

  1. 绿盟科技《APT组织研究年鉴》(2026 版)正式发布 (2026-06-16 20:21:10)
  2. 【已复现】Linux内核Fragnesia权限提升漏洞(CVE-2026-46300) (2026-06-15 10:53:58)
  3. 企业文档安全最佳实践(二):给文档上“身份证”——手动标密与智能自动标密 (2026-06-12 17:18:33)

查看更多 安全 文章 →

建议继续学习

  1. 用Hyer来进行网站的抓取 (累计阅读 158,250)
  2. 配置Nginx+uwsgi更方便地部署python应用 (累计阅读 107,164)
  3. 程序员技术练级攻略 (累计阅读 35,468)
  4. python实现自动登录discuz论坛 (累计阅读 32,833)
  5. python编程细节──遍历dict的两种方法比较 (累计阅读 20,370)
  6. 每个程序员都应该学习使用Python或Ruby (累计阅读 17,917)
  7. Chrome和goagent的配置方法,你懂的 (累计阅读 16,842)
  8. 30分钟3300%性能提升――python+memcached网页优化小记 (累计阅读 13,741)
  9. 使用python爬虫抓站的一些技巧总结:进阶篇 (累计阅读 13,300)
  10. 我的PHP,Python和Ruby之路 (累计阅读 13,146)