拼凑了一个socks2http代理服务器

2010-09-16 13:13

从安全的角度考虑, 本文将不对本文所提供的脚本的功能提供介绍, 只是就技术实现做了介绍. 本脚本将一个本地的socks代理服务器转换成http代理服务器. 并有日志记录, 访问来源限制等功能, 供个人使用应该无太大问题. 废话不多说, 先把脚本贴出来:

#!/usr/bin/env python
#coding=utf-8
"""
Author:         Xia Kai <xiaket@corp.netease.com/xiaket@gmail.com>
Filename:       socks2http.py
Type:           Service utility
Last modified:  2011-05-11 10:28

Description:
This script is based on SUZUKI Hisao's work on TinyHTTPProxy.py
    http://www.okisoft.co.jp/esc/python/proxy/

while I added socks proxy support for it.
"""
import BaseHTTPServer
import select
import socket
import SocketServer
import socks
from urlparse import urlparse, urlunparse


class ProxyHandler (BaseHTTPServer.BaseHTTPRequestHandler):
    __base = BaseHTTPServer.BaseHTTPRequestHandler
    __base_handle = __base.handle
    rbufsize = 0
    allow_from = ["127.0.0.1",]

    def handle(self):
        (ip, port) =  self.client_address
        if ip not in self.allow_from:
            self.raw_requestline = self.rfile.readline()
            if self.parse_request():
                self.send_error(403)
        else:
            self.__base_handle()

    def _connect_to(self, netloc, soc):
        i = netloc.find(':')
        if i >= 0:
            host_port = netloc[:i], int(netloc[i+1:])
        else:
            host_port = netloc, 80
        try:
            soc.connect(host_port)
        except socket.error, arg:
            try: msg = arg[1]
            except: msg = arg
            self.send_error(404, msg)
            return 0
        return 1

    def do_CONNECT(self):
        socket.socket = socks.socksocket
        soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            if self._connect_to(self.path, soc):
                self.log_request(200)
                self.wfile.write(self.protocol_version +
                                 " 200 Connection established\r\n")
                self.wfile.write("Proxy-agent: socks2http proxy.\r\n")
                self.wfile.write("\r\n")
                self._read_write(soc, 300)
        finally:
            soc.close()
            self.connection.close()

    def do_GET(self):
        socket.socket = socks.socksocket
        (scm, netloc, path, params, query, fragment) = urlparse(
            self.path, 'http')
        if scm != 'http' or fragment or not netloc:
            self.send_error(400, "bad url %s" % self.path)
            return
        soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            if self._connect_to(netloc, soc):
                self.log_request()
                soc.send("%s %s %s\r\n" % (
                    self.command,
                    urlunparse(('', '', path, params, query, '')),
                    self.request_version))
                self.headers['Connection'] = 'close'
                del self.headers['Proxy-Connection']
                for key_val in self.headers.items():
                    soc.send("%s: %s\r\n" % key_val)
                soc.send("\r\n")
                self._read_write(soc)
        finally:
            soc.close()
            self.connection.close()

    def _read_write(self, soc, max_idling=20):
        iw = [self.connection, soc]
        ow = []
        count = 0
        while 1:
            count += 1
            (ins, _, exs) = select.select(iw, ow, iw, 3)
            if exs:
                break
            if ins:
                for i in ins:
                    if i is soc:
                        out = self.connection
                    else:
                        out = soc
                    data = i.recv(8192)
                    if data:
                        out.send(data)
                        count = 0
            else:
                pass
            if count == max_idling:
                break

    do_HEAD = do_GET
    do_POST = do_GET
    do_PUT  = do_GET
    do_DELETE=do_GET


class ThreadingHTTPServer (SocketServer.ThreadingMixIn,
                           BaseHTTPServer.HTTPServer): pass


if __name__ == '__main__':
    socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, "127.0.0.1", 47931)
    BaseHTTPServer.test(ProxyHandler, ThreadingHTTPServer)

脚本里面的注释写得比较清楚了, 这个脚本实际上主要基于了SUZUKI Hisao的TinyHTTPProxy.py. 原作者在他的脚本中实现了HTTP协议几种方法, 不过他的脚本的主要作用是建立一个HTTPProxy服务器供其他人使用, 而不能实现socks代理的翻译. 因此我又用SocksiPy中提供的替换正常socket对象的方法来实现了socks代理的翻译. 基本就是在主函数里设置了socks代理的地址和端口. 然后在需要使用socket的地方用SocksiPy的socket对象来替代原有的socket对象:

socket.socket = socks.socksocket

基本上, 这个脚本就没啥好说的了, 因为我本身做的事情就很简单. 这个里面涉及到的网络编程知识偶也相当匮乏, 因此问偶问不出什么来的...