PageRenderTime 73ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 1ms

/local/proxy.py

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

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