PageRenderTime 147ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 1ms

/local/proxy.py

https://github.com/panzt/goagent
Python | 3159 lines | 3041 code | 60 blank | 58 comment | 187 complexity | 0bd3ffb6fcaf45dff3fea9417f7de7d9 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.16'
  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. import gevent
  52. import gevent.socket
  53. import gevent.server
  54. import gevent.queue
  55. import gevent.monkey
  56. gevent.monkey.patch_all(subprocess=True)
  57. except ImportError:
  58. gevent = None
  59. except TypeError:
  60. gevent.monkey.patch_all()
  61. sys.stderr.write('\033[31m Warning: Please update gevent to the latest 1.0 version!\033[0m\n')
  62. import errno
  63. import time
  64. import struct
  65. import collections
  66. import binascii
  67. import zlib
  68. import itertools
  69. import re
  70. import io
  71. import fnmatch
  72. import traceback
  73. import random
  74. import base64
  75. import string
  76. import hashlib
  77. import threading
  78. import thread
  79. import socket
  80. import ssl
  81. import select
  82. import Queue
  83. import SocketServer
  84. import ConfigParser
  85. import BaseHTTPServer
  86. import httplib
  87. import urllib
  88. import urllib2
  89. import urlparse
  90. try:
  91. import dnslib
  92. except ImportError:
  93. dnslib = None
  94. try:
  95. import OpenSSL
  96. except ImportError:
  97. OpenSSL = None
  98. try:
  99. import pygeoip
  100. except ImportError:
  101. pygeoip = None
  102. HAS_PYPY = hasattr(sys, 'pypy_version_info')
  103. NetWorkIOError = (socket.error, ssl.SSLError, OSError) if not OpenSSL else (socket.error, ssl.SSLError, OpenSSL.SSL.Error, OSError)
  104. class Logging(type(sys)):
  105. CRITICAL = 50
  106. FATAL = CRITICAL
  107. ERROR = 40
  108. WARNING = 30
  109. WARN = WARNING
  110. INFO = 20
  111. DEBUG = 10
  112. NOTSET = 0
  113. def __init__(self, *args, **kwargs):
  114. self.level = self.__class__.INFO
  115. self.__set_error_color = lambda: None
  116. self.__set_warning_color = lambda: None
  117. self.__set_debug_color = lambda: None
  118. self.__reset_color = lambda: None
  119. if hasattr(sys.stderr, 'isatty') and sys.stderr.isatty():
  120. if os.name == 'nt':
  121. import ctypes
  122. SetConsoleTextAttribute = ctypes.windll.kernel32.SetConsoleTextAttribute
  123. GetStdHandle = ctypes.windll.kernel32.GetStdHandle
  124. self.__set_error_color = lambda: SetConsoleTextAttribute(GetStdHandle(-11), 0x04)
  125. self.__set_warning_color = lambda: SetConsoleTextAttribute(GetStdHandle(-11), 0x06)
  126. self.__set_debug_color = lambda: SetConsoleTextAttribute(GetStdHandle(-11), 0x002)
  127. self.__reset_color = lambda: SetConsoleTextAttribute(GetStdHandle(-11), 0x07)
  128. elif os.name == 'posix':
  129. self.__set_error_color = lambda: sys.stderr.write('\033[31m')
  130. self.__set_warning_color = lambda: sys.stderr.write('\033[33m')
  131. self.__set_debug_color = lambda: sys.stderr.write('\033[32m')
  132. self.__reset_color = lambda: sys.stderr.write('\033[0m')
  133. @classmethod
  134. def getLogger(cls, *args, **kwargs):
  135. return cls(*args, **kwargs)
  136. def basicConfig(self, *args, **kwargs):
  137. self.level = int(kwargs.get('level', self.__class__.INFO))
  138. if self.level > self.__class__.DEBUG:
  139. self.debug = self.dummy
  140. def log(self, level, fmt, *args, **kwargs):
  141. sys.stderr.write('%s - [%s] %s\n' % (level, time.ctime()[4:-5], fmt % args))
  142. def dummy(self, *args, **kwargs):
  143. pass
  144. def debug(self, fmt, *args, **kwargs):
  145. self.__set_debug_color()
  146. self.log('DEBUG', fmt, *args, **kwargs)
  147. self.__reset_color()
  148. def info(self, fmt, *args, **kwargs):
  149. self.log('INFO', fmt, *args)
  150. def warning(self, fmt, *args, **kwargs):
  151. self.__set_warning_color()
  152. self.log('WARNING', fmt, *args, **kwargs)
  153. self.__reset_color()
  154. def warn(self, fmt, *args, **kwargs):
  155. self.warning(fmt, *args, **kwargs)
  156. def error(self, fmt, *args, **kwargs):
  157. self.__set_error_color()
  158. self.log('ERROR', fmt, *args, **kwargs)
  159. self.__reset_color()
  160. def exception(self, fmt, *args, **kwargs):
  161. self.error(fmt, *args, **kwargs)
  162. sys.stderr.write(traceback.format_exc() + '\n')
  163. def critical(self, fmt, *args, **kwargs):
  164. self.__set_error_color()
  165. self.log('CRITICAL', fmt, *args, **kwargs)
  166. self.__reset_color()
  167. logging = sys.modules['logging'] = Logging('logging')
  168. class LRUCache(object):
  169. """http://pypi.python.org/pypi/lru/"""
  170. def __init__(self, max_items=100):
  171. self.cache = {}
  172. self.key_order = []
  173. self.max_items = max_items
  174. def __setitem__(self, key, value):
  175. self.cache[key] = value
  176. self._mark(key)
  177. def __getitem__(self, key):
  178. value = self.cache[key]
  179. self._mark(key)
  180. return value
  181. def __contains__(self, key):
  182. return key in self.cache
  183. def _mark(self, key):
  184. if key in self.key_order:
  185. self.key_order.remove(key)
  186. self.key_order.insert(0, key)
  187. if len(self.key_order) > self.max_items:
  188. index = self.max_items // 2
  189. delitem = self.cache.__delitem__
  190. key_order = self.key_order
  191. any(delitem(key_order[x]) for x in xrange(index, len(key_order)))
  192. self.key_order = self.key_order[:index]
  193. def clear(self):
  194. self.cache = {}
  195. self.key_order = []
  196. class CertUtil(object):
  197. """CertUtil module, based on mitmproxy"""
  198. ca_vendor = 'GoAgent'
  199. ca_keyfile = 'CA.crt'
  200. ca_certdir = 'certs'
  201. ca_lock = threading.Lock()
  202. @staticmethod
  203. def create_ca():
  204. key = OpenSSL.crypto.PKey()
  205. key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)
  206. ca = OpenSSL.crypto.X509()
  207. ca.set_serial_number(0)
  208. ca.set_version(2)
  209. subj = ca.get_subject()
  210. subj.countryName = 'CN'
  211. subj.stateOrProvinceName = 'Internet'
  212. subj.localityName = 'Cernet'
  213. subj.organizationName = CertUtil.ca_vendor
  214. subj.organizationalUnitName = '%s Root' % CertUtil.ca_vendor
  215. subj.commonName = '%s CA' % CertUtil.ca_vendor
  216. ca.gmtime_adj_notBefore(0)
  217. ca.gmtime_adj_notAfter(24 * 60 * 60 * 3652)
  218. ca.set_issuer(ca.get_subject())
  219. ca.set_pubkey(key)
  220. ca.add_extensions([
  221. OpenSSL.crypto.X509Extension(b'basicConstraints', True, b'CA:TRUE'),
  222. OpenSSL.crypto.X509Extension(b'nsCertType', True, b'sslCA'),
  223. OpenSSL.crypto.X509Extension(b'extendedKeyUsage', True, b'serverAuth,clientAuth,emailProtection,timeStamping,msCodeInd,msCodeCom,msCTLSign,msSGC,msEFS,nsSGC'),
  224. OpenSSL.crypto.X509Extension(b'keyUsage', False, b'keyCertSign, cRLSign'),
  225. OpenSSL.crypto.X509Extension(b'subjectKeyIdentifier', False, b'hash', subject=ca), ])
  226. ca.sign(key, 'sha1')
  227. return key, ca
  228. @staticmethod
  229. def dump_ca():
  230. key, ca = CertUtil.create_ca()
  231. with open(CertUtil.ca_keyfile, 'wb') as fp:
  232. fp.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, ca))
  233. fp.write(OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key))
  234. @staticmethod
  235. def _get_cert(commonname, sans=()):
  236. with open(CertUtil.ca_keyfile, 'rb') as fp:
  237. content = fp.read()
  238. key = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, content)
  239. ca = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, content)
  240. pkey = OpenSSL.crypto.PKey()
  241. pkey.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)
  242. req = OpenSSL.crypto.X509Req()
  243. subj = req.get_subject()
  244. subj.countryName = 'CN'
  245. subj.stateOrProvinceName = 'Internet'
  246. subj.localityName = 'Cernet'
  247. subj.organizationalUnitName = '%s Branch' % CertUtil.ca_vendor
  248. if commonname[0] == '.':
  249. subj.commonName = '*' + commonname
  250. subj.organizationName = '*' + commonname
  251. sans = ['*'+commonname] + [x for x in sans if x != '*'+commonname]
  252. else:
  253. subj.commonName = commonname
  254. subj.organizationName = commonname
  255. sans = [commonname] + [x for x in sans if x != commonname]
  256. #req.add_extensions([OpenSSL.crypto.X509Extension(b'subjectAltName', True, ', '.join('DNS: %s' % x for x in sans)).encode()])
  257. req.set_pubkey(pkey)
  258. req.sign(pkey, 'sha1')
  259. cert = OpenSSL.crypto.X509()
  260. cert.set_version(2)
  261. try:
  262. cert.set_serial_number(int(hashlib.md5(commonname.encode('utf-8')).hexdigest(), 16))
  263. except OpenSSL.SSL.Error:
  264. cert.set_serial_number(int(time.time()*1000))
  265. cert.gmtime_adj_notBefore(0)
  266. cert.gmtime_adj_notAfter(60 * 60 * 24 * 3652)
  267. cert.set_issuer(ca.get_subject())
  268. cert.set_subject(req.get_subject())
  269. cert.set_pubkey(req.get_pubkey())
  270. if commonname[0] == '.':
  271. sans = ['*'+commonname] + [s for s in sans if s != '*'+commonname]
  272. else:
  273. sans = [commonname] + [s for s in sans if s != commonname]
  274. #cert.add_extensions([OpenSSL.crypto.X509Extension(b'subjectAltName', True, ', '.join('DNS: %s' % x for x in sans))])
  275. cert.sign(key, 'sha1')
  276. certfile = os.path.join(CertUtil.ca_certdir, commonname + '.crt')
  277. with open(certfile, 'wb') as fp:
  278. fp.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert))
  279. fp.write(OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, pkey))
  280. return certfile
  281. @staticmethod
  282. def get_cert(commonname, sans=()):
  283. if commonname.count('.') >= 2 and [len(x) for x in reversed(commonname.split('.'))] > [2, 4]:
  284. commonname = '.'+commonname.partition('.')[-1]
  285. certfile = os.path.join(CertUtil.ca_certdir, commonname + '.crt')
  286. if os.path.exists(certfile):
  287. return certfile
  288. elif OpenSSL is None:
  289. return CertUtil.ca_keyfile
  290. else:
  291. with CertUtil.ca_lock:
  292. if os.path.exists(certfile):
  293. return certfile
  294. return CertUtil._get_cert(commonname, sans)
  295. @staticmethod
  296. def import_ca(certfile):
  297. commonname = os.path.splitext(os.path.basename(certfile))[0]
  298. sha1digest = 'AB:70:2C:DF:18:EB:E8:B4:38:C5:28:69:CD:4A:5D:EF:48:B4:0E:33'
  299. if OpenSSL:
  300. try:
  301. with open(certfile, 'rb') as fp:
  302. x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, fp.read())
  303. commonname = next(v.decode() for k, v in x509.get_subject().get_components() if k == b'O')
  304. sha1digest = x509.digest('sha1')
  305. except StandardError as e:
  306. logging.error('load_certificate(certfile=%r) failed:%s', certfile, e)
  307. if sys.platform.startswith('win'):
  308. import ctypes
  309. with open(certfile, 'rb') as fp:
  310. certdata = fp.read()
  311. if certdata.startswith(b'-----'):
  312. begin = b'-----BEGIN CERTIFICATE-----'
  313. end = b'-----END CERTIFICATE-----'
  314. certdata = base64.b64decode(b''.join(certdata[certdata.find(begin)+len(begin):certdata.find(end)].strip().splitlines()))
  315. crypt32 = ctypes.WinDLL(b'crypt32.dll'.decode())
  316. store_handle = crypt32.CertOpenStore(10, 0, 0, 0x4000 | 0x20000, b'ROOT'.decode())
  317. if not store_handle:
  318. return -1
  319. X509_ASN_ENCODING = 0x00000001
  320. CERT_FIND_HASH = 0x10000
  321. class CRYPT_HASH_BLOB(ctypes.Structure):
  322. _fields_ = [('cbData', ctypes.c_ulong), ('pbData', ctypes.c_char_p)]
  323. crypt_hash = CRYPT_HASH_BLOB(20, binascii.a2b_hex(sha1digest.replace(':', '')))
  324. crypt_handle = crypt32.CertFindCertificateInStore(store_handle, X509_ASN_ENCODING, 0, CERT_FIND_HASH, ctypes.byref(crypt_hash), None)
  325. if crypt_handle:
  326. crypt32.CertFreeCertificateContext(crypt_handle)
  327. return 0
  328. ret = crypt32.CertAddEncodedCertificateToStore(store_handle, 0x1, certdata, len(certdata), 4, None)
  329. crypt32.CertCloseStore(store_handle, 0)
  330. del crypt32
  331. return 0 if ret else -1
  332. elif sys.platform == 'darwin':
  333. 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'))
  334. elif sys.platform.startswith('linux'):
  335. import platform
  336. platform_distname = platform.dist()[0]
  337. if platform_distname == 'Ubuntu':
  338. pemfile = "/etc/ssl/certs/%s.pem" % commonname
  339. new_certfile = "/usr/local/share/ca-certificates/%s.crt" % commonname
  340. if not os.path.exists(pemfile):
  341. return os.system('cp "%s" "%s" && update-ca-certificates' % (certfile, new_certfile))
  342. elif any(os.path.isfile('%s/certutil' % x) for x in os.environ['PATH'].split(os.pathsep)):
  343. 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))
  344. else:
  345. logging.warning('please install *libnss3-tools* package to import GoAgent root ca')
  346. return 0
  347. @staticmethod
  348. def check_ca():
  349. #Check CA exists
  350. capath = os.path.join(os.path.dirname(os.path.abspath(__file__)), CertUtil.ca_keyfile)
  351. certdir = os.path.join(os.path.dirname(os.path.abspath(__file__)), CertUtil.ca_certdir)
  352. if not os.path.exists(capath):
  353. if not OpenSSL:
  354. logging.critical('CA.key is not exist and OpenSSL is disabled, ABORT!')
  355. sys.exit(-1)
  356. if os.path.exists(certdir):
  357. if os.path.isdir(certdir):
  358. any(os.remove(x) for x in glob.glob(certdir+'/*.crt')+glob.glob(certdir+'/.*.crt'))
  359. else:
  360. os.remove(certdir)
  361. os.mkdir(certdir)
  362. CertUtil.dump_ca()
  363. if glob.glob('%s/*.key' % CertUtil.ca_certdir):
  364. for filename in glob.glob('%s/*.key' % CertUtil.ca_certdir):
  365. try:
  366. os.remove(filename)
  367. os.remove(os.path.splitext(filename)[0]+'.crt')
  368. except EnvironmentError:
  369. pass
  370. #Check CA imported
  371. if CertUtil.import_ca(capath) != 0:
  372. logging.warning('install root certificate failed, Please run as administrator/root/sudo')
  373. #Check Certs Dir
  374. if not os.path.exists(certdir):
  375. os.makedirs(certdir)
  376. class DetectMobileBrowser:
  377. """detect mobile function from http://detectmobilebrowsers.com"""
  378. 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
  379. 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
  380. @staticmethod
  381. def detect(user_agent):
  382. return DetectMobileBrowser.regex_match_a(user_agent) or DetectMobileBrowser.regex_match_b(user_agent)
  383. class SSLConnection(object):
  384. """OpenSSL Connection Wapper"""
  385. def __init__(self, context, sock):
  386. self._context = context
  387. self._sock = sock
  388. self._connection = OpenSSL.SSL.Connection(context, sock)
  389. self._makefile_refs = 0
  390. def __getattr__(self, attr):
  391. if attr not in ('_context', '_sock', '_connection', '_makefile_refs'):
  392. return getattr(self._connection, attr)
  393. def __wait_sock_io(self, sock, io_func, *args, **kwargs):
  394. timeout = self._sock.gettimeout() or 0.1
  395. fd = self._sock.fileno()
  396. while True:
  397. try:
  398. return io_func(*args, **kwargs)
  399. except (OpenSSL.SSL.WantReadError, OpenSSL.SSL.WantX509LookupError):
  400. sys.exc_clear()
  401. _, _, errors = select.select([fd], [], [fd], timeout)
  402. if errors:
  403. break
  404. except OpenSSL.SSL.WantWriteError:
  405. sys.exc_clear()
  406. _, _, errors = select.select([], [fd], [fd], timeout)
  407. if errors:
  408. break
  409. def accept(self):
  410. sock, addr = self._sock.accept()
  411. client = OpenSSL.SSL.Connection(sock._context, sock)
  412. return client, addr
  413. def do_handshake(self):
  414. return self.__wait_sock_io(self._sock, self._connection.do_handshake)
  415. def connect(self, *args, **kwargs):
  416. return self.__wait_sock_io(self._sock, self._connection.connect, *args, **kwargs)
  417. def send(self, data, flags=0):
  418. try:
  419. return self.__wait_sock_io(self._sock, self._connection.send, data, flags)
  420. except OpenSSL.SSL.SysCallError as e:
  421. if e[0] == -1 and not data:
  422. # errors when writing empty strings are expected and can be ignored
  423. return 0
  424. raise
  425. def recv(self, bufsiz, flags=0):
  426. pending = self._connection.pending()
  427. if pending:
  428. return self._connection.recv(min(pending, bufsiz))
  429. try:
  430. return self.__wait_sock_io(self._sock, self._connection.recv, bufsiz, flags)
  431. except OpenSSL.SSL.ZeroReturnError:
  432. return ''
  433. def read(self, bufsiz, flags=0):
  434. return self.recv(bufsiz, flags)
  435. def write(self, buf, flags=0):
  436. return self.sendall(buf, flags)
  437. def close(self):
  438. if self._makefile_refs < 1:
  439. self._connection = None
  440. if self._sock:
  441. socket.socket.close(self._sock)
  442. else:
  443. self._makefile_refs -= 1
  444. def makefile(self, mode='r', bufsize=-1):
  445. self._makefile_refs += 1
  446. return socket._fileobject(self, mode, bufsize, close=True)
  447. class ProxyUtil(object):
  448. """ProxyUtil module, based on urllib2"""
  449. @staticmethod
  450. def parse_proxy(proxy):
  451. return urllib2._parse_proxy(proxy)
  452. @staticmethod
  453. def get_system_proxy():
  454. proxies = urllib2.getproxies()
  455. return proxies.get('https') or proxies.get('http') or {}
  456. @staticmethod
  457. def get_listen_ip():
  458. listen_ip = '127.0.0.1'
  459. sock = None
  460. try:
  461. sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  462. sock.connect(('8.8.8.8', 53))
  463. listen_ip = sock.getsockname()[0]
  464. except socket.error:
  465. pass
  466. finally:
  467. if sock:
  468. sock.close()
  469. return listen_ip
  470. def parse_hostport(host, default_port=80):
  471. m = re.match(r'(.+)[#](\d+)$', host)
  472. if m:
  473. return m.group(1).strip('[]'), int(m.group(2))
  474. else:
  475. return host.strip('[]'), default_port
  476. def dnslib_resolve_over_udp(query, dnsservers, timeout, **kwargs):
  477. """
  478. http://gfwrev.blogspot.com/2009/11/gfwdns.html
  479. 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
  480. http://support.microsoft.com/kb/241352
  481. """
  482. if not isinstance(query, (basestring, dnslib.DNSRecord)):
  483. raise TypeError('query argument requires string/DNSRecord')
  484. blacklist = kwargs.get('blacklist', ())
  485. turstservers = kwargs.get('turstservers', ())
  486. dns_v4_servers = [x for x in dnsservers if ':' not in x]
  487. dns_v6_servers = [x for x in dnsservers if ':' in x]
  488. sock_v4 = sock_v6 = None
  489. socks = []
  490. if dns_v4_servers:
  491. sock_v4 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  492. socks.append(sock_v4)
  493. if dns_v6_servers:
  494. sock_v6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
  495. socks.append(sock_v6)
  496. timeout_at = time.time() + timeout
  497. try:
  498. for _ in xrange(4):
  499. try:
  500. for dnsserver in dns_v4_servers:
  501. if isinstance(query, basestring):
  502. query = dnslib.DNSRecord(q=dnslib.DNSQuestion(query))
  503. query_data = query.pack()
  504. sock_v4.sendto(query_data, parse_hostport(dnsserver, 53))
  505. for dnsserver in dns_v6_servers:
  506. if isinstance(query, basestring):
  507. query = dnslib.DNSRecord(q=dnslib.DNSQuestion(query, qtype=dnslib.QTYPE.AAAA))
  508. query_data = query.pack()
  509. sock_v6.sendto(query_data, parse_hostport(dnsserver, 53))
  510. while time.time() < timeout_at:
  511. ins, _, _ = select.select(socks, [], [], 0.1)
  512. for sock in ins:
  513. reply_data, reply_address = sock.recvfrom(512)
  514. reply_server = reply_address[0]
  515. record = dnslib.DNSRecord.parse(reply_data)
  516. iplist = [str(x.rdata) for x in record.rr if x.rtype in (1, 28, 255)]
  517. if any(x in blacklist for x in iplist):
  518. logging.warning('query=%r dnsservers=%r record bad iplist=%r', query, dnsservers, iplist)
  519. elif record.header.rcode and not iplist and reply_server in turstservers:
  520. logging.info('query=%r trust reply_server=%r record rcode=%s', query, reply_server, record.header.rcode)
  521. return record
  522. elif iplist:
  523. logging.debug('query=%r reply_server=%r record iplist=%s', query, reply_server, iplist)
  524. return record
  525. else:
  526. logging.debug('query=%r reply_server=%r record null iplist=%s', query, reply_server, iplist)
  527. continue
  528. except socket.error as e:
  529. logging.warning('handle dns query=%s socket: %r', query, e)
  530. raise socket.gaierror(11004, 'getaddrinfo %r from %r failed' % (query, dnsservers))
  531. finally:
  532. for sock in socks:
  533. sock.close()
  534. def dnslib_resolve_over_tcp(query, dnsservers, timeout, **kwargs):
  535. """dns query over tcp"""
  536. if not isinstance(query, (basestring, dnslib.DNSRecord)):
  537. raise TypeError('query argument requires string/DNSRecord')
  538. blacklist = kwargs.get('blacklist', ())
  539. def do_resolve(query, dnsserver, timeout, queobj):
  540. if isinstance(query, basestring):
  541. qtype = dnslib.QTYPE.AAAA if ':' in dnsserver else dnslib.QTYPE.A
  542. query = dnslib.DNSRecord(q=dnslib.DNSQuestion(query, qtype=qtype))
  543. query_data = query.pack()
  544. sock_family = socket.AF_INET6 if ':' in dnsserver else socket.AF_INET
  545. sock = socket.socket(sock_family)
  546. rfile = None
  547. try:
  548. sock.settimeout(timeout or None)
  549. sock.connect(parse_hostport(dnsserver, 53))
  550. sock.send(struct.pack('>h', len(query_data)) + query_data)
  551. rfile = sock.makefile('r', 1024)
  552. reply_data_length = rfile.read(2)
  553. if len(reply_data_length) < 2:
  554. raise socket.gaierror(11004, 'getaddrinfo %r from %r failed' % (query, dnsserver))
  555. reply_data = rfile.read(struct.unpack('>h', reply_data_length)[0])
  556. record = dnslib.DNSRecord.parse(reply_data)
  557. iplist = [str(x.rdata) for x in record.rr if x.rtype in (1, 28, 255)]
  558. if any(x in blacklist for x in iplist):
  559. logging.debug('query=%r dnsserver=%r record bad iplist=%r', query, dnsserver, iplist)
  560. raise socket.gaierror(11004, 'getaddrinfo %r from %r failed' % (query, dnsserver))
  561. else:
  562. logging.debug('query=%r dnsserver=%r record iplist=%s', query, dnsserver, iplist)
  563. queobj.put(record)
  564. except socket.error as e:
  565. logging.debug('query=%r dnsserver=%r failed %r', query, dnsserver, e)
  566. queobj.put(e)
  567. finally:
  568. if rfile:
  569. rfile.close()
  570. sock.close()
  571. queobj = Queue.Queue()
  572. for dnsserver in dnsservers:
  573. thread.start_new_thread(do_resolve, (query, dnsserver, timeout, queobj))
  574. for i in range(len(dnsservers)):
  575. try:
  576. result = queobj.get(timeout)
  577. except Queue.Empty:
  578. raise socket.gaierror(11004, 'getaddrinfo %r from %r failed' % (query, dnsservers))
  579. if result and not isinstance(result, Exception):
  580. return result
  581. elif i == len(dnsservers) - 1:
  582. logging.warning('dnslib_resolve_over_tcp %r with %s return %r', query, dnsservers, result)
  583. raise socket.gaierror(11004, 'getaddrinfo %r from %r failed' % (query, dnsservers))
  584. def dnslib_record2iplist(record):
  585. """convert dnslib.DNSRecord to iplist"""
  586. assert isinstance(record, dnslib.DNSRecord)
  587. 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]
  588. return iplist
  589. def get_dnsserver_list():
  590. if os.name == 'nt':
  591. import ctypes, ctypes.wintypes, struct, socket
  592. DNS_CONFIG_DNS_SERVER_LIST = 6
  593. buf = ctypes.create_string_buffer(2048)
  594. ctypes.windll.dnsapi.DnsQueryConfig(DNS_CONFIG_DNS_SERVER_LIST, 0, None, None, ctypes.byref(buf), ctypes.byref(ctypes.wintypes.DWORD(len(buf))))
  595. ipcount = struct.unpack('I', buf[0:4])[0]
  596. iplist = [socket.inet_ntoa(buf[i:i+4]) for i in xrange(4, ipcount*4+4, 4)]
  597. return iplist
  598. elif os.path.isfile('/etc/resolv.conf'):
  599. with open('/etc/resolv.conf', 'rb') as fp:
  600. return re.findall(r'(?m)^nameserver\s+(\S+)', fp.read())
  601. else:
  602. logging.warning("get_dnsserver_list failed: unsupport platform '%s-%s'", sys.platform, os.name)
  603. return []
  604. def spawn_later(seconds, target, *args, **kwargs):
  605. def wrap(*args, **kwargs):
  606. __import__('time').sleep(seconds)
  607. return target(*args, **kwargs)
  608. return __import__('thread').start_new_thread(wrap, args, kwargs)
  609. def is_clienthello(data):
  610. if len(data) < 20:
  611. return False
  612. if data.startswith('\x16\x03'):
  613. # TLSv12/TLSv11/TLSv1/SSLv3
  614. length, = struct.unpack('>h', data[3:5])
  615. return len(data) == 5 + length
  616. elif data[0] == '\x80' and data[2:4] == '\x01\x03':
  617. # SSLv23
  618. return len(data) == 2 + ord(data[1])
  619. else:
  620. return False
  621. def extract_sni_name(packet):
  622. if packet.startswith('\x16\x03'):
  623. stream = io.BytesIO(packet)
  624. stream.read(0x2b)
  625. session_id_length = ord(stream.read(1))
  626. stream.read(session_id_length)
  627. cipher_suites_length, = struct.unpack('>h', stream.read(2))
  628. stream.read(cipher_suites_length+2)
  629. extensions_length, = struct.unpack('>h', stream.read(2))
  630. extensions = {}
  631. while True:
  632. data = stream.read(2)
  633. if not data:
  634. break
  635. etype, = struct.unpack('>h', data)
  636. elen, = struct.unpack('>h', stream.read(2))
  637. edata = stream.read(elen)
  638. if etype == 0:
  639. server_name = edata[5:]
  640. return server_name
  641. class URLFetch(object):
  642. """URLFetch for gae/php fetchservers"""
  643. skip_headers = frozenset(['Vary', 'Via', 'X-Forwarded-For', 'Proxy-Authorization', 'Proxy-Connection', 'Upgrade', 'X-Chrome-Variations', 'Connection', 'Cache-Control'])
  644. def __init__(self, fetchserver, create_http_request):
  645. assert isinstance(fetchserver, basestring) and callable(create_http_request)
  646. self.fetchserver = fetchserver
  647. self.create_http_request = create_http_request
  648. def fetch(self, method, url, headers, body, timeout, **kwargs):
  649. if '.appspot.com/' in self.fetchserver:
  650. response = self.__gae_fetch(method, url, headers, body, timeout, **kwargs)
  651. response.app_header_parsed = True
  652. else:
  653. response = self.__php_fetch(method, url, headers, body, timeout, **kwargs)
  654. response.app_header_parsed = False
  655. return response
  656. def __gae_fetch(self, method, url, headers, body, timeout, **kwargs):
  657. # deflate = lambda x:zlib.compress(x)[2:-4]
  658. rc4crypt = lambda s, k: RC4Cipher(k).encrypt(s) if k else s
  659. if body:
  660. if len(body) < 10 * 1024 * 1024 and 'Content-Encoding' not in headers:
  661. zbody = zlib.compress(body)[2:-4]
  662. if len(zbody) < len(body):
  663. body = zbody
  664. headers['Content-Encoding'] = 'deflate'
  665. headers['Content-Length'] = str(len(body))
  666. # GAE donot allow set `Host` header
  667. if 'Host' in headers:
  668. del headers['Host']
  669. 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))
  670. skip_headers = self.skip_headers
  671. metadata += ''.join('%s:%s\n' % (k.title(), v) for k, v in headers.items() if k not in skip_headers)
  672. # prepare GAE request
  673. request_fetchserver = self.fetchserver
  674. request_method = 'POST'
  675. request_headers = {}
  676. if common.GAE_OBFUSCATE:
  677. request_method = 'GET'
  678. query_string = base64.b64encode(zlib.compress(metadata + '\n\n' + (body or ''))[2:-4]).strip()
  679. request_fetchserver += '?' + query_string
  680. if common.GAE_PAGESPEED:
  681. request_fetchserver = re.sub(r'^(\w+://)', r'\g<1>1-ps.googleusercontent.com/h/', request_fetchserver)
  682. else:
  683. metadata = zlib.compress(metadata)[2:-4]
  684. body = '%s%s%s' % (struct.pack('!h', len(metadata)), metadata, body)
  685. if 'rc4' in common.GAE_OPTIONS:
  686. request_headers['X-GOA-Options'] = 'rc4'
  687. body = rc4crypt(body, kwargs.get('password'))
  688. request_headers['Content-Length'] = str(len(body))
  689. # post data
  690. need_crlf = 0 if common.GAE_MODE == 'https' else 1
  691. need_validate = common.GAE_VALIDATE
  692. cache_key = '%s:%d' % (common.HOST_POSTFIX_MAP['.appspot.com'], 443 if common.GAE_MODE == 'https' else 80)
  693. response = self.create_http_request(request_method, request_fetchserver, request_headers, body, timeout, crlf=need_crlf, validate=need_validate, cache_key=cache_key)
  694. response.app_status = response.status
  695. response.app_options = response.getheader('X-GOA-Options', '')
  696. if response.status != 200:
  697. return response
  698. data = response.read(4)
  699. if len(data) < 4:
  700. response.status = 502
  701. response.fp = io.BytesIO(b'connection aborted. too short leadbyte data=' + data)
  702. response.read = response.fp.read
  703. return response
  704. response.status, headers_length = struct.unpack('!hh', data)
  705. data = response.read(headers_length)
  706. if len(data) < headers_length:
  707. response.status = 502
  708. response.fp = io.BytesIO(b'connection aborted. too short headers data=' + data)
  709. response.read = response.fp.read
  710. return response
  711. if 'rc4' not in response.app_options:
  712. response.msg = httplib.HTTPMessage(io.BytesIO(zlib.decompress(data, -zlib.MAX_WBITS)))
  713. else:
  714. response.msg = httplib.HTTPMessage(io.BytesIO(zlib.decompress(rc4crypt(data, kwargs.get('password')), -zlib.MAX_WBITS)))
  715. if kwargs.get('password') and response.fp:
  716. response.fp = CipherFileObject(response.fp, RC4Cipher(kwargs['password']))
  717. return response
  718. def __php_fetch(self, method, url, headers, body, timeout, **kwargs):
  719. if body:
  720. if len(body) < 10 * 1024 * 1024 and 'Content-Encoding' not in headers:
  721. zbody = zlib.compress(body)[2:-4]
  722. if len(zbody) < len(body):
  723. body = zbody
  724. headers['Content-Encoding'] = 'deflate'
  725. headers['Content-Length'] = str(len(body))
  726. skip_headers = self.skip_headers
  727. 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))
  728. metadata = zlib.compress(metadata)[2:-4]
  729. app_body = b''.join((struct.pack('!h', len(metadata)), metadata, body))
  730. app_headers = {'Content-Length': len(app_body), 'Content-Type': 'application/octet-stream'}
  731. fetchserver = '%s?%s' % (self.fetchserver, random.random())
  732. crlf = 0
  733. cache_key = '%s//:%s' % urlparse.urlsplit(fetchserver)[:2]
  734. response = self.create_http_request('POST', fetchserver, app_headers, app_body, timeout, crlf=crlf, cache_key=cache_key)
  735. if not response:
  736. raise socket.error(errno.ECONNRESET, 'urlfetch %r return None' % url)
  737. if response.status >= 400:
  738. return response
  739. response.app_status = response.status
  740. need_decrypt = kwargs.get('password') and response.app_status == 200 and response.getheader('Content-Type', '') == 'image/gif' and response.fp
  741. if need_decrypt:
  742. response.fp = CipherFileObject(response.fp, XORCipher(kwargs['password'][0]))
  743. return response
  744. class BaseProxyHandlerFilter(object):
  745. """base proxy handler filter"""
  746. def filter(self, handler):
  747. raise NotImplementedError
  748. class SimpleProxyHandlerFilter(BaseProxyHandlerFilter):
  749. """simple proxy handler filter"""
  750. def filter(self, handler):
  751. if handler.command == 'CONNECT':
  752. return [handler.FORWARD, handler.host, handler.port, handler.connect_timeout]
  753. else:
  754. return [handler.DIRECT, {}]
  755. class AuthFilter(BaseProxyHandlerFilter):
  756. """authorization filter"""
  757. auth_info = "Proxy authentication required"""
  758. white_list = set(['127.0.0.1'])
  759. def __init__(self, username, password):
  760. self.username = username
  761. self.password = password
  762. def check_auth_header(self, auth_header):
  763. method, _, auth_data = auth_header.partition(' ')
  764. if method == 'Basic':
  765. username, _, password = base64.b64decode(auth_data).partition(':')
  766. if username == self.username and password == self.password:
  767. return True
  768. return False
  769. def filter(self, handler):
  770. if self.white_list and handler.client_address[0] in self.white_list:
  771. return None
  772. auth_header = handler.headers.get('Proxy-Authorization') or getattr(handler, 'auth_header', None)
  773. if auth_header and self.check_auth_header(auth_header):
  774. handler.auth_header = auth_header
  775. else:
  776. headers = {'Access-Control-Allow-Origin': '*',
  777. 'Proxy-Authenticate': 'Basic realm="%s"' % self.auth_info,
  778. 'Content-Length': '0',
  779. 'Connection': 'keep-alive'}
  780. return [handler.MOCK, 407, headers, '']
  781. class SimpleProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
  782. """SimpleProxyHandler for GoAgent 3.x"""
  783. protocol_version = 'HTTP/1.1'
  784. ssl_version = ssl.PROTOCOL_SSLv23
  785. disable_transport_ssl = True
  786. scheme = 'http'
  787. skip_headers = frozenset(['Vary', 'Via', 'X-Forwarded-For', 'Proxy-Authorization', 'Proxy-Connection', 'Upgrade', 'X-Chrome-Variations', 'Connection', 'Cache-Control'])
  788. bufsize = 256 * 1024
  789. max_timeout = 4
  790. connect_timeout = 4
  791. first_run_lock = threading.Lock()
  792. handler_filters = [SimpleProxyHandlerFilter()]
  793. sticky_filter = None
  794. def finish(self):
  795. """make python2 BaseHTTPRequestHandler happy"""
  796. try:
  797. BaseHTTPServer.BaseHTTPRequestHandler.finish(self)
  798. except NetWorkIOError as e:
  799. if e[0] not in (errno.ECONNABORTED, errno.ECONNRESET, errno.EPIPE):
  800. raise
  801. def address_string(self):
  802. return '%s:%s' % self.client_address[:2]
  803. def send_response(self, code, message=None):
  804. if message is None:
  805. if code in self.responses:
  806. message = self.responses[code][0]
  807. else:
  808. message = ''
  809. if self.request_version != 'HTTP/0.9':
  810. self.wfile.write('%s %d %s\r\n' % (self.protocol_version, code, message))
  811. def send_header(self, keyword, value):
  812. """Send a MIME header."""
  813. base_send_header = BaseHTTPServer.BaseHTTPRequestHandler.send_header
  814. keyword = keyword.title()
  815. if keyword == 'Set-Cookie':
  816. for cookie in re.split(r', (?=[^ =]+(?:=|$))', value):
  817. base_send_header(self, keyword, cookie)
  818. elif keyword == 'Content-Disposition' and '"' not in value:
  819. value = re.sub(r'filename=([^"\']+)', 'filename="\\1"', value)
  820. base_send_header(self, keyword, value)
  821. else:
  822. base_send_header(self, keyword, value)
  823. def setup(self):
  824. if isinstance(self.__class__.first_run, collections.Callable):
  825. try:
  826. with self.__class__.first_run_lock:
  827. if isinstance(self.__class__.first_run, collections.Callable):
  828. self.first_run()
  829. self.__class__.first_run = None
  830. except StandardError as e:
  831. logging.exception('%s.first_run() return %r', self.__class__, e)
  832. self.__class__.setup = BaseHTTPServer.BaseHTTPRequestHandler.setup
  833. self.__class__.do_CONNECT = self.__class__.do_METHOD
  834. self.__class__.do_GET = self.__class__.do_METHOD
  835. self.__class__.do_PUT = self.__class__.do_METHOD
  836. self.__class__.do_POST = self.__class__.do_METHOD
  837. self.__class__.do_HEAD = self.__class__.do_METHOD
  838. self.__class__.do_DELETE = self.__class__.do_METHOD
  839. self.__class__.do_OPTIONS = self.__class__.do_METHOD
  840. self.setup()
  841. def handle_one_request(self):
  842. if not self.disable_transport_ssl and self.scheme == 'http':
  843. leadbyte = self.connection.recv(1, socket.MSG_PEEK)
  844. if leadbyte in ('\x80', '\x16'):
  845. server_name = ''
  846. if leadbyte == '\x16':
  847. for _ in xrange(2):
  848. leaddata = self.connection.recv(1024, socket.MSG_PEEK)
  849. if is_clienthello(leaddata):
  850. try:
  851. server_name = extract_sni_name(leaddata)
  852. finally:
  853. break
  854. try:
  855. certfile = CertUtil.get_cert(server_name or 'www.google.com')
  856. ssl_sock = ssl.wrap_socket(self.connection, ssl_version=self.ssl_version, keyfile=certfile, certfile=certfile, server_side=True)
  857. except StandardError as e:
  858. if e.args[0] not in (errno.ECONNABORTED, errno.ECONNRESET):
  859. logging.exception('ssl.wrap_socket(self.connection=%r) failed: %s', self.connection, e)
  860. return
  861. self.connection = ssl_sock
  862. self.rfile = self.connection.makefile('rb', self.bufsize)
  863. self.wfile = self.connection.makefile('wb', 0)
  864. self.scheme = 'https'
  865. return BaseHTTPServer.BaseHTTPRequestHandler.handle_one_request(self)
  866. def first_run(self):
  867. pass
  868. def gethostbyname2(self, hostname):
  869. return socket.gethostbyname_ex(hostname)[-1]
  870. def create_tcp_connection(self, hostname, port, timeout, **kwargs):
  871. return socket.create_connection((hostname, port), timeout)
  872. def create_ssl_connection(self, hostname, port, timeout, **kwargs):
  873. sock = self.create_tcp_connection(hostname, port, timeout, **kwargs)
  874. ssl_sock = ssl.wrap_socket(sock, ssl_version=self.ssl_version)
  875. return ssl_sock
  876. def create_http_request(self, method, url, headers, body, timeout, **kwargs):
  877. scheme, netloc, path, query, _ = urlparse.urlsplit(url)
  878. if netloc.rfind(':') <= netloc.rfind(']'):
  879. # no port number
  880. host = netloc
  881. port = 443 if scheme == 'https' else 80
  882. else:
  883. host, _, port = netloc.rpartition(':')
  884. port = int(port)
  885. if query:
  886. path += '?' + query
  887. if 'Host' not in headers:
  888. headers['Host'] = host
  889. if body and 'Content-Length' not in headers:
  890. headers['Content-Length'] = str(len(body))
  891. ConnectionType = httplib.HTTPSConnection if scheme == 'https' else httplib.HTTPConnection
  892. connection = ConnectionType(netloc, timeout=timeout)
  893. connection.request(method, path, body=body, headers=headers)
  894. response = connection.getresponse()
  895. return response
  896. def create_http_request_withserver(self, fetchserver, method, url, headers, body, timeout, **kwargs):
  897. return URLFetch(fetchserver, self.create_http_request).fetch(method, url, headers, body, timeout, **kwargs)
  898. def handle_urlfetch_error(self, fetchserver, response):
  899. pass
  900. def handle_urlfetch_response_close(self, fetchserver, response):
  901. pass
  902. def parse_header(self):
  903. if self.command == 'CONNECT':
  904. netloc = self.path
  905. elif self.path[0] == '/':
  906. netloc = self.headers.get('Host', 'localhost')
  907. self.path = '%s://%s%s' % (self.scheme, netloc, self.path)
  908. else:
  909. netloc = urlparse.urlsplit(self.path).netloc
  910. m = re.match(r'^(.+):(\d+)$', netloc)
  911. if m:
  912. self.host = m.group(1).strip('[]')
  913. self.port = int(m.group(2))
  914. else:
  915. self.host = netloc
  916. self.port = 443 if self.scheme == 'https' else 80
  917. def forward_socket(self, local, remote, timeout):
  918. try:
  919. tick = 1
  920. bufsize = self.bufsize
  921. timecount = timeout
  922. while 1:
  923. timecount -= tick
  924. if timecount <= 0:
  925. break
  926. (ins, _, errors) = select.select([local, remote], [], [local, remote], tick)
  927. if errors:
  928. break
  929. for sock in ins:
  930. data = sock.recv(bufsize)
  931. if not data:
  932. break
  933. if sock is remote:
  934. local.sendall(data)
  935. timecount = timeout
  936. else:
  937. remote.sendall(data)
  938. timecount = timeout
  939. except socket.timeout:
  940. pass
  941. except NetWorkIOError as e:
  942. if e.args[0] not in (errno.ECONNABORTED, errno.ECONNRESET, errno.ENOTCONN, errno.EPIPE):
  943. raise
  944. if e.args[0] in (errno.EBADF,):
  945. return
  946. finally:
  947. for sock in (remote, local):
  948. try:
  949. sock.close()
  950. except StandardError:
  951. pass
  952. def MOCK(self, status, headers, content):
  953. """mock response"""
  954. logging.info('%s "MOCK %s %s %s" %d %d', self.address_string(), self.command, self.path, self.protocol_version, status, len(content))
  955. headers = dict((k.title(), v) for k, v in headers.items())
  956. if 'Transfer-Encoding' in headers:
  957. del headers['Transfer-Encoding']
  958. if 'Content-Length' not in headers:
  959. headers['Content-Length'] = len(content)
  960. if 'Connection' not in headers:
  961. headers['Connection'] = 'close'
  962. self.send_response(status)
  963. for key, value in headers.items():
  964. self.send_header(key, value)
  965. self.end_headers()
  966. self.wfile.write(content)
  967. def STRIP(self, do_ssl_handshake=True, sticky_filter=None):
  968. """strip connect"""
  969. certfile = CertUtil.get_cert(self.host)
  970. logging.info('%s "STRIP %s %s:%d %s" - -', self.address_string(), self.command, self.host, self.port, self.protocol_version)
  971. self.send_response(200)
  972. self.end_headers()
  973. if do_ssl_handshake:
  974. try:
  975. ssl_sock = ssl.wrap_socket(self.connection, ssl_version=self.ssl_version, keyfile=certfile, certfile=certfile, server_side=True)
  976. except StandardError as e:
  977. if e.args[0] not in (errno.ECONNABORTED, errno.ECONNRESET):
  978. logging.exception('ssl.wrap_socket(self.connection=%r) failed: %s', self.connection, e)
  979. return
  980. self.connection = ssl_sock
  981. self.rfile = self.connection.makefile('rb', self.bufsize)
  982. self.wfile = self.connection.makefile('wb', 0)
  983. self.scheme = 'https'
  984. try:
  985. self.raw_requestline = self.rfile.readline(65537)
  986. if len(self.raw_requestline) > 65536:
  987. self.requestline = ''
  988. self.request_version = ''
  989. self.command = ''
  990. self.send_error(414)
  991. return
  992. if not self.raw_requestline:
  993. self.close_connection = 1
  994. return
  995. if not self.parse_request():
  996. return
  997. except NetWorkIOError as e:
  998. if e.args[0] not in (errno.ECONNABORTED, errno.ECONNRESET, errno.EPIPE):
  999. raise
  1000. self.sticky_filter = sticky_filter
  1001. try:
  1002. self.do_METHOD()
  1003. except NetWorkIOError as e:
  1004. if e.args[0] not in (errno.ECONNABORTED, errno.ETIMEDOUT, errno.EPIPE):
  1005. raise
  1006. def FORWARD(self, hostname, port, timeout, kwargs={}):
  1007. """forward socket"""
  1008. do_ssl_handshake = kwargs.pop('do_ssl_handshake', False)
  1009. local = self.connection
  1010. remote = None
  1011. self.send_response(200)
  1012. self.end_headers()
  1013. self.close_connection = 1
  1014. data = local.recv(1024)
  1015. if not data:
  1016. local.close()
  1017. return
  1018. data_is_clienthello = is_clienthello(data)
  1019. if data_is_clienthello:
  1020. kwargs['client_hello'] = data
  1021. max_retry = kwargs.get('max_retry', 3)
  1022. for i in xrange(max_retry):
  1023. try:
  1024. if do_ssl_handshake:
  1025. remote = self.create_ssl_connection(hostname, port, timeout, **kwargs)

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