PageRenderTime 63ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/goagent-local/proxy.py

https://github.com/greatagent3/esr
Python | 3490 lines | 3368 code | 64 blank | 58 comment | 188 complexity | 739c094e8b2c4504e77212b650d0a03a MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. #!/usr/bin/env python
  2. # coding:utf-8
  3. # Based on GAppProxy 2.0.0 by Du XiaoGang <dugang.2008@gmail.com>
  4. # Based on WallProxy 0.4.0 by Hust Moon <www.ehust@gmail.com>
  5. # Contributor:
  6. # Phus Lu <phus.lu@gmail.com>
  7. # Hewig Xu <hewigovens@gmail.com>
  8. # Ayanamist Yang <ayanamist@gmail.com>
  9. # V.E.O <V.E.O@tom.com>
  10. # Max Lv <max.c.lv@gmail.com>
  11. # AlsoTang <alsotang@gmail.com>
  12. # Christopher Meng <i@cicku.me>
  13. # Yonsm Guo <YonsmGuo@gmail.com>
  14. # Parkman <cseparkman@gmail.com>
  15. # Ming Bai <mbbill@gmail.com>
  16. # Bin Yu <yubinlove1991@gmail.com>
  17. # lileixuan <lileixuan@gmail.com>
  18. # Cong Ding <cong@cding.org>
  19. # Zhang Youfu <zhangyoufu@gmail.com>
  20. # Lu Wei <luwei@barfoo>
  21. # Harmony Meow <harmony.meow@gmail.com>
  22. # logostream <logostream@gmail.com>
  23. # Rui Wang <isnowfy@gmail.com>
  24. # Wang Wei Qiang <wwqgtxx@gmail.com>
  25. # Felix Yan <felixonmars@gmail.com>
  26. # Sui Feng <suifeng.me@qq.com>
  27. # QXO <qxodream@gmail.com>
  28. # Geek An <geekan@foxmail.com>
  29. # Poly Rabbit <mcx_221@foxmail.com>
  30. # oxnz <yunxinyi@gmail.com>
  31. # Shusen Liu <liushusen.smart@gmail.com>
  32. # Yad Smood <y.s.inside@gmail.com>
  33. # Chen Shuang <cs0x7f@gmail.com>
  34. # cnfuyu <cnfuyu@gmail.com>
  35. # cuixin <steven.cuixin@gmail.com>
  36. # s2marine0 <s2marine0@gmail.com>
  37. # Toshio Xiang <snachx@gmail.com>
  38. # Bo Tian <dxmtb@163.com>
  39. # Virgil <variousvirgil@gmail.com>
  40. # hub01 <miaojiabumiao@yeah.net>
  41. # v3aqb <sgzz.cj@gmail.com>
  42. # Oling Cat <olingcat@gmail.com>
  43. __version__ = '3.1.18'
  44. import sys
  45. import os
  46. import glob
  47. reload(sys).setdefaultencoding('UTF-8')
  48. sys.dont_write_bytecode = True
  49. sys.path += glob.glob('%s/*.egg' % os.path.dirname(os.path.abspath(__file__)))
  50. try:
  51. from key_config import __RSA_KEY__
  52. except (ImportError, SystemError):
  53. __RSA_KEY__ = None
  54. try:
  55. from key_config import __RANGEFETCH_RSA_KEY__
  56. except (ImportError, SystemError):
  57. __RANGEFETCH_RSA_KEY__ = __RSA_KEY__
  58. try:
  59. import gevent
  60. import gevent.socket
  61. import gevent.server
  62. import gevent.queue
  63. import gevent.monkey
  64. gevent.monkey.patch_all(subprocess=True)
  65. except ImportError:
  66. gevent = None
  67. except TypeError:
  68. gevent.monkey.patch_all()
  69. sys.stderr.write('\033[31m Warning: Please update gevent to the latest 1.0 version!\033[0m\n')
  70. import errno
  71. import time
  72. import struct
  73. import collections
  74. import binascii
  75. import zlib
  76. import itertools
  77. import re
  78. import io
  79. import fnmatch
  80. import traceback
  81. import random
  82. import base64
  83. import string
  84. import hashlib
  85. import uuid
  86. import threading
  87. import thread
  88. import socket
  89. import ssl
  90. import select
  91. import Queue
  92. import SocketServer
  93. import ConfigParser
  94. import BaseHTTPServer
  95. import httplib
  96. import urllib
  97. import urllib2
  98. import urlparse
  99. try:
  100. import dnslib
  101. except ImportError:
  102. dnslib = None
  103. try:
  104. import OpenSSL
  105. except ImportError:
  106. OpenSSL = None
  107. try:
  108. import pygeoip
  109. except ImportError:
  110. pygeoip = None
  111. HAS_PYPY = hasattr(sys, 'pypy_version_info')
  112. NetWorkIOError = (socket.error, ssl.SSLError, OSError) if not OpenSSL else (socket.error, ssl.SSLError, OpenSSL.SSL.Error, OSError)
  113. class Logging(type(sys)):
  114. CRITICAL = 50
  115. FATAL = CRITICAL
  116. ERROR = 40
  117. WARNING = 30
  118. WARN = WARNING
  119. INFO = 20
  120. DEBUG = 10
  121. NOTSET = 0
  122. def __init__(self, *args, **kwargs):
  123. self.level = self.__class__.INFO
  124. self.__set_error_color = lambda: None
  125. self.__set_warning_color = lambda: None
  126. self.__set_debug_color = lambda: None
  127. self.__reset_color = lambda: None
  128. if hasattr(sys.stderr, 'isatty') and sys.stderr.isatty():
  129. if os.name == 'nt':
  130. import ctypes
  131. SetConsoleTextAttribute = ctypes.windll.kernel32.SetConsoleTextAttribute
  132. GetStdHandle = ctypes.windll.kernel32.GetStdHandle
  133. self.__set_error_color = lambda: SetConsoleTextAttribute(GetStdHandle(-11), 0x04)
  134. self.__set_warning_color = lambda: SetConsoleTextAttribute(GetStdHandle(-11), 0x06)
  135. self.__set_debug_color = lambda: SetConsoleTextAttribute(GetStdHandle(-11), 0x002)
  136. self.__reset_color = lambda: SetConsoleTextAttribute(GetStdHandle(-11), 0x07)
  137. elif os.name == 'posix':
  138. self.__set_error_color = lambda: sys.stderr.write('\033[31m')
  139. self.__set_warning_color = lambda: sys.stderr.write('\033[33m')
  140. self.__set_debug_color = lambda: sys.stderr.write('\033[32m')
  141. self.__reset_color = lambda: sys.stderr.write('\033[0m')
  142. @classmethod
  143. def getLogger(cls, *args, **kwargs):
  144. return cls(*args, **kwargs)
  145. def basicConfig(self, *args, **kwargs):
  146. self.level = int(kwargs.get('level', self.__class__.INFO))
  147. if self.level > self.__class__.DEBUG:
  148. self.debug = self.dummy
  149. def log(self, level, fmt, *args, **kwargs):
  150. sys.stderr.write('%s - [%s] %s\n' % (level, time.ctime()[4:-5], fmt % args))
  151. def dummy(self, *args, **kwargs):
  152. pass
  153. def debug(self, fmt, *args, **kwargs):
  154. self.__set_debug_color()
  155. self.log('DEBUG', fmt, *args, **kwargs)
  156. self.__reset_color()
  157. def info(self, fmt, *args, **kwargs):
  158. self.log('INFO', fmt, *args)
  159. def warning(self, fmt, *args, **kwargs):
  160. self.__set_warning_color()
  161. self.log('WARNING', fmt, *args, **kwargs)
  162. self.__reset_color()
  163. def warn(self, fmt, *args, **kwargs):
  164. self.warning(fmt, *args, **kwargs)
  165. def error(self, fmt, *args, **kwargs):
  166. self.__set_error_color()
  167. self.log('ERROR', fmt, *args, **kwargs)
  168. self.__reset_color()
  169. def exception(self, fmt, *args, **kwargs):
  170. self.error(fmt, *args, **kwargs)
  171. sys.stderr.write(traceback.format_exc() + '\n')
  172. def critical(self, fmt, *args, **kwargs):
  173. self.__set_error_color()
  174. self.log('CRITICAL', fmt, *args, **kwargs)
  175. self.__reset_color()
  176. logging = sys.modules['logging'] = Logging('logging')
  177. class LRUCache(object):
  178. """http://pypi.python.org/pypi/lru/"""
  179. def __init__(self, max_items=100):
  180. self.cache = {}
  181. self.key_order = []
  182. self.max_items = max_items
  183. def __setitem__(self, key, value):
  184. self.cache[key] = value
  185. self._mark(key)
  186. def __getitem__(self, key):
  187. value = self.cache[key]
  188. self._mark(key)
  189. return value
  190. def __contains__(self, key):
  191. return key in self.cache
  192. def _mark(self, key):
  193. if key in self.key_order:
  194. self.key_order.remove(key)
  195. self.key_order.insert(0, key)
  196. if len(self.key_order) > self.max_items:
  197. index = self.max_items // 2
  198. delitem = self.cache.__delitem__
  199. key_order = self.key_order
  200. any(delitem(key_order[x]) for x in xrange(index, len(key_order)))
  201. self.key_order = self.key_order[:index]
  202. def clear(self):
  203. self.cache = {}
  204. self.key_order = []
  205. class CertUtil(object):
  206. """CertUtil module, based on mitmproxy"""
  207. ca_vendor = 'GoAgent'
  208. ca_keyfile = 'CA.crt'
  209. ca_certdir = 'certs'
  210. ca_lock = threading.Lock()
  211. @staticmethod
  212. def create_ca():
  213. key = OpenSSL.crypto.PKey()
  214. key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)
  215. ca = OpenSSL.crypto.X509()
  216. ca.set_serial_number(0)
  217. ca.set_version(2)
  218. subj = ca.get_subject()
  219. subj.countryName = 'CN'
  220. subj.stateOrProvinceName = 'Internet'
  221. subj.localityName = 'Cernet'
  222. subj.organizationName = CertUtil.ca_vendor
  223. subj.organizationalUnitName = '%s Root' % CertUtil.ca_vendor
  224. subj.commonName = '%s CA' % CertUtil.ca_vendor
  225. ca.gmtime_adj_notBefore(0)
  226. ca.gmtime_adj_notAfter(24 * 60 * 60 * 3652)
  227. ca.set_issuer(ca.get_subject())
  228. ca.set_pubkey(key)
  229. ca.add_extensions([
  230. OpenSSL.crypto.X509Extension(b'basicConstraints', True, b'CA:TRUE'),
  231. # OpenSSL.crypto.X509Extension(b'nsCertType', True, b'sslCA'),
  232. OpenSSL.crypto.X509Extension(b'extendedKeyUsage', True, b'serverAuth,clientAuth,emailProtection,timeStamping,msCodeInd,msCodeCom,msCTLSign,msSGC,msEFS,nsSGC'),
  233. OpenSSL.crypto.X509Extension(b'keyUsage', False, b'keyCertSign, cRLSign'),
  234. OpenSSL.crypto.X509Extension(b'subjectKeyIdentifier', False, b'hash', subject=ca), ])
  235. ca.sign(key, 'sha1')
  236. return key, ca
  237. @staticmethod
  238. def dump_ca():
  239. key, ca = CertUtil.create_ca()
  240. with open(CertUtil.ca_keyfile, 'wb') as fp:
  241. fp.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, ca))
  242. fp.write(OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key))
  243. @staticmethod
  244. def _get_cert(commonname, sans=()):
  245. with open(CertUtil.ca_keyfile, 'rb') as fp:
  246. content = fp.read()
  247. key = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, content)
  248. ca = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, content)
  249. pkey = OpenSSL.crypto.PKey()
  250. pkey.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)
  251. req = OpenSSL.crypto.X509Req()
  252. subj = req.get_subject()
  253. subj.countryName = 'CN'
  254. subj.stateOrProvinceName = 'Internet'
  255. subj.localityName = 'Cernet'
  256. subj.organizationalUnitName = '%s Branch' % CertUtil.ca_vendor
  257. if commonname[0] == '.':
  258. subj.commonName = '*' + commonname
  259. subj.organizationName = '*' + commonname
  260. sans = ['*'+commonname] + [x for x in sans if x != '*'+commonname]
  261. else:
  262. subj.commonName = commonname
  263. subj.organizationName = commonname
  264. sans = [commonname] + [x for x in sans if x != commonname]
  265. #req.add_extensions([OpenSSL.crypto.X509Extension(b'subjectAltName', True, ', '.join('DNS: %s' % x for x in sans)).encode()])
  266. req.set_pubkey(pkey)
  267. req.sign(pkey, 'sha1')
  268. cert = OpenSSL.crypto.X509()
  269. cert.set_version(2)
  270. try:
  271. cert.set_serial_number(int(hashlib.md5(commonname.encode('utf-8')).hexdigest(), 16))
  272. except OpenSSL.SSL.Error:
  273. cert.set_serial_number(int(time.time()*1000))
  274. cert.gmtime_adj_notBefore(0)
  275. cert.gmtime_adj_notAfter(60 * 60 * 24 * 3652)
  276. cert.set_issuer(ca.get_subject())
  277. cert.set_subject(req.get_subject())
  278. cert.set_pubkey(req.get_pubkey())
  279. if commonname[0] == '.':
  280. sans = ['*'+commonname] + [s for s in sans if s != '*'+commonname]
  281. else:
  282. sans = [commonname] + [s for s in sans if s != commonname]
  283. #cert.add_extensions([OpenSSL.crypto.X509Extension(b'subjectAltName', True, ', '.join('DNS: %s' % x for x in sans))])
  284. cert.sign(key, 'sha1')
  285. certfile = os.path.join(CertUtil.ca_certdir, commonname + '.crt')
  286. with open(certfile, 'wb') as fp:
  287. fp.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert))
  288. fp.write(OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, pkey))
  289. return certfile
  290. @staticmethod
  291. def get_cert(commonname, sans=()):
  292. if commonname.count('.') >= 2 and [len(x) for x in reversed(commonname.split('.'))] > [2, 4]:
  293. commonname = '.'+commonname.partition('.')[-1]
  294. certfile = os.path.join(CertUtil.ca_certdir, commonname + '.crt')
  295. if os.path.exists(certfile):
  296. return certfile
  297. elif OpenSSL is None:
  298. return CertUtil.ca_keyfile
  299. else:
  300. with CertUtil.ca_lock:
  301. if os.path.exists(certfile):
  302. return certfile
  303. return CertUtil._get_cert(commonname, sans)
  304. @staticmethod
  305. def import_ca(certfile):
  306. commonname = os.path.splitext(os.path.basename(certfile))[0]
  307. sha1digest = 'AB:70:2C:DF:18:EB:E8:B4:38:C5:28:69:CD:4A:5D:EF:48:B4:0E:33'
  308. if OpenSSL:
  309. try:
  310. with open(certfile, 'rb') as fp:
  311. x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, fp.read())
  312. commonname = next(v.decode() for k, v in x509.get_subject().get_components() if k == b'O')
  313. sha1digest = x509.digest('sha1')
  314. except StandardError as e:
  315. logging.error('load_certificate(certfile=%r) failed:%s', certfile, e)
  316. if sys.platform.startswith('win'):
  317. import ctypes
  318. with open(certfile, 'rb') as fp:
  319. certdata = fp.read()
  320. if certdata.startswith(b'-----'):
  321. begin = b'-----BEGIN CERTIFICATE-----'
  322. end = b'-----END CERTIFICATE-----'
  323. certdata = base64.b64decode(b''.join(certdata[certdata.find(begin)+len(begin):certdata.find(end)].strip().splitlines()))
  324. crypt32 = ctypes.WinDLL(b'crypt32.dll'.decode())
  325. store_handle = crypt32.CertOpenStore(10, 0, 0, 0x4000 | 0x20000, b'ROOT'.decode())
  326. if not store_handle:
  327. return -1
  328. X509_ASN_ENCODING = 0x00000001
  329. CERT_FIND_HASH = 0x10000
  330. class CRYPT_HASH_BLOB(ctypes.Structure):
  331. _fields_ = [('cbData', ctypes.c_ulong), ('pbData', ctypes.c_char_p)]
  332. crypt_hash = CRYPT_HASH_BLOB(20, binascii.a2b_hex(sha1digest.replace(':', '')))
  333. crypt_handle = crypt32.CertFindCertificateInStore(store_handle, X509_ASN_ENCODING, 0, CERT_FIND_HASH, ctypes.byref(crypt_hash), None)
  334. if crypt_handle:
  335. crypt32.CertFreeCertificateContext(crypt_handle)
  336. return 0
  337. ret = crypt32.CertAddEncodedCertificateToStore(store_handle, 0x1, certdata, len(certdata), 4, None)
  338. crypt32.CertCloseStore(store_handle, 0)
  339. del crypt32
  340. return 0 if ret else -1
  341. elif sys.platform == 'darwin':
  342. return os.system(('security find-certificate -a -c "%s" | grep "%s" >/dev/null || security add-trusted-cert -d -r trustRoot -k "/Library/Keychains/System.keychain" "%s"' % (commonname, commonname, certfile.decode('utf-8'))).encode('utf-8'))
  343. elif sys.platform.startswith('linux'):
  344. import platform
  345. platform_distname = platform.dist()[0]
  346. if platform_distname == 'Ubuntu':
  347. pemfile = "/etc/ssl/certs/%s.pem" % commonname
  348. new_certfile = "/usr/local/share/ca-certificates/%s.crt" % commonname
  349. if not os.path.exists(pemfile):
  350. return os.system('cp "%s" "%s" && update-ca-certificates' % (certfile, new_certfile))
  351. elif any(os.path.isfile('%s/certutil' % x) for x in os.environ['PATH'].split(os.pathsep)):
  352. return os.system('certutil -L -d sql:$HOME/.pki/nssdb | grep "%s" || certutil -d sql:$HOME/.pki/nssdb -A -t "C,," -n "%s" -i "%s"' % (commonname, commonname, certfile))
  353. else:
  354. logging.warning('please install *libnss3-tools* package to import GoAgent root ca')
  355. return 0
  356. @staticmethod
  357. def check_ca():
  358. #Check CA exists
  359. capath = os.path.join(os.path.dirname(os.path.abspath(__file__)), CertUtil.ca_keyfile)
  360. certdir = os.path.join(os.path.dirname(os.path.abspath(__file__)), CertUtil.ca_certdir)
  361. if not os.path.exists(capath):
  362. if not OpenSSL:
  363. logging.critical('CA.key is not exist and OpenSSL is disabled, ABORT!')
  364. sys.exit(-1)
  365. if os.path.exists(certdir):
  366. if os.path.isdir(certdir):
  367. any(os.remove(x) for x in glob.glob(certdir+'/*.crt')+glob.glob(certdir+'/.*.crt'))
  368. else:
  369. os.remove(certdir)
  370. os.mkdir(certdir)
  371. CertUtil.dump_ca()
  372. if glob.glob('%s/*.key' % CertUtil.ca_certdir):
  373. for filename in glob.glob('%s/*.key' % CertUtil.ca_certdir):
  374. try:
  375. os.remove(filename)
  376. os.remove(os.path.splitext(filename)[0]+'.crt')
  377. except EnvironmentError:
  378. pass
  379. #Check CA imported
  380. if CertUtil.import_ca(capath) != 0:
  381. logging.warning('install root certificate failed, Please run as administrator/root/sudo')
  382. #Check Certs Dir
  383. if not os.path.exists(certdir):
  384. os.makedirs(certdir)
  385. class DetectMobileBrowser:
  386. """detect mobile function from http://detectmobilebrowsers.com"""
  387. regex_match_a = re.compile(r"(android|bb\\d+|meego).+mobile|avantgo|bada\\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino", re.I|re.M).search
  388. regex_match_b = re.compile(r"1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\\-(n|u)|c55\\/|capi|ccwa|cdm\\-|cell|chtm|cldc|cmd\\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\\-s|devi|dica|dmob|do(c|p)o|ds(12|\\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\\-|_)|g1 u|g560|gene|gf\\-5|g\\-mo|go(\\.w|od)|gr(ad|un)|haie|hcit|hd\\-(m|p|t)|hei\\-|hi(pt|ta)|hp( i|ip)|hs\\-c|ht(c(\\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\\-(20|go|ma)|i230|iac( |\\-|\\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\\/)|klon|kpt |kwc\\-|kyo(c|k)|le(no|xi)|lg( g|\\/(k|l|u)|50|54|\\-[a-w])|libw|lynx|m1\\-w|m3ga|m50\\/|ma(te|ui|xo)|mc(01|21|ca)|m\\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\\-2|po(ck|rt|se)|prox|psio|pt\\-g|qa\\-a|qc(07|12|21|32|60|\\-[2-7]|i\\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\\-|oo|p\\-)|sdk\\/|se(c(\\-|0|1)|47|mc|nd|ri)|sgh\\-|shar|sie(\\-|m)|sk\\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\\-|v\\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\\-|tdg\\-|tel(i|m)|tim\\-|t\\-mo|to(pl|sh)|ts(70|m\\-|m3|m5)|tx\\-9|up(\\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\\-|your|zeto|zte\\-", re.I|re.M).search
  389. @staticmethod
  390. def detect(user_agent):
  391. return DetectMobileBrowser.regex_match_a(user_agent) or DetectMobileBrowser.regex_match_b(user_agent)
  392. class SSLConnection(object):
  393. """OpenSSL Connection Wapper"""
  394. def __init__(self, context, sock):
  395. self._context = context
  396. self._sock = sock
  397. self._connection = OpenSSL.SSL.Connection(context, sock)
  398. self._makefile_refs = 0
  399. def __getattr__(self, attr):
  400. if attr not in ('_context', '_sock', '_connection', '_makefile_refs'):
  401. return getattr(self._connection, attr)
  402. def __wait_sock_io(self, io_func, *args, **kwargs):
  403. timeout = self._sock.gettimeout() or 0.1
  404. fd = self._sock.fileno()
  405. while True:
  406. try:
  407. return io_func(*args, **kwargs)
  408. except (OpenSSL.SSL.WantReadError, OpenSSL.SSL.WantX509LookupError):
  409. sys.exc_clear()
  410. _, _, errors = select.select([fd], [], [fd], timeout)
  411. if errors:
  412. break
  413. except OpenSSL.SSL.WantWriteError:
  414. sys.exc_clear()
  415. _, _, errors = select.select([], [fd], [fd], timeout)
  416. if errors:
  417. break
  418. def accept(self):
  419. sock, addr = self._sock.accept()
  420. client = OpenSSL.SSL.Connection(sock._context, sock)
  421. return client, addr
  422. def do_handshake(self):
  423. return self.__wait_sock_io(self._connection.do_handshake)
  424. def connect(self, *args, **kwargs):
  425. return self.__wait_sock_io(self._connection.connect, *args, **kwargs)
  426. def send(self, data, flags=0):
  427. try:
  428. return self.__wait_sock_io(self._connection.send, data, flags)
  429. except OpenSSL.SSL.SysCallError as e:
  430. if e[0] == -1 and not data:
  431. # errors when writing empty strings are expected and can be ignored
  432. return 0
  433. raise
  434. def recv(self, bufsiz, flags=0):
  435. pending = self._connection.pending()
  436. if pending:
  437. return self._connection.recv(min(pending, bufsiz))
  438. try:
  439. return self.__wait_sock_io(self._connection.recv, bufsiz, flags)
  440. except OpenSSL.SSL.ZeroReturnError:
  441. return ''
  442. def read(self, bufsiz, flags=0):
  443. return self.recv(bufsiz, flags)
  444. def write(self, buf, flags=0):
  445. return self.sendall(buf, flags)
  446. def close(self):
  447. if self._makefile_refs < 1:
  448. self._connection = None
  449. if self._sock:
  450. socket.socket.close(self._sock)
  451. else:
  452. self._makefile_refs -= 1
  453. def makefile(self, mode='r', bufsize=-1):
  454. self._makefile_refs += 1
  455. return socket._fileobject(self, mode, bufsize, close=True)
  456. @staticmethod
  457. def context_builder(ssl_version='SSLv23', ca_certs=None, cipher_suites=('ALL', '!aNULL', '!eNULL')):
  458. protocol_version = getattr(OpenSSL.SSL, '%s_METHOD' % ssl_version)
  459. ssl_context = OpenSSL.SSL.Context(protocol_version)
  460. if ca_certs:
  461. ssl_context.load_verify_locations(os.path.abspath(ca_certs))
  462. ssl_context.set_verify(OpenSSL.SSL.VERIFY_PEER, lambda c, x, e, d, ok: ok)
  463. else:
  464. ssl_context.set_verify(OpenSSL.SSL.VERIFY_NONE, lambda c, x, e, d, ok: ok)
  465. ssl_context.set_cipher_list(':'.join(cipher_suites))
  466. if hasattr(OpenSSL.SSL, 'SESS_CACHE_BOTH'):
  467. ssl_context.set_session_cache_mode(OpenSSL.SSL.SESS_CACHE_BOTH)
  468. return ssl_context
  469. class ProxyUtil(object):
  470. """ProxyUtil module, based on urllib2"""
  471. @staticmethod
  472. def parse_proxy(proxy):
  473. return urllib2._parse_proxy(proxy)
  474. @staticmethod
  475. def get_system_proxy():
  476. proxies = urllib2.getproxies()
  477. return proxies.get('https') or proxies.get('http') or {}
  478. @staticmethod
  479. def get_listen_ip():
  480. listen_ip = '127.0.0.1'
  481. sock = None
  482. try:
  483. sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  484. sock.connect(('8.8.8.8', 53))
  485. listen_ip = sock.getsockname()[0]
  486. except socket.error:
  487. pass
  488. finally:
  489. if sock:
  490. sock.close()
  491. return listen_ip
  492. def inflate(data):
  493. return zlib.decompress(data, -zlib.MAX_WBITS)
  494. def deflate(data):
  495. return zlib.compress(data)[2:-4]
  496. def parse_hostport(host, default_port=80):
  497. m = re.match(r'(.+)[#](\d+)$', host)
  498. if m:
  499. return m.group(1).strip('[]'), int(m.group(2))
  500. else:
  501. return host.strip('[]'), default_port
  502. def dnslib_resolve_over_udp(query, dnsservers, timeout, **kwargs):
  503. """
  504. http://gfwrev.blogspot.com/2009/11/gfwdns.html
  505. http://zh.wikipedia.org/wiki/%E5%9F%9F%E5%90%8D%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%BC%93%E5%AD%98%E6%B1%A1%E6%9F%93
  506. http://support.microsoft.com/kb/241352
  507. """
  508. if not isinstance(query, (basestring, dnslib.DNSRecord)):
  509. raise TypeError('query argument requires string/DNSRecord')
  510. blacklist = kwargs.get('blacklist', ())
  511. turstservers = kwargs.get('turstservers', ())
  512. dns_v4_servers = [x for x in dnsservers if ':' not in x]
  513. dns_v6_servers = [x for x in dnsservers if ':' in x]
  514. sock_v4 = sock_v6 = None
  515. socks = []
  516. if dns_v4_servers:
  517. sock_v4 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  518. socks.append(sock_v4)
  519. if dns_v6_servers:
  520. sock_v6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
  521. socks.append(sock_v6)
  522. timeout_at = time.time() + timeout
  523. try:
  524. for _ in xrange(4):
  525. try:
  526. for dnsserver in dns_v4_servers:
  527. if isinstance(query, basestring):
  528. query = dnslib.DNSRecord(q=dnslib.DNSQuestion(query))
  529. query_data = query.pack()
  530. sock_v4.sendto(query_data, parse_hostport(dnsserver, 53))
  531. for dnsserver in dns_v6_servers:
  532. if isinstance(query, basestring):
  533. query = dnslib.DNSRecord(q=dnslib.DNSQuestion(query, qtype=dnslib.QTYPE.AAAA))
  534. query_data = query.pack()
  535. sock_v6.sendto(query_data, parse_hostport(dnsserver, 53))
  536. while time.time() < timeout_at:
  537. ins, _, _ = select.select(socks, [], [], 0.1)
  538. for sock in ins:
  539. reply_data, reply_address = sock.recvfrom(512)
  540. reply_server = reply_address[0]
  541. record = dnslib.DNSRecord.parse(reply_data)
  542. iplist = [str(x.rdata) for x in record.rr if x.rtype in (1, 28, 255)]
  543. if any(x in blacklist for x in iplist):
  544. logging.debug('query=%r dnsservers=%r record bad iplist=%r', query, dnsservers, iplist)
  545. elif record.header.rcode and not iplist and reply_server in turstservers:
  546. logging.info('query=%r trust reply_server=%r record rcode=%s', query, reply_server, record.header.rcode)
  547. return record
  548. elif iplist:
  549. logging.debug('query=%r reply_server=%r record iplist=%s', query, reply_server, iplist)
  550. return record
  551. else:
  552. logging.debug('query=%r reply_server=%r record null iplist=%s', query, reply_server, iplist)
  553. continue
  554. except socket.error as e:
  555. logging.warning('handle dns query=%s socket: %r', query, e)
  556. raise socket.gaierror(11004, 'getaddrinfo %r from %r failed' % (query, dnsservers))
  557. finally:
  558. for sock in socks:
  559. sock.close()
  560. def dnslib_resolve_over_tcp(query, dnsservers, timeout, **kwargs):
  561. """dns query over tcp"""
  562. if not isinstance(query, (basestring, dnslib.DNSRecord)):
  563. raise TypeError('query argument requires string/DNSRecord')
  564. blacklist = kwargs.get('blacklist', ())
  565. def do_resolve(query, dnsserver, timeout, queobj):
  566. if isinstance(query, basestring):
  567. qtype = dnslib.QTYPE.AAAA if ':' in dnsserver else dnslib.QTYPE.A
  568. query = dnslib.DNSRecord(q=dnslib.DNSQuestion(query, qtype=qtype))
  569. query_data = query.pack()
  570. sock_family = socket.AF_INET6 if ':' in dnsserver else socket.AF_INET
  571. sock = socket.socket(sock_family)
  572. rfile = None
  573. try:
  574. sock.settimeout(timeout or None)
  575. sock.connect(parse_hostport(dnsserver, 53))
  576. sock.send(struct.pack('>h', len(query_data)) + query_data)
  577. rfile = sock.makefile('r', 1024)
  578. reply_data_length = rfile.read(2)
  579. if len(reply_data_length) < 2:
  580. raise socket.gaierror(11004, 'getaddrinfo %r from %r failed' % (query, dnsserver))
  581. reply_data = rfile.read(struct.unpack('>h', reply_data_length)[0])
  582. record = dnslib.DNSRecord.parse(reply_data)
  583. iplist = [str(x.rdata) for x in record.rr if x.rtype in (1, 28, 255)]
  584. if any(x in blacklist for x in iplist):
  585. logging.debug('query=%r dnsserver=%r record bad iplist=%r', query, dnsserver, iplist)
  586. raise socket.gaierror(11004, 'getaddrinfo %r from %r failed' % (query, dnsserver))
  587. else:
  588. logging.debug('query=%r dnsserver=%r record iplist=%s', query, dnsserver, iplist)
  589. queobj.put(record)
  590. except socket.error as e:
  591. logging.debug('query=%r dnsserver=%r failed %r', query, dnsserver, e)
  592. queobj.put(e)
  593. finally:
  594. if rfile:
  595. rfile.close()
  596. sock.close()
  597. queobj = Queue.Queue()
  598. for dnsserver in dnsservers:
  599. thread.start_new_thread(do_resolve, (query, dnsserver, timeout, queobj))
  600. for i in range(len(dnsservers)):
  601. try:
  602. result = queobj.get(timeout)
  603. except Queue.Empty:
  604. raise socket.gaierror(11004, 'getaddrinfo %r from %r failed' % (query, dnsservers))
  605. if result and not isinstance(result, Exception):
  606. return result
  607. elif i == len(dnsservers) - 1:
  608. logging.debug('dnslib_resolve_over_tcp %r with %s return %r', query, dnsservers, result)
  609. raise socket.gaierror(11004, 'getaddrinfo %r from %r failed' % (query, dnsservers))
  610. def dnslib_record2iplist(record):
  611. """convert dnslib.DNSRecord to iplist"""
  612. assert isinstance(record, dnslib.DNSRecord)
  613. iplist = [x for x in (str(r.rdata) for r in record.rr) if re.match(r'^\d+\.\d+\.\d+\.\d+$', x) or ':' in x]
  614. return iplist
  615. def get_dnsserver_list():
  616. if os.name == 'nt':
  617. import ctypes
  618. import ctypes.wintypes
  619. DNS_CONFIG_DNS_SERVER_LIST = 6
  620. buf = ctypes.create_string_buffer(2048)
  621. ctypes.windll.dnsapi.DnsQueryConfig(DNS_CONFIG_DNS_SERVER_LIST, 0, None, None, ctypes.byref(buf), ctypes.byref(ctypes.wintypes.DWORD(len(buf))))
  622. ipcount = struct.unpack('I', buf[0:4])[0]
  623. iplist = [socket.inet_ntoa(buf[i:i+4]) for i in xrange(4, ipcount*4+4, 4)]
  624. return iplist
  625. elif os.path.isfile('/etc/resolv.conf'):
  626. with open('/etc/resolv.conf', 'rb') as fp:
  627. return re.findall(r'(?m)^nameserver\s+(\S+)', fp.read())
  628. else:
  629. logging.warning("get_dnsserver_list failed: unsupport platform '%s-%s'", sys.platform, os.name)
  630. return []
  631. def spawn_later(seconds, target, *args, **kwargs):
  632. def wrap(*args, **kwargs):
  633. time.sleep(seconds)
  634. return target(*args, **kwargs)
  635. return thread.start_new_thread(wrap, args, kwargs)
  636. def spawn_period(seconds, target, *args, **kwargs):
  637. def wrap(*args, **kwargs):
  638. try:
  639. time.sleep(seconds)
  640. target(*args, **kwargs)
  641. except StandardError as e:
  642. logging.warning('%r(%s, %s) error: %r', target, args, kwargs, e)
  643. return thread.start_new_thread(wrap, args, kwargs)
  644. def is_clienthello(data):
  645. if len(data) < 20:
  646. return False
  647. if data.startswith('\x16\x03'):
  648. # TLSv12/TLSv11/TLSv1/SSLv3
  649. length, = struct.unpack('>h', data[3:5])
  650. return len(data) == 5 + length
  651. elif data[0] == '\x80' and data[2:4] == '\x01\x03':
  652. # SSLv23
  653. return len(data) == 2 + ord(data[1])
  654. else:
  655. return False
  656. def is_google_ip(ipaddr):
  657. return ipaddr.startswith(('173.194.', '207.126.', '209.85.', '216.239.', '64.18.', '64.233.', '66.102.', '66.249.', '72.14.', '74.125.'))
  658. def extract_sni_name(packet):
  659. if packet.startswith('\x16\x03'):
  660. stream = io.BytesIO(packet)
  661. stream.read(0x2b)
  662. session_id_length = ord(stream.read(1))
  663. stream.read(session_id_length)
  664. cipher_suites_length, = struct.unpack('>h', stream.read(2))
  665. stream.read(cipher_suites_length+2)
  666. extensions_length, = struct.unpack('>h', stream.read(2))
  667. # extensions = {}
  668. while True:
  669. data = stream.read(2)
  670. if not data:
  671. break
  672. etype, = struct.unpack('>h', data)
  673. elen, = struct.unpack('>h', stream.read(2))
  674. edata = stream.read(elen)
  675. if etype == 0:
  676. server_name = edata[5:]
  677. return server_name
  678. class URLFetch(object):
  679. """URLFetch for gae/php fetchservers"""
  680. skip_headers = frozenset(['Vary', 'Via', 'X-Forwarded-For', 'Proxy-Authorization', 'Proxy-Connection', 'Upgrade', 'X-Chrome-Variations', 'Connection', 'Cache-Control'])
  681. def __init__(self, handler, fetchserver):
  682. assert isinstance(fetchserver, basestring) and callable(create_http_request)
  683. self.handler = handler
  684. self.fetchserver = fetchserver
  685. self.create_http_request = handler.create_http_request
  686. def fetch(self, method, url, headers, body, timeout, **kwargs):
  687. if '.appspot.com/' in self.fetchserver:
  688. response = self.__gae_fetch(method, url, headers, body, timeout, **kwargs)
  689. response.app_header_parsed = True
  690. else:
  691. response = self.__php_fetch(method, url, headers, body, timeout, **kwargs)
  692. response.app_header_parsed = False
  693. return response
  694. def __gae_fetch(self, method, url, headers, body, timeout, **kwargs):
  695. rc4crypt = lambda s, k: RC4Cipher(k).encrypt(s) if k else s
  696. options = ''
  697. rsa_key = None
  698. if 'options' in kwargs:
  699. options = kwargs.get('options')
  700. del kwargs['options']
  701. if 'rsa_key' in kwargs:
  702. rsa_key = kwargs.get('rsa_key')
  703. del kwargs['rsa_key']
  704. if isinstance(body, basestring) and body:
  705. if len(body) < 10 * 1024 * 1024 and 'Content-Encoding' not in headers:
  706. zbody = deflate(body)
  707. if len(zbody) < len(body):
  708. body = zbody
  709. headers['Content-Encoding'] = 'deflate'
  710. headers['Content-Length'] = str(len(body))
  711. # GAE donot allow set `Host` header
  712. if 'Host' in headers:
  713. del headers['Host']
  714. metadata = 'G-Method:%s\nG-Url:%s\n%s' % (method, url, ''.join('G-%s:%s\n' % (k, v) for k, v in kwargs.items() if v))
  715. skip_headers = self.skip_headers
  716. metadata += ''.join('%s:%s\n' % (k.title(), v) for k, v in headers.items() if k not in skip_headers)
  717. # prepare GAE request
  718. request_fetchserver = self.fetchserver
  719. request_method = 'POST'
  720. request_headers = {}
  721. if 'rsa' in options and rsa_key:
  722. from Crypto.PublicKey import RSA
  723. from Crypto.Cipher import PKCS1_OAEP
  724. from Crypto.Random.random import StrongRandom
  725. rsakey = RSA.importKey(rsa_key.strip())
  726. rsakey = PKCS1_OAEP.new(rsakey)
  727. crypt_cookie_key = base64.b64encode(read_random_bits(256))
  728. crypt_payload_key = base64.b64encode(read_random_bits(256))
  729. crypt_response_msg_key = base64.b64encode(read_random_bits(256))
  730. crypt_response_fp_key = base64.b64encode(read_random_bits(256))
  731. crypt_keys = crypt_cookie_key + '|' + crypt_payload_key + '|' + crypt_response_msg_key + '|' + crypt_response_fp_key
  732. request_headers['X-GOA-KEYS'] = base64.b64encode(rsakey.encrypt(crypt_keys))
  733. else:
  734. crypt_cookie_key = kwargs.get('password')
  735. crypt_payload_key = kwargs.get('password')
  736. crypt_response_msg_key = kwargs.get('password')
  737. crypt_response_fp_key = kwargs.get('password')
  738. if common.GAE_OBFUSCATE:
  739. # if 'rc4' in options:
  740. # request_headers['X-GOA-Options'] = 'rc4'
  741. # cookie = base64.b64encode(rc4crypt(zlib.compress(metadata)[2:-4], crypt_cookie_key)).strip()
  742. # body = rc4crypt(body, crypt_payload_key)
  743. # else:
  744. # cookie = base64.b64encode(zlib.compress(metadata)[2:-4]).strip()
  745. # request_headers['Cookie'] = cookie
  746. # if body:
  747. # request_headers['Content-Length'] = str(len(body))
  748. # else:
  749. # request_method = 'GET'
  750. request_method = 'GET'
  751. request_fetchserver += '/ps/%s.gif' % uuid.uuid1()
  752. request_headers['X-GOA-PS1'] = base64.b64encode(deflate(metadata)).strip()
  753. if body:
  754. request_headers['X-GOA-PS2'] = base64.b64encode(deflate(body)).strip()
  755. body = ''
  756. if common.GAE_PAGESPEED:
  757. request_fetchserver = re.sub(r'^(\w+://)', r'\g<1>1-ps.googleusercontent.com/h/', request_fetchserver)
  758. else:
  759. metadata = deflate(metadata)
  760. body = '%s%s%s' % (struct.pack('!h', len(metadata)), metadata, body)
  761. if 'rc4' in options:
  762. request_headers['X-GOA-Options'] = 'rc4'
  763. body = rc4crypt(body, crypt_payload_key)
  764. request_headers['Content-Length'] = str(len(body))
  765. # post data
  766. need_crlf = 0 if common.GAE_MODE == 'https' else 1
  767. need_validate = common.GAE_VALIDATE
  768. cache_key = '%s:%d' % (common.HOST_POSTFIX_MAP['.appspot.com'], 443 if common.GAE_MODE == 'https' else 80)
  769. response = self.create_http_request(request_method, request_fetchserver, request_headers, body, timeout, crlf=need_crlf, validate=need_validate, cache_key=cache_key)
  770. response.app_status = response.status
  771. response.app_options = response.getheader('X-GOA-Options', '')
  772. if response.status != 200:
  773. return response
  774. data = response.read(4)
  775. if len(data) < 4:
  776. response.status = 502
  777. response.fp = io.BytesIO(b'connection aborted. too short leadbyte data=' + data)
  778. response.read = response.fp.read
  779. return response
  780. response.status, headers_length = struct.unpack('!hh', data)
  781. data = response.read(headers_length)
  782. if len(data) < headers_length:
  783. response.status = 502
  784. response.fp = io.BytesIO(b'connection aborted. too short headers data=' + data)
  785. response.read = response.fp.read
  786. return response
  787. if 'rc4' not in response.app_options:
  788. response.msg = httplib.HTTPMessage(io.BytesIO(inflate(data)))
  789. else:
  790. response.msg = httplib.HTTPMessage(io.BytesIO(inflate(rc4crypt(data, crypt_response_msg_key))))
  791. if crypt_response_fp_key and response.fp:
  792. response.fp = CipherFileObject(response.fp, RC4Cipher(crypt_response_fp_key))
  793. gae_appid = urlparse.urlsplit(self.fetchserver).netloc.split('.')[-3]
  794. if response.status == 206:
  795. logging.debug('%s "GAE %s %s %s" %s %s %s %s', self.handler.address_string(), method, url, self.handler.protocol_version, gae_appid, options, response.status, response.getheader('Content-Length', '-'))
  796. else:
  797. logging.info('%s "GAE %s %s %s" %s %s %s %s', self.handler.address_string(), method, url, self.handler.protocol_version, gae_appid, options, response.status, response.getheader('Content-Length', '-'))
  798. return response
  799. def __php_fetch(self, method, url, headers, body, timeout, **kwargs):
  800. if body:
  801. if len(body) < 10 * 1024 * 1024 and 'Content-Encoding' not in headers:
  802. zbody = deflate(body)
  803. if len(zbody) < len(body):
  804. body = zbody
  805. headers['Content-Encoding'] = 'deflate'
  806. headers['Content-Length'] = str(len(body))
  807. skip_headers = self.skip_headers
  808. metadata = 'G-Method:%s\nG-Url:%s\n%s%s' % (method, url, ''.join('G-%s:%s\n' % (k, v) for k, v in kwargs.items() if v), ''.join('%s:%s\n' % (k, v) for k, v in headers.items() if k not in skip_headers))
  809. metadata = deflate(metadata)
  810. app_body = b''.join((struct.pack('!h', len(metadata)), metadata, body))
  811. app_headers = {'Content-Length': len(app_body), 'Content-Type': 'application/octet-stream'}
  812. fetchserver = '%s?%s' % (self.fetchserver, random.random())
  813. crlf = 0
  814. cache_key = '%s//:%s' % urlparse.urlsplit(fetchserver)[:2]
  815. response = self.create_http_request('POST', fetchserver, app_headers, app_body, timeout, crlf=crlf, cache_key=cache_key)
  816. if not response:
  817. raise socket.error(errno.ECONNRESET, 'urlfetch %r return None' % url)
  818. if response.status >= 400:
  819. return response
  820. response.app_status = response.status
  821. need_decrypt = kwargs.get('password') and response.app_status == 200 and response.getheader('Content-Type', '') == 'image/gif' and response.fp
  822. if need_decrypt:
  823. response.fp = CipherFileObject(response.fp, XORCipher(kwargs['password'][0]))
  824. return response
  825. class BaseProxyHandlerFilter(object):
  826. """base proxy handler filter"""
  827. def filter(self, handler):
  828. raise NotImplementedError
  829. class SimpleProxyHandlerFilter(BaseProxyHandlerFilter):
  830. """simple proxy handler filter"""
  831. def filter(self, handler):
  832. if handler.command == 'CONNECT':
  833. return [handler.FORWARD, handler.host, handler.port, handler.connect_timeout]
  834. else:
  835. return [handler.DIRECT, {}]
  836. class AuthFilter(BaseProxyHandlerFilter):
  837. """authorization filter"""
  838. auth_info = "Proxy authentication required"""
  839. white_list = set(['127.0.0.1'])
  840. def __init__(self, username, password):
  841. self.username = username
  842. self.password = password
  843. def check_auth_header(self, auth_header):
  844. method, _, auth_data = auth_header.partition(' ')
  845. if method == 'Basic':
  846. username, _, password = base64.b64decode(auth_data).partition(':')
  847. if username == self.username and password == self.password:
  848. return True
  849. return False
  850. def filter(self, handler):
  851. if self.white_list and handler.client_address[0] in self.white_list:
  852. return None
  853. auth_header = handler.headers.get('Proxy-Authorization') or getattr(handler, 'auth_header', None)
  854. if auth_header and self.check_auth_header(auth_header):
  855. handler.auth_header = auth_header
  856. else:
  857. headers = {'Access-Control-Allow-Origin': '*',
  858. 'Proxy-Authenticate': 'Basic realm="%s"' % self.auth_info,
  859. 'Content-Length': '0',
  860. 'Connection': 'keep-alive'}
  861. return [handler.MOCK, 407, headers, '']
  862. class SimpleProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
  863. """SimpleProxyHandler for GoAgent 3.x"""
  864. protocol_version = 'HTTP/1.1'
  865. ssl_version = ssl.PROTOCOL_SSLv23
  866. disable_transport_ssl = True
  867. scheme = 'http'
  868. skip_headers = frozenset(['Vary', 'Via', 'X-Forwarded-For', 'Proxy-Authorization', 'Proxy-Connection', 'Upgrade', 'X-Chrome-Variations', 'Connection', 'Cache-Control'])
  869. bufsize = 256 * 1024
  870. max_timeout = 4
  871. connect_timeout = 2
  872. first_run_lock = threading.Lock()
  873. handler_filters = [SimpleProxyHandlerFilter()]
  874. sticky_filter = None
  875. def finish(self):
  876. """make python2 BaseHTTPRequestHandler happy"""
  877. try:
  878. BaseHTTPServer.BaseHTTPRequestHandler.finish(self)
  879. except NetWorkIOError as e:
  880. if e[0] not in (errno.ECONNABORTED, errno.ECONNRESET, errno.EPIPE):
  881. raise
  882. def address_string(self):
  883. return '%s:%s' % self.client_address[:2]
  884. def send_response(self, code, message=None):
  885. if message is None:
  886. if code in self.responses:
  887. message = self.responses[code][0]
  888. else:
  889. message = ''
  890. if self.request_version != 'HTTP/0.9':
  891. self.wfile.write('%s %d %s\r\n' % (self.protocol_version, code, message))
  892. def send_header(self, keyword, value):
  893. """Send a MIME header."""
  894. base_send_header = BaseHTTPServer.BaseHTTPRequestHandler.send_header
  895. keyword = keyword.title()
  896. if keyword == 'Set-Cookie':
  897. for cookie in re.split(r', (?=[^ =]+(?:=|$))', value):
  898. base_send_header(self, keyword, cookie)
  899. elif keyword == 'Content-Disposition' and '"' not in value:
  900. value = re.sub(r'filename=([^"\']+)', 'filename="\\1"', value)
  901. base_send_header(self, keyword, value)
  902. else:
  903. base_send_header(self, keyword, value)
  904. def setup(self):
  905. if isinstance(self.__class__.first_run, collections.Callable):
  906. try:
  907. with self.__class__.first_run_lock:
  908. if isinstance(self.__class__.first_run, collections.Callable):
  909. self.first_run()
  910. self.__class__.first_run = None
  911. except StandardError as e:
  912. logging.exception('%s.first_run() return %r', self.__class__, e)
  913. self.__class__.setup = BaseHTTPServer.BaseHTTPRequestHandler.setup
  914. self.__class__.do_CONNECT = self.__class__.do_METHOD
  915. self.__class__.do_GET = self.__class__.do_METHOD
  916. self.__class__.do_PUT = self.__class__.do_METHOD
  917. self.__class__.do_POST = self.__class__.do_METHOD
  918. self.__class__.do_HEAD = self.__class__.do_METHOD
  919. self.__class__.do_DELETE = self.__class__.do_METHOD
  920. self.__class__.do_OPTIONS = self.__class__.do_METHOD
  921. self.setup()
  922. def handle_one_request(self):
  923. if not self.disable_transport_ssl and self.scheme == 'http':
  924. leadbyte = self.connection.recv(1, socket.MSG_PEEK)
  925. if leadbyte in ('\x80', '\x16'):
  926. server_name = ''
  927. if leadbyte == '\x16':
  928. for _ in xrange(2):
  929. leaddata = self.connection.recv(1024, socket.MSG_PEEK)
  930. if is_clienthello(leaddata):
  931. try:
  932. server_name = extract_sni_name(leaddata)
  933. finally:
  934. break
  935. try:
  936. certfile = CertUtil.get_cert(server_name or 'www.google.com')
  937. ssl_sock = ssl.wrap_socket(self.connection, ssl_version=self.ssl_version, keyfile=certfile, certfile=certfile, server_side=True)
  938. except StandardError as e:
  939. if e.args[0] not in (errno.ECONNABORTED, errno.ECONNRESET):
  940. logging.exception('ssl.wrap_socket(self.connection=%r) failed: %s', self.connection, e)
  941. return
  942. self.connection = ssl_sock
  943. self.rfile = self.connection.makefile('rb', self.bufsize)
  944. self.wfile = self.connection.makefile('wb', 0)
  945. self.scheme = 'https'
  946. return BaseHTTPServer.BaseHTTPRequestHandler.handle_one_request(self)
  947. def first_run(self):
  948. pass
  949. def gethostbyname2(self, hostname):
  950. return socket.gethostbyname_ex(hostname)[-1]
  951. def create_tcp_connection(self, hostname, port, timeout, **kwargs):
  952. return socket.create_connection((hostname, port), timeout)
  953. def create_ssl_connection(self, hostname, port, timeout, **kwargs):
  954. sock = self.create_tcp_connection(hostname, port, timeout, **kwargs)
  955. ssl_sock = ssl.wrap_socket(sock, ssl_version=self.ssl_version)
  956. return ssl_sock
  957. def create_http_request(self, method, url, headers, body, timeout, **kwargs):
  958. scheme, netloc, path, query, _ = urlparse.urlsplit(url)
  959. if netloc.rfind(':') <= netloc.rfind(']'):
  960. # no port number
  961. host = netloc
  962. port = 443 if scheme == 'https' else 80
  963. else:
  964. host, _, port = netloc.rpartition(':')
  965. port = int(port)
  966. if query:
  967. path += '?' + query
  968. if 'Host' not in headers:
  969. headers['Host'] = host
  970. if body and 'Content-Length' not in headers:
  971. headers['Content-Length'] = str(len(body))
  972. ConnectionType = httplib.HTTPSConnection if scheme == 'https' else httplib.HTTPConnection
  973. connection = ConnectionType(netloc, timeout=timeout)
  974. connection.request(method, path, body=body, headers=headers)
  975. response = connection.getresponse()
  976. return response
  977. def create_http_request_withserver(self, fetchserver, method, url, headers, body, timeout, **kwargs):
  978. return URLFetch(self, fetchserver).fetch(method, url, headers, body, timeout, **kwargs)
  979. def handle_urlfetch_error(self, fetchserver, response):
  980. pass
  981. def handle_urlfetch_response_close(self, fetchserver, response):
  982. pass
  983. def parse_header(self):
  984. if self.command == 'CONNECT':
  985. netloc = self.path
  986. elif self.path[0] == '/':
  987. netloc = self.headers.get('Host', 'localhost')
  988. self.path = '%s://%s%s' % (self.scheme, netloc, self.path)
  989. else:
  990. netloc = urlparse.urlsplit(self.path).netloc
  991. m = re.match(r'^(.+):(\d+)$', netloc)
  992. if m:
  993. self.host = m.group(1).strip('[]')
  994. self.port = int(m.group(2))
  995. else:
  996. self.host = netloc
  997. self.port = 443 if self.scheme == 'https' else 80
  998. def forward_socket(self, local, remote, timeout):
  999. try:
  1000. tick = 1
  1001. bufsize = self.bufsize
  1002. timecount = timeout
  1003. while 1:
  1004. timecount -= tick
  1005. if timecount <= 0:
  1006. break
  1007. (ins, _, errors) = select.select([local, remote], [], [local, remote], tick)
  1008. if errors:
  1009. break
  1010. for sock in ins:
  1011. data = sock.recv(bufsize)
  1012. if not data:
  1013. break

Large files files are truncated, but you can click here to view the full file