PageRenderTime 54ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/local/proxy.py

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

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