diff options
-rw-r--r-- | libmproxy/console/__init__.py | 4 | ||||
-rw-r--r-- | libmproxy/dump.py | 7 | ||||
-rw-r--r-- | libmproxy/flow.py | 16 | ||||
-rw-r--r-- | libmproxy/proxy.py | 78 | ||||
-rw-r--r-- | test/test_dump.py | 4 | ||||
-rw-r--r-- | test/test_server.py | 6 |
6 files changed, 81 insertions, 34 deletions
diff --git a/libmproxy/console/__init__.py b/libmproxy/console/__init__.py index 2fef5beb..054cc66e 100644 --- a/libmproxy/console/__init__.py +++ b/libmproxy/console/__init__.py @@ -949,6 +949,10 @@ class ConsoleMaster(flow.FlowMaster): self.eventlist.set_focus(len(self.eventlist)) # Handlers + def handle_log(self, l): + self.add_event(l.msg) + l._ack() + def handle_error(self, r): f = flow.FlowMaster.handle_error(self, r) if f: diff --git a/libmproxy/dump.py b/libmproxy/dump.py index 36f54608..a58405ed 100644 --- a/libmproxy/dump.py +++ b/libmproxy/dump.py @@ -143,6 +143,11 @@ class DumpMaster(flow.FlowMaster): def add_event(self, e, level="info"): if self.eventlog: print >> self.outfile, e + self.outfile.flush() + + def handle_log(self, l): + self.add_event(l.msg) + l._ack() def handle_request(self, r): f = flow.FlowMaster.handle_request(self, r) @@ -196,6 +201,8 @@ class DumpMaster(flow.FlowMaster): print >> self.outfile print >> self.outfile, result print >> self.outfile, "\n" + if self.o.verbosity: + self.outfile.flush() self.state.delete_flow(f) if self.o.wfile: diff --git a/libmproxy/flow.py b/libmproxy/flow.py index 4de8bc96..edd10f49 100644 --- a/libmproxy/flow.py +++ b/libmproxy/flow.py @@ -637,7 +637,7 @@ class ClientConnect(controller.Msg): address: (address, port) tuple, or None if the connection is replayed. requestcount: Number of requests created by this client connection. close: Is the client connection closed? - connection_error: Error string or None. + error: Error string or None. """ def __init__(self, address): """ @@ -647,7 +647,7 @@ class ClientConnect(controller.Msg): self.address = address self.close = False self.requestcount = 0 - self.connection_error = None + self.error = None controller.Msg.__init__(self) def __eq__(self, other): @@ -655,12 +655,14 @@ class ClientConnect(controller.Msg): def _load_state(self, state): self.close = True + self.error = state["error"] self.requestcount = state["requestcount"] def _get_state(self): return dict( address = list(self.address), requestcount = self.requestcount, + error = self.error, ) @classmethod @@ -1388,20 +1390,10 @@ class FlowMaster(controller.Master): def handle_clientconnect(self, cc): self.run_script_hook("clientconnect", cc) - self.add_event("Connect from: %s:%s"%cc.address) cc._ack() def handle_clientdisconnect(self, r): self.run_script_hook("clientdisconnect", r) - s = "Disconnect from: %s:%s"%r.client_conn.address - self.add_event(s) - if r.client_conn.requestcount: - s = " -> handled %s requests"%r.client_conn.requestcount - self.add_event(s) - if r.client_conn.connection_error: - self.add_event( - " -> error: %s"%r.client_conn.connection_error, "error" - ) r._ack() def handle_error(self, r): diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py index 3ec22fb4..8f7210ca 100644 --- a/libmproxy/proxy.py +++ b/libmproxy/proxy.py @@ -16,8 +16,8 @@ import sys, os, string, socket, time import shutil, tempfile, threading import optparse, SocketServer from OpenSSL import SSL -from netlib import odict, tcp, http, wsgi, certutils -import utils, flow, version, platform +from netlib import odict, tcp, http, wsgi, certutils, http_status +import utils, flow, version, platform, controller class ProxyError(Exception): @@ -28,6 +28,13 @@ class ProxyError(Exception): return "ProxyError(%s, %s)"%(self.code, self.msg) +class Log(controller.Msg): + def __init__(self, msg): + controller.Msg.__init__(self) + self.msg = msg + + + class ProxyConfig: def __init__(self, certfile = None, cacert = None, clientcerts = None, cert_wait_time=0, upstream_cert=False, body_size_limit = None, reverse_proxy=None, transparent_proxy=None): assert not (reverse_proxy and transparent_proxy) @@ -81,7 +88,10 @@ class ServerConnection(tcp.TCPClient): path = os.path.join(self.config.clientcerts, self.host) + ".pem" if os.path.exists(clientcert): clientcert = path - self.convert_to_ssl(clientcert=clientcert) + try: + self.convert_to_ssl(clientcert=clientcert, sni=self.host) + except tcp.NetLibError, v: + raise ProxyError(400, str(v)) def send(self, request): self.requestcount += 1 @@ -114,11 +124,18 @@ class ProxyHandler(tcp.BaseHandler): def handle(self): cc = flow.ClientConnect(self.client_address) + self.log(cc, "connect") cc._send(self.mqueue) while self.handle_request(cc) and not cc.close: pass cc.close = True cd = flow.ClientDisconnect(cc) + + self.log( + cc, "disconnect", + [ + "handled %s requests"%cc.requestcount] + ) cd._send(self.mqueue) def server_connect(self, scheme, host, port): @@ -182,17 +199,37 @@ class ProxyHandler(tcp.BaseHandler): # disconnect. if http.response_connection_close(response.httpversion, response.headers): return - except IOError, v: - cc.connection_error = v - except (ProxyError, http.HttpError), e: - cc.connection_error = "%s: %s"%(e.code, e.msg) + except (IOError, ProxyError, http.HttpError), e: + if isinstance(e, IOError): + cc.error = str(e) + else: + cc.error = "%s: %s"%(e.code, e.msg) + if request: - err = flow.Error(request, e.msg) + err = flow.Error(request, cc.error) err._send(self.mqueue) - self.send_error(e.code, e.msg) + self.log( + cc, cc.error, + ["url: %s"%request.get_url()] + ) + else: + self.log(cc, cc.error) + + if isinstance(e, ProxyError): + self.send_error(e.code, e.msg) else: return True + def log(self, cc, msg, subs=()): + msg = [ + "%s:%s: "%cc.address + msg + ] + for i in subs: + msg.append(" -> "+i) + msg = "\n".join(msg) + l = Log(msg) + l._send(self.mqueue) + def find_cert(self, host, port, sni): if self.config.certfile: return self.config.certfile @@ -226,7 +263,10 @@ class ProxyHandler(tcp.BaseHandler): if not self.ssl_established and (port in self.config.transparent_proxy["sslports"]): scheme = "https" certfile = self.find_cert(host, port, None) - self.convert_to_ssl(certfile, self.config.certfile or self.config.cacert) + try: + self.convert_to_ssl(certfile, self.config.certfile or self.config.cacert) + except tcp.NetLibError, v: + raise ProxyError(400, str(v)) else: scheme = "http" host = self.sni or host @@ -235,7 +275,7 @@ class ProxyHandler(tcp.BaseHandler): return None r = http.parse_init_http(line) if not r: - raise ProxyError(400, "Bad HTTP request line: %s"%line) + raise ProxyError(400, "Bad HTTP request line: %s"%repr(line)) method, path, httpversion = r headers = http.read_headers(self.rfile) content = http.read_http_body_request( @@ -249,7 +289,7 @@ class ProxyHandler(tcp.BaseHandler): scheme, host, port = self.config.reverse_proxy r = http.parse_init_http(line) if not r: - raise ProxyError(400, "Bad HTTP request line: %s"%line) + raise ProxyError(400, "Bad HTTP request line: %s"%repr(line)) method, path, httpversion = r headers = http.read_headers(self.rfile) content = http.read_http_body_request( @@ -263,7 +303,7 @@ class ProxyHandler(tcp.BaseHandler): if line.startswith("CONNECT"): r = http.parse_init_connect(line) if not r: - raise ProxyError(400, "Bad HTTP request line: %s"%line) + raise ProxyError(400, "Bad HTTP request line: %s"%repr(line)) host, port, httpversion = r # FIXME: Discard additional headers sent to the proxy. Should I expose # these to users? @@ -278,14 +318,17 @@ class ProxyHandler(tcp.BaseHandler): ) self.wfile.flush() certfile = self.find_cert(host, port, None) - self.convert_to_ssl(certfile, self.config.certfile or self.config.cacert) + try: + self.convert_to_ssl(certfile, self.config.certfile or self.config.cacert) + except tcp.NetLibError, v: + raise ProxyError(400, str(v)) self.proxy_connect_state = (host, port, httpversion) line = self.rfile.readline(line) if self.proxy_connect_state: host, port, httpversion = self.proxy_connect_state r = http.parse_init_http(line) if not r: - raise ProxyError(400, "Bad HTTP request line: %s"%line) + raise ProxyError(400, "Bad HTTP request line: %s"%repr(line)) method, path, httpversion = r headers = http.read_headers(self.rfile) content = http.read_http_body_request( @@ -295,7 +338,7 @@ class ProxyHandler(tcp.BaseHandler): else: r = http.parse_init_proxy(line) if not r: - raise ProxyError(400, "Bad HTTP request line: %s"%line) + raise ProxyError(400, "Bad HTTP request line: %s"%repr(line)) method, scheme, host, port, path, httpversion = http.parse_init_proxy(line) headers = http.read_headers(self.rfile) content = http.read_http_body_request( @@ -312,8 +355,7 @@ class ProxyHandler(tcp.BaseHandler): def send_error(self, code, body): try: - import BaseHTTPServer - response = BaseHTTPServer.BaseHTTPRequestHandler.responses[code][0] + response = http_status.RESPONSES.get(code, "Unknown") self.wfile.write("HTTP/1.1 %s %s\r\n" % (code, response)) self.wfile.write("Server: %s\r\n"%version.NAMEVERSION) self.wfile.write("Connection: close\r\n") diff --git a/test/test_dump.py b/test/test_dump.py index f13245ed..5e2fcdc4 100644 --- a/test/test_dump.py +++ b/test/test_dump.py @@ -1,7 +1,7 @@ import os from cStringIO import StringIO import libpry -from libmproxy import dump, flow +from libmproxy import dump, flow, proxy import tutils def test_strfuncs(): @@ -20,6 +20,8 @@ class TestDumpMaster: def _cycle(self, m, content): req = tutils.treq() req.content = content + l = proxy.Log("connect") + m.handle_log(l) cc = req.client_conn cc.connection_error = "error" resp = tutils.tresp(req) diff --git a/test/test_server.py b/test/test_server.py index e4a62fa5..1f83c496 100644 --- a/test/test_server.py +++ b/test/test_server.py @@ -55,6 +55,6 @@ class TestProxy(tutils.HTTPProxTest): assert f.status_code == 304 l = self.log() - assert l[0].address - assert "host" in l[1].headers - assert l[2].code == 304 + assert l[1].address + assert "host" in l[2].headers + assert l[3].code == 304 |