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

/local/proxy.py

https://github.com/wahgo/goagent-1
Python | 3324 lines | 3201 code | 64 blank | 59 comment | 190 complexity | 3f1384f2f53527b2f9d2857c93fffb27 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
  610. import ctypes.wintypes
  611. DNS_CONFIG_DNS_SERVER_LIST = 6
  612. buf = ctypes.create_string_buffer(2048)
  613. ctypes.windll.dnsapi.DnsQueryConfig(DNS_CONFIG_DNS_SERVER_LIST, 0, None, None, ctypes.byref(buf), ctypes.byref(ctypes.wintypes.DWORD(len(buf))))
  614. ipcount = struct.unpack('I', buf[0:4])[0]
  615. iplist = [socket.inet_ntoa(buf[i:i+4]) for i in xrange(4, ipcount*4+4, 4)]
  616. return iplist
  617. elif os.path.isfile('/etc/resolv.conf'):
  618. with open('/etc/resolv.conf', 'rb') as fp:
  619. return re.findall(r'(?m)^nameserver\s+(\S+)', fp.read())
  620. else:
  621. logging.warning("get_dnsserver_list failed: unsupport platform '%s-%s'", sys.platform, os.name)
  622. return []
  623. def spawn_later(seconds, target, *args, **kwargs):
  624. def wrap(*args, **kwargs):
  625. time.sleep(seconds)
  626. return target(*args, **kwargs)
  627. return thread.start_new_thread(wrap, args, kwargs)
  628. def spawn_period(seconds, target, *args, **kwargs):
  629. def wrap(*args, **kwargs):
  630. try:
  631. time.sleep(seconds)
  632. target(*args, **kwargs)
  633. except StandardError as e:
  634. logging.warning('%r(%s, %s) error: %r', target, args, kwargs, e)
  635. return thread.start_new_thread(wrap, args, kwargs)
  636. def is_clienthello(data):
  637. if len(data) < 20:
  638. return False
  639. if data.startswith('\x16\x03'):
  640. # TLSv12/TLSv11/TLSv1/SSLv3
  641. length, = struct.unpack('>h', data[3:5])
  642. return len(data) == 5 + length
  643. elif data[0] == '\x80' and data[2:4] == '\x01\x03':
  644. # SSLv23
  645. return len(data) == 2 + ord(data[1])
  646. else:
  647. return False
  648. def is_google_ip(ipaddr):
  649. return ipaddr.startswith(('173.194.', '207.126.', '209.85.', '216.239.', '64.18.', '64.233.', '66.102.', '66.249.', '72.14.', '74.125.'))
  650. def extract_sni_name(packet):
  651. if packet.startswith('\x16\x03'):
  652. stream = io.BytesIO(packet)
  653. stream.read(0x2b)
  654. session_id_length = ord(stream.read(1))
  655. stream.read(session_id_length)
  656. cipher_suites_length, = struct.unpack('>h', stream.read(2))
  657. stream.read(cipher_suites_length+2)
  658. extensions_length, = struct.unpack('>h', stream.read(2))
  659. # extensions = {}
  660. while True:
  661. data = stream.read(2)
  662. if not data:
  663. break
  664. etype, = struct.unpack('>h', data)
  665. elen, = struct.unpack('>h', stream.read(2))
  666. edata = stream.read(elen)
  667. if etype == 0:
  668. server_name = edata[5:]
  669. return server_name
  670. class URLFetch(object):
  671. """URLFetch for gae/php fetchservers"""
  672. skip_headers = frozenset(['Vary', 'Via', 'X-Forwarded-For', 'Proxy-Authorization', 'Proxy-Connection', 'Upgrade', 'X-Chrome-Variations', 'Connection', 'Cache-Control'])
  673. def __init__(self, fetchserver, create_http_request):
  674. assert isinstance(fetchserver, basestring) and callable(create_http_request)
  675. self.fetchserver = fetchserver
  676. self.create_http_request = create_http_request
  677. def fetch(self, method, url, headers, body, timeout, **kwargs):
  678. if '.appspot.com/' in self.fetchserver:
  679. response = self.__gae_fetch(method, url, headers, body, timeout, **kwargs)
  680. response.app_header_parsed = True
  681. else:
  682. response = self.__php_fetch(method, url, headers, body, timeout, **kwargs)
  683. response.app_header_parsed = False
  684. return response
  685. def __gae_fetch(self, method, url, headers, body, timeout, **kwargs):
  686. rc4crypt = lambda s, k: RC4Cipher(k).encrypt(s) if k else s
  687. if isinstance(body, basestring) and body:
  688. if len(body) < 10 * 1024 * 1024 and 'Content-Encoding' not in headers:
  689. zbody = deflate(body)
  690. if len(zbody) < len(body):
  691. body = zbody
  692. headers['Content-Encoding'] = 'deflate'
  693. headers['Content-Length'] = str(len(body))
  694. # GAE donot allow set `Host` header
  695. if 'Host' in headers:
  696. del headers['Host']
  697. 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))
  698. skip_headers = self.skip_headers
  699. metadata += ''.join('%s:%s\n' % (k.title(), v) for k, v in headers.items() if k not in skip_headers)
  700. # prepare GAE request
  701. request_fetchserver = self.fetchserver
  702. request_method = 'POST'
  703. request_headers = {}
  704. if common.GAE_OBFUSCATE:
  705. request_method = 'GET'
  706. request_fetchserver += '/ps/%s.gif' % uuid.uuid1()
  707. request_headers['X-GOA-PS1'] = base64.b64encode(deflate(metadata)).strip()
  708. if body:
  709. request_headers['X-GOA-PS2'] = base64.b64encode(deflate(body)).strip()
  710. body = ''
  711. if common.GAE_PAGESPEED:
  712. request_fetchserver = re.sub(r'^(\w+://)', r'\g<1>1-ps.googleusercontent.com/h/', request_fetchserver)
  713. else:
  714. metadata = deflate(metadata)
  715. body = '%s%s%s' % (struct.pack('!h', len(metadata)), metadata, body)
  716. if 'rc4' in common.GAE_OPTIONS:
  717. request_headers['X-GOA-Options'] = 'rc4'
  718. body = rc4crypt(body, kwargs.get('password'))
  719. request_headers['Content-Length'] = str(len(body))
  720. # post data
  721. need_crlf = 0 if common.GAE_MODE == 'https' else 1
  722. need_validate = common.GAE_VALIDATE
  723. cache_key = '%s:%d' % (common.HOST_POSTFIX_MAP['.appspot.com'], 443 if common.GAE_MODE == 'https' else 80)
  724. response = self.create_http_request(request_method, request_fetchserver, request_headers, body, timeout, crlf=need_crlf, validate=need_validate, cache_key=cache_key)
  725. response.app_status = response.status
  726. response.app_options = response.getheader('X-GOA-Options', '')
  727. if response.status != 200:
  728. return response
  729. data = response.read(4)
  730. if len(data) < 4:
  731. response.status = 502
  732. response.fp = io.BytesIO(b'connection aborted. too short leadbyte data=' + data)
  733. response.read = response.fp.read
  734. return response
  735. response.status, headers_length = struct.unpack('!hh', data)
  736. data = response.read(headers_length)
  737. if len(data) < headers_length:
  738. response.status = 502
  739. response.fp = io.BytesIO(b'connection aborted. too short headers data=' + data)
  740. response.read = response.fp.read
  741. return response
  742. if 'rc4' not in response.app_options:
  743. response.msg = httplib.HTTPMessage(io.BytesIO(inflate(data)))
  744. else:
  745. response.msg = httplib.HTTPMessage(io.BytesIO(inflate(rc4crypt(data, kwargs.get('password')))))
  746. if kwargs.get('password') and response.fp:
  747. response.fp = CipherFileObject(response.fp, RC4Cipher(kwargs['password']))
  748. return response
  749. def __php_fetch(self, method, url, headers, body, timeout, **kwargs):
  750. if body:
  751. if len(body) < 10 * 1024 * 1024 and 'Content-Encoding' not in headers:
  752. zbody = deflate(body)
  753. if len(zbody) < len(body):
  754. body = zbody
  755. headers['Content-Encoding'] = 'deflate'
  756. headers['Content-Length'] = str(len(body))
  757. skip_headers = self.skip_headers
  758. 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))
  759. metadata = deflate(metadata)
  760. app_body = b''.join((struct.pack('!h', len(metadata)), metadata, body))
  761. app_headers = {'Content-Length': len(app_body), 'Content-Type': 'application/octet-stream'}
  762. fetchserver = '%s?%s' % (self.fetchserver, random.random())
  763. crlf = 0
  764. cache_key = '%s//:%s' % urlparse.urlsplit(fetchserver)[:2]
  765. response = self.create_http_request('POST', fetchserver, app_headers, app_body, timeout, crlf=crlf, cache_key=cache_key)
  766. if not response:
  767. raise socket.error(errno.ECONNRESET, 'urlfetch %r return None' % url)
  768. if response.status >= 400:
  769. return response
  770. response.app_status = response.status
  771. need_decrypt = kwargs.get('password') and response.app_status == 200 and response.getheader('Content-Type', '') == 'image/gif' and response.fp
  772. if need_decrypt:
  773. response.fp = CipherFileObject(response.fp, XORCipher(kwargs['password'][0]))
  774. return response
  775. class BaseProxyHandlerFilter(object):
  776. """base proxy handler filter"""
  777. def filter(self, handler):
  778. raise NotImplementedError
  779. class SimpleProxyHandlerFilter(BaseProxyHandlerFilter):
  780. """simple proxy handler filter"""
  781. def filter(self, handler):
  782. if handler.command == 'CONNECT':
  783. return [handler.FORWARD, handler.host, handler.port, handler.connect_timeout]
  784. else:
  785. return [handler.DIRECT, {}]
  786. class AuthFilter(BaseProxyHandlerFilter):
  787. """authorization filter"""
  788. auth_info = "Proxy authentication required"""
  789. white_list = set(['127.0.0.1'])
  790. def __init__(self, username, password):
  791. self.username = username
  792. self.password = password
  793. def check_auth_header(self, auth_header):
  794. method, _, auth_data = auth_header.partition(' ')
  795. if method == 'Basic':
  796. username, _, password = base64.b64decode(auth_data).partition(':')
  797. if username == self.username and password == self.password:
  798. return True
  799. return False
  800. def filter(self, handler):
  801. if self.white_list and handler.client_address[0] in self.white_list:
  802. return None
  803. auth_header = handler.headers.get('Proxy-Authorization') or getattr(handler, 'auth_header', None)
  804. if auth_header and self.check_auth_header(auth_header):
  805. handler.auth_header = auth_header
  806. else:
  807. headers = {'Access-Control-Allow-Origin': '*',
  808. 'Proxy-Authenticate': 'Basic realm="%s"' % self.auth_info,
  809. 'Content-Length': '0',
  810. 'Connection': 'keep-alive'}
  811. return [handler.MOCK, 407, headers, '']
  812. class SimpleProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
  813. """SimpleProxyHandler for GoAgent 3.x"""
  814. protocol_version = 'HTTP/1.1'
  815. ssl_version = ssl.PROTOCOL_SSLv23
  816. disable_transport_ssl = True
  817. scheme = 'http'
  818. skip_headers = frozenset(['Vary', 'Via', 'X-Forwarded-For', 'Proxy-Authorization', 'Proxy-Connection', 'Upgrade', 'X-Chrome-Variations', 'Connection', 'Cache-Control'])
  819. bufsize = 256 * 1024
  820. max_timeout = 4
  821. connect_timeout = 2
  822. first_run_lock = threading.Lock()
  823. handler_filters = [SimpleProxyHandlerFilter()]
  824. sticky_filter = None
  825. def finish(self):
  826. """make python2 BaseHTTPRequestHandler happy"""
  827. try:
  828. BaseHTTPServer.BaseHTTPRequestHandler.finish(self)
  829. except NetWorkIOError as e:
  830. if e[0] not in (errno.ECONNABORTED, errno.ECONNRESET, errno.EPIPE):
  831. raise
  832. def address_string(self):
  833. return '%s:%s' % self.client_address[:2]
  834. def send_response(self, code, message=None):
  835. if message is None:
  836. if code in self.responses:
  837. message = self.responses[code][0]
  838. else:
  839. message = ''
  840. if self.request_version != 'HTTP/0.9':
  841. self.wfile.write('%s %d %s\r\n' % (self.protocol_version, code, message))
  842. def send_header(self, keyword, value):
  843. """Send a MIME header."""
  844. base_send_header = BaseHTTPServer.BaseHTTPRequestHandler.send_header
  845. keyword = keyword.title()
  846. if keyword == 'Set-Cookie':
  847. for cookie in re.split(r', (?=[^ =]+(?:=|$))', value):
  848. base_send_header(self, keyword, cookie)
  849. elif keyword == 'Content-Disposition' and '"' not in value:
  850. value = re.sub(r'filename=([^"\']+)', 'filename="\\1"', value)
  851. base_send_header(self, keyword, value)
  852. else:
  853. base_send_header(self, keyword, value)
  854. def setup(self):
  855. if isinstance(self.__class__.first_run, collections.Callable):
  856. try:
  857. with self.__class__.first_run_lock:
  858. if isinstance(self.__class__.first_run, collections.Callable):
  859. self.first_run()
  860. self.__class__.first_run = None
  861. except StandardError as e:
  862. logging.exception('%s.first_run() return %r', self.__class__, e)
  863. self.__class__.setup = BaseHTTPServer.BaseHTTPRequestHandler.setup
  864. self.__class__.do_CONNECT = self.__class__.do_METHOD
  865. self.__class__.do_GET = self.__class__.do_METHOD
  866. self.__class__.do_PUT = self.__class__.do_METHOD
  867. self.__class__.do_POST = self.__class__.do_METHOD
  868. self.__class__.do_HEAD = self.__class__.do_METHOD
  869. self.__class__.do_DELETE = self.__class__.do_METHOD
  870. self.__class__.do_OPTIONS = self.__class__.do_METHOD
  871. self.setup()
  872. def handle_one_request(self):
  873. if not self.disable_transport_ssl and self.scheme == 'http':
  874. leadbyte = self.connection.recv(1, socket.MSG_PEEK)
  875. if leadbyte in ('\x80', '\x16'):
  876. server_name = ''
  877. if leadbyte == '\x16':
  878. for _ in xrange(2):
  879. leaddata = self.connection.recv(1024, socket.MSG_PEEK)
  880. if is_clienthello(leaddata):
  881. try:
  882. server_name = extract_sni_name(leaddata)
  883. finally:
  884. break
  885. try:
  886. certfile = CertUtil.get_cert(server_name or 'www.google.com')
  887. ssl_sock = ssl.wrap_socket(self.connection, ssl_version=self.ssl_version, keyfile=certfile, certfile=certfile, server_side=True)
  888. except StandardError as e:
  889. if e.args[0] not in (errno.ECONNABORTED, errno.ECONNRESET):
  890. logging.exception('ssl.wrap_socket(self.connection=%r) failed: %s', self.connection, e)
  891. return
  892. self.connection = ssl_sock
  893. self.rfile = self.connection.makefile('rb', self.bufsize)
  894. self.wfile = self.connection.makefile('wb', 0)
  895. self.scheme = 'https'
  896. return BaseHTTPServer.BaseHTTPRequestHandler.handle_one_request(self)
  897. def first_run(self):
  898. pass
  899. def gethostbyname2(self, hostname):
  900. return socket.gethostbyname_ex(hostname)[-1]
  901. def create_tcp_connection(self, hostname, port, timeout, **kwargs):
  902. return socket.create_connection((hostname, port), timeout)
  903. def create_ssl_connection(self, hostname, port, timeout, **kwargs):
  904. sock = self.create_tcp_connection(hostname, port, timeout, **kwargs)
  905. ssl_sock = ssl.wrap_socket(sock, ssl_version=self.ssl_version)
  906. return ssl_sock
  907. def create_http_request(self, method, url, headers, body, timeout, **kwargs):
  908. scheme, netloc, path, query, _ = urlparse.urlsplit(url)
  909. if netloc.rfind(':') <= netloc.rfind(']'):
  910. # no port number
  911. host = netloc
  912. port = 443 if scheme == 'https' else 80
  913. else:
  914. host, _, port = netloc.rpartition(':')
  915. port = int(port)
  916. if query:
  917. path += '?' + query
  918. if 'Host' not in headers:
  919. headers['Host'] = host
  920. if body and 'Content-Length' not in headers:
  921. headers['Content-Length'] = str(len(body))
  922. ConnectionType = httplib.HTTPSConnection if scheme == 'https' else httplib.HTTPConnection
  923. connection = ConnectionType(netloc, timeout=timeout)
  924. connection.request(method, path, body=body, headers=headers)
  925. response = connection.getresponse()
  926. return response
  927. def create_http_request_withserver(self, fetchserver, method, url, headers, body, timeout, **kwargs):
  928. return URLFetch(fetchserver, self.create_http_request).fetch(method, url, headers, body, timeout, **kwargs)
  929. def handle_urlfetch_error(self, fetchserver, response):
  930. pass
  931. def handle_urlfetch_response_close(self, fetchserver, response):
  932. pass
  933. def parse_header(self):
  934. if self.command == 'CONNECT':
  935. netloc = self.path
  936. elif self.path[0] == '/':
  937. netloc = self.headers.get('Host', 'localhost')
  938. self.path = '%s://%s%s' % (self.scheme, netloc, self.path)
  939. else:
  940. netloc = urlparse.urlsplit(self.path).netloc
  941. m = re.match(r'^(.+):(\d+)$', netloc)
  942. if m:
  943. self.host = m.group(1).strip('[]')
  944. self.port = int(m.group(2))
  945. else:
  946. self.host = netloc
  947. self.port = 443 if self.scheme == 'https' else 80
  948. def forward_socket(self, local, remote, timeout):
  949. try:
  950. tick = 1
  951. bufsize = self.bufsize
  952. timecount = timeout
  953. while 1:
  954. timecount -= tick
  955. if timecount <= 0:
  956. break
  957. (ins, _, errors) = select.select([local, remote], [], [local, remote], tick)
  958. if errors:
  959. break
  960. for sock in ins:
  961. data = sock.recv(bufsize)
  962. if not data:
  963. break
  964. if sock is remote:
  965. local.sendall(data)
  966. timecount = timeout
  967. else:
  968. remote.sendall(data)
  969. timecount = timeout
  970. except socket.timeout:
  971. pass
  972. except NetWorkIOError as e:
  973. if e.args[0] not in (errno.ECONNABORTED, errno.ECONNRESET, errno.ENOTCONN, errno.EPIPE):
  974. raise
  975. if e.args[0] in (errno.EBADF,):
  976. return
  977. finally:
  978. for sock in (remote, local):
  979. try:
  980. sock.close()
  981. except StandardError:
  982. pass
  983. def MOCK(self, status, headers, content):
  984. """mock response"""
  985. logging.info('%s "MOCK %s %s %s" %d %d', self.address_string(), self.command, self.path, self.protocol_version, status, len(content))
  986. headers = dict((k.title(), v) for k, v in headers.items())
  987. if 'Transfer-Encoding' in headers:
  988. del headers['Transfer-Encoding']
  989. if 'Content-Length' not in headers:
  990. headers['Content-Length'] = len(content)
  991. if 'Connection' not in headers:
  992. headers['Connection'] = 'close'
  993. self.send_response(status)
  994. for key, value in headers.items():
  995. self.send_header(key, value)
  996. self.end_headers()
  997. self.wfile.write(content)
  998. def STRIP(self, do_ssl_handshake=True, sticky_filter=None):
  999. """strip connect"""
  1000. certfile = CertUtil.get_cert(self.host)
  1001. logging.info('%s "STRIP %s %s:%d %s" - -', self.address_string(), self.command, self.host, self.port, self.protocol_version)
  1002. self.send_response(200)
  1003. self.end_headers()
  1004. if do_ssl_handshake:
  1005. try:
  1006. # ssl_sock = ssl.wrap_socket(self.connection, ssl_version=self.ssl_version, keyfile=certfile, certfile=certfile, server_side=True)
  1007. # bugfix for youtube-dl
  1008. ssl_sock = ssl.wrap_socket(self.connection, keyfile=certfile, certfile=certfile, server_side=True)
  1009. except StandardError as e:
  1010. if e.args[0] not in (errno.ECONNABORTED, errno.ECONNRESET):
  1011. logging.exception('ssl.wrap_socket(self.connection=%r) failed: %s', self.connection, e)
  1012. return
  1013. self.connection = ssl_sock
  1014. self.rfile = self.connection.makefile('rb', self.bufsize)
  1015. self.wfile = self.connection.makefile('wb', 0)
  1016. self.scheme = 'https'
  1017. try:
  1018. self.raw_requestline = self.rfile.readline(65537)
  1019. if len(self.raw_requ

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