PageRenderTime 74ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/local/proxy.py

https://github.com/v3aqb/goagent
Python | 3290 lines | 3167 code | 64 blank | 59 comment | 190 complexity | e68c15ee85b15756e066815556593a70 MD5 | raw file

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

  1. #!/usr/bin/env python
  2. # coding:utf-8
  3. # Based on GAppProxy 2.0.0 by Du XiaoGang <dugang.2008@gmail.com>
  4. # Based on WallProxy 0.4.0 by Hust Moon <www.ehust@gmail.com>
  5. # Contributor:
  6. # Phus Lu <phus.lu@gmail.com>
  7. # Hewig Xu <hewigovens@gmail.com>
  8. # Ayanamist Yang <ayanamist@gmail.com>
  9. # V.E.O <V.E.O@tom.com>
  10. # Max Lv <max.c.lv@gmail.com>
  11. # AlsoTang <alsotang@gmail.com>
  12. # Christopher Meng <i@cicku.me>
  13. # Yonsm Guo <YonsmGuo@gmail.com>
  14. # Parkman <cseparkman@gmail.com>
  15. # Ming Bai <mbbill@gmail.com>
  16. # Bin Yu <yubinlove1991@gmail.com>
  17. # lileixuan <lileixuan@gmail.com>
  18. # Cong Ding <cong@cding.org>
  19. # Zhang Youfu <zhangyoufu@gmail.com>
  20. # Lu Wei <luwei@barfoo>
  21. # Harmony Meow <harmony.meow@gmail.com>
  22. # logostream <logostream@gmail.com>
  23. # Rui Wang <isnowfy@gmail.com>
  24. # Wang Wei Qiang <wwqgtxx@gmail.com>
  25. # Felix Yan <felixonmars@gmail.com>
  26. # Sui Feng <suifeng.me@qq.com>
  27. # QXO <qxodream@gmail.com>
  28. # Geek An <geekan@foxmail.com>
  29. # Poly Rabbit <mcx_221@foxmail.com>
  30. # oxnz <yunxinyi@gmail.com>
  31. # Shusen Liu <liushusen.smart@gmail.com>
  32. # Yad Smood <y.s.inside@gmail.com>
  33. # Chen Shuang <cs0x7f@gmail.com>
  34. # cnfuyu <cnfuyu@gmail.com>
  35. # cuixin <steven.cuixin@gmail.com>
  36. # s2marine0 <s2marine0@gmail.com>
  37. # Toshio Xiang <snachx@gmail.com>
  38. # Bo Tian <dxmtb@163.com>
  39. # Virgil <variousvirgil@gmail.com>
  40. # hub01 <miaojiabumiao@yeah.net>
  41. # v3aqb <sgzz.cj@gmail.com>
  42. # Oling Cat <olingcat@gmail.com>
  43. __version__ = '3.1.18'
  44. import sys
  45. import os
  46. import glob
  47. reload(sys).setdefaultencoding('UTF-8')
  48. sys.dont_write_bytecode = True
  49. sys.path += glob.glob('%s/*.egg' % os.path.dirname(os.path.abspath(__file__)))
  50. try:
  51. 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, 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._connection.do_handshake)
  416. def connect(self, *args, **kwargs):
  417. return self.__wait_sock_io(self._connection.connect, *args, **kwargs)
  418. def send(self, data, flags=0):
  419. try:
  420. return self.__wait_sock_io(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._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. time.sleep(seconds)
  625. return target(*args, **kwargs)
  626. return thread.start_new_thread(wrap, args, kwargs)
  627. def spawn_period(seconds, target, *args, **kwargs):
  628. def wrap(*args, **kwargs):
  629. try:
  630. time.sleep(seconds)
  631. target(*args, **kwargs)
  632. except StandardError as e:
  633. logging.warning('%r(%s, %s) error: %r', target, args, kwargs, e)
  634. return thread.start_new_thread(wrap, args, kwargs)
  635. def is_clienthello(data):
  636. if len(data) < 20:
  637. return False
  638. if data.startswith('\x16\x03'):
  639. # TLSv12/TLSv11/TLSv1/SSLv3
  640. length, = struct.unpack('>h', data[3:5])
  641. return len(data) == 5 + length
  642. elif data[0] == '\x80' and data[2:4] == '\x01\x03':
  643. # SSLv23
  644. return len(data) == 2 + ord(data[1])
  645. else:
  646. return False
  647. def is_google_ip(ipaddr):
  648. return ipaddr.startswith(('173.194.', '207.126.', '209.85.', '216.239.', '64.18.', '64.233.', '66.102.', '66.249.', '72.14.', '74.125.'))
  649. def extract_sni_name(packet):
  650. if packet.startswith('\x16\x03'):
  651. stream = io.BytesIO(packet)
  652. stream.read(0x2b)
  653. session_id_length = ord(stream.read(1))
  654. stream.read(session_id_length)
  655. cipher_suites_length, = struct.unpack('>h', stream.read(2))
  656. stream.read(cipher_suites_length+2)
  657. extensions_length, = struct.unpack('>h', stream.read(2))
  658. # extensions = {}
  659. while True:
  660. data = stream.read(2)
  661. if not data:
  662. break
  663. etype, = struct.unpack('>h', data)
  664. elen, = struct.unpack('>h', stream.read(2))
  665. edata = stream.read(elen)
  666. if etype == 0:
  667. server_name = edata[5:]
  668. return server_name
  669. class URLFetch(object):
  670. """URLFetch for gae/php fetchservers"""
  671. skip_headers = frozenset(['Vary', 'Via', 'X-Forwarded-For', 'Proxy-Authorization', 'Proxy-Connection', 'Upgrade', 'X-Chrome-Variations', 'Connection', 'Cache-Control'])
  672. def __init__(self, fetchserver, create_http_request):
  673. assert isinstance(fetchserver, basestring) and callable(create_http_request)
  674. self.fetchserver = fetchserver
  675. self.create_http_request = create_http_request
  676. def fetch(self, method, url, headers, body, timeout, **kwargs):
  677. if '.appspot.com/' in self.fetchserver:
  678. response = self.__gae_fetch(method, url, headers, body, timeout, **kwargs)
  679. response.app_header_parsed = True
  680. else:
  681. response = self.__php_fetch(method, url, headers, body, timeout, **kwargs)
  682. response.app_header_parsed = False
  683. return response
  684. def __gae_fetch(self, method, url, headers, body, timeout, **kwargs):
  685. rc4crypt = lambda s, k: RC4Cipher(k).encrypt(s) if k else s
  686. if isinstance(body, basestring) and body:
  687. if len(body) < 10 * 1024 * 1024 and 'Content-Encoding' not in headers:
  688. zbody = deflate(body)
  689. if len(zbody) < len(body):
  690. body = zbody
  691. headers['Content-Encoding'] = 'deflate'
  692. headers['Content-Length'] = str(len(body))
  693. # GAE donot allow set `Host` header
  694. if 'Host' in headers:
  695. del headers['Host']
  696. 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))
  697. skip_headers = self.skip_headers
  698. metadata += ''.join('%s:%s\n' % (k.title(), v) for k, v in headers.items() if k not in skip_headers)
  699. # prepare GAE request
  700. request_fetchserver = self.fetchserver
  701. request_method = 'POST'
  702. request_headers = {}
  703. if common.GAE_OBFUSCATE:
  704. request_method = 'GET'
  705. request_fetchserver += '/ps/%s.gif' % uuid.uuid1()
  706. request_headers['X-GOA-PS1'] = base64.b64encode(deflate(metadata)).strip()
  707. if body:
  708. request_headers['X-GOA-PS2'] = base64.b64encode(deflate(body)).strip()
  709. body = ''
  710. if common.GAE_PAGESPEED:
  711. request_fetchserver = re.sub(r'^(\w+://)', r'\g<1>1-ps.googleusercontent.com/h/', request_fetchserver)
  712. else:
  713. metadata = deflate(metadata)
  714. body = '%s%s%s' % (struct.pack('!h', len(metadata)), metadata, body)
  715. if 'rc4' in common.GAE_OPTIONS:
  716. request_headers['X-GOA-Options'] = 'rc4'
  717. body = rc4crypt(body, kwargs.get('password'))
  718. request_headers['Content-Length'] = str(len(body))
  719. # post data
  720. need_crlf = 0 if common.GAE_MODE == 'https' else 1
  721. need_validate = common.GAE_VALIDATE
  722. cache_key = '%s:%d' % (common.HOST_POSTFIX_MAP['.appspot.com'], 443 if common.GAE_MODE == 'https' else 80)
  723. response = self.create_http_request(request_method, request_fetchserver, request_headers, body, timeout, crlf=need_crlf, validate=need_validate, cache_key=cache_key)
  724. response.app_status = response.status
  725. response.app_options = response.getheader('X-GOA-Options', '')
  726. if response.status != 200:
  727. return response
  728. data = response.read(4)
  729. if len(data) < 4:
  730. response.status = 502
  731. response.fp = io.BytesIO(b'connection aborted. too short leadbyte data=' + data)
  732. response.read = response.fp.read
  733. return response
  734. response.status, headers_length = struct.unpack('!hh', data)
  735. data = response.read(headers_length)
  736. if len(data) < headers_length:
  737. response.status = 502
  738. response.fp = io.BytesIO(b'connection aborted. too short headers data=' + data)
  739. response.read = response.fp.read
  740. return response
  741. if 'rc4' not in response.app_options:
  742. response.msg = httplib.HTTPMessage(io.BytesIO(inflate(data)))
  743. else:
  744. response.msg = httplib.HTTPMessage(io.BytesIO(inflate(rc4crypt(data, kwargs.get('password')))))
  745. if kwargs.get('password') and response.fp:
  746. response.fp = CipherFileObject(response.fp, RC4Cipher(kwargs['password']))
  747. return response
  748. def __php_fetch(self, method, url, headers, body, timeout, **kwargs):
  749. if body:
  750. if len(body) < 10 * 1024 * 1024 and 'Content-Encoding' not in headers:
  751. zbody = deflate(body)
  752. if len(zbody) < len(body):
  753. body = zbody
  754. headers['Content-Encoding'] = 'deflate'
  755. headers['Content-Length'] = str(len(body))
  756. skip_headers = self.skip_headers
  757. 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))
  758. metadata = deflate(metadata)
  759. app_body = b''.join((struct.pack('!h', len(metadata)), metadata, body))
  760. app_headers = {'Content-Length': len(app_body), 'Content-Type': 'application/octet-stream'}
  761. fetchserver = '%s?%s' % (self.fetchserver, random.random())
  762. crlf = 0
  763. cache_key = '%s//:%s' % urlparse.urlsplit(fetchserver)[:2]
  764. response = self.create_http_request('POST', fetchserver, app_headers, app_body, timeout, crlf=crlf, cache_key=cache_key)
  765. if not response:
  766. raise socket.error(errno.ECONNRESET, 'urlfetch %r return None' % url)
  767. if response.status >= 400:
  768. return response
  769. response.app_status = response.status
  770. need_decrypt = kwargs.get('password') and response.app_status == 200 and response.getheader('Content-Type', '') == 'image/gif' and response.fp
  771. if need_decrypt:
  772. response.fp = CipherFileObject(response.fp, XORCipher(kwargs['password'][0]))
  773. return response
  774. class BaseProxyHandlerFilter(object):
  775. """base proxy handler filter"""
  776. def filter(self, handler):
  777. raise NotImplementedError
  778. class SimpleProxyHandlerFilter(BaseProxyHandlerFilter):
  779. """simple proxy handler filter"""
  780. def filter(self, handler):
  781. if handler.command == 'CONNECT':
  782. return [handler.FORWARD, handler.host, handler.port, handler.connect_timeout]
  783. else:
  784. return [handler.DIRECT, {}]
  785. class AuthFilter(BaseProxyHandlerFilter):
  786. """authorization filter"""
  787. auth_info = "Proxy authentication required"""
  788. white_list = set(['127.0.0.1'])
  789. def __init__(self, username, password):
  790. self.username = username
  791. self.password = password
  792. def check_auth_header(self, auth_header):
  793. method, _, auth_data = auth_header.partition(' ')
  794. if method == 'Basic':
  795. username, _, password = base64.b64decode(auth_data).partition(':')
  796. if username == self.username and password == self.password:
  797. return True
  798. return False
  799. def filter(self, handler):
  800. if self.white_list and handler.client_address[0] in self.white_list:
  801. return None
  802. auth_header = handler.headers.get('Proxy-Authorization') or getattr(handler, 'auth_header', None)
  803. if auth_header and self.check_auth_header(auth_header):
  804. handler.auth_header = auth_header
  805. else:
  806. headers = {'Access-Control-Allow-Origin': '*',
  807. 'Proxy-Authenticate': 'Basic realm="%s"' % self.auth_info,
  808. 'Content-Length': '0',
  809. 'Connection': 'keep-alive'}
  810. return [handler.MOCK, 407, headers, '']
  811. class SimpleProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
  812. """SimpleProxyHandler for GoAgent 3.x"""
  813. protocol_version = 'HTTP/1.1'
  814. ssl_version = ssl.PROTOCOL_SSLv23
  815. disable_transport_ssl = True
  816. scheme = 'http'
  817. skip_headers = frozenset(['Vary', 'Via', 'X-Forwarded-For', 'Proxy-Authorization', 'Proxy-Connection', 'Upgrade', 'X-Chrome-Variations', 'Connection', 'Cache-Control'])
  818. bufsize = 256 * 1024
  819. max_timeout = 4
  820. connect_timeout = 2
  821. first_run_lock = threading.Lock()
  822. handler_filters = [SimpleProxyHandlerFilter()]
  823. sticky_filter = None
  824. def finish(self):
  825. """make python2 BaseHTTPRequestHandler happy"""
  826. try:
  827. BaseHTTPServer.BaseHTTPRequestHandler.finish(self)
  828. except NetWorkIOError as e:
  829. if e[0] not in (errno.ECONNABORTED, errno.ECONNRESET, errno.EPIPE):
  830. raise
  831. def address_string(self):
  832. return '%s:%s' % self.client_address[:2]
  833. def send_response(self, code, message=None):
  834. if message is None:
  835. if code in self.responses:
  836. message = self.responses[code][0]
  837. else:
  838. message = ''
  839. if self.request_version != 'HTTP/0.9':
  840. self.wfile.write('%s %d %s\r\n' % (self.protocol_version, code, message))
  841. def send_header(self, keyword, value):
  842. """Send a MIME header."""
  843. base_send_header = BaseHTTPServer.BaseHTTPRequestHandler.send_header
  844. keyword = keyword.title()
  845. if keyword == 'Set-Cookie':
  846. for cookie in re.split(r', (?=[^ =]+(?:=|$))', value):
  847. base_send_header(self, keyword, cookie)
  848. elif keyword == 'Content-Disposition' and '"' not in value:
  849. value = re.sub(r'filename=([^"\']+)', 'filename="\\1"', value)
  850. base_send_header(self, keyword, value)
  851. else:
  852. base_send_header(self, keyword, value)
  853. def setup(self):
  854. if isinstance(self.__class__.first_run, collections.Callable):
  855. try:
  856. with self.__class__.first_run_lock:
  857. if isinstance(self.__class__.first_run, collections.Callable):
  858. self.first_run()
  859. self.__class__.first_run = None
  860. except StandardError as e:
  861. logging.exception('%s.first_run() return %r', self.__class__, e)
  862. self.__class__.setup = BaseHTTPServer.BaseHTTPRequestHandler.setup
  863. self.__class__.do_CONNECT = self.__class__.do_METHOD
  864. self.__class__.do_GET = self.__class__.do_METHOD
  865. self.__class__.do_PUT = self.__class__.do_METHOD
  866. self.__class__.do_POST = self.__class__.do_METHOD
  867. self.__class__.do_HEAD = self.__class__.do_METHOD
  868. self.__class__.do_DELETE = self.__class__.do_METHOD
  869. self.__class__.do_OPTIONS = self.__class__.do_METHOD
  870. self.setup()
  871. def handle_one_request(self):
  872. if not self.disable_transport_ssl and self.scheme == 'http':
  873. leadbyte = self.connection.recv(1, socket.MSG_PEEK)
  874. if leadbyte in ('\x80', '\x16'):
  875. server_name = ''
  876. if leadbyte == '\x16':
  877. for _ in xrange(2):
  878. leaddata = self.connection.recv(1024, socket.MSG_PEEK)
  879. if is_clienthello(leaddata):
  880. try:
  881. server_name = extract_sni_name(leaddata)
  882. finally:
  883. break
  884. try:
  885. certfile = CertUtil.get_cert(server_name or 'www.google.com')
  886. ssl_sock = ssl.wrap_socket(self.connection, ssl_version=self.ssl_version, keyfile=certfile, certfile=certfile, server_side=True)
  887. except StandardError as e:
  888. if e.args[0] not in (errno.ECONNABORTED, errno.ECONNRESET):
  889. logging.exception('ssl.wrap_socket(self.connection=%r) failed: %s', self.connection, e)
  890. return
  891. self.connection = ssl_sock
  892. self.rfile = self.connection.makefile('rb', self.bufsize)
  893. self.wfile = self.connection.makefile('wb', 0)
  894. self.scheme = 'https'
  895. return BaseHTTPServer.BaseHTTPRequestHandler.handle_one_request(self)
  896. def first_run(self):
  897. pass
  898. def gethostbyname2(self, hostname):
  899. return socket.gethostbyname_ex(hostname)[-1]
  900. def create_tcp_connection(self, hostname, port, timeout, **kwargs):
  901. return socket.create_connection((hostname, port), timeout)
  902. def create_ssl_connection(self, hostname, port, timeout, **kwargs):
  903. sock = self.create_tcp_connection(hostname, port, timeout, **kwargs)
  904. ssl_sock = ssl.wrap_socket(sock, ssl_version=self.ssl_version)
  905. return ssl_sock
  906. def create_http_request(self, method, url, headers, body, timeout, **kwargs):
  907. scheme, netloc, path, query, _ = urlparse.urlsplit(url)
  908. if netloc.rfind(':') <= netloc.rfind(']'):
  909. # no port number
  910. host = netloc
  911. port = 443 if scheme == 'https' else 80
  912. else:
  913. host, _, port = netloc.rpartition(':')
  914. port = int(port)
  915. if query:
  916. path += '?' + query
  917. if 'Host' not in headers:
  918. headers['Host'] = host
  919. if body and 'Content-Length' not in headers:
  920. headers['Content-Length'] = str(len(body))
  921. ConnectionType = httplib.HTTPSConnection if scheme == 'https' else httplib.HTTPConnection
  922. connection = ConnectionType(netloc, timeout=timeout)
  923. connection.request(method, path, body=body, headers=headers)
  924. response = connection.getresponse()
  925. return response
  926. def create_http_request_withserver(self, fetchserver, method, url, headers, body, timeout, **kwargs):
  927. return URLFetch(fetchserver, self.create_http_request).fetch(method, url, headers, body, timeout, **kwargs)
  928. def handle_urlfetch_error(self, fetchserver, response):
  929. pass
  930. def handle_urlfetch_response_close(self, fetchserver, response):
  931. pass
  932. def parse_header(self):
  933. if self.command == 'CONNECT':
  934. netloc = self.path
  935. elif self.path[0] == '/':
  936. netloc = self.headers.get('Host', 'localhost')
  937. self.path = '%s://%s%s' % (self.scheme, netloc, self.path)
  938. else:
  939. netloc = urlparse.urlsplit(self.path).netloc
  940. m = re.match(r'^(.+):(\d+)$', netloc)
  941. if m:
  942. self.host = m.group(1).strip('[]')
  943. self.port = int(m.group(2))
  944. else:
  945. self.host = netloc
  946. self.port = 443 if self.scheme == 'https' else 80
  947. def forward_socket(self, local, remote, timeout):
  948. try:
  949. tick = 1
  950. bufsize = self.bufsize
  951. timecount = timeout
  952. while 1:
  953. timecount -= tick
  954. if timecount <= 0:
  955. break
  956. (ins, _, errors) = select.select([local, remote], [], [local, remote], tick)
  957. if errors:
  958. break
  959. for sock in ins:
  960. data = sock.recv(bufsize)
  961. if not data:
  962. break
  963. if sock is remote:
  964. local.sendall(data)
  965. timecount = timeout
  966. else:
  967. remote.sendall(data)
  968. timecount = timeout
  969. except socket.timeout:
  970. pass
  971. except NetWorkIOError as e:
  972. if e.args[0] not in (errno.ECONNABORTED, errno.ECONNRESET, errno.ENOTCONN, errno.EPIPE):
  973. raise
  974. if e.args[0] in (errno.EBADF,):
  975. return
  976. finally:
  977. for sock in (remote, local):
  978. try:
  979. sock.close()
  980. except StandardError:
  981. pass
  982. def MOCK(self, status, headers, content):
  983. """mock response"""
  984. logging.info('%s "MOCK %s %s %s" %d %d', self.address_string(), self.command, self.path, self.protocol_version, status, len(content))
  985. headers = dict((k.title(), v) for k, v in headers.items())
  986. if 'Transfer-Encoding' in headers:
  987. del headers['Transfer-Encoding']
  988. if 'Content-Length' not in headers:
  989. headers['Content-Length'] = len(content)
  990. if 'Connection' not in headers:
  991. headers['Connection'] = 'close'
  992. self.send_response(status)
  993. for key, value in headers.items():
  994. self.send_header(key, value)
  995. self.end_headers()
  996. self.wfile.write(content)
  997. def STRIP(self, do_ssl_handshake=True, sticky_filter=None):
  998. """strip connect"""
  999. certfile = CertUtil.get_cert(self.host)
  1000. logging.info('%s "STRIP %s %s:%d %s" - -', self.address_string(), self.command, self.host, self.port, self.protocol_version)
  1001. self.send_response(200)
  1002. self.end_headers()
  1003. if do_ssl_handshake:
  1004. try:
  1005. # ssl_sock = ssl.wrap_socket(self.connection, ssl_version=self.ssl_version, keyfile=certfile, certfile=certfile, server_side=True)
  1006. # bugfix for youtube-dl
  1007. ssl_sock = ssl.wrap_socket(self.connection, keyfile=certfile, certfile=certfile, server_side=True)
  1008. except StandardError as e:
  1009. if e.args[0] not in (errno.ECONNABORTED, errno.ECONNRESET):
  1010. logging.exception('ssl.wrap_socket(self.connection=%r) failed: %s', self.connection, e)
  1011. return
  1012. self.connection = ssl_sock
  1013. self.rfile = self.connection.makefile('rb', self.bufsize)
  1014. self.wfile = self.connection.makefile('wb', 0)
  1015. self.scheme = 'https'
  1016. try:
  1017. self.raw_requestline = self.rfile.readline(65537)
  1018. if len(self.raw_re

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