aboutsummaryrefslogtreecommitdiffstats
path: root/libmproxy
diff options
context:
space:
mode:
Diffstat (limited to 'libmproxy')
-rw-r--r--libmproxy/cmdline.py8
-rw-r--r--libmproxy/console/__init__.py1
-rw-r--r--libmproxy/console/common.py121
-rw-r--r--libmproxy/console/flowdetailview.py5
-rw-r--r--libmproxy/console/flowlist.py19
-rw-r--r--libmproxy/console/flowview.py18
-rw-r--r--libmproxy/console/grideditor.py6
-rw-r--r--libmproxy/console/help.py1
-rw-r--r--libmproxy/console/options.py1
-rw-r--r--libmproxy/console/palettepicker.py1
-rw-r--r--libmproxy/console/palettes.py2
-rw-r--r--libmproxy/console/pathedit.py2
-rw-r--r--libmproxy/console/searchable.py2
-rw-r--r--libmproxy/console/select.py5
-rw-r--r--libmproxy/console/signals.py2
-rw-r--r--libmproxy/console/statusbar.py2
-rw-r--r--libmproxy/console/tabs.py3
-rw-r--r--libmproxy/console/window.py1
-rw-r--r--libmproxy/contentviews.py5
-rw-r--r--libmproxy/contrib/jsbeautifier/unpackers/__init__.py1
-rw-r--r--libmproxy/contrib/jsbeautifier/unpackers/packer.py1
-rw-r--r--libmproxy/controller.py5
-rw-r--r--libmproxy/dump.py9
-rw-r--r--libmproxy/exceptions.py5
-rw-r--r--libmproxy/filt.py6
-rw-r--r--libmproxy/flow.py19
-rw-r--r--libmproxy/models/connections.py4
-rw-r--r--libmproxy/models/flow.py2
-rw-r--r--libmproxy/models/http.py5
-rw-r--r--libmproxy/onboarding/app.py4
-rw-r--r--libmproxy/platform/linux.py1
-rw-r--r--libmproxy/platform/osx.py2
-rw-r--r--libmproxy/platform/windows.py4
-rw-r--r--libmproxy/protocol/__init__.py8
-rw-r--r--libmproxy/protocol/base.py5
-rw-r--r--libmproxy/protocol/http.py11
-rw-r--r--libmproxy/protocol/rawtcp.py2
-rw-r--r--libmproxy/protocol/tls.py137
-rw-r--r--libmproxy/proxy/config.py2
-rw-r--r--libmproxy/proxy/modes/http_proxy.py2
-rw-r--r--libmproxy/proxy/modes/reverse_proxy.py1
-rw-r--r--libmproxy/proxy/modes/socks_proxy.py1
-rw-r--r--libmproxy/proxy/modes/transparent_proxy.py1
-rw-r--r--libmproxy/proxy/root_context.py23
-rw-r--r--libmproxy/proxy/server.py1
-rw-r--r--libmproxy/script/__init__.py2
-rw-r--r--libmproxy/script/concurrent.py1
-rw-r--r--libmproxy/script/reloader.py8
-rw-r--r--libmproxy/script/script.py5
-rw-r--r--libmproxy/script/script_context.py1
-rw-r--r--libmproxy/stateobject.py1
-rw-r--r--libmproxy/utils.py4
-rw-r--r--libmproxy/web/__init__.py4
-rw-r--r--libmproxy/web/app.py15
54 files changed, 374 insertions, 134 deletions
diff --git a/libmproxy/cmdline.py b/libmproxy/cmdline.py
index cd1a8bba..111ab145 100644
--- a/libmproxy/cmdline.py
+++ b/libmproxy/cmdline.py
@@ -372,10 +372,10 @@ def proxy_options(parser):
rawtcp = group.add_mutually_exclusive_group()
rawtcp.add_argument("--raw-tcp", action="store_true", dest="rawtcp")
rawtcp.add_argument("--no-raw-tcp", action="store_false", dest="rawtcp",
- help="Explicitly enable/disable experimental raw tcp support. "
- "Disabled by default. "
- "Default value will change in a future version."
- )
+ help="Explicitly enable/disable experimental raw tcp support. "
+ "Disabled by default. "
+ "Default value will change in a future version."
+ )
def proxy_ssl_options(parser):
diff --git a/libmproxy/console/__init__.py b/libmproxy/console/__init__.py
index 3d0fb1aa..80a6e28a 100644
--- a/libmproxy/console/__init__.py
+++ b/libmproxy/console/__init__.py
@@ -22,6 +22,7 @@ EVENTLOG_SIZE = 500
class ConsoleState(flow.State):
+
def __init__(self):
flow.State.__init__(self)
self.focus = None
diff --git a/libmproxy/console/common.py b/libmproxy/console/common.py
index 12fdfe27..276e45c2 100644
--- a/libmproxy/console/common.py
+++ b/libmproxy/console/common.py
@@ -207,7 +207,7 @@ def raw_format_flow(f, focus, extended, padding):
# Save file to disk
-def save_data(path, data, master, state):
+def save_data(path, data):
if not path:
return
try:
@@ -217,32 +217,32 @@ def save_data(path, data, master, state):
signals.status_message.send(message=v.strerror)
-def ask_save_overwite(path, data, master, state):
+def ask_save_overwrite(path, data):
if not path:
return
path = os.path.expanduser(path)
if os.path.exists(path):
- def save_overwite(k):
+ def save_overwrite(k):
if k == "y":
- save_data(path, data, master, state)
+ save_data(path, data)
signals.status_prompt_onekey.send(
- prompt = "'" + path + "' already exists. Overwite?",
+ prompt = "'" + path + "' already exists. Overwrite?",
keys = (
("yes", "y"),
("no", "n"),
),
- callback = save_overwite
+ callback = save_overwrite
)
else:
- save_data(path, data, master, state)
+ save_data(path, data)
-def ask_save_path(prompt, data, master, state):
+def ask_save_path(prompt, data):
signals.status_prompt_path.send(
prompt = prompt,
- callback = ask_save_overwite,
- args = (data, master, state)
+ callback = ask_save_overwrite,
+ args = (data, )
)
@@ -277,26 +277,60 @@ def copy_flow_format_data(part, scope, flow):
return data, False
-def copy_flow(part, scope, flow, master, state):
- """
- part: _c_ontent, _h_eaders+content, _u_rl
- scope: _a_ll, re_q_uest, re_s_ponse
- """
- data, err = copy_flow_format_data(part, scope, flow)
+def export_prompt(k, flow):
+ if k == "c":
+ copy_as_curl_command(flow)
+ elif k == "p":
+ copy_as_python_code(flow)
+ elif k == "r":
+ copy_as_raw_request(flow)
- if err:
- signals.status_message.send(message=err)
- return
- if not data:
- if scope == "q":
- signals.status_message.send(message="No request content to copy.")
- elif scope == "s":
- signals.status_message.send(message="No response content to copy.")
- else:
- signals.status_message.send(message="No contents to copy.")
+def copy_as_curl_command(flow):
+ data = "curl "
+
+ for k, v in flow.request.headers.fields:
+ data += "-H '%s:%s' " % (k, v)
+
+ if flow.request.method != "GET":
+ data += "-X %s " % flow.request.method
+
+ full_url = flow.request.scheme + "://" + flow.request.host + flow.request.path
+ data += "'%s'" % full_url
+
+ if flow.request.content:
+ data += " --data-binary '%s'" % flow.request.content
+
+ copy_to_clipboard_or_prompt(data)
+
+
+def copy_as_python_code(flow):
+ if flow.request.method != "GET":
+ signals.status_message.send(message="Currently, only GET methods are supported")
return
+ data = ("import requests\n"
+ "headers = {%s}\n"
+ "url = '%s'\n"
+ "resp = requests.get(url, headers=headers)")
+
+ headers = "\n"
+ for k, v in flow.request.headers.fields:
+ headers += " '%s': '%s',\n" % (k, v)
+
+ full_url = flow.request.scheme + "://" + flow.request.host + flow.request.path
+
+ data = data % (headers, full_url)
+
+ copy_to_clipboard_or_prompt(data)
+
+
+def copy_as_raw_request(flow):
+ data = netlib.http.http1.assemble_request(flow.request)
+ copy_to_clipboard_or_prompt(data)
+
+
+def copy_to_clipboard_or_prompt(data):
# pyperclip calls encode('utf-8') on data to be copied without checking.
# if data are already encoded that way UnicodeDecodeError is thrown.
toclip = ""
@@ -310,7 +344,7 @@ def copy_flow(part, scope, flow, master, state):
except (RuntimeError, UnicodeDecodeError, AttributeError):
def save(k):
if k == "y":
- ask_save_path("Save data", data, master, state)
+ ask_save_path("Save data", data)
signals.status_prompt_onekey.send(
prompt = "Cannot copy data to clipboard. Save as file?",
keys = (
@@ -321,6 +355,29 @@ def copy_flow(part, scope, flow, master, state):
)
+def copy_flow(part, scope, flow, master, state):
+ """
+ part: _c_ontent, _h_eaders+content, _u_rl
+ scope: _a_ll, re_q_uest, re_s_ponse
+ """
+ data, err = copy_flow_format_data(part, scope, flow)
+
+ if err:
+ signals.status_message.send(message=err)
+ return
+
+ if not data:
+ if scope == "q":
+ signals.status_message.send(message="No request content to copy.")
+ elif scope == "s":
+ signals.status_message.send(message="No response content to copy.")
+ else:
+ signals.status_message.send(message="No contents to copy.")
+ return
+
+ copy_to_clipboard_or_prompt(data)
+
+
def ask_copy_part(scope, flow, master, state):
choices = [
("content", "c"),
@@ -367,16 +424,12 @@ def ask_save_body(part, master, state, flow):
elif part == "q" and request_has_content:
ask_save_path(
"Save request content",
- flow.request.get_decoded_content(),
- master,
- state
+ flow.request.get_decoded_content()
)
elif part == "s" and response_has_content:
ask_save_path(
"Save response content",
- flow.response.get_decoded_content(),
- master,
- state
+ flow.response.get_decoded_content()
)
else:
signals.status_message.send(message="No content to save.")
@@ -386,7 +439,7 @@ flowcache = utils.LRUCache(800)
def format_flow(f, focus, extended=False, hostheader=False, padding=2,
- marked=False):
+ marked=False):
d = dict(
intercepted = f.intercepted,
acked = f.reply.acked,
diff --git a/libmproxy/console/flowdetailview.py b/libmproxy/console/flowdetailview.py
index 40769c95..f4b4262e 100644
--- a/libmproxy/console/flowdetailview.py
+++ b/libmproxy/console/flowdetailview.py
@@ -9,7 +9,6 @@ def maybe_timestamp(base, attr):
return utils.format_timestamp_with_milli(getattr(base, attr))
else:
return "active"
- pass
def flowdetails(state, flow):
@@ -20,7 +19,7 @@ def flowdetails(state, flow):
req = flow.request
resp = flow.response
- if sc:
+ if sc is not None:
text.append(urwid.Text([("head", "Server Connection:")]))
parts = [
["Address", "%s:%s" % sc.address()],
@@ -76,7 +75,7 @@ def flowdetails(state, flow):
common.format_keyvals(parts, key="key", val="text", indent=4)
)
- if cc:
+ if cc is not None:
text.append(urwid.Text([("head", "Client Connection:")]))
parts = [
diff --git a/libmproxy/console/flowlist.py b/libmproxy/console/flowlist.py
index 2b77f4a3..c2201055 100644
--- a/libmproxy/console/flowlist.py
+++ b/libmproxy/console/flowlist.py
@@ -1,7 +1,6 @@
from __future__ import absolute_import
import urwid
-from netlib import http
import netlib.utils
from . import common, signals
@@ -16,6 +15,7 @@ def _mkhelp():
("C", "clear flow list or eventlog"),
("d", "delete flow"),
("D", "duplicate flow"),
+ ("E", "export"),
("e", "toggle eventlog"),
("F", "toggle follow flow list"),
("l", "set limit filter pattern"),
@@ -43,6 +43,7 @@ footer = [
class EventListBox(urwid.ListBox):
+
def __init__(self, master):
self.master = master
urwid.ListBox.__init__(self, master.eventlist)
@@ -60,6 +61,7 @@ class EventListBox(urwid.ListBox):
class BodyPile(urwid.Pile):
+
def __init__(self, master):
h = urwid.Text("Event log")
h = urwid.Padding(h, align="left", width=("relative", 100))
@@ -103,6 +105,7 @@ class BodyPile(urwid.Pile):
class ConnectionItem(urwid.WidgetWrap):
+
def __init__(self, master, state, flow, focus):
self.master, self.state, self.flow = master, state, flow
self.f = focus
@@ -254,6 +257,18 @@ class ConnectionItem(urwid.WidgetWrap):
)
elif key == "P":
common.ask_copy_part("a", self.flow, self.master, self.state)
+ elif key == "E":
+ signals.status_prompt_onekey.send(
+ self,
+ prompt = "Export",
+ keys = (
+ ("as curl command", "c"),
+ ("as python code", "p"),
+ ("as raw request", "r"),
+ ),
+ callback = common.export_prompt,
+ args = (self.flow,)
+ )
elif key == "b":
common.ask_save_body(None, self.master, self.state, self.flow)
else:
@@ -261,6 +276,7 @@ class ConnectionItem(urwid.WidgetWrap):
class FlowListWalker(urwid.ListWalker):
+
def __init__(self, master, state):
self.master, self.state = master, state
signals.flowlist_change.connect(self.sig_flowlist_change)
@@ -289,6 +305,7 @@ class FlowListWalker(urwid.ListWalker):
class FlowListBox(urwid.ListBox):
+
def __init__(self, master):
self.master = master
urwid.ListBox.__init__(
diff --git a/libmproxy/console/flowview.py b/libmproxy/console/flowview.py
index 0038558b..8102de55 100644
--- a/libmproxy/console/flowview.py
+++ b/libmproxy/console/flowview.py
@@ -25,8 +25,9 @@ def _mkhelp():
("A", "accept all intercepted flows"),
("a", "accept this intercepted flow"),
("b", "save request/response body"),
- ("d", "delete flow"),
("D", "duplicate flow"),
+ ("d", "delete flow"),
+ ("E", "export"),
("e", "edit request/response"),
("f", "load full body data"),
("m", "change body display mode for this entity"),
@@ -95,6 +96,7 @@ footer = [
class FlowViewHeader(urwid.WidgetWrap):
+
def __init__(self, master, f):
self.master, self.flow = master, f
self._w = common.format_flow(
@@ -217,7 +219,7 @@ class FlowView(tabs.Tabs):
txt = []
for (style, text) in line:
if total_chars + len(text) > max_chars:
- text = text[:max_chars-total_chars]
+ text = text[:max_chars - total_chars]
txt.append((style, text))
total_chars += len(text)
if total_chars == max_chars:
@@ -574,6 +576,18 @@ class FlowView(tabs.Tabs):
callback = self.master.save_one_flow,
args = (self.flow,)
)
+ elif key == "E":
+ signals.status_prompt_onekey.send(
+ self,
+ prompt = "Export",
+ keys = (
+ ("as curl command", "c"),
+ ("as python code", "p"),
+ ("as raw request", "r"),
+ ),
+ callback = common.export_prompt,
+ args = (self.flow,)
+ )
elif key == "|":
signals.status_prompt_path.send(
prompt = "Send flow to script",
diff --git a/libmproxy/console/grideditor.py b/libmproxy/console/grideditor.py
index 237eea28..a11c962c 100644
--- a/libmproxy/console/grideditor.py
+++ b/libmproxy/console/grideditor.py
@@ -63,6 +63,7 @@ class TextColumn:
class SubgridColumn:
+
def __init__(self, heading, subeditor):
self.heading = heading
self.subeditor = subeditor
@@ -97,6 +98,7 @@ class SubgridColumn:
class SEscaped(urwid.WidgetWrap):
+
def __init__(self, txt):
txt = txt.encode("string-escape")
w = urwid.Text(txt, wrap="any")
@@ -113,6 +115,7 @@ class SEscaped(urwid.WidgetWrap):
class SEdit(urwid.WidgetWrap):
+
def __init__(self, txt):
txt = txt.encode("string-escape")
w = urwid.Edit(edit_text=txt, wrap="any", multiline=True)
@@ -127,6 +130,7 @@ class SEdit(urwid.WidgetWrap):
class GridRow(urwid.WidgetWrap):
+
def __init__(self, focused, editing, editor, values):
self.focused, self.editing, self.editor = focused, editing, editor
@@ -172,6 +176,7 @@ class GridRow(urwid.WidgetWrap):
class GridWalker(urwid.ListWalker):
+
"""
Stores rows as a list of (rows, errors) tuples, where rows is a list
and errors is a set with an entry of each offset in rows that is an
@@ -311,6 +316,7 @@ class GridWalker(urwid.ListWalker):
class GridListBox(urwid.ListBox):
+
def __init__(self, lw):
urwid.ListBox.__init__(self, lw)
diff --git a/libmproxy/console/help.py b/libmproxy/console/help.py
index 74748030..0c264ebf 100644
--- a/libmproxy/console/help.py
+++ b/libmproxy/console/help.py
@@ -12,6 +12,7 @@ footer = [
class HelpView(urwid.ListBox):
+
def __init__(self, help_context):
self.help_context = help_context or []
urwid.ListBox.__init__(
diff --git a/libmproxy/console/options.py b/libmproxy/console/options.py
index a365a78c..5c9e0cc9 100644
--- a/libmproxy/console/options.py
+++ b/libmproxy/console/options.py
@@ -22,6 +22,7 @@ help_context = _mkhelp()
class Options(urwid.WidgetWrap):
+
def __init__(self, master):
self.master = master
self.lb = select.Select(
diff --git a/libmproxy/console/palettepicker.py b/libmproxy/console/palettepicker.py
index 7e2c10cd..51ad0606 100644
--- a/libmproxy/console/palettepicker.py
+++ b/libmproxy/console/palettepicker.py
@@ -18,6 +18,7 @@ help_context = _mkhelp()
class PalettePicker(urwid.WidgetWrap):
+
def __init__(self, master):
self.master = master
low, high = [], []
diff --git a/libmproxy/console/palettes.py b/libmproxy/console/palettes.py
index d897a0a2..bd370181 100644
--- a/libmproxy/console/palettes.py
+++ b/libmproxy/console/palettes.py
@@ -65,6 +65,7 @@ class Palette:
class LowDark(Palette):
+
"""
Low-color dark background
"""
@@ -129,6 +130,7 @@ class Dark(LowDark):
class LowLight(Palette):
+
"""
Low-color light background
"""
diff --git a/libmproxy/console/pathedit.py b/libmproxy/console/pathedit.py
index dccec14a..4447070b 100644
--- a/libmproxy/console/pathedit.py
+++ b/libmproxy/console/pathedit.py
@@ -5,6 +5,7 @@ import urwid
class _PathCompleter:
+
def __init__(self, _testing=False):
"""
_testing: disables reloading of the lookup table to make testing
@@ -55,6 +56,7 @@ class _PathCompleter:
class PathEdit(urwid.Edit, _PathCompleter):
+
def __init__(self, *args, **kwargs):
urwid.Edit.__init__(self, *args, **kwargs)
_PathCompleter.__init__(self)
diff --git a/libmproxy/console/searchable.py b/libmproxy/console/searchable.py
index dea0ac7f..cff1f0a1 100644
--- a/libmproxy/console/searchable.py
+++ b/libmproxy/console/searchable.py
@@ -4,6 +4,7 @@ from . import signals
class Highlight(urwid.AttrMap):
+
def __init__(self, t):
urwid.AttrMap.__init__(
self,
@@ -14,6 +15,7 @@ class Highlight(urwid.AttrMap):
class Searchable(urwid.ListBox):
+
def __init__(self, state, contents):
self.walker = urwid.SimpleFocusListWalker(contents)
urwid.ListBox.__init__(self, self.walker)
diff --git a/libmproxy/console/select.py b/libmproxy/console/select.py
index bf96a785..928a7ca5 100644
--- a/libmproxy/console/select.py
+++ b/libmproxy/console/select.py
@@ -4,6 +4,7 @@ from . import common
class _OptionWidget(urwid.WidgetWrap):
+
def __init__(self, option, text, shortcut, active, focus):
self.option = option
textattr = "text"
@@ -36,6 +37,7 @@ class _OptionWidget(urwid.WidgetWrap):
class OptionWalker(urwid.ListWalker):
+
def __init__(self, options):
urwid.ListWalker.__init__(self)
self.options = options
@@ -59,6 +61,7 @@ class OptionWalker(urwid.ListWalker):
class Heading:
+
def __init__(self, text):
self.text = text
@@ -73,6 +76,7 @@ _neg = lambda: False
class Option:
+
def __init__(self, text, shortcut, getstate=None, activate=None):
self.text = text
self.shortcut = shortcut
@@ -89,6 +93,7 @@ class Option:
class Select(urwid.ListBox):
+
def __init__(self, options):
self.walker = OptionWalker(options)
urwid.ListBox.__init__(
diff --git a/libmproxy/console/signals.py b/libmproxy/console/signals.py
index 52f72d34..6a439bf3 100644
--- a/libmproxy/console/signals.py
+++ b/libmproxy/console/signals.py
@@ -2,6 +2,8 @@ import blinker
# Show a status message in the action bar
sig_add_event = blinker.Signal()
+
+
def add_event(e, level):
sig_add_event.send(
None,
diff --git a/libmproxy/console/statusbar.py b/libmproxy/console/statusbar.py
index fd084f8c..4cc63a54 100644
--- a/libmproxy/console/statusbar.py
+++ b/libmproxy/console/statusbar.py
@@ -7,6 +7,7 @@ from . import pathedit, signals, common
class ActionBar(urwid.WidgetWrap):
+
def __init__(self):
urwid.WidgetWrap.__init__(self, None)
self.clear()
@@ -108,6 +109,7 @@ class ActionBar(urwid.WidgetWrap):
class StatusBar(urwid.WidgetWrap):
+
def __init__(self, master, helptext):
self.master, self.helptext = master, helptext
self.ab = ActionBar()
diff --git a/libmproxy/console/tabs.py b/libmproxy/console/tabs.py
index fea9bbde..b5423038 100644
--- a/libmproxy/console/tabs.py
+++ b/libmproxy/console/tabs.py
@@ -1,8 +1,8 @@
import urwid
-import signals
class Tab(urwid.WidgetWrap):
+
def __init__(self, offset, content, attr, onclick):
"""
onclick is called on click with the tab offset as argument
@@ -21,6 +21,7 @@ class Tab(urwid.WidgetWrap):
class Tabs(urwid.WidgetWrap):
+
def __init__(self, tabs, tab_offset=0):
urwid.WidgetWrap.__init__(self, "")
self.tab_offset = tab_offset
diff --git a/libmproxy/console/window.py b/libmproxy/console/window.py
index 69d5e242..47c284e4 100644
--- a/libmproxy/console/window.py
+++ b/libmproxy/console/window.py
@@ -3,6 +3,7 @@ from . import signals
class Window(urwid.Frame):
+
def __init__(self, master, body, header, footer, helpctx):
urwid.Frame.__init__(
self,
diff --git a/libmproxy/contentviews.py b/libmproxy/contentviews.py
index eaab8169..526f5ff4 100644
--- a/libmproxy/contentviews.py
+++ b/libmproxy/contentviews.py
@@ -275,6 +275,7 @@ class ViewMultipart(View):
if pyamf:
class DummyObject(dict):
+
def __init__(self, alias):
dict.__init__(self)
@@ -282,7 +283,6 @@ if pyamf:
data = input.readObject()
self["data"] = data
-
def pyamf_class_loader(s):
for i in pyamf.CLASS_LOADERS:
if i != pyamf_class_loader:
@@ -291,10 +291,8 @@ if pyamf:
return v
return DummyObject
-
pyamf.register_class_loader(pyamf_class_loader)
-
class ViewAMF(View):
name = "AMF"
prompt = ("amf", "f")
@@ -417,6 +415,7 @@ class ViewImage(View):
class ViewProtobuf(View):
+
"""Human friendly view of protocol buffers
The view uses the protoc compiler to decode the binary
"""
diff --git a/libmproxy/contrib/jsbeautifier/unpackers/__init__.py b/libmproxy/contrib/jsbeautifier/unpackers/__init__.py
index 96318a5a..fcb5b07a 100644
--- a/libmproxy/contrib/jsbeautifier/unpackers/__init__.py
+++ b/libmproxy/contrib/jsbeautifier/unpackers/__init__.py
@@ -15,7 +15,6 @@ BLACKLIST = ['jsbeautifier.unpackers.evalbased']
class UnpackingError(Exception):
"""Badly packed source or general error. Argument is a
meaningful description."""
- pass
def getunpackers():
"""Scans the unpackers dir, finds unpackers and add them to UNPACKERS list.
diff --git a/libmproxy/contrib/jsbeautifier/unpackers/packer.py b/libmproxy/contrib/jsbeautifier/unpackers/packer.py
index 37aa7123..4ada669e 100644
--- a/libmproxy/contrib/jsbeautifier/unpackers/packer.py
+++ b/libmproxy/contrib/jsbeautifier/unpackers/packer.py
@@ -13,7 +13,6 @@
"""Unpacker for Dean Edward's p.a.c.k.e.r"""
import re
-import string
from . import UnpackingError
PRIORITY = 1
diff --git a/libmproxy/controller.py b/libmproxy/controller.py
index 24b229c5..712ab1d2 100644
--- a/libmproxy/controller.py
+++ b/libmproxy/controller.py
@@ -4,6 +4,7 @@ import threading
class DummyReply:
+
"""
A reply object that does nothing. Useful when we need an object to seem
like it has a channel, and during testing.
@@ -17,6 +18,7 @@ class DummyReply:
class Reply:
+
"""
Messages sent through a channel are decorated with a "reply" attribute.
This object is used to respond to the message through the return
@@ -38,6 +40,7 @@ class Reply:
class Channel:
+
def __init__(self, q, should_exit):
self.q = q
self.should_exit = should_exit
@@ -67,6 +70,7 @@ class Channel:
class Slave(threading.Thread):
+
"""
Slaves get a channel end-point through which they can send messages to
the master.
@@ -84,6 +88,7 @@ class Slave(threading.Thread):
class Master(object):
+
"""
Masters get and respond to messages from slaves.
"""
diff --git a/libmproxy/dump.py b/libmproxy/dump.py
index c2a3268a..65e212bf 100644
--- a/libmproxy/dump.py
+++ b/libmproxy/dump.py
@@ -1,6 +1,4 @@
from __future__ import absolute_import, print_function
-import sys
-import os
import traceback
import click
@@ -10,7 +8,7 @@ from netlib.http import CONTENT_MISSING
import netlib.utils
from . import flow, filt, contentviews
from .exceptions import ContentViewException
-from .models import HTTPRequest
+
class DumpError(Exception):
pass
@@ -58,6 +56,7 @@ class Options(object):
class DumpMaster(flow.FlowMaster):
+
def __init__(self, server, options, outfile=None):
flow.FlowMaster.__init__(self, server, flow.State())
self.outfile = outfile
@@ -171,7 +170,7 @@ class DumpMaster(flow.FlowMaster):
"{}: {}".format(
click.style(k, fg="blue", bold=True),
click.style(v, fg="blue"))
- for k, v in message.headers.fields
+ for k, v in message.headers.fields
)
self.echo(headers, indent=4)
if self.o.flow_detail >= 3:
@@ -237,7 +236,7 @@ class DumpMaster(flow.FlowMaster):
client = click.style("[replay]", fg="yellow", bold=True)
method = flow.request.method
- method_color=dict(
+ method_color = dict(
GET="green",
DELETE="red"
).get(method.upper(), "magenta")
diff --git a/libmproxy/exceptions.py b/libmproxy/exceptions.py
index e2bde980..a5d35263 100644
--- a/libmproxy/exceptions.py
+++ b/libmproxy/exceptions.py
@@ -9,9 +9,11 @@ from __future__ import (absolute_import, print_function, division)
class ProxyException(Exception):
+
"""
Base class for all exceptions thrown by libmproxy.
"""
+
def __init__(self, message=None):
super(ProxyException, self).__init__(message)
@@ -25,6 +27,7 @@ class TlsProtocolException(ProtocolException):
class ClientHandshakeException(TlsProtocolException):
+
def __init__(self, message, server):
super(ClientHandshakeException, self).__init__(message)
self.server = server
@@ -51,4 +54,4 @@ class ReplayException(ProxyException):
class ScriptException(ProxyException):
- pass \ No newline at end of file
+ pass
diff --git a/libmproxy/filt.py b/libmproxy/filt.py
index acbf4ef9..aa62b717 100644
--- a/libmproxy/filt.py
+++ b/libmproxy/filt.py
@@ -38,6 +38,7 @@ import pyparsing as pp
class _Token:
+
def dump(self, indent=0, fp=sys.stdout):
print >> fp, "\t" * indent, self.__class__.__name__,
if hasattr(self, "expr"):
@@ -46,6 +47,7 @@ class _Token:
class _Action(_Token):
+
@classmethod
def make(klass, s, loc, toks):
return klass(*toks[1:])
@@ -261,6 +263,7 @@ class FDst(_Rex):
class _Int(_Action):
+
def __init__(self, num):
self.num = int(num)
@@ -275,6 +278,7 @@ class FCode(_Int):
class FAnd(_Token):
+
def __init__(self, lst):
self.lst = lst
@@ -288,6 +292,7 @@ class FAnd(_Token):
class FOr(_Token):
+
def __init__(self, lst):
self.lst = lst
@@ -301,6 +306,7 @@ class FOr(_Token):
class FNot(_Token):
+
def __init__(self, itm):
self.itm = itm[0]
diff --git a/libmproxy/flow.py b/libmproxy/flow.py
index 1f28166f..9fe21810 100644
--- a/libmproxy/flow.py
+++ b/libmproxy/flow.py
@@ -22,6 +22,7 @@ from .models import ClientConnection, ServerConnection, HTTPResponse, HTTPFlow,
class AppRegistry:
+
def __init__(self):
self.apps = {}
@@ -49,6 +50,7 @@ class AppRegistry:
class ReplaceHooks:
+
def __init__(self):
self.lst = []
@@ -101,6 +103,7 @@ class ReplaceHooks:
class SetHeaders:
+
def __init__(self):
self.lst = []
@@ -155,6 +158,7 @@ class SetHeaders:
class StreamLargeBodies(object):
+
def __init__(self, max_size):
self.max_size = max_size
@@ -169,6 +173,7 @@ class StreamLargeBodies(object):
class ClientPlaybackState:
+
def __init__(self, flows, exit):
self.flows, self.exit = flows, exit
self.current = None
@@ -203,6 +208,7 @@ class ClientPlaybackState:
class ServerPlaybackState:
+
def __init__(
self,
headers,
@@ -295,6 +301,7 @@ class ServerPlaybackState:
class StickyCookieState:
+
def __init__(self, flt):
"""
flt: Compiled filter.
@@ -342,10 +349,11 @@ class StickyCookieState:
l.append(self.jar[i].output(header="").strip())
if l:
f.request.stickycookie = True
- f.request.headers.set_all("cookie",l)
+ f.request.headers.set_all("cookie", l)
class StickyAuthState:
+
def __init__(self, flt):
"""
flt: Compiled filter.
@@ -397,6 +405,7 @@ class FlowList(object):
class FlowView(FlowList):
+
def __init__(self, store, filt=None):
self._list = []
if not filt:
@@ -433,6 +442,7 @@ class FlowView(FlowList):
class FlowStore(FlowList):
+
"""
Responsible for handling flows in the state:
Keeps a list of all flows and provides views on them.
@@ -526,6 +536,7 @@ class FlowStore(FlowList):
class State(object):
+
def __init__(self):
self.flows = FlowStore()
self.view = FlowView(self.flows, None)
@@ -613,6 +624,7 @@ class State(object):
class FlowMaster(controller.Master):
+
def __init__(self, server, state):
controller.Master.__init__(self, server)
self.state = state
@@ -652,7 +664,6 @@ class FlowMaster(controller.Master):
"""
level: debug, info, error
"""
- pass
def unload_scripts(self):
for s in self.scripts[:]:
@@ -1100,6 +1111,7 @@ def read_flows_from_paths(paths):
class FlowWriter:
+
def __init__(self, fo):
self.fo = fo
@@ -1109,12 +1121,14 @@ class FlowWriter:
class FlowReadError(Exception):
+
@property
def strerror(self):
return self.args[0]
class FlowReader:
+
def __init__(self, fo):
self.fo = fo
@@ -1140,6 +1154,7 @@ class FlowReader:
class FilteredFlowWriter:
+
def __init__(self, fo, filt):
self.fo = fo
self.filt = filt
diff --git a/libmproxy/models/connections.py b/libmproxy/models/connections.py
index f5dabe4e..a45e1629 100644
--- a/libmproxy/models/connections.py
+++ b/libmproxy/models/connections.py
@@ -8,6 +8,7 @@ from .. import stateobject, utils
class ClientConnection(tcp.BaseHandler, stateobject.StateObject):
+
def __init__(self, client_connection, address, server):
# Eventually, this object is restored from state. We don't have a
# connection then.
@@ -88,6 +89,7 @@ class ClientConnection(tcp.BaseHandler, stateobject.StateObject):
class ServerConnection(tcp.TCPClient, stateobject.StateObject):
+
def __init__(self, address, source_address=None):
tcp.TCPClient.__init__(self, address, source_address)
@@ -134,7 +136,7 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject):
d = super(ServerConnection, self).get_state(short)
d.update(
address=({"address": self.address(),
- "use_ipv6": self.address.use_ipv6} if self.address else {}),
+ "use_ipv6": self.address.use_ipv6} if self.address else {}),
source_address=({"address": self.source_address(),
"use_ipv6": self.source_address.use_ipv6} if self.source_address else None),
cert=self.cert.to_pem() if self.cert else None
diff --git a/libmproxy/models/flow.py b/libmproxy/models/flow.py
index 8eff18f4..b4e8cb88 100644
--- a/libmproxy/models/flow.py
+++ b/libmproxy/models/flow.py
@@ -7,6 +7,7 @@ from .connections import ClientConnection, ServerConnection
class Error(stateobject.StateObject):
+
"""
An Error.
@@ -53,6 +54,7 @@ class Error(stateobject.StateObject):
class Flow(stateobject.StateObject):
+
"""
A Flow is a collection of objects representing a single transaction.
This class is usually subclassed for each protocol, e.g. HTTPFlow.
diff --git a/libmproxy/models/http.py b/libmproxy/models/http.py
index dfa3a824..e07dff69 100644
--- a/libmproxy/models/http.py
+++ b/libmproxy/models/http.py
@@ -102,6 +102,7 @@ class MessageMixin(stateobject.StateObject):
class HTTPRequest(MessageMixin, Request):
+
"""
An HTTP request.
@@ -264,6 +265,7 @@ class HTTPRequest(MessageMixin, Request):
class HTTPResponse(MessageMixin, Response):
+
"""
An HTTP response.
@@ -411,6 +413,7 @@ class HTTPResponse(MessageMixin, Response):
class HTTPFlow(Flow):
+
"""
A HTTPFlow is a collection of objects representing a single HTTP
transaction.
@@ -544,4 +547,4 @@ def make_connect_response(http_version):
"",
)
-expect_continue_response = HTTPResponse(b"HTTP/1.1", 100, "Continue", Headers(), b"") \ No newline at end of file
+expect_continue_response = HTTPResponse(b"HTTP/1.1", 100, "Continue", Headers(), b"")
diff --git a/libmproxy/onboarding/app.py b/libmproxy/onboarding/app.py
index 6edd74b1..ff5ed63c 100644
--- a/libmproxy/onboarding/app.py
+++ b/libmproxy/onboarding/app.py
@@ -15,6 +15,7 @@ class Adapter(tornado.wsgi.WSGIAdapter):
# Tornado doesn't make the WSGI environment available to pages, so this
# hideous monkey patch is the easiest way to get to the mitmproxy.master
# variable.
+
def __init__(self, application):
self._application = application
@@ -32,12 +33,14 @@ class Adapter(tornado.wsgi.WSGIAdapter):
class Index(tornado.web.RequestHandler):
+
def get(self):
t = loader.load("index.html")
self.write(t.generate())
class PEM(tornado.web.RequestHandler):
+
@property
def filename(self):
return config.CONF_BASENAME + "-ca-cert.pem"
@@ -55,6 +58,7 @@ class PEM(tornado.web.RequestHandler):
class P12(tornado.web.RequestHandler):
+
@property
def filename(self):
return config.CONF_BASENAME + "-ca-cert.p12"
diff --git a/libmproxy/platform/linux.py b/libmproxy/platform/linux.py
index e60a9950..38bfbe42 100644
--- a/libmproxy/platform/linux.py
+++ b/libmproxy/platform/linux.py
@@ -6,6 +6,7 @@ SO_ORIGINAL_DST = 80
class Resolver(object):
+
def original_addr(self, csock):
odestdata = csock.getsockopt(socket.SOL_IP, SO_ORIGINAL_DST, 16)
_, port, a1, a2, a3, a4 = struct.unpack("!HHBBBBxxxxxxxx", odestdata)
diff --git a/libmproxy/platform/osx.py b/libmproxy/platform/osx.py
index 2824718e..afbc919b 100644
--- a/libmproxy/platform/osx.py
+++ b/libmproxy/platform/osx.py
@@ -21,7 +21,7 @@ class Resolver(object):
peer = csock.getpeername()
try:
stxt = subprocess.check_output(self.STATECMD, stderr=subprocess.STDOUT)
- except subprocess.CalledProcessError, e:
+ except subprocess.CalledProcessError as e:
if "sudo: a password is required" in e.output:
insufficient_priv = True
else:
diff --git a/libmproxy/platform/windows.py b/libmproxy/platform/windows.py
index 09a4422f..9fe04cfa 100644
--- a/libmproxy/platform/windows.py
+++ b/libmproxy/platform/windows.py
@@ -18,6 +18,7 @@ PROXY_API_PORT = 8085
class Resolver(object):
+
def __init__(self):
TransparentProxy.setup()
self.socket = None
@@ -53,6 +54,7 @@ class Resolver(object):
class APIRequestHandler(SocketServer.StreamRequestHandler):
+
"""
TransparentProxy API: Returns the pickled server address, port tuple
for each received pickled client address, port tuple.
@@ -77,6 +79,7 @@ class APIRequestHandler(SocketServer.StreamRequestHandler):
class APIServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
+
def __init__(self, proxifier, *args, **kwargs):
SocketServer.TCPServer.__init__(self, *args, **kwargs)
self.proxifier = proxifier
@@ -110,6 +113,7 @@ def MIB_TCPTABLE2(size):
class TransparentProxy(object):
+
"""
Transparent Windows Proxy for mitmproxy based on WinDivert/PyDivert.
diff --git a/libmproxy/protocol/__init__.py b/libmproxy/protocol/__init__.py
index 0d624fd7..d46f16f5 100644
--- a/libmproxy/protocol/__init__.py
+++ b/libmproxy/protocol/__init__.py
@@ -28,12 +28,14 @@ as late as possible; this makes server replay without any outgoing connections p
from __future__ import (absolute_import, print_function, division)
from .base import Layer, ServerConnectionMixin, Kill
from .http import Http1Layer, UpstreamConnectLayer, Http2Layer
-from .tls import TlsLayer, is_tls_record_magic
+from .tls import TlsLayer
+from .tls import is_tls_record_magic
+from .tls import TlsClientHello
from .rawtcp import RawTCPLayer
__all__ = [
"Layer", "ServerConnectionMixin", "Kill",
"Http1Layer", "UpstreamConnectLayer", "Http2Layer",
- "TlsLayer", "is_tls_record_magic",
- "RawTCPLayer"
+ "TlsLayer", "is_tls_record_magic", "TlsClientHello",
+ "RawTCPLayer",
]
diff --git a/libmproxy/protocol/base.py b/libmproxy/protocol/base.py
index d984cadb..4eb034c0 100644
--- a/libmproxy/protocol/base.py
+++ b/libmproxy/protocol/base.py
@@ -3,13 +3,13 @@ import sys
import six
-from netlib import tcp
from ..models import ServerConnection
from ..exceptions import ProtocolException
from netlib.exceptions import TcpException
class _LayerCodeCompletion(object):
+
"""
Dummy class that provides type hinting in PyCharm, which simplifies development a lot.
"""
@@ -31,6 +31,7 @@ class _LayerCodeCompletion(object):
class Layer(_LayerCodeCompletion):
+
"""
Base class for all layers. All other protocol layers should inherit from this class.
"""
@@ -91,6 +92,7 @@ class Layer(_LayerCodeCompletion):
class ServerConnectionMixin(object):
+
"""
Mixin that provides a layer with the capabilities to manage a server connection.
The server address can be passed in the constructor or set by calling :py:meth:`set_server`.
@@ -190,6 +192,7 @@ class ServerConnectionMixin(object):
class Kill(Exception):
+
"""
Signal that both client and server connection(s) should be killed immediately.
"""
diff --git a/libmproxy/protocol/http.py b/libmproxy/protocol/http.py
index d72adc37..12d09e71 100644
--- a/libmproxy/protocol/http.py
+++ b/libmproxy/protocol/http.py
@@ -72,6 +72,7 @@ class _StreamingHttpLayer(_HttpLayer):
class Http1Layer(_StreamingHttpLayer):
+
def __init__(self, ctx, mode):
super(Http1Layer, self).__init__(ctx)
self.mode = mode
@@ -132,6 +133,7 @@ class Http1Layer(_StreamingHttpLayer):
# TODO: The HTTP2 layer is missing multiplexing, which requires a major rewrite.
class Http2Layer(_HttpLayer):
+
def __init__(self, ctx, mode):
super(Http2Layer, self).__init__(ctx)
self.mode = mode
@@ -229,6 +231,7 @@ class Http2Layer(_HttpLayer):
class ConnectServerConnection(object):
+
"""
"Fake" ServerConnection to represent state after a CONNECT request to an upstream proxy.
"""
@@ -249,6 +252,7 @@ class ConnectServerConnection(object):
class UpstreamConnectLayer(Layer):
+
def __init__(self, ctx, connect_request):
super(UpstreamConnectLayer, self).__init__(ctx)
self.connect_request = connect_request
@@ -293,6 +297,7 @@ class UpstreamConnectLayer(Layer):
class HttpLayer(Layer):
+
def __init__(self, ctx, mode):
super(HttpLayer, self).__init__(ctx)
self.mode = mode
@@ -328,7 +333,8 @@ class HttpLayer(Layer):
return
except NetlibException as e:
self.send_error_response(400, repr(e))
- six.reraise(ProtocolException, ProtocolException("Error in HTTP connection: %s" % repr(e)), sys.exc_info()[2])
+ six.reraise(ProtocolException, ProtocolException(
+ "Error in HTTP connection: %s" % repr(e)), sys.exc_info()[2])
try:
flow = HTTPFlow(self.client_conn, self.server_conn, live=self)
@@ -376,7 +382,8 @@ class HttpLayer(Layer):
self.log(traceback.format_exc(), "debug")
return
else:
- six.reraise(ProtocolException, ProtocolException("Error in HTTP connection: %s" % repr(e)), sys.exc_info()[2])
+ six.reraise(ProtocolException, ProtocolException(
+ "Error in HTTP connection: %s" % repr(e)), sys.exc_info()[2])
finally:
flow.live = False
diff --git a/libmproxy/protocol/rawtcp.py b/libmproxy/protocol/rawtcp.py
index ccd3c7ec..b87899e4 100644
--- a/libmproxy/protocol/rawtcp.py
+++ b/libmproxy/protocol/rawtcp.py
@@ -1,6 +1,5 @@
from __future__ import (absolute_import, print_function, division)
import socket
-import select
import six
import sys
@@ -14,6 +13,7 @@ from .base import Layer
class TcpMessage(object):
+
def __init__(self, client_conn, server_conn, sender, receiver, message):
self.client_conn = client_conn
self.server_conn = server_conn
diff --git a/libmproxy/protocol/tls.py b/libmproxy/protocol/tls.py
index ed747643..28f8c177 100644
--- a/libmproxy/protocol/tls.py
+++ b/libmproxy/protocol/tls.py
@@ -5,14 +5,14 @@ import sys
from construct import ConstructError
import six
-from netlib.exceptions import InvalidCertificateException, TcpException, TlsException
+from netlib.exceptions import InvalidCertificateException
+from netlib.exceptions import TlsException
from ..contrib.tls._constructs import ClientHello
from ..exceptions import ProtocolException, TlsProtocolException, ClientHandshakeException
from .base import Layer
-
# taken from https://testssl.sh/openssl-rfc.mappping.html
CIPHER_ID_NAME_MAP = {
0x00: 'NULL-MD5',
@@ -222,7 +222,85 @@ def is_tls_record_magic(d):
)
+def get_client_hello(client_conn):
+ """
+ Peek into the socket and read all records that contain the initial client hello message.
+
+ client_conn:
+ The :py:class:`client connection <libmproxy.models.ClientConnection>`.
+
+ Returns:
+ The raw handshake packet bytes, without TLS record header(s).
+ """
+ client_hello = ""
+ client_hello_size = 1
+ offset = 0
+ while len(client_hello) < client_hello_size:
+ record_header = client_conn.rfile.peek(offset + 5)[offset:]
+ if not is_tls_record_magic(record_header) or len(record_header) != 5:
+ raise TlsProtocolException('Expected TLS record, got "%s" instead.' % record_header)
+ record_size = struct.unpack("!H", record_header[3:])[0] + 5
+ record_body = client_conn.rfile.peek(offset + record_size)[offset + 5:]
+ if len(record_body) != record_size - 5:
+ raise TlsProtocolException("Unexpected EOF in TLS handshake: %s" % record_body)
+ client_hello += record_body
+ offset += record_size
+ client_hello_size = struct.unpack("!I", '\x00' + client_hello[1:4])[0] + 4
+ return client_hello
+
+
+class TlsClientHello(object):
+
+ def __init__(self, raw_client_hello):
+ self._client_hello = ClientHello.parse(raw_client_hello)
+
+ def raw(self):
+ return self._client_hello
+
+ @property
+ def client_cipher_suites(self):
+ return self._client_hello.cipher_suites.cipher_suites
+
+ @property
+ def client_sni(self):
+ for extension in self._client_hello.extensions:
+ if (extension.type == 0x00 and len(extension.server_names) == 1
+ and extension.server_names[0].type == 0):
+ return extension.server_names[0].name
+
+ @property
+ def client_alpn_protocols(self):
+ for extension in self._client_hello.extensions:
+ if extension.type == 0x10:
+ return list(extension.alpn_protocols)
+
+ @classmethod
+ def from_client_conn(cls, client_conn):
+ """
+ Peek into the connection, read the initial client hello and parse it to obtain ALPN values.
+ client_conn:
+ The :py:class:`client connection <libmproxy.models.ClientConnection>`.
+ Returns:
+ :py:class:`client hello <libmproxy.protocol.tls.TlsClientHello>`.
+ """
+ try:
+ raw_client_hello = get_client_hello(client_conn)[4:] # exclude handshake header.
+ except ProtocolException as e:
+ raise TlsProtocolException('Cannot read raw Client Hello: %s' % repr(e))
+
+ try:
+ return cls(raw_client_hello)
+ except ConstructError as e:
+ raise TlsProtocolException('Cannot parse Client Hello: %s, Raw Client Hello: %s' %
+ (repr(e), raw_client_hello.encode("hex")))
+
+ def __repr__(self):
+ return "TlsClientHello( sni: %s alpn_protocols: %s, cipher_suites: %s)" % \
+ (self.client_sni, self.client_alpn_protocols, self.client_cipher_suites)
+
+
class TlsLayer(Layer):
+
def __init__(self, ctx, client_tls, server_tls):
self.client_sni = None
self.client_alpn_protocols = None
@@ -281,60 +359,17 @@ class TlsLayer(Layer):
else:
return "TlsLayer(inactive)"
- def _get_client_hello(self):
- """
- Peek into the socket and read all records that contain the initial client hello message.
-
- Returns:
- The raw handshake packet bytes, without TLS record header(s).
- """
- client_hello = ""
- client_hello_size = 1
- offset = 0
- while len(client_hello) < client_hello_size:
- record_header = self.client_conn.rfile.peek(offset + 5)[offset:]
- if not is_tls_record_magic(record_header) or len(record_header) != 5:
- raise TlsProtocolException('Expected TLS record, got "%s" instead.' % record_header)
- record_size = struct.unpack("!H", record_header[3:])[0] + 5
- record_body = self.client_conn.rfile.peek(offset + record_size)[offset + 5:]
- if len(record_body) != record_size - 5:
- raise TlsProtocolException("Unexpected EOF in TLS handshake: %s" % record_body)
- client_hello += record_body
- offset += record_size
- client_hello_size = struct.unpack("!I", '\x00' + client_hello[1:4])[0] + 4
- return client_hello
-
def _parse_client_hello(self):
"""
Peek into the connection, read the initial client hello and parse it to obtain ALPN values.
"""
try:
- raw_client_hello = self._get_client_hello()[4:] # exclude handshake header.
- except ProtocolException as e:
+ parsed = TlsClientHello.from_client_conn(self.client_conn)
+ self.client_sni = parsed.client_sni
+ self.client_alpn_protocols = parsed.client_alpn_protocols
+ self.client_ciphers = parsed.client_cipher_suites
+ except TlsProtocolException as e:
self.log("Cannot parse Client Hello: %s" % repr(e), "error")
- return
-
- try:
- client_hello = ClientHello.parse(raw_client_hello)
- except ConstructError as e:
- self.log("Cannot parse Client Hello: %s" % repr(e), "error")
- self.log("Raw Client Hello: %s" % raw_client_hello.encode("hex"), "debug")
- return
-
- self.client_ciphers = client_hello.cipher_suites.cipher_suites
-
- for extension in client_hello.extensions:
- if extension.type == 0x00:
- if len(extension.server_names) != 1 or extension.server_names[0].type != 0:
- self.log("Unknown Server Name Indication: %s" % extension.server_names, "error")
- self.client_sni = extension.server_names[0].name
- elif extension.type == 0x10:
- self.client_alpn_protocols = list(extension.alpn_protocols)
-
- self.log(
- "Parsed Client Hello: sni=%s, alpn=%s" % (self.client_sni, self.client_alpn_protocols),
- "debug"
- )
def connect(self):
if not self.server_conn:
@@ -435,7 +470,7 @@ class TlsLayer(Layer):
alpn = [x for x in self.client_alpn_protocols if not deprecated_http2_variant(x)]
else:
alpn = None
- if alpn and "h2" in alpn and not self.config.http2 :
+ if alpn and "h2" in alpn and not self.config.http2:
alpn.remove("h2")
ciphers_server = self.config.ciphers_server
diff --git a/libmproxy/proxy/config.py b/libmproxy/proxy/config.py
index b1478655..bf765d81 100644
--- a/libmproxy/proxy/config.py
+++ b/libmproxy/proxy/config.py
@@ -19,6 +19,7 @@ DEFAULT_CLIENT_CIPHERS = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA
class HostMatcher(object):
+
def __init__(self, patterns=tuple()):
self.patterns = list(patterns)
self.regexes = [re.compile(p, re.IGNORECASE) for p in self.patterns]
@@ -41,6 +42,7 @@ ServerSpec = collections.namedtuple("ServerSpec", "scheme address")
class ProxyConfig:
+
def __init__(
self,
host='',
diff --git a/libmproxy/proxy/modes/http_proxy.py b/libmproxy/proxy/modes/http_proxy.py
index c7502c24..e19062b9 100644
--- a/libmproxy/proxy/modes/http_proxy.py
+++ b/libmproxy/proxy/modes/http_proxy.py
@@ -4,6 +4,7 @@ from ...protocol import Layer, ServerConnectionMixin
class HttpProxy(Layer, ServerConnectionMixin):
+
def __call__(self):
layer = self.ctx.next_layer(self)
try:
@@ -14,6 +15,7 @@ class HttpProxy(Layer, ServerConnectionMixin):
class HttpUpstreamProxy(Layer, ServerConnectionMixin):
+
def __init__(self, ctx, server_address):
super(HttpUpstreamProxy, self).__init__(ctx, server_address=server_address)
diff --git a/libmproxy/proxy/modes/reverse_proxy.py b/libmproxy/proxy/modes/reverse_proxy.py
index 28f4e6f8..c8e80a10 100644
--- a/libmproxy/proxy/modes/reverse_proxy.py
+++ b/libmproxy/proxy/modes/reverse_proxy.py
@@ -4,6 +4,7 @@ from ...protocol import Layer, ServerConnectionMixin
class ReverseProxy(Layer, ServerConnectionMixin):
+
def __init__(self, ctx, server_address, server_tls):
super(ReverseProxy, self).__init__(ctx, server_address=server_address)
self.server_tls = server_tls
diff --git a/libmproxy/proxy/modes/socks_proxy.py b/libmproxy/proxy/modes/socks_proxy.py
index 90788e37..e2ce44ae 100644
--- a/libmproxy/proxy/modes/socks_proxy.py
+++ b/libmproxy/proxy/modes/socks_proxy.py
@@ -8,6 +8,7 @@ from ...protocol import Layer, ServerConnectionMixin
class Socks5Proxy(Layer, ServerConnectionMixin):
+
def __init__(self, ctx):
super(Socks5Proxy, self).__init__(ctx)
diff --git a/libmproxy/proxy/modes/transparent_proxy.py b/libmproxy/proxy/modes/transparent_proxy.py
index da1d4632..3fdda656 100644
--- a/libmproxy/proxy/modes/transparent_proxy.py
+++ b/libmproxy/proxy/modes/transparent_proxy.py
@@ -6,6 +6,7 @@ from ...protocol import Layer, ServerConnectionMixin
class TransparentProxy(Layer, ServerConnectionMixin):
+
def __init__(self, ctx):
super(TransparentProxy, self).__init__(ctx)
self.resolver = platform.resolver()
diff --git a/libmproxy/proxy/root_context.py b/libmproxy/proxy/root_context.py
index f62b0c8e..f56aee6d 100644
--- a/libmproxy/proxy/root_context.py
+++ b/libmproxy/proxy/root_context.py
@@ -4,16 +4,17 @@ import sys
import six
-from libmproxy.exceptions import ProtocolException
+from libmproxy.exceptions import ProtocolException, TlsProtocolException
from netlib.exceptions import TcpException
from ..protocol import (
RawTCPLayer, TlsLayer, Http1Layer, Http2Layer, is_tls_record_magic, ServerConnectionMixin,
- UpstreamConnectLayer
+ UpstreamConnectLayer, TlsClientHello
)
from .modes import HttpProxy, HttpUpstreamProxy, ReverseProxy
class RootContext(object):
+
"""
The outermost context provided to the root layer.
As a consequence, every layer has access to methods and attributes defined here.
@@ -48,16 +49,25 @@ class RootContext(object):
return self.channel.ask("next_layer", layer)
def _next_layer(self, top_layer):
- # 1. Check for --ignore.
- if self.config.check_ignore(top_layer.server_conn.address):
- return RawTCPLayer(top_layer, logging=False)
-
try:
d = top_layer.client_conn.rfile.peek(3)
except TcpException as e:
six.reraise(ProtocolException, ProtocolException(str(e)), sys.exc_info()[2])
client_tls = is_tls_record_magic(d)
+ # 1. check for --ignore
+ if self.config.check_ignore:
+ ignore = self.config.check_ignore(top_layer.server_conn.address)
+ if not ignore and client_tls:
+ try:
+ client_hello = TlsClientHello.from_client_conn(self.client_conn)
+ except TlsProtocolException as e:
+ self.log("Cannot parse Client Hello: %s" % repr(e), "error")
+ else:
+ ignore = self.config.check_ignore((client_hello.client_sni, 443))
+ if ignore:
+ return RawTCPLayer(top_layer, logging=False)
+
# 2. Always insert a TLS layer, even if there's neither client nor server tls.
# An inline script may upgrade from http to https,
# in which case we need some form of TLS layer.
@@ -123,6 +133,7 @@ class RootContext(object):
class Log(object):
+
def __init__(self, msg, level="info"):
self.msg = msg
self.level = level
diff --git a/libmproxy/proxy/server.py b/libmproxy/proxy/server.py
index 8917f99a..750cb1a4 100644
--- a/libmproxy/proxy/server.py
+++ b/libmproxy/proxy/server.py
@@ -65,6 +65,7 @@ class ProxyServer(tcp.TCPServer):
class ConnectionHandler(object):
+
def __init__(self, client_conn, client_address, config, channel):
self.config = config
"""@type: libmproxy.proxy.config.ProxyConfig"""
diff --git a/libmproxy/script/__init__.py b/libmproxy/script/__init__.py
index 8bcdc5a2..3ee19b04 100644
--- a/libmproxy/script/__init__.py
+++ b/libmproxy/script/__init__.py
@@ -10,4 +10,4 @@ __all__ = [
"concurrent",
"ScriptException",
"reloader"
-] \ No newline at end of file
+]
diff --git a/libmproxy/script/concurrent.py b/libmproxy/script/concurrent.py
index bee2d43b..f0f5e3cd 100644
--- a/libmproxy/script/concurrent.py
+++ b/libmproxy/script/concurrent.py
@@ -7,6 +7,7 @@ import threading
class ReplyProxy(object):
+
def __init__(self, original_reply, script_thread):
self.original_reply = original_reply
self.script_thread = script_thread
diff --git a/libmproxy/script/reloader.py b/libmproxy/script/reloader.py
index e81bdef6..b4acf51b 100644
--- a/libmproxy/script/reloader.py
+++ b/libmproxy/script/reloader.py
@@ -1,11 +1,11 @@
import os
import sys
-from watchdog.events import RegexMatchingEventHandler
+from watchdog.events import RegexMatchingEventHandler
if sys.platform == 'darwin':
from watchdog.observers.polling import PollingObserver as Observer
else:
from watchdog.observers import Observer
-# The OSX reloader in watchdog 0.8.3 breaks when unobserving paths.
+# The OSX reloader in watchdog 0.8.3 breaks when unobserving paths.
# We use the PollingObserver instead.
_observers = {}
@@ -31,11 +31,12 @@ def unwatch(script):
class _ScriptModificationHandler(RegexMatchingEventHandler):
+
def __init__(self, callback, filename='.*'):
super(_ScriptModificationHandler, self).__init__(
ignore_directories=True,
- regexes=['.*'+filename]
+ regexes=['.*' + filename]
)
self.callback = callback
@@ -43,4 +44,3 @@ class _ScriptModificationHandler(RegexMatchingEventHandler):
self.callback()
__all__ = ["watch", "unwatch"]
-
diff --git a/libmproxy/script/script.py b/libmproxy/script/script.py
index 498caf94..a4ed3aba 100644
--- a/libmproxy/script/script.py
+++ b/libmproxy/script/script.py
@@ -3,7 +3,7 @@ The script object representing mitmproxy inline scripts.
Script objects know nothing about mitmproxy or mitmproxy's API - this knowledge is provided
by the mitmproxy-specific ScriptContext.
"""
-from __future__ import absolute_import, print_function, division
+# Do not import __future__ here, this would apply transitively to the inline scripts.
import os
import shlex
import traceback
@@ -12,6 +12,7 @@ from ..exceptions import ScriptException
class Script(object):
+
"""
Script object representing an inline script.
"""
@@ -94,4 +95,4 @@ class Script(object):
except Exception as e:
raise ScriptException(traceback.format_exc(e))
else:
- return None \ No newline at end of file
+ return None
diff --git a/libmproxy/script/script_context.py b/libmproxy/script/script_context.py
index d8748cb2..cd5d4b61 100644
--- a/libmproxy/script/script_context.py
+++ b/libmproxy/script/script_context.py
@@ -6,6 +6,7 @@ from .. import contentviews
class ScriptContext(object):
+
"""
The script context should be used to interact with the global mitmproxy state from within a
script.
diff --git a/libmproxy/stateobject.py b/libmproxy/stateobject.py
index 53ccef0e..52a8347f 100644
--- a/libmproxy/stateobject.py
+++ b/libmproxy/stateobject.py
@@ -2,6 +2,7 @@ from __future__ import absolute_import
class StateObject(object):
+
"""
An object with serializable state.
diff --git a/libmproxy/utils.py b/libmproxy/utils.py
index 8bd843a0..a697a637 100644
--- a/libmproxy/utils.py
+++ b/libmproxy/utils.py
@@ -37,7 +37,7 @@ def isBin(s):
def isMostlyBin(s):
s = s[:100]
- return sum(isBin(ch) for ch in s)/len(s) > 0.3
+ return sum(isBin(ch) for ch in s) / len(s) > 0.3
def isXML(s):
@@ -73,6 +73,7 @@ def pretty_duration(secs):
class Data:
+
def __init__(self, name):
m = __import__(name)
dirname, _ = os.path.split(m.__file__)
@@ -93,6 +94,7 @@ pkg_data = Data(__name__)
class LRUCache:
+
"""
A simple LRU cache for generated values.
"""
diff --git a/libmproxy/web/__init__.py b/libmproxy/web/__init__.py
index 90da6ffe..43fc993d 100644
--- a/libmproxy/web/__init__.py
+++ b/libmproxy/web/__init__.py
@@ -2,7 +2,6 @@ from __future__ import absolute_import, print_function
import collections
import tornado.ioloop
import tornado.httpserver
-import os
from .. import controller, flow
from . import app
@@ -12,6 +11,7 @@ class Stop(Exception):
class WebFlowView(flow.FlowView):
+
def __init__(self, store):
super(WebFlowView, self).__init__(store, None)
@@ -48,6 +48,7 @@ class WebFlowView(flow.FlowView):
class WebState(flow.State):
+
def __init__(self):
super(WebState, self).__init__()
self.view._close()
@@ -122,6 +123,7 @@ class Options(object):
class WebMaster(flow.FlowMaster):
+
def __init__(self, server, options):
self.options = options
super(WebMaster, self).__init__(server, WebState())
diff --git a/libmproxy/web/app.py b/libmproxy/web/app.py
index 5c80584d..79f76013 100644
--- a/libmproxy/web/app.py
+++ b/libmproxy/web/app.py
@@ -12,6 +12,7 @@ class APIError(tornado.web.HTTPError):
class RequestHandler(tornado.web.RequestHandler):
+
def set_default_headers(self):
super(RequestHandler, self).set_default_headers()
self.set_header("Server", version.NAMEVERSION)
@@ -56,12 +57,14 @@ class RequestHandler(tornado.web.RequestHandler):
class IndexHandler(RequestHandler):
+
def get(self):
_ = self.xsrf_token # https://github.com/tornadoweb/tornado/issues/645
self.render("index.html")
class FiltHelp(RequestHandler):
+
def get(self):
self.write(dict(
commands=filt.help
@@ -94,6 +97,7 @@ class ClientConnection(WebSocketEventBroadcaster):
class Flows(RequestHandler):
+
def get(self):
self.write(dict(
data=[f.get_state(short=True) for f in self.state.flows]
@@ -101,21 +105,25 @@ class Flows(RequestHandler):
class ClearAll(RequestHandler):
+
def post(self):
self.state.clear()
class AcceptFlows(RequestHandler):
+
def post(self):
self.state.flows.accept_all(self.master)
class AcceptFlow(RequestHandler):
+
def post(self, flow_id):
self.flow.accept_intercept(self.master)
class FlowHandler(RequestHandler):
+
def delete(self, flow_id):
self.flow.kill(self.master)
self.state.delete_flow(self.flow)
@@ -156,16 +164,19 @@ class FlowHandler(RequestHandler):
class DuplicateFlow(RequestHandler):
+
def post(self, flow_id):
self.master.duplicate_flow(self.flow)
class RevertFlow(RequestHandler):
+
def post(self, flow_id):
self.state.revert(self.flow)
class ReplayFlow(RequestHandler):
+
def post(self, flow_id):
self.flow.backup()
self.flow.response = None
@@ -177,6 +188,7 @@ class ReplayFlow(RequestHandler):
class FlowContent(RequestHandler):
+
def get(self, flow_id, message):
message = getattr(self.flow, message)
@@ -207,6 +219,7 @@ class FlowContent(RequestHandler):
class Events(RequestHandler):
+
def get(self):
self.write(dict(
data=list(self.state.events)
@@ -214,6 +227,7 @@ class Events(RequestHandler):
class Settings(RequestHandler):
+
def get(self):
self.write(dict(
data=dict(
@@ -240,6 +254,7 @@ class Settings(RequestHandler):
class Application(tornado.web.Application):
+
def __init__(self, master, debug):
self.master = master
handlers = [