PPub.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. #!/usr/bin/env python
  2. import sys, httplib, socket, select, gzip, zlib, re, traceback, argparse
  3. from SocketServer import ThreadingMixIn
  4. from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
  5. from threading import Lock, Timer
  6. from cStringIO import StringIO
  7. from urlparse import urlsplit
  8. parser = argparse.ArgumentParser()
  9. parser.add_argument("-p", "--port", help="Nombre de archivo a procesar")
  10. parser.add_argument("-t", "--texto", help="Nombre de archivo a procesar")
  11. args = parser.parse_args()
  12. if args.port:
  13. port = int(args.port)
  14. else:
  15. print " Deve ingresar el puerto que usara como socks..."
  16. sys.exit()
  17. if args.texto:
  18. msg1 = args.texto
  19. else:
  20. msg1 = '<strong><font color="#00FFFF">A</font><font color="#6bffff">D</font><font color="#99ffff">M</font><font color="#ebffff">@</font><font color="#ebffff">R</font><font color="#ccffff">u</font><font color="#99ffff">f</font><font color="#6bffff">u</font><font color="#2effff">9</font><font color="#00FFFF">9</font></strong>'
  21. class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
  22. address_family = socket.AF_INET
  23. def handle_error(self, request, client_address):
  24. print >>sys.stderr, '-'*40
  25. print >>sys.stderr, 'Exception happened during processing of request from', client_address
  26. traceback.print_exc()
  27. print >>sys.stderr, '-'*40
  28. class ThreadingHTTPServer6(ThreadingHTTPServer):
  29. address_family = socket.AF_INET6
  30. class SimpleHTTPProxyHandler(BaseHTTPRequestHandler):
  31. global_lock = Lock()
  32. conn_table = {}
  33. timeout = 300
  34. upstream_timeout = 300
  35. proxy_via = None
  36. def log_error(self, format, *args):
  37. if format == "Request timed out: %r":
  38. return
  39. self.log_message(format, *args)
  40. def do_CONNECT(self):
  41. req = self
  42. reqbody = None
  43. req.path = "https://%s/" % req.path.replace(':443', '')
  44. replaced_reqbody = self.request_handler(req, reqbody)
  45. if replaced_reqbody is True:
  46. return
  47. u = urlsplit(req.path)
  48. address = (u.hostname, u.port or 443)
  49. try:
  50. conn = socket.create_connection(address)
  51. except socket.error:
  52. return
  53. self.send_response(200, msg1)
  54. self.send_header('Connection', 'close')
  55. self.end_headers()
  56. conns = [self.connection, conn]
  57. keep_connection = True
  58. while keep_connection:
  59. keep_connection = False
  60. rlist, wlist, xlist = select.select(conns, [], conns, self.timeout)
  61. if xlist:
  62. break
  63. for r in rlist:
  64. other = conns[1] if r is conns[0] else conns[0]
  65. data = r.recv(8192)
  66. if data:
  67. other.sendall(data)
  68. keep_connection = True
  69. conn.close()
  70. def do_HEAD(self):
  71. self.do_SPAM()
  72. def do_GET(self):
  73. self.do_SPAM()
  74. def do_POST(self):
  75. self.do_SPAM()
  76. def do_SPAM(self):
  77. req = self
  78. content_length = int(req.headers.get('Content-Length', 0))
  79. if content_length > 0:
  80. reqbody = self.rfile.read(content_length)
  81. else:
  82. reqbody = None
  83. replaced_reqbody = self.request_handler(req, reqbody)
  84. if replaced_reqbody is True:
  85. return
  86. elif replaced_reqbody is not None:
  87. reqbody = replaced_reqbody
  88. if 'Content-Length' in req.headers:
  89. req.headers['Content-Length'] = str(len(reqbody))
  90. self.remove_hop_by_hop_headers(req.headers)
  91. if self.upstream_timeout:
  92. req.headers['Connection'] = 'Keep-Alive'
  93. else:
  94. req.headers['Connection'] = 'close'
  95. if self.proxy_via:
  96. self.modify_via_header(req.headers)
  97. try:
  98. res, resdata = self.request_to_upstream_server(req, reqbody)
  99. except socket.error:
  100. return
  101. content_encoding = res.headers.get('Content-Encoding', 'identity')
  102. resbody = self.decode_content_body(resdata, content_encoding)
  103. replaced_resbody = self.response_handler(req, reqbody, res, resbody)
  104. if replaced_resbody is True:
  105. return
  106. elif replaced_resbody is not None:
  107. resdata = self.encode_content_body(replaced_resbody, content_encoding)
  108. if 'Content-Length' in res.headers:
  109. res.headers['Content-Length'] = str(len(resdata))
  110. resbody = replaced_resbody
  111. self.remove_hop_by_hop_headers(res.headers)
  112. if self.timeout:
  113. res.headers['Connection'] = 'Keep-Alive'
  114. else:
  115. res.headers['Connection'] = 'close'
  116. if self.proxy_via:
  117. self.modify_via_header(res.headers)
  118. self.send_response(res.status, res.reason)
  119. for k, v in res.headers.items():
  120. if k == 'set-cookie':
  121. for value in self.split_set_cookie_header(v):
  122. self.send_header(k, value)
  123. else:
  124. self.send_header(k, v)
  125. self.end_headers()
  126. if self.command != 'HEAD':
  127. self.wfile.write(resdata)
  128. with self.global_lock:
  129. self.save_handler(req, reqbody, res, resbody)
  130. def request_to_upstream_server(self, req, reqbody):
  131. u = urlsplit(req.path)
  132. origin = (u.scheme, u.netloc)
  133. req.headers['Host'] = u.netloc
  134. selector = "%s?%s" % (u.path, u.query) if u.query else u.path
  135. while True:
  136. with self.lock_origin(origin):
  137. conn = self.open_origin(origin)
  138. try:
  139. conn.request(req.command, selector, reqbody, headers=dict(req.headers))
  140. except socket.error:
  141. self.close_origin(origin)
  142. raise
  143. try:
  144. res = conn.getresponse(buffering=True)
  145. except httplib.BadStatusLine as e:
  146. if e.line == "''":
  147. self.close_origin(origin)
  148. continue
  149. else:
  150. raise
  151. resdata = res.read()
  152. res.headers = res.msg
  153. if not self.upstream_timeout or 'close' in res.headers.get('Connection', ''):
  154. self.close_origin(origin)
  155. else:
  156. self.reset_timer(origin)
  157. return res, resdata
  158. def lock_origin(self, origin):
  159. d = self.conn_table.setdefault(origin, {})
  160. if not 'lock' in d:
  161. d['lock'] = Lock()
  162. return d['lock']
  163. def open_origin(self, origin):
  164. conn = self.conn_table[origin].get('connection')
  165. if not conn:
  166. scheme, netloc = origin
  167. if scheme == 'https':
  168. conn = httplib.HTTPSConnection(netloc)
  169. else:
  170. conn = httplib.HTTPConnection(netloc)
  171. self.reset_timer(origin)
  172. self.conn_table[origin]['connection'] = conn
  173. return conn
  174. def reset_timer(self, origin):
  175. timer = self.conn_table[origin].get('timer')
  176. if timer:
  177. timer.cancel()
  178. if self.upstream_timeout:
  179. timer = Timer(self.upstream_timeout, self.close_origin, args=[origin])
  180. timer.daemon = True
  181. timer.start()
  182. else:
  183. timer = None
  184. self.conn_table[origin]['timer'] = timer
  185. def close_origin(self, origin):
  186. timer = self.conn_table[origin]['timer']
  187. if timer:
  188. timer.cancel()
  189. conn = self.conn_table[origin]['connection']
  190. conn.close()
  191. del self.conn_table[origin]['connection']
  192. def remove_hop_by_hop_headers(self, headers):
  193. hop_by_hop_headers = ['Connection', 'Keep-Alive', 'Proxy-Authenticate', 'Proxy-Authorization', 'TE', 'Trailers', 'Trailer', 'Transfer-Encoding', 'Upgrade']
  194. connection = headers.get('Connection')
  195. if connection:
  196. keys = re.split(r',\s*', connection)
  197. hop_by_hop_headers.extend(keys)
  198. for k in hop_by_hop_headers:
  199. if k in headers:
  200. del headers[k]
  201. def modify_via_header(self, headers):
  202. via_string = "%s %s" % (self.protocol_version, self.proxy_via)
  203. via_string = re.sub(r'^HTTP/', '', via_string)
  204. original = headers.get('Via')
  205. if original:
  206. headers['Via'] = original + ', ' + via_string
  207. else:
  208. headers['Via'] = via_string
  209. def decode_content_body(self, data, content_encoding):
  210. if content_encoding in ('gzip', 'x-gzip'):
  211. io = StringIO(data)
  212. with gzip.GzipFile(fileobj=io) as f:
  213. body = f.read()
  214. elif content_encoding == 'deflate':
  215. body = zlib.decompress(data)
  216. elif content_encoding == 'identity':
  217. body = data
  218. else:
  219. raise Exception("Unknown Content-Encoding: %s" % content_encoding)
  220. return body
  221. def encode_content_body(self, body, content_encoding):
  222. if content_encoding in ('gzip', 'x-gzip'):
  223. io = StringIO()
  224. with gzip.GzipFile(fileobj=io, mode='wb') as f:
  225. f.write(body)
  226. data = io.getvalue()
  227. elif content_encoding == 'deflate':
  228. data = zlib.compress(body)
  229. elif content_encoding == 'identity':
  230. data = body
  231. else:
  232. raise Exception("Unknown Content-Encoding: %s" % content_encoding)
  233. return data
  234. def split_set_cookie_header(self, value):
  235. re_cookies = r'([^=]+=[^,;]+(?:;\s*Expires=[^,]+,[^,;]+|;[^,;]+)*)(?:,\s*)?'
  236. return re.findall(re_cookies, value, flags=re.IGNORECASE)
  237. def request_handler(self, req, reqbody):
  238. pass
  239. def response_handler(self, req, reqbody, res, resbody):
  240. pass
  241. def save_handler(self, req, reqbody, res, resbody):
  242. pass
  243. def test(HandlerClass=SimpleHTTPProxyHandler, ServerClass=ThreadingHTTPServer, protocol="HTTP/1.1"):
  244. server_address = ('', port)
  245. HandlerClass.protocol_version = protocol
  246. httpd = ServerClass(server_address, HandlerClass)
  247. sa = httpd.socket.getsockname()
  248. print "Serving HTTP on", sa[0], "port", sa[1], "..."
  249. httpd.serve_forever()
  250. if __name__ == '__main__':
  251. test()