Merge pull request #1183 from mitmproxy/module-imports

use module-level imports only
This commit is contained in:
Aldo Cortesi 2016-06-01 16:26:55 +12:00
commit 92b24c6653
66 changed files with 624 additions and 510 deletions

View File

@ -1,15 +1,17 @@
from __future__ import absolute_import from __future__ import absolute_import, print_function, division
import base64
import os import os
import re import re
import base64
import configargparse import configargparse
from netlib.tcp import Address, sslversion_choices from mitmproxy import filt
import netlib.http.url from mitmproxy import version
from mitmproxy.proxy import config
from netlib import human from netlib import human
from . import filt, version from netlib import tcp
from .proxy import config from netlib.http import url
APP_HOST = "mitm.it" APP_HOST = "mitm.it"
APP_PORT = 80 APP_PORT = 80
@ -104,17 +106,17 @@ def parse_setheader(s):
return _parse_hook(s) return _parse_hook(s)
def parse_server_spec(url): def parse_server_spec(spec):
try: try:
p = netlib.http.url.parse(url) p = url.parse(spec)
if p[0] not in ("http", "https"): if p[0] not in ("http", "https"):
raise ValueError() raise ValueError()
except ValueError: except ValueError:
raise configargparse.ArgumentTypeError( raise configargparse.ArgumentTypeError(
"Invalid server specification: %s" % url "Invalid server specification: %s" % spec
) )
address = Address(p[1:3]) address = tcp.Address(p[1:3])
scheme = p[0].lower() scheme = p[0].lower()
return config.ServerSpec(scheme, address) return config.ServerSpec(scheme, address)
@ -477,14 +479,14 @@ def proxy_ssl_options(parser):
group.add_argument( group.add_argument(
"--ssl-version-client", dest="ssl_version_client", "--ssl-version-client", dest="ssl_version_client",
default="secure", action="store", default="secure", action="store",
choices=sslversion_choices.keys(), choices=tcp.sslversion_choices.keys(),
help="Set supported SSL/TLS versions for client connections. " help="Set supported SSL/TLS versions for client connections. "
"SSLv2, SSLv3 and 'all' are INSECURE. Defaults to secure, which is TLS1.0+." "SSLv2, SSLv3 and 'all' are INSECURE. Defaults to secure, which is TLS1.0+."
) )
group.add_argument( group.add_argument(
"--ssl-version-server", dest="ssl_version_server", "--ssl-version-server", dest="ssl_version_server",
default="secure", action="store", default="secure", action="store",
choices=sslversion_choices.keys(), choices=tcp.sslversion_choices.keys(),
help="Set supported SSL/TLS versions for server connections. " help="Set supported SSL/TLS versions for server connections. "
"SSLv2, SSLv3 and 'all' are INSECURE. Defaults to secure, which is TLS1.0+." "SSLv2, SSLv3 and 'all' are INSECURE. Defaults to secure, which is TLS1.0+."
) )

View File

@ -1,8 +1,7 @@
from __future__ import absolute_import, print_function from __future__ import absolute_import, print_function, division
import mailcap import mailcap
import mimetypes import mimetypes
import tempfile
import os import os
import os.path import os.path
import shlex import shlex
@ -10,16 +9,28 @@ import signal
import stat import stat
import subprocess import subprocess
import sys import sys
import tempfile
import traceback import traceback
import urwid
import weakref import weakref
from netlib import tcp import urwid
from .. import flow, script, contentviews, controller from mitmproxy import contentviews
from . import flowlist, flowview, help, window, signals, options from mitmproxy import controller
from . import grideditor, palettes, statusbar, palettepicker from mitmproxy import exceptions
from ..exceptions import FlowReadException, ScriptException from mitmproxy import flow
from mitmproxy import script
from mitmproxy.console import flowlist
from mitmproxy.console import flowview
from mitmproxy.console import grideditor
from mitmproxy.console import help
from mitmproxy.console import options
from mitmproxy.console import palettepicker
from mitmproxy.console import palettes
from mitmproxy.console import signals
from mitmproxy.console import statusbar
from mitmproxy.console import window
from netlib import tcp
EVENTLOG_SIZE = 500 EVENTLOG_SIZE = 500
@ -231,7 +242,7 @@ class ConsoleMaster(flow.FlowMaster):
for i in options.scripts: for i in options.scripts:
try: try:
self.load_script(i) self.load_script(i)
except ScriptException as e: except exceptions.ScriptException as e:
print("Script load error: {}".format(e), file=sys.stderr) print("Script load error: {}".format(e), file=sys.stderr)
sys.exit(1) sys.exit(1)
@ -352,7 +363,7 @@ class ConsoleMaster(flow.FlowMaster):
""" """
try: try:
return flow.read_flows_from_paths(path) return flow.read_flows_from_paths(path)
except FlowReadException as e: except exceptions.FlowReadException as e:
signals.status_message.send(message=e.strerror) signals.status_message.send(message=e.strerror)
def client_playback_path(self, path): def client_playback_path(self, path):
@ -636,7 +647,7 @@ class ConsoleMaster(flow.FlowMaster):
reterr = None reterr = None
try: try:
flow.FlowMaster.load_flows_file(self, path) flow.FlowMaster.load_flows_file(self, path)
except FlowReadException as e: except exceptions.FlowReadException as e:
reterr = str(e) reterr = str(e)
signals.flowlist_change.send(self) signals.flowlist_change.send(self)
return reterr return reterr
@ -666,7 +677,7 @@ class ConsoleMaster(flow.FlowMaster):
for command in commands: for command in commands:
try: try:
self.load_script(command) self.load_script(command)
except ScriptException as e: except exceptions.ScriptException as e:
signals.status_message.send( signals.status_message.send(
message='Error loading "{}".'.format(command) message='Error loading "{}".'.format(command)
) )

View File

@ -1,18 +1,17 @@
from __future__ import absolute_import from __future__ import absolute_import, print_function, division
import os
import urwid import urwid
import urwid.util import urwid.util
import os
import netlib import netlib
from mitmproxy import flow
from mitmproxy import models
from mitmproxy import utils
from mitmproxy.console import signals
from netlib import human from netlib import human
from .. import utils
from .. import flow
from ..models import decoded
from . import signals
try: try:
import pyperclip import pyperclip
except: except:
@ -260,7 +259,7 @@ def copy_flow_format_data(part, scope, flow):
if scope in ("q", "a"): if scope in ("q", "a"):
if flow.request.content is None: if flow.request.content is None:
return None, "Request content is missing" return None, "Request content is missing"
with decoded(flow.request): with models.decoded(flow.request):
if part == "h": if part == "h":
data += netlib.http.http1.assemble_request(flow.request) data += netlib.http.http1.assemble_request(flow.request)
elif part == "c": elif part == "c":
@ -273,7 +272,7 @@ def copy_flow_format_data(part, scope, flow):
if scope in ("s", "a") and flow.response: if scope in ("s", "a") and flow.response:
if flow.response.content is None: if flow.response.content is None:
return None, "Response content is missing" return None, "Response content is missing"
with decoded(flow.response): with models.decoded(flow.response):
if part == "h": if part == "h":
data += netlib.http.http1.assemble_response(flow.response) data += netlib.http.http1.assemble_response(flow.response)
elif part == "c": elif part == "c":

View File

@ -1,7 +1,9 @@
from __future__ import absolute_import from __future__ import absolute_import, print_function, division
import urwid import urwid
from . import common, searchable
from .. import utils from mitmproxy import utils
from mitmproxy.console import common, searchable
def maybe_timestamp(base, attr): def maybe_timestamp(base, attr):

View File

@ -1,9 +1,10 @@
from __future__ import absolute_import from __future__ import absolute_import, print_function, division
import urwid import urwid
import netlib.http.url import netlib.http.url
from mitmproxy.console import common
from . import common, signals from mitmproxy.console import signals
def _mkhelp(): def _mkhelp():

View File

@ -1,17 +1,25 @@
from __future__ import absolute_import, division from __future__ import absolute_import, print_function, division
import os
import traceback
import sys
import math import math
import os
import sys
import traceback
import urwid import urwid
from netlib.http import Headers, status_codes from mitmproxy import contentviews
from . import common, grideditor, signals, searchable, tabs from mitmproxy import controller
from . import flowdetailview from mitmproxy import exceptions
from .. import utils, controller, contentviews from mitmproxy import models
from ..models import HTTPRequest, HTTPResponse, decoded from mitmproxy import utils
from ..exceptions import ContentViewException from mitmproxy.console import common
from mitmproxy.console import flowdetailview
from mitmproxy.console import grideditor
from mitmproxy.console import searchable
from mitmproxy.console import signals
from mitmproxy.console import tabs
from netlib.http import Headers
from netlib.http import status_codes
class SearchError(Exception): class SearchError(Exception):
@ -193,12 +201,12 @@ class FlowView(tabs.Tabs):
try: try:
query = None query = None
if isinstance(message, HTTPRequest): if isinstance(message, models.HTTPRequest):
query = message.query query = message.query
description, lines = contentviews.get_content_view( description, lines = contentviews.get_content_view(
viewmode, message.content, headers=message.headers, query=query viewmode, message.content, headers=message.headers, query=query
) )
except ContentViewException: except exceptions.ContentViewException:
s = "Content viewer failed: \n" + traceback.format_exc() s = "Content viewer failed: \n" + traceback.format_exc()
signals.add_event(s, "error") signals.add_event(s, "error")
description, lines = contentviews.get_content_view( description, lines = contentviews.get_content_view(
@ -207,7 +215,7 @@ class FlowView(tabs.Tabs):
description = description.replace("Raw", "Couldn't parse: falling back to Raw") description = description.replace("Raw", "Couldn't parse: falling back to Raw")
# Give hint that you have to tab for the response. # Give hint that you have to tab for the response.
if description == "No content" and isinstance(message, HTTPRequest): if description == "No content" and isinstance(message, models.HTTPRequest):
description = "No request content (press tab to view response)" description = "No request content (press tab to view response)"
# If the users has a wide terminal, he gets fewer lines; this should not be an issue. # If the users has a wide terminal, he gets fewer lines; this should not be an issue.
@ -372,7 +380,7 @@ class FlowView(tabs.Tabs):
message = self.flow.request message = self.flow.request
else: else:
if not self.flow.response: if not self.flow.response:
self.flow.response = HTTPResponse( self.flow.response = models.HTTPResponse(
self.flow.request.http_version, self.flow.request.http_version,
200, "OK", Headers(), "" 200, "OK", Headers(), ""
) )
@ -399,7 +407,7 @@ class FlowView(tabs.Tabs):
) )
) )
if part == "r": if part == "r":
with decoded(message): with models.decoded(message):
# Fix an issue caused by some editors when editing a # Fix an issue caused by some editors when editing a
# request/response body. Many editors make it hard to save a # request/response body. Many editors make it hard to save a
# file without a terminating newline on the last line. When # file without a terminating newline on the last line. When

View File

@ -1,15 +1,18 @@
from __future__ import absolute_import from __future__ import absolute_import, print_function, division
import copy import copy
import re
import os import os
import re
import urwid import urwid
from netlib.http import user_agents, cookies from mitmproxy import filt
from mitmproxy import script
from . import common, signals from mitmproxy import utils
from .. import utils, filt, script from mitmproxy.console import common
from mitmproxy.console import signals
from netlib.http import cookies
from netlib.http import user_agents
FOOTER = [ FOOTER = [
('heading_key', "enter"), ":edit ", ('heading_key', "enter"), ":edit ",

View File

@ -1,9 +1,11 @@
from __future__ import absolute_import from __future__ import absolute_import, print_function, division
import urwid import urwid
from . import common, signals from mitmproxy import filt
from .. import filt, version from mitmproxy import version
from mitmproxy.console import common
from mitmproxy.console import signals
footer = [ footer = [
("heading", 'mitmproxy v%s ' % version.VERSION), ("heading", 'mitmproxy v%s ' % version.VERSION),

View File

@ -1,8 +1,13 @@
from __future__ import absolute_import, print_function, division
import urwid import urwid
from .. import contentviews from mitmproxy import contentviews
from . import common, signals, grideditor from mitmproxy.console import common
from . import select, palettes from mitmproxy.console import grideditor
from mitmproxy.console import palettes
from mitmproxy.console import select
from mitmproxy.console import signals
footer = [ footer = [
('heading_key', "enter/space"), ":toggle ", ('heading_key', "enter/space"), ":toggle ",

View File

@ -1,6 +1,11 @@
from __future__ import absolute_import, print_function, division
import urwid import urwid
from . import select, common, palettes, signals from mitmproxy.console import common
from mitmproxy.console import palettes
from mitmproxy.console import select
from mitmproxy.console import signals
footer = [ footer = [
('heading_key', "enter/space"), ":select", ('heading_key', "enter/space"), ":select",

View File

@ -3,6 +3,7 @@
# #
# http://urwid.org/manual/displayattributes.html # http://urwid.org/manual/displayattributes.html
# #
from __future__ import absolute_import, print_function, division
class Palette: class Palette:

View File

@ -1,3 +1,5 @@
from __future__ import absolute_import, print_function, division
import glob import glob
import os.path import os.path

View File

@ -1,6 +1,8 @@
from __future__ import absolute_import, print_function, division
import urwid import urwid
from . import signals from mitmproxy.console import signals
class Highlight(urwid.AttrMap): class Highlight(urwid.AttrMap):

View File

@ -1,6 +1,8 @@
from __future__ import absolute_import, print_function, division
import urwid import urwid
from . import common from mitmproxy.console import common
class _OptionWidget(urwid.WidgetWrap): class _OptionWidget(urwid.WidgetWrap):

View File

@ -1,3 +1,5 @@
from __future__ import absolute_import, print_function, division
import blinker import blinker
# Show a status message in the action bar # Show a status message in the action bar

View File

@ -1,10 +1,14 @@
from __future__ import absolute_import, print_function, division
import os.path import os.path
import urwid import urwid
import netlib.utils import netlib.utils
from mitmproxy.console import common
from mitmproxy.console import pathedit
from mitmproxy.console import signals
from netlib import human from netlib import human
from . import pathedit, signals, common
class ActionBar(urwid.WidgetWrap): class ActionBar(urwid.WidgetWrap):

View File

@ -1,3 +1,5 @@
from __future__ import absolute_import, print_function, division
import urwid import urwid

View File

@ -1,5 +1,8 @@
from __future__ import absolute_import, print_function, division
import urwid import urwid
from . import signals
from mitmproxy.console import signals
class Window(urwid.Frame): class Window(urwid.Frame):

View File

@ -12,27 +12,31 @@ use. For HTTP, the message headers are passed as the ``headers`` keyword argumen
requests, the query parameters are passed as the ``query`` keyword argument. requests, the query parameters are passed as the ``query`` keyword argument.
""" """
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
from six.moves import cStringIO as StringIO
import datetime
import json import json
import logging import logging
import subprocess import subprocess
import sys import sys
import lxml.html
import lxml.etree
import datetime
from PIL import Image
from PIL.ExifTags import TAGS
import html2text import html2text
import lxml.etree
import lxml.html
import six import six
from netlib.odict import ODict from PIL import ExifTags
from netlib import encoding, http from PIL import Image
from six.moves import cStringIO as StringIO
import mitmproxy.utils
from mitmproxy import exceptions
from mitmproxy.contrib import jsbeautifier
from mitmproxy.contrib.wbxml import ASCommandResponse
from netlib import encoding
from netlib import http
from netlib import odict
from netlib.http import url from netlib.http import url
from netlib.utils import clean_bin, hexdump import netlib.utils
from . import utils
from .exceptions import ContentViewException
from .contrib import jsbeautifier
from .contrib.wbxml.ASCommandResponse import ASCommandResponse
try: try:
import pyamf import pyamf
@ -125,11 +129,11 @@ class ViewAuto(View):
ct = "%s/%s" % (ct[0], ct[1]) ct = "%s/%s" % (ct[0], ct[1])
if ct in content_types_map: if ct in content_types_map:
return content_types_map[ct][0](data, **metadata) return content_types_map[ct][0](data, **metadata)
elif utils.isXML(data): elif mitmproxy.utils.isXML(data):
return get("XML")(data, **metadata) return get("XML")(data, **metadata)
if metadata.get("query"): if metadata.get("query"):
return get("Query")(data, **metadata) return get("Query")(data, **metadata)
if data and utils.isMostlyBin(data): if data and mitmproxy.utils.isMostlyBin(data):
return get("Hex")(data) return get("Hex")(data)
if not data: if not data:
return "No content", [] return "No content", []
@ -152,7 +156,7 @@ class ViewHex(View):
@staticmethod @staticmethod
def _format(data): def _format(data):
for offset, hexa, s in hexdump(data): for offset, hexa, s in netlib.utils.hexdump(data):
yield [ yield [
("offset", offset + " "), ("offset", offset + " "),
("text", hexa + " "), ("text", hexa + " "),
@ -211,7 +215,7 @@ class ViewJSON(View):
content_types = ["application/json"] content_types = ["application/json"]
def __call__(self, data, **metadata): def __call__(self, data, **metadata):
pretty_json = utils.pretty_json(data) pretty_json = mitmproxy.utils.pretty_json(data)
if pretty_json: if pretty_json:
return "JSON", format_text(pretty_json) return "JSON", format_text(pretty_json)
@ -222,7 +226,7 @@ class ViewHTML(View):
content_types = ["text/html"] content_types = ["text/html"]
def __call__(self, data, **metadata): def __call__(self, data, **metadata):
if utils.isXML(data): if mitmproxy.utils.isXML(data):
parser = lxml.etree.HTMLParser( parser = lxml.etree.HTMLParser(
strip_cdata=True, strip_cdata=True,
remove_blank_text=True remove_blank_text=True
@ -259,7 +263,7 @@ class ViewURLEncoded(View):
def __call__(self, data, **metadata): def __call__(self, data, **metadata):
d = url.decode(data) d = url.decode(data)
return "URLEncoded form", format_dict(ODict(d)) return "URLEncoded form", format_dict(odict.ODict(d))
class ViewMultipart(View): class ViewMultipart(View):
@ -270,7 +274,7 @@ class ViewMultipart(View):
@staticmethod @staticmethod
def _format(v): def _format(v):
yield [("highlight", "Form data:\n")] yield [("highlight", "Form data:\n")]
for message in format_dict(ODict(v)): for message in format_dict(odict.ODict(v)):
yield message yield message
def __call__(self, data, **metadata): def __call__(self, data, **metadata):
@ -415,11 +419,11 @@ class ViewImage(View):
ex = img._getexif() ex = img._getexif()
if ex: if ex:
for i in sorted(ex.keys()): for i in sorted(ex.keys()):
tag = TAGS.get(i, i) tag = ExifTags.TAGS.get(i, i)
parts.append( parts.append(
(str(tag), str(ex[i])) (str(tag), str(ex[i]))
) )
fmt = format_dict(ODict(parts)) fmt = format_dict(odict.ODict(parts))
return "%s image" % img.format, fmt return "%s image" % img.format, fmt
@ -490,7 +494,7 @@ class ViewWBXML(View):
def __call__(self, data, **metadata): def __call__(self, data, **metadata):
try: try:
parser = ASCommandResponse(data) parser = ASCommandResponse.ASCommandResponse(data)
parsedContent = parser.xmlString parsedContent = parser.xmlString
if parsedContent: if parsedContent:
return "WBXML", format_text(parsedContent) return "WBXML", format_text(parsedContent)
@ -519,12 +523,12 @@ def add(view):
# TODO: auto-select a different name (append an integer?) # TODO: auto-select a different name (append an integer?)
for i in views: for i in views:
if i.name == view.name: if i.name == view.name:
raise ContentViewException("Duplicate view: " + view.name) raise exceptions.ContentViewException("Duplicate view: " + view.name)
# TODO: the UI should auto-prompt for a replacement shortcut # TODO: the UI should auto-prompt for a replacement shortcut
for prompt in view_prompts: for prompt in view_prompts:
if prompt[1] == view.prompt[1]: if prompt[1] == view.prompt[1]:
raise ContentViewException("Duplicate view shortcut: " + view.prompt[1]) raise exceptions.ContentViewException("Duplicate view shortcut: " + view.prompt[1])
views.append(view) views.append(view)
@ -577,9 +581,9 @@ def safe_to_print(lines, encoding="utf8"):
clean_line = [] clean_line = []
for (style, text) in line: for (style, text) in line:
try: try:
text = clean_bin(text.decode(encoding, "strict")) text = netlib.utils.clean_bin(text.decode(encoding, "strict"))
except UnicodeDecodeError: except UnicodeDecodeError:
text = clean_bin(text).decode(encoding, "strict") text = netlib.utils.clean_bin(text).decode(encoding, "strict")
clean_line.append((style, text)) clean_line.append((style, text))
yield clean_line yield clean_line
@ -611,8 +615,8 @@ def get_content_view(viewmode, data, **metadata):
# Third-party viewers can fail in unexpected ways... # Third-party viewers can fail in unexpected ways...
except Exception as e: except Exception as e:
six.reraise( six.reraise(
ContentViewException, exceptions.ContentViewException,
ContentViewException(str(e)), exceptions.ContentViewException(str(e)),
sys.exc_info()[2] sys.exc_info()[2]
) )
if not ret: if not ret:

View File

@ -1,9 +1,11 @@
from __future__ import absolute_import from __future__ import absolute_import, print_function, division
from six.moves import queue
import threading
import functools
from . import exceptions import functools
import threading
from six.moves import queue
from mitmproxy import exceptions
Events = frozenset([ Events = frozenset([
"clientconnect", "clientconnect",

View File

@ -1,13 +1,19 @@
from __future__ import absolute_import, print_function from __future__ import absolute_import, print_function, division
import traceback
import sys
import click
import itertools
from netlib import tcp, human import itertools
from netlib.utils import bytes_to_escaped_str import sys
from . import flow, filt, contentviews, controller import traceback
from .exceptions import ContentViewException, FlowReadException, ScriptException
import click
from mitmproxy import contentviews
from mitmproxy import controller
from mitmproxy import exceptions
from mitmproxy import filt
from mitmproxy import flow
from netlib import human
from netlib import tcp
from netlib import utils
class DumpError(Exception): class DumpError(Exception):
@ -127,13 +133,13 @@ class DumpMaster(flow.FlowMaster):
for command in scripts: for command in scripts:
try: try:
self.load_script(command, use_reloader=True) self.load_script(command, use_reloader=True)
except ScriptException as e: except exceptions.ScriptException as e:
raise DumpError(str(e)) raise DumpError(str(e))
if options.rfile: if options.rfile:
try: try:
self.load_flows_file(options.rfile) self.load_flows_file(options.rfile)
except FlowReadException as v: except exceptions.FlowReadException as v:
self.add_event("Flow file corrupted.", "error") self.add_event("Flow file corrupted.", "error")
raise DumpError(v) raise DumpError(v)
@ -147,7 +153,7 @@ class DumpMaster(flow.FlowMaster):
""" """
try: try:
return flow.read_flows_from_paths(paths) return flow.read_flows_from_paths(paths)
except FlowReadException as e: except exceptions.FlowReadException as e:
raise DumpError(str(e)) raise DumpError(str(e))
def add_event(self, e, level="info"): def add_event(self, e, level="info"):
@ -175,8 +181,8 @@ class DumpMaster(flow.FlowMaster):
if self.o.flow_detail >= 2: if self.o.flow_detail >= 2:
headers = "\r\n".join( headers = "\r\n".join(
"{}: {}".format( "{}: {}".format(
click.style(bytes_to_escaped_str(k), fg="blue", bold=True), click.style(utils.bytes_to_escaped_str(k), fg="blue", bold=True),
click.style(bytes_to_escaped_str(v), fg="blue")) click.style(utils.bytes_to_escaped_str(v), fg="blue"))
for k, v in message.headers.fields for k, v in message.headers.fields
) )
self.echo(headers, indent=4) self.echo(headers, indent=4)
@ -192,7 +198,7 @@ class DumpMaster(flow.FlowMaster):
message.content, message.content,
headers=message.headers headers=message.headers
) )
except ContentViewException: except exceptions.ContentViewException:
s = "Content viewer failed: \n" + traceback.format_exc() s = "Content viewer failed: \n" + traceback.format_exc()
self.add_event(s, "debug") self.add_event(s, "debug")
type, lines = contentviews.get_content_view( type, lines = contentviews.get_content_view(
@ -238,7 +244,7 @@ class DumpMaster(flow.FlowMaster):
stickycookie = "" stickycookie = ""
if flow.client_conn: if flow.client_conn:
client = click.style(bytes_to_escaped_str(flow.client_conn.address.host), bold=True) client = click.style(utils.bytes_to_escaped_str(flow.client_conn.address.host), bold=True)
else: else:
client = click.style("[replay]", fg="yellow", bold=True) client = click.style("[replay]", fg="yellow", bold=True)
@ -247,12 +253,12 @@ class DumpMaster(flow.FlowMaster):
GET="green", GET="green",
DELETE="red" DELETE="red"
).get(method.upper(), "magenta") ).get(method.upper(), "magenta")
method = click.style(bytes_to_escaped_str(method), fg=method_color, bold=True) method = click.style(utils.bytes_to_escaped_str(method), fg=method_color, bold=True)
if self.showhost: if self.showhost:
url = flow.request.pretty_url url = flow.request.pretty_url
else: else:
url = flow.request.url url = flow.request.url
url = click.style(bytes_to_escaped_str(url), bold=True) url = click.style(utils.bytes_to_escaped_str(url), bold=True)
httpversion = "" httpversion = ""
if flow.request.http_version not in ("HTTP/1.1", "HTTP/1.0"): if flow.request.http_version not in ("HTTP/1.1", "HTTP/1.0"):
@ -282,7 +288,7 @@ class DumpMaster(flow.FlowMaster):
elif 400 <= code < 600: elif 400 <= code < 600:
code_color = "red" code_color = "red"
code = click.style(str(code), fg=code_color, bold=True, blink=(code == 418)) code = click.style(str(code), fg=code_color, bold=True, blink=(code == 418))
reason = click.style(bytes_to_escaped_str(flow.response.reason), fg=code_color, bold=True) reason = click.style(utils.bytes_to_escaped_str(flow.response.reason), fg=code_color, bold=True)
if flow.response.content is None: if flow.response.content is None:
size = "(content missing)" size = "(content missing)"

View File

@ -5,11 +5,10 @@ Every Exception mitmproxy raises shall be a subclass of ProxyException.
See also: http://lucumr.pocoo.org/2014/10/16/on-error-handling/ See also: http://lucumr.pocoo.org/2014/10/16/on-error-handling/
""" """
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
import traceback
import sys import sys
import traceback
class ProxyException(Exception): class ProxyException(Exception):

View File

@ -31,9 +31,11 @@
~c CODE Response code. ~c CODE Response code.
rex Equivalent to ~u rex rex Equivalent to ~u rex
""" """
from __future__ import absolute_import, print_function from __future__ import absolute_import, print_function, division
import re import re
import sys import sys
import pyparsing as pp import pyparsing as pp

View File

@ -1,3 +1,5 @@
from __future__ import absolute_import, print_function, division
import json import json
import re import re
from textwrap import dedent from textwrap import dedent

View File

@ -1,7 +1,10 @@
from __future__ import absolute_import, print_function, division
import os import os
from mitmproxy import tnetstring, models from mitmproxy import exceptions
from mitmproxy.exceptions import FlowReadException from mitmproxy import models
from mitmproxy import tnetstring
from mitmproxy.flow import io_compat from mitmproxy.flow import io_compat
@ -38,17 +41,17 @@ class FlowReader:
try: try:
data = io_compat.migrate_flow(data) data = io_compat.migrate_flow(data)
except ValueError as e: except ValueError as e:
raise FlowReadException(str(e)) raise exceptions.FlowReadException(str(e))
if can_tell: if can_tell:
off = self.fo.tell() off = self.fo.tell()
if data["type"] not in models.FLOW_TYPES: if data["type"] not in models.FLOW_TYPES:
raise FlowReadException("Unknown flow type: {}".format(data["type"])) raise exceptions.FlowReadException("Unknown flow type: {}".format(data["type"]))
yield models.FLOW_TYPES[data["type"]].from_state(data) yield models.FLOW_TYPES[data["type"]].from_state(data)
except ValueError: except ValueError:
# Error is due to EOF # Error is due to EOF
if can_tell and self.fo.tell() == off and self.fo.read() == '': if can_tell and self.fo.tell() == off and self.fo.read() == '':
return return
raise FlowReadException("Invalid data format.") raise exceptions.FlowReadException("Invalid data format.")
class FilteredFlowWriter: class FilteredFlowWriter:
@ -79,5 +82,5 @@ def read_flows_from_paths(paths):
with open(path, "rb") as f: with open(path, "rb") as f:
flows.extend(FlowReader(f).stream()) flows.extend(FlowReader(f).stream())
except IOError as e: except IOError as e:
raise FlowReadException(e.strerror) raise exceptions.FlowReadException(e.strerror)
return flows return flows

View File

@ -1,16 +1,22 @@
from __future__ import absolute_import, print_function, division
import os import os
import sys import sys
from typing import List, Optional, Set # noqa from typing import List, Optional, Set # noqa
from mitmproxy import controller, script, filt, models import netlib.exceptions
from mitmproxy.exceptions import FlowReadException, Kill from mitmproxy import controller
from mitmproxy.flow import io, modules from mitmproxy import exceptions
from mitmproxy import filt
from mitmproxy import models
from mitmproxy import script
from mitmproxy.flow import io
from mitmproxy.flow import modules
from mitmproxy.onboarding import app from mitmproxy.onboarding import app
from mitmproxy.protocol.http_replay import RequestReplayThread from mitmproxy.protocol import http_replay
from mitmproxy.proxy.config import HostMatcher from mitmproxy.proxy.config import HostMatcher
from netlib import utils from netlib import utils
from netlib.exceptions import HttpException
class FlowMaster(controller.Master): class FlowMaster(controller.Master):
@ -311,7 +317,7 @@ class FlowMaster(controller.Master):
freader = io.FlowReader(f) freader = io.FlowReader(f)
return self.load_flows(freader) return self.load_flows(freader)
except IOError as v: except IOError as v:
raise FlowReadException(v.strerror) raise exceptions.FlowReadException(v.strerror)
def process_new_request(self, f): def process_new_request(self, f):
if self.stickycookie_state: if self.stickycookie_state:
@ -351,7 +357,7 @@ class FlowMaster(controller.Master):
f.response = None f.response = None
f.error = None f.error = None
self.process_new_request(f) self.process_new_request(f)
rt = RequestReplayThread( rt = http_replay.RequestReplayThread(
self.server.config, self.server.config,
f, f,
self.event_queue if run_scripthooks else False, self.event_queue if run_scripthooks else False,
@ -405,7 +411,7 @@ class FlowMaster(controller.Master):
) )
if err: if err:
self.add_event("Error in wsgi app. %s" % err, "error") self.add_event("Error in wsgi app. %s" % err, "error")
f.reply(Kill) f.reply(exceptions.Kill)
return return
if f not in self.state.flows: # don't add again on replay if f not in self.state.flows: # don't add again on replay
self.state.add_flow(f) self.state.add_flow(f)
@ -421,8 +427,8 @@ class FlowMaster(controller.Master):
try: try:
if self.stream_large_bodies: if self.stream_large_bodies:
self.stream_large_bodies.run(f, False) self.stream_large_bodies.run(f, False)
except HttpException: except netlib.exceptions.HttpException:
f.reply(Kill) f.reply(exceptions.Kill)
return return
self.run_script_hook("responseheaders", f) self.run_script_hook("responseheaders", f)
return f return f

View File

@ -1,3 +1,5 @@
from __future__ import absolute_import, print_function, division
import collections import collections
import hashlib import hashlib
import re import re
@ -5,9 +7,12 @@ import re
from six.moves import http_cookiejar from six.moves import http_cookiejar
from six.moves import urllib from six.moves import urllib
from mitmproxy import version, filt, controller from mitmproxy import controller
from mitmproxy import filt
from mitmproxy import version
from netlib import wsgi from netlib import wsgi
from netlib.http import http1, cookies from netlib.http import cookies
from netlib.http import http1
class AppRegistry: class AppRegistry:

View File

@ -1,9 +1,12 @@
from __future__ import absolute_import, print_function, division
from abc import abstractmethod, ABCMeta from abc import abstractmethod, ABCMeta
import six import six
from typing import List # noqa from typing import List # noqa
from mitmproxy import models, filt # noqa from mitmproxy import filt
from mitmproxy import models # noqa
@six.add_metaclass(ABCMeta) @six.add_metaclass(ABCMeta)

View File

@ -1,13 +1,16 @@
from __future__ import print_function, absolute_import from __future__ import absolute_import, print_function, division
import os import os
import signal import signal
import sys import sys
from six.moves import _thread # PY3: We only need _thread.error, which is an alias of RuntimeError in 3.3+ from six.moves import _thread # PY3: We only need _thread.error, which is an alias of RuntimeError in 3.3+
from netlib.version_check import check_pyopenssl_version
from . import cmdline from mitmproxy import cmdline
from .exceptions import ServerException from mitmproxy import exceptions
from .proxy.server import DummyServer, ProxyServer from mitmproxy.proxy import config
from .proxy.config import process_proxy_options from mitmproxy.proxy import server
from netlib import version_check
def assert_utf8_env(): def assert_utf8_env():
@ -28,11 +31,11 @@ def assert_utf8_env():
def get_server(dummy_server, options): def get_server(dummy_server, options):
if dummy_server: if dummy_server:
return DummyServer(options) return server.DummyServer(options)
else: else:
try: try:
return ProxyServer(options) return server.ProxyServer(options)
except ServerException as v: except exceptions.ServerException as v:
print(str(v), file=sys.stderr) print(str(v), file=sys.stderr)
sys.exit(1) sys.exit(1)
@ -44,7 +47,7 @@ def mitmproxy(args=None): # pragma: no cover
sys.exit(1) sys.exit(1)
from . import console from . import console
check_pyopenssl_version() version_check.check_pyopenssl_version()
assert_utf8_env() assert_utf8_env()
parser = cmdline.mitmproxy() parser = cmdline.mitmproxy()
@ -52,7 +55,7 @@ def mitmproxy(args=None): # pragma: no cover
if options.quiet: if options.quiet:
options.verbose = 0 options.verbose = 0
proxy_config = process_proxy_options(parser, options) proxy_config = cmdline.process_proxy_options(parser, options)
console_options = console.Options(**cmdline.get_common_options(options)) console_options = console.Options(**cmdline.get_common_options(options))
console_options.palette = options.palette console_options.palette = options.palette
console_options.palette_transparent = options.palette_transparent console_options.palette_transparent = options.palette_transparent
@ -74,7 +77,7 @@ def mitmproxy(args=None): # pragma: no cover
def mitmdump(args=None): # pragma: no cover def mitmdump(args=None): # pragma: no cover
from . import dump from . import dump
check_pyopenssl_version() version_check.check_pyopenssl_version()
parser = cmdline.mitmdump() parser = cmdline.mitmdump()
options = parser.parse_args(args) options = parser.parse_args(args)
@ -82,7 +85,7 @@ def mitmdump(args=None): # pragma: no cover
options.verbose = 0 options.verbose = 0
options.flow_detail = 0 options.flow_detail = 0
proxy_config = process_proxy_options(parser, options) proxy_config = config.process_proxy_options(parser, options)
dump_options = dump.Options(**cmdline.get_common_options(options)) dump_options = dump.Options(**cmdline.get_common_options(options))
dump_options.flow_detail = options.flow_detail dump_options.flow_detail = options.flow_detail
dump_options.keepserving = options.keepserving dump_options.keepserving = options.keepserving
@ -108,7 +111,7 @@ def mitmdump(args=None): # pragma: no cover
def mitmweb(args=None): # pragma: no cover def mitmweb(args=None): # pragma: no cover
from . import web from . import web
check_pyopenssl_version() version_check.check_pyopenssl_version()
parser = cmdline.mitmweb() parser = cmdline.mitmweb()
@ -116,7 +119,7 @@ def mitmweb(args=None): # pragma: no cover
if options.quiet: if options.quiet:
options.verbose = 0 options.verbose = 0
proxy_config = process_proxy_options(parser, options) proxy_config = config.process_proxy_options(parser, options)
web_options = web.Options(**cmdline.get_common_options(options)) web_options = web.Options(**cmdline.get_common_options(options))
web_options.intercept = options.intercept web_options.intercept = options.intercept
web_options.wdebug = options.wdebug web_options.wdebug = options.wdebug

View File

@ -1,12 +1,12 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
from netlib.http import decoded
from .connections import ClientConnection, ServerConnection
from .flow import Flow, Error
from .http import ( from .http import (
HTTPFlow, HTTPRequest, HTTPResponse, Headers, HTTPFlow, HTTPRequest, HTTPResponse, Headers,
make_error_response, make_connect_request, make_connect_response, expect_continue_response make_error_response, make_connect_request, make_connect_response, expect_continue_response
) )
from netlib.http import decoded
from .connections import ClientConnection, ServerConnection
from .flow import Flow, Error
from .tcp import TCPFlow from .tcp import TCPFlow
FLOW_TYPES = dict( FLOW_TYPES = dict(

View File

@ -1,12 +1,14 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
import copy import copy
import os import os
import six import six
from netlib import tcp, certutils from mitmproxy import stateobject
from .. import stateobject, utils from mitmproxy import utils
from netlib import certutils
from netlib import tcp
class ClientConnection(tcp.BaseHandler, stateobject.StateObject): class ClientConnection(tcp.BaseHandler, stateobject.StateObject):

View File

@ -1,10 +1,14 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
import copy import copy
import uuid import uuid
from .. import stateobject, utils, version from mitmproxy import exceptions
from .connections import ClientConnection, ServerConnection from mitmproxy import stateobject
from ..exceptions import Kill from mitmproxy import utils
from mitmproxy import version
from mitmproxy.models.connections import ClientConnection
from mitmproxy.models.connections import ServerConnection
class Error(stateobject.StateObject): class Error(stateobject.StateObject):
@ -151,7 +155,7 @@ class Flow(stateobject.StateObject):
""" """
self.error = Error("Connection killed") self.error = Error("Connection killed")
self.intercepted = False self.intercepted = False
self.reply(Kill) self.reply(exceptions.Kill)
master.error(self) master.error(self)
def intercept(self, master): def intercept(self, master):

View File

@ -1,11 +1,15 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
import cgi import cgi
from mitmproxy import version
from mitmproxy.models.flow import Flow
from netlib import encoding from netlib import encoding
from netlib.http import status_codes, Headers, Request, Response from netlib.http import Headers
from netlib.http import Request
from netlib.http import Response
from netlib.http import status_codes
from netlib.tcp import Address from netlib.tcp import Address
from .. import version
from .flow import Flow
class MessageMixin(object): class MessageMixin(object):

View File

@ -1,8 +1,11 @@
from __future__ import absolute_import, print_function, division
import time import time
from typing import List from typing import List
import netlib.basetypes import netlib.basetypes
from .flow import Flow from mitmproxy.models.flow import Flow
class TCPMessage(netlib.basetypes.Serializable): class TCPMessage(netlib.basetypes.Serializable):

View File

@ -1,12 +1,13 @@
from __future__ import absolute_import from __future__ import absolute_import, print_function, division
import os import os
import tornado.template
import tornado.web import tornado.web
import tornado.wsgi import tornado.wsgi
import tornado.template
from .. import utils
from ..proxy import config
from mitmproxy import utils
from mitmproxy.proxy import config
loader = tornado.template.Loader(utils.pkg_data.path("onboarding/templates")) loader = tornado.template.Loader(utils.pkg_data.path("onboarding/templates"))

View File

@ -1,4 +1,5 @@
import subprocess import subprocess
import pf import pf
""" """

View File

@ -1,18 +1,17 @@
import configargparse import collections
from six.moves import cPickle as pickle import ctypes
from ctypes import byref, windll, Structure import ctypes.wintypes
from ctypes.wintypes import DWORD
import os import os
import socket import socket
from six.moves import socketserver
import struct import struct
import threading import threading
import time import time
from collections import OrderedDict
from pydivert.windivert import WinDivert
from pydivert.enum import Direction, Layer, Flag
import configargparse
from pydivert import enum
from pydivert import windivert
from six.moves import cPickle as pickle
from six.moves import socketserver
PROXY_API_PORT = 8085 PROXY_API_PORT = 8085
@ -91,22 +90,22 @@ ERROR_INSUFFICIENT_BUFFER = 0x7A
# http://msdn.microsoft.com/en-us/library/windows/desktop/bb485761(v=vs.85).aspx # http://msdn.microsoft.com/en-us/library/windows/desktop/bb485761(v=vs.85).aspx
class MIB_TCPROW2(Structure): class MIB_TCPROW2(ctypes.Structure):
_fields_ = [ _fields_ = [
('dwState', DWORD), ('dwState', ctypes.wintypes.DWORD),
('dwLocalAddr', DWORD), ('dwLocalAddr', ctypes.wintypes.DWORD),
('dwLocalPort', DWORD), ('dwLocalPort', ctypes.wintypes.DWORD),
('dwRemoteAddr', DWORD), ('dwRemoteAddr', ctypes.wintypes.DWORD),
('dwRemotePort', DWORD), ('dwRemotePort', ctypes.wintypes.DWORD),
('dwOwningPid', DWORD), ('dwOwningPid', ctypes.wintypes.DWORD),
('dwOffloadState', DWORD) ('dwOffloadState', ctypes.wintypes.DWORD)
] ]
# http://msdn.microsoft.com/en-us/library/windows/desktop/bb485772(v=vs.85).aspx # http://msdn.microsoft.com/en-us/library/windows/desktop/bb485772(v=vs.85).aspx
def MIB_TCPTABLE2(size): def MIB_TCPTABLE2(size):
class _MIB_TCPTABLE2(Structure): class _MIB_TCPTABLE2(ctypes.Structure):
_fields_ = [('dwNumEntries', DWORD), _fields_ = [('dwNumEntries', ctypes.wintypes.DWORD),
('table', MIB_TCPROW2 * size)] ('table', MIB_TCPROW2 * size)]
return _MIB_TCPTABLE2() return _MIB_TCPTABLE2()
@ -192,13 +191,13 @@ class TransparentProxy(object):
self.proxy_addr, self.proxy_port = proxy_addr, proxy_port self.proxy_addr, self.proxy_port = proxy_addr, proxy_port
self.connection_cache_size = cache_size self.connection_cache_size = cache_size
self.client_server_map = OrderedDict() self.client_server_map = collections.OrderedDict()
self.api = APIServer(self, (api_host, api_port), APIRequestHandler) self.api = APIServer(self, (api_host, api_port), APIRequestHandler)
self.api_thread = threading.Thread(target=self.api.serve_forever) self.api_thread = threading.Thread(target=self.api.serve_forever)
self.api_thread.daemon = True self.api_thread.daemon = True
self.driver = WinDivert() self.driver = windivert.WinDivert()
self.driver.register() self.driver.register()
self.request_filter = custom_filter or " or ".join( self.request_filter = custom_filter or " or ".join(
@ -212,7 +211,7 @@ class TransparentProxy(object):
self.addr_pid_map = dict() self.addr_pid_map = dict()
self.trusted_pids = set() self.trusted_pids = set()
self.tcptable2 = MIB_TCPTABLE2(0) self.tcptable2 = MIB_TCPTABLE2(0)
self.tcptable2_size = DWORD(0) self.tcptable2_size = ctypes.wintypes.DWORD(0)
self.request_local_handle = None self.request_local_handle = None
self.request_local_thread = threading.Thread(target=self.request_local) self.request_local_thread = threading.Thread(target=self.request_local)
self.request_local_thread.daemon = True self.request_local_thread.daemon = True
@ -244,23 +243,23 @@ class TransparentProxy(object):
# real gateway if they are on the same network. # real gateway if they are on the same network.
self.icmp_handle = self.driver.open_handle( self.icmp_handle = self.driver.open_handle(
filter="icmp", filter="icmp",
layer=Layer.NETWORK, layer=enum.Layer.NETWORK,
flags=Flag.DROP) flags=enum.Flag.DROP)
self.response_handle = self.driver.open_handle( self.response_handle = self.driver.open_handle(
filter=self.response_filter, filter=self.response_filter,
layer=Layer.NETWORK) layer=enum.Layer.NETWORK)
self.response_thread.start() self.response_thread.start()
if self.mode == "forward" or self.mode == "both": if self.mode == "forward" or self.mode == "both":
self.request_forward_handle = self.driver.open_handle( self.request_forward_handle = self.driver.open_handle(
filter=self.request_filter, filter=self.request_filter,
layer=Layer.NETWORK_FORWARD) layer=enum.Layer.NETWORK_FORWARD)
self.request_forward_thread.start() self.request_forward_thread.start()
if self.mode == "local" or self.mode == "both": if self.mode == "local" or self.mode == "both":
self.request_local_handle = self.driver.open_handle( self.request_local_handle = self.driver.open_handle(
filter=self.request_filter, filter=self.request_filter,
layer=Layer.NETWORK) layer=enum.Layer.NETWORK)
self.request_local_thread.start() self.request_local_thread.start()
def shutdown(self): def shutdown(self):
@ -288,9 +287,9 @@ class TransparentProxy(object):
raise raise
def fetch_pids(self): def fetch_pids(self):
ret = windll.iphlpapi.GetTcpTable2( ret = ctypes.windll.iphlpapi.GetTcpTable2(
byref( ctypes.byref(
self.tcptable2), byref( self.tcptable2), ctypes.byref(
self.tcptable2_size), 0) self.tcptable2_size), 0)
if ret == ERROR_INSUFFICIENT_BUFFER: if ret == ERROR_INSUFFICIENT_BUFFER:
self.tcptable2 = MIB_TCPTABLE2(self.tcptable2_size.value) self.tcptable2 = MIB_TCPTABLE2(self.tcptable2_size.value)
@ -352,7 +351,7 @@ class TransparentProxy(object):
self.client_server_map[client] = server self.client_server_map[client] = server
packet.dst_addr, packet.dst_port = self.proxy_addr, self.proxy_port packet.dst_addr, packet.dst_port = self.proxy_addr, self.proxy_port
metadata.direction = Direction.INBOUND metadata.direction = enum.Direction.INBOUND
packet = self.driver.update_packet_checksums(packet) packet = self.driver.update_packet_checksums(packet)
# Use any handle thats on the NETWORK layer - request_local may be # Use any handle thats on the NETWORK layer - request_local may be

View File

@ -25,15 +25,16 @@ Another subtle design goal of this architecture is that upstream connections sho
as late as possible; this makes server replay without any outgoing connections possible. as late as possible; this makes server replay without any outgoing connections possible.
""" """
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
from .base import Layer, ServerConnectionMixin from .base import Layer, ServerConnectionMixin
from .tls import TlsLayer
from .tls import is_tls_record_magic
from .tls import TlsClientHello
from .http import UpstreamConnectLayer from .http import UpstreamConnectLayer
from .http1 import Http1Layer from .http1 import Http1Layer
from .http2 import Http2Layer from .http2 import Http2Layer
from .rawtcp import RawTCPLayer from .rawtcp import RawTCPLayer
from .tls import TlsClientHello
from .tls import TlsLayer
from .tls import is_tls_record_magic
__all__ = [ __all__ = [
"Layer", "ServerConnectionMixin", "Layer", "ServerConnectionMixin",

View File

@ -1,11 +1,12 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
import sys import sys
import six import six
from ..models import ServerConnection import netlib.exceptions
from ..exceptions import ProtocolException from mitmproxy import exceptions
from netlib.exceptions import TcpException from mitmproxy import models
class _LayerCodeCompletion(object): class _LayerCodeCompletion(object):
@ -113,7 +114,7 @@ class ServerConnectionMixin(object):
def __init__(self, server_address=None): def __init__(self, server_address=None):
super(ServerConnectionMixin, self).__init__() super(ServerConnectionMixin, self).__init__()
self.server_conn = ServerConnection(server_address, (self.config.host, 0)) self.server_conn = models.ServerConnection(server_address, (self.config.host, 0))
self.__check_self_connect() self.__check_self_connect()
def __check_self_connect(self): def __check_self_connect(self):
@ -128,7 +129,7 @@ class ServerConnectionMixin(object):
address.host in ("localhost", "127.0.0.1", "::1") address.host in ("localhost", "127.0.0.1", "::1")
) )
if self_connect: if self_connect:
raise ProtocolException( raise exceptions.ProtocolException(
"Invalid server address: {}\r\n" "Invalid server address: {}\r\n"
"The proxy shall not connect to itself.".format(repr(address)) "The proxy shall not connect to itself.".format(repr(address))
) )
@ -154,7 +155,7 @@ class ServerConnectionMixin(object):
self.server_conn.finish() self.server_conn.finish()
self.server_conn.close() self.server_conn.close()
self.channel.tell("serverdisconnect", self.server_conn) self.channel.tell("serverdisconnect", self.server_conn)
self.server_conn = ServerConnection(address, (source_address.host, 0)) self.server_conn = models.ServerConnection(address, (source_address.host, 0))
def connect(self): def connect(self):
""" """
@ -165,15 +166,15 @@ class ServerConnectionMixin(object):
~mitmproxy.exceptions.ProtocolException: if the connection could not be established. ~mitmproxy.exceptions.ProtocolException: if the connection could not be established.
""" """
if not self.server_conn.address: if not self.server_conn.address:
raise ProtocolException("Cannot connect to server, no server address given.") raise exceptions.ProtocolException("Cannot connect to server, no server address given.")
self.log("serverconnect", "debug", [repr(self.server_conn.address)]) self.log("serverconnect", "debug", [repr(self.server_conn.address)])
self.channel.ask("serverconnect", self.server_conn) self.channel.ask("serverconnect", self.server_conn)
try: try:
self.server_conn.connect() self.server_conn.connect()
except TcpException as e: except netlib.exceptions.TcpException as e:
six.reraise( six.reraise(
ProtocolException, exceptions.ProtocolException,
ProtocolException( exceptions.ProtocolException(
"Server connection to {} failed: {}".format( "Server connection to {} failed: {}".format(
repr(self.server_conn.address), str(e) repr(self.server_conn.address), str(e)
) )

View File

@ -1,30 +1,21 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
import sys import sys
import traceback import traceback
import h2.exceptions
import six import six
import netlib.exceptions
from mitmproxy import exceptions
from mitmproxy import models
from mitmproxy import utils
from mitmproxy.protocol import base
from netlib import http
from netlib import tcp from netlib import tcp
from netlib.exceptions import HttpException, HttpReadDisconnect, NetlibException
from netlib.http import Headers
from h2.exceptions import H2Error
from .. import utils
from ..exceptions import HttpProtocolException, Http2ProtocolException, ProtocolException
from ..models import (
HTTPFlow,
HTTPResponse,
make_error_response,
make_connect_response,
Error,
expect_continue_response
)
from .base import Layer
class _HttpTransmissionLayer(Layer): class _HttpTransmissionLayer(base.Layer):
def read_request(self): def read_request(self):
raise NotImplementedError() raise NotImplementedError()
@ -51,7 +42,7 @@ class _HttpTransmissionLayer(Layer):
def send_response(self, response): def send_response(self, response):
if response.content is None: if response.content is None:
raise HttpException("Cannot assemble flow with missing content") raise netlib.exceptions.HttpException("Cannot assemble flow with missing content")
self.send_response_headers(response) self.send_response_headers(response)
self.send_response_body(response, [response.content]) self.send_response_body(response, [response.content])
@ -89,7 +80,7 @@ class ConnectServerConnection(object):
__nonzero__ = __bool__ __nonzero__ = __bool__
class UpstreamConnectLayer(Layer): class UpstreamConnectLayer(base.Layer):
def __init__(self, ctx, connect_request): def __init__(self, ctx, connect_request):
super(UpstreamConnectLayer, self).__init__(ctx) super(UpstreamConnectLayer, self).__init__(ctx)
@ -107,7 +98,7 @@ class UpstreamConnectLayer(Layer):
self.send_request(self.connect_request) self.send_request(self.connect_request)
resp = self.read_response(self.connect_request) resp = self.read_response(self.connect_request)
if resp.status_code != 200: if resp.status_code != 200:
raise ProtocolException("Reconnect: Upstream server refuses CONNECT request") raise exceptions.ProtocolException("Reconnect: Upstream server refuses CONNECT request")
def connect(self): def connect(self):
if not self.server_conn: if not self.server_conn:
@ -129,7 +120,7 @@ class UpstreamConnectLayer(Layer):
self.server_conn.address = address self.server_conn.address = address
class HttpLayer(Layer): class HttpLayer(base.Layer):
def __init__(self, ctx, mode): def __init__(self, ctx, mode):
super(HttpLayer, self).__init__(ctx) super(HttpLayer, self).__init__(ctx)
@ -166,16 +157,16 @@ class HttpLayer(Layer):
self.handle_regular_mode_connect(request) self.handle_regular_mode_connect(request)
return return
except HttpReadDisconnect: except netlib.exceptions.HttpReadDisconnect:
# don't throw an error for disconnects that happen before/between requests. # don't throw an error for disconnects that happen before/between requests.
return return
except NetlibException as e: except netlib.exceptions.NetlibException as e:
self.send_error_response(400, repr(e)) self.send_error_response(400, repr(e))
six.reraise(ProtocolException, ProtocolException( six.reraise(exceptions.ProtocolException, exceptions.ProtocolException(
"Error in HTTP connection: %s" % repr(e)), sys.exc_info()[2]) "Error in HTTP connection: %s" % repr(e)), sys.exc_info()[2])
try: try:
flow = HTTPFlow(self.client_conn, self.server_conn, live=self) flow = models.HTTPFlow(self.client_conn, self.server_conn, live=self)
flow.request = request flow.request = request
# set upstream auth # set upstream auth
if self.mode == "upstream" and self.config.upstream_auth is not None: if self.mode == "upstream" and self.config.upstream_auth is not None:
@ -210,16 +201,16 @@ class HttpLayer(Layer):
self.handle_upstream_mode_connect(flow.request.copy()) self.handle_upstream_mode_connect(flow.request.copy())
return return
except (ProtocolException, NetlibException) as e: except (exceptions.ProtocolException, netlib.exceptions.NetlibException) as e:
self.send_error_response(502, repr(e)) self.send_error_response(502, repr(e))
if not flow.response: if not flow.response:
flow.error = Error(str(e)) flow.error = models.Error(str(e))
self.channel.ask("error", flow) self.channel.ask("error", flow)
self.log(traceback.format_exc(), "debug") self.log(traceback.format_exc(), "debug")
return return
else: else:
six.reraise(ProtocolException, ProtocolException( six.reraise(exceptions.ProtocolException, exceptions.ProtocolException(
"Error in HTTP connection: %s" % repr(e)), sys.exc_info()[2]) "Error in HTTP connection: %s" % repr(e)), sys.exc_info()[2])
finally: finally:
if flow: if flow:
@ -229,16 +220,16 @@ class HttpLayer(Layer):
request = self.read_request() request = self.read_request()
if request.headers.get("expect", "").lower() == "100-continue": if request.headers.get("expect", "").lower() == "100-continue":
# TODO: We may have to use send_response_headers for HTTP2 here. # TODO: We may have to use send_response_headers for HTTP2 here.
self.send_response(expect_continue_response) self.send_response(models.expect_continue_response)
request.headers.pop("expect") request.headers.pop("expect")
request.body = b"".join(self.read_request_body(request)) request.body = b"".join(self.read_request_body(request))
return request return request
def send_error_response(self, code, message): def send_error_response(self, code, message):
try: try:
response = make_error_response(code, message) response = models.make_error_response(code, message)
self.send_response(response) self.send_response(response)
except (NetlibException, H2Error, Http2ProtocolException): except (netlib.exceptions.NetlibException, h2.exceptions.H2Error, exceptions.Http2ProtocolException):
self.log(traceback.format_exc(), "debug") self.log(traceback.format_exc(), "debug")
def change_upstream_proxy_server(self, address): def change_upstream_proxy_server(self, address):
@ -249,7 +240,7 @@ class HttpLayer(Layer):
def handle_regular_mode_connect(self, request): def handle_regular_mode_connect(self, request):
self.set_server((request.host, request.port)) self.set_server((request.host, request.port))
self.send_response(make_connect_response(request.data.http_version)) self.send_response(models.make_connect_response(request.data.http_version))
layer = self.ctx.next_layer(self) layer = self.ctx.next_layer(self)
layer() layer()
@ -283,7 +274,7 @@ class HttpLayer(Layer):
try: try:
get_response() get_response()
except NetlibException as e: except netlib.exceptions.NetlibException as e:
self.log( self.log(
"server communication error: %s" % repr(e), "server communication error: %s" % repr(e),
level="debug" level="debug"
@ -300,9 +291,9 @@ class HttpLayer(Layer):
# > read (100-n)% of large request # > read (100-n)% of large request
# > send large request upstream # > send large request upstream
if isinstance(e, Http2ProtocolException): if isinstance(e, exceptions.Http2ProtocolException):
# do not try to reconnect for HTTP2 # do not try to reconnect for HTTP2
raise ProtocolException("First and only attempt to get response via HTTP2 failed.") raise exceptions.ProtocolException("First and only attempt to get response via HTTP2 failed.")
self.disconnect() self.disconnect()
self.connect() self.connect()
@ -345,7 +336,7 @@ class HttpLayer(Layer):
flow.request.scheme = "https" if self.__initial_server_tls else "http" flow.request.scheme = "https" if self.__initial_server_tls else "http"
request_reply = self.channel.ask("request", flow) request_reply = self.channel.ask("request", flow)
if isinstance(request_reply, HTTPResponse): if isinstance(request_reply, models.HTTPResponse):
flow.response = request_reply flow.response = request_reply
return return
@ -365,7 +356,7 @@ class HttpLayer(Layer):
if not self.server_conn: if not self.server_conn:
self.connect() self.connect()
if tls: if tls:
raise HttpProtocolException("Cannot change scheme in upstream proxy mode.") raise exceptions.HttpProtocolException("Cannot change scheme in upstream proxy mode.")
""" """
# This is a very ugly (untested) workaround to solve a very ugly problem. # This is a very ugly (untested) workaround to solve a very ugly problem.
if self.server_conn and self.server_conn.tls_established and not ssl: if self.server_conn and self.server_conn.tls_established and not ssl:
@ -383,7 +374,7 @@ class HttpLayer(Layer):
def validate_request(self, request): def validate_request(self, request):
if request.first_line_format == "absolute" and request.scheme != "http": if request.first_line_format == "absolute" and request.scheme != "http":
raise HttpException("Invalid request scheme: %s" % request.scheme) raise netlib.exceptions.HttpException("Invalid request scheme: %s" % request.scheme)
expected_request_forms = { expected_request_forms = {
"regular": ("authority", "absolute",), "regular": ("authority", "absolute",),
@ -396,7 +387,7 @@ class HttpLayer(Layer):
err_message = "Invalid HTTP request form (expected: %s, got: %s)" % ( err_message = "Invalid HTTP request form (expected: %s, got: %s)" % (
" or ".join(allowed_request_forms), request.first_line_format " or ".join(allowed_request_forms), request.first_line_format
) )
raise HttpException(err_message) raise netlib.exceptions.HttpException(err_message)
if self.mode == "regular" and request.first_line_format == "absolute": if self.mode == "regular" and request.first_line_format == "absolute":
request.first_line_format = "relative" request.first_line_format = "relative"
@ -406,10 +397,10 @@ class HttpLayer(Layer):
if self.config.authenticator.authenticate(request.headers): if self.config.authenticator.authenticate(request.headers):
self.config.authenticator.clean(request.headers) self.config.authenticator.clean(request.headers)
else: else:
self.send_response(make_error_response( self.send_response(models.make_error_response(
407, 407,
"Proxy Authentication Required", "Proxy Authentication Required",
Headers(**self.config.authenticator.auth_challenge_headers()) http.Headers(**self.config.authenticator.auth_challenge_headers())
)) ))
return False return False
return True return True

View File

@ -1,13 +1,11 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
from mitmproxy import models
from mitmproxy.protocol import http
from netlib.http import http1 from netlib.http import http1
from .http import _HttpTransmissionLayer, HttpLayer
from ..models import HTTPRequest, HTTPResponse
class Http1Layer(http._HttpTransmissionLayer):
class Http1Layer(_HttpTransmissionLayer):
def __init__(self, ctx, mode): def __init__(self, ctx, mode):
super(Http1Layer, self).__init__(ctx) super(Http1Layer, self).__init__(ctx)
@ -15,7 +13,7 @@ class Http1Layer(_HttpTransmissionLayer):
def read_request(self): def read_request(self):
req = http1.read_request(self.client_conn.rfile, body_size_limit=self.config.body_size_limit) req = http1.read_request(self.client_conn.rfile, body_size_limit=self.config.body_size_limit)
return HTTPRequest.wrap(req) return models.HTTPRequest.wrap(req)
def read_request_body(self, request): def read_request_body(self, request):
expected_size = http1.expected_http_body_size(request) expected_size = http1.expected_http_body_size(request)
@ -27,7 +25,7 @@ class Http1Layer(_HttpTransmissionLayer):
def read_response_headers(self): def read_response_headers(self):
resp = http1.read_response_head(self.server_conn.rfile) resp = http1.read_response_head(self.server_conn.rfile)
return HTTPResponse.wrap(resp) return models.HTTPResponse.wrap(resp)
def read_response_body(self, request, response): def read_response_body(self, request, response):
expected_size = http1.expected_http_body_size(request, response) expected_size = http1.expected_http_body_size(request, response)
@ -63,5 +61,5 @@ class Http1Layer(_HttpTransmissionLayer):
return close_connection return close_connection
def __call__(self): def __call__(self):
layer = HttpLayer(self, self.mode) layer = http.HttpLayer(self, self.mode)
layer() layer()

View File

@ -1,29 +1,27 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
import threading import threading
import time import time
import traceback
import h2.exceptions
import hyperframe
import six
from h2 import connection
from h2 import events
from six.moves import queue from six.moves import queue
import traceback import netlib.exceptions
import six from mitmproxy import exceptions
from h2.connection import H2Connection from mitmproxy import models
from h2.exceptions import StreamClosedError from mitmproxy.protocol import base
from h2 import events from mitmproxy.protocol import http
from hyperframe.frame import PriorityFrame import netlib.http
from netlib import tcp
from netlib.tcp import ssl_read_select from netlib.http import http2
from netlib.exceptions import HttpException
from netlib.http import Headers
from netlib.http.http2 import framereader
import netlib.http.url
from .base import Layer
from .http import _HttpTransmissionLayer, HttpLayer
from ..exceptions import ProtocolException, Http2ProtocolException
from ..models import HTTPRequest, HTTPResponse
class SafeH2Connection(H2Connection): class SafeH2Connection(connection.H2Connection):
def __init__(self, conn, *args, **kwargs): def __init__(self, conn, *args, **kwargs):
super(SafeH2Connection, self).__init__(*args, **kwargs) super(SafeH2Connection, self).__init__(*args, **kwargs)
@ -46,7 +44,7 @@ class SafeH2Connection(H2Connection):
with self.lock: with self.lock:
try: try:
self.reset_stream(stream_id, error_code) self.reset_stream(stream_id, error_code)
except StreamClosedError: # pragma: no cover except h2.exceptions.StreamClosedError: # pragma: no cover
# stream is already closed - good # stream is already closed - good
pass pass
self.conn.send(self.data_to_send()) self.conn.send(self.data_to_send())
@ -59,7 +57,7 @@ class SafeH2Connection(H2Connection):
def safe_send_headers(self, is_zombie, stream_id, headers): def safe_send_headers(self, is_zombie, stream_id, headers):
with self.lock: with self.lock:
if is_zombie(): # pragma: no cover if is_zombie(): # pragma: no cover
raise Http2ProtocolException("Zombie Stream") raise exceptions.Http2ProtocolException("Zombie Stream")
self.send_headers(stream_id, headers.fields) self.send_headers(stream_id, headers.fields)
self.conn.send(self.data_to_send()) self.conn.send(self.data_to_send())
@ -70,7 +68,7 @@ class SafeH2Connection(H2Connection):
self.lock.acquire() self.lock.acquire()
if is_zombie(): # pragma: no cover if is_zombie(): # pragma: no cover
self.lock.release() self.lock.release()
raise Http2ProtocolException("Zombie Stream") raise exceptions.Http2ProtocolException("Zombie Stream")
max_outbound_frame_size = self.max_outbound_frame_size max_outbound_frame_size = self.max_outbound_frame_size
frame_chunk = chunk[position:position + max_outbound_frame_size] frame_chunk = chunk[position:position + max_outbound_frame_size]
if self.local_flow_control_window(stream_id) < len(frame_chunk): if self.local_flow_control_window(stream_id) < len(frame_chunk):
@ -83,12 +81,12 @@ class SafeH2Connection(H2Connection):
position += max_outbound_frame_size position += max_outbound_frame_size
with self.lock: with self.lock:
if is_zombie(): # pragma: no cover if is_zombie(): # pragma: no cover
raise Http2ProtocolException("Zombie Stream") raise exceptions.Http2ProtocolException("Zombie Stream")
self.end_stream(stream_id) self.end_stream(stream_id)
self.conn.send(self.data_to_send()) self.conn.send(self.data_to_send())
class Http2Layer(Layer): class Http2Layer(base.Layer):
def __init__(self, ctx, mode): def __init__(self, ctx, mode):
super(Http2Layer, self).__init__(ctx) super(Http2Layer, self).__init__(ctx)
@ -108,13 +106,13 @@ class Http2Layer(Layer):
self.active_conns.append(self.server_conn.connection) self.active_conns.append(self.server_conn.connection)
def connect(self): # pragma: no cover def connect(self): # pragma: no cover
raise Http2ProtocolException("HTTP2 layer should already have a connection.") raise exceptions.Http2ProtocolException("HTTP2 layer should already have a connection.")
def set_server(self): # pragma: no cover def set_server(self): # pragma: no cover
raise Http2ProtocolException("Cannot change server for HTTP2 connections.") raise exceptions.Http2ProtocolException("Cannot change server for HTTP2 connections.")
def disconnect(self): # pragma: no cover def disconnect(self): # pragma: no cover
raise Http2ProtocolException("Cannot dis- or reconnect in HTTP2 connections.") raise exceptions.Http2ProtocolException("Cannot dis- or reconnect in HTTP2 connections.")
def next_layer(self): # pragma: no cover def next_layer(self): # pragma: no cover
# WebSockets over HTTP/2? # WebSockets over HTTP/2?
@ -135,19 +133,19 @@ class Http2Layer(Layer):
eid = event.stream_id eid = event.stream_id
if isinstance(event, events.RequestReceived): if isinstance(event, events.RequestReceived):
headers = Headers([[k, v] for k, v in event.headers]) headers = netlib.http.Headers([[k, v] for k, v in event.headers])
self.streams[eid] = Http2SingleStreamLayer(self, eid, headers) self.streams[eid] = Http2SingleStreamLayer(self, eid, headers)
self.streams[eid].timestamp_start = time.time() self.streams[eid].timestamp_start = time.time()
self.streams[eid].start() self.streams[eid].start()
elif isinstance(event, events.ResponseReceived): elif isinstance(event, events.ResponseReceived):
headers = Headers([[k, v] for k, v in event.headers]) headers = netlib.http.Headers([[k, v] for k, v in event.headers])
self.streams[eid].queued_data_length = 0 self.streams[eid].queued_data_length = 0
self.streams[eid].timestamp_start = time.time() self.streams[eid].timestamp_start = time.time()
self.streams[eid].response_headers = headers self.streams[eid].response_headers = headers
self.streams[eid].response_arrived.set() self.streams[eid].response_arrived.set()
elif isinstance(event, events.DataReceived): elif isinstance(event, events.DataReceived):
if self.config.body_size_limit and self.streams[eid].queued_data_length > self.config.body_size_limit: if self.config.body_size_limit and self.streams[eid].queued_data_length > self.config.body_size_limit:
raise HttpException("HTTP body too large. Limit is {}.".format(self.config.body_size_limit)) raise netlib.exceptions.HttpException("HTTP body too large. Limit is {}.".format(self.config.body_size_limit))
self.streams[eid].data_queue.put(event.data) self.streams[eid].data_queue.put(event.data)
self.streams[eid].queued_data_length += len(event.data) self.streams[eid].queued_data_length += len(event.data)
source_conn.h2.safe_increment_flow_control(event.stream_id, event.flow_controlled_length) source_conn.h2.safe_increment_flow_control(event.stream_id, event.flow_controlled_length)
@ -178,7 +176,7 @@ class Http2Layer(Layer):
self.client_conn.h2.push_stream(parent_eid, event.pushed_stream_id, event.headers) self.client_conn.h2.push_stream(parent_eid, event.pushed_stream_id, event.headers)
self.client_conn.send(self.client_conn.h2.data_to_send()) self.client_conn.send(self.client_conn.h2.data_to_send())
headers = Headers([[str(k), str(v)] for k, v in event.headers]) headers = netlib.http.Headers([[str(k), str(v)] for k, v in event.headers])
headers['x-mitmproxy-pushed'] = 'true' headers['x-mitmproxy-pushed'] = 'true'
self.streams[event.pushed_stream_id] = Http2SingleStreamLayer(self, event.pushed_stream_id, headers) self.streams[event.pushed_stream_id] = Http2SingleStreamLayer(self, event.pushed_stream_id, headers)
self.streams[event.pushed_stream_id].timestamp_start = time.time() self.streams[event.pushed_stream_id].timestamp_start = time.time()
@ -197,7 +195,7 @@ class Http2Layer(Layer):
depends_on = self.streams[depends_on].server_stream_id depends_on = self.streams[depends_on].server_stream_id
# weight is between 1 and 256 (inclusive), but represented as uint8 (0 to 255) # weight is between 1 and 256 (inclusive), but represented as uint8 (0 to 255)
frame = PriorityFrame(stream_id, depends_on, event.weight - 1, event.exclusive) frame = hyperframe.frame.PriorityFrame(stream_id, depends_on, event.weight - 1, event.exclusive)
self.server_conn.send(frame.serialize()) self.server_conn.send(frame.serialize())
elif isinstance(event, events.TrailersReceived): elif isinstance(event, events.TrailersReceived):
raise NotImplementedError() raise NotImplementedError()
@ -226,7 +224,7 @@ class Http2Layer(Layer):
self.client_conn.send(self.client_conn.h2.data_to_send()) self.client_conn.send(self.client_conn.h2.data_to_send())
while True: while True:
r = ssl_read_select(self.active_conns, 1) r = tcp.ssl_read_select(self.active_conns, 1)
for conn in r: for conn in r:
source_conn = self.client_conn if conn == self.client_conn.connection else self.server_conn source_conn = self.client_conn if conn == self.client_conn.connection else self.server_conn
other_conn = self.server_conn if conn == self.client_conn.connection else self.client_conn other_conn = self.server_conn if conn == self.client_conn.connection else self.client_conn
@ -234,7 +232,7 @@ class Http2Layer(Layer):
with source_conn.h2.lock: with source_conn.h2.lock:
try: try:
raw_frame = b''.join(framereader.http2_read_raw_frame(source_conn.rfile)) raw_frame = b''.join(http2.framereader.http2_read_raw_frame(source_conn.rfile))
except: except:
# read frame failed: connection closed # read frame failed: connection closed
self._kill_all_streams() self._kill_all_streams()
@ -252,7 +250,7 @@ class Http2Layer(Layer):
self._cleanup_streams() self._cleanup_streams()
class Http2SingleStreamLayer(_HttpTransmissionLayer, threading.Thread): class Http2SingleStreamLayer(http._HttpTransmissionLayer, threading.Thread):
def __init__(self, ctx, stream_id, request_headers): def __init__(self, ctx, stream_id, request_headers):
super(Http2SingleStreamLayer, self).__init__(ctx, name="Thread-Http2SingleStreamLayer-{}".format(stream_id)) super(Http2SingleStreamLayer, self).__init__(ctx, name="Thread-Http2SingleStreamLayer-{}".format(stream_id))
@ -336,7 +334,7 @@ class Http2SingleStreamLayer(_HttpTransmissionLayer, threading.Thread):
data.append(self.request_data_queue.get()) data.append(self.request_data_queue.get())
data = b"".join(data) data = b"".join(data)
return HTTPRequest( return models.HTTPRequest(
first_line_format, first_line_format,
method, method,
scheme, scheme,
@ -361,7 +359,7 @@ class Http2SingleStreamLayer(_HttpTransmissionLayer, threading.Thread):
with self.server_conn.h2.lock: with self.server_conn.h2.lock:
# We must not assign a stream id if we are already a zombie. # We must not assign a stream id if we are already a zombie.
if self.zombie: # pragma: no cover if self.zombie: # pragma: no cover
raise Http2ProtocolException("Zombie Stream") raise exceptions.Http2ProtocolException("Zombie Stream")
self.server_stream_id = self.server_conn.h2.get_next_available_stream_id() self.server_stream_id = self.server_conn.h2.get_next_available_stream_id()
self.server_to_client_stream_ids[self.server_stream_id] = self.client_stream_id self.server_to_client_stream_ids[self.server_stream_id] = self.client_stream_id
@ -382,7 +380,7 @@ class Http2SingleStreamLayer(_HttpTransmissionLayer, threading.Thread):
message.body message.body
) )
if self.zombie: # pragma: no cover if self.zombie: # pragma: no cover
raise Http2ProtocolException("Zombie Stream") raise exceptions.Http2ProtocolException("Zombie Stream")
def read_response_headers(self): def read_response_headers(self):
self.response_arrived.wait() self.response_arrived.wait()
@ -391,7 +389,7 @@ class Http2SingleStreamLayer(_HttpTransmissionLayer, threading.Thread):
headers = self.response_headers.copy() headers = self.response_headers.copy()
headers.clear(":status") headers.clear(":status")
return HTTPResponse( return models.HTTPResponse(
http_version=b"HTTP/2.0", http_version=b"HTTP/2.0",
status_code=status_code, status_code=status_code,
reason='', reason='',
@ -412,7 +410,7 @@ class Http2SingleStreamLayer(_HttpTransmissionLayer, threading.Thread):
yield self.response_data_queue.get() yield self.response_data_queue.get()
return return
if self.zombie: # pragma: no cover if self.zombie: # pragma: no cover
raise Http2ProtocolException("Zombie Stream") raise exceptions.Http2ProtocolException("Zombie Stream")
def send_response_headers(self, response): def send_response_headers(self, response):
headers = response.headers.copy() headers = response.headers.copy()
@ -423,7 +421,7 @@ class Http2SingleStreamLayer(_HttpTransmissionLayer, threading.Thread):
headers headers
) )
if self.zombie: # pragma: no cover if self.zombie: # pragma: no cover
raise Http2ProtocolException("Zombie Stream") raise exceptions.Http2ProtocolException("Zombie Stream")
def send_response_body(self, _response, chunks): def send_response_body(self, _response, chunks):
self.client_conn.h2.safe_send_body( self.client_conn.h2.safe_send_body(
@ -432,7 +430,7 @@ class Http2SingleStreamLayer(_HttpTransmissionLayer, threading.Thread):
chunks chunks
) )
if self.zombie: # pragma: no cover if self.zombie: # pragma: no cover
raise Http2ProtocolException("Zombie Stream") raise exceptions.Http2ProtocolException("Zombie Stream")
def check_close_connection(self, flow): def check_close_connection(self, flow):
# This layer only handles a single stream. # This layer only handles a single stream.
@ -447,11 +445,11 @@ class Http2SingleStreamLayer(_HttpTransmissionLayer, threading.Thread):
self() self()
def __call__(self): def __call__(self):
layer = HttpLayer(self, self.mode) layer = http.HttpLayer(self, self.mode)
try: try:
layer() layer()
except ProtocolException as e: except exceptions.ProtocolException as e:
self.log(repr(e), "info") self.log(repr(e), "info")
self.log(traceback.format_exc(), "debug") self.log(traceback.format_exc(), "debug")

View File

@ -1,13 +1,14 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
import threading import threading
import traceback import traceback
from mitmproxy.exceptions import ReplayException
from netlib.exceptions import HttpException, TcpException import netlib.exceptions
from mitmproxy import controller
from mitmproxy import exceptions
from mitmproxy import models
from netlib.http import http1 from netlib.http import http1
from ..controller import Channel
from ..models import Error, HTTPResponse, ServerConnection, make_connect_request
from ..exceptions import Kill
# TODO: Doesn't really belong into mitmproxy.protocol... # TODO: Doesn't really belong into mitmproxy.protocol...
@ -22,7 +23,7 @@ class RequestReplayThread(threading.Thread):
""" """
self.config, self.flow = config, flow self.config, self.flow = config, flow
if event_queue: if event_queue:
self.channel = Channel(event_queue, should_exit) self.channel = controller.Channel(event_queue, should_exit)
else: else:
self.channel = None self.channel = None
super(RequestReplayThread, self).__init__() super(RequestReplayThread, self).__init__()
@ -36,17 +37,17 @@ class RequestReplayThread(threading.Thread):
# If we have a channel, run script hooks. # If we have a channel, run script hooks.
if self.channel: if self.channel:
request_reply = self.channel.ask("request", self.flow) request_reply = self.channel.ask("request", self.flow)
if isinstance(request_reply, HTTPResponse): if isinstance(request_reply, models.HTTPResponse):
self.flow.response = request_reply self.flow.response = request_reply
if not self.flow.response: if not self.flow.response:
# In all modes, we directly connect to the server displayed # In all modes, we directly connect to the server displayed
if self.config.mode == "upstream": if self.config.mode == "upstream":
server_address = self.config.upstream_server.address server_address = self.config.upstream_server.address
server = ServerConnection(server_address, (self.config.host, 0)) server = models.ServerConnection(server_address, (self.config.host, 0))
server.connect() server.connect()
if r.scheme == "https": if r.scheme == "https":
connect_request = make_connect_request((r.host, r.port)) connect_request = models.make_connect_request((r.host, r.port))
server.wfile.write(http1.assemble_request(connect_request)) server.wfile.write(http1.assemble_request(connect_request))
server.wfile.flush() server.wfile.flush()
resp = http1.read_response( resp = http1.read_response(
@ -55,7 +56,7 @@ class RequestReplayThread(threading.Thread):
body_size_limit=self.config.body_size_limit body_size_limit=self.config.body_size_limit
) )
if resp.status_code != 200: if resp.status_code != 200:
raise ReplayException("Upstream server refuses CONNECT request") raise exceptions.ReplayException("Upstream server refuses CONNECT request")
server.establish_ssl( server.establish_ssl(
self.config.clientcerts, self.config.clientcerts,
sni=self.flow.server_conn.sni sni=self.flow.server_conn.sni
@ -65,7 +66,7 @@ class RequestReplayThread(threading.Thread):
r.first_line_format = "absolute" r.first_line_format = "absolute"
else: else:
server_address = (r.host, r.port) server_address = (r.host, r.port)
server = ServerConnection(server_address, (self.config.host, 0)) server = models.ServerConnection(server_address, (self.config.host, 0))
server.connect() server.connect()
if r.scheme == "https": if r.scheme == "https":
server.establish_ssl( server.establish_ssl(
@ -77,20 +78,20 @@ class RequestReplayThread(threading.Thread):
server.wfile.write(http1.assemble_request(r)) server.wfile.write(http1.assemble_request(r))
server.wfile.flush() server.wfile.flush()
self.flow.server_conn = server self.flow.server_conn = server
self.flow.response = HTTPResponse.wrap(http1.read_response( self.flow.response = models.HTTPResponse.wrap(http1.read_response(
server.rfile, server.rfile,
r, r,
body_size_limit=self.config.body_size_limit body_size_limit=self.config.body_size_limit
)) ))
if self.channel: if self.channel:
response_reply = self.channel.ask("response", self.flow) response_reply = self.channel.ask("response", self.flow)
if response_reply == Kill: if response_reply == exceptions.Kill:
raise Kill() raise exceptions.Kill()
except (ReplayException, HttpException, TcpException) as e: except (exceptions.ReplayException, netlib.exceptions.NetlibException) as e:
self.flow.error = Error(str(e)) self.flow.error = models.Error(str(e))
if self.channel: if self.channel:
self.channel.ask("error", self.flow) self.channel.ask("error", self.flow)
except Kill: except exceptions.Kill:
# Kill should only be raised if there's a channel in the # Kill should only be raised if there's a channel in the
# first place. # first place.
from ..proxy.root_context import Log from ..proxy.root_context import Log

View File

@ -1,17 +1,17 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
import socket import socket
from OpenSSL import SSL from OpenSSL import SSL
from netlib.exceptions import TcpException
from netlib.tcp import ssl_read_select import netlib.exceptions
from ..models import Error import netlib.tcp
from ..models.tcp import TCPFlow, TCPMessage from mitmproxy import models
from mitmproxy.models import tcp
from .base import Layer from mitmproxy.protocol import base
class RawTCPLayer(Layer): class RawTCPLayer(base.Layer):
chunk_size = 4096 chunk_size = 4096
def __init__(self, ctx, ignore=False): def __init__(self, ctx, ignore=False):
@ -22,7 +22,7 @@ class RawTCPLayer(Layer):
self.connect() self.connect()
if not self.ignore: if not self.ignore:
flow = TCPFlow(self.client_conn, self.server_conn, self) flow = models.TCPFlow(self.client_conn, self.server_conn, self)
self.channel.ask("tcp_open", flow) self.channel.ask("tcp_open", flow)
buf = memoryview(bytearray(self.chunk_size)) buf = memoryview(bytearray(self.chunk_size))
@ -33,7 +33,7 @@ class RawTCPLayer(Layer):
try: try:
while not self.channel.should_exit.is_set(): while not self.channel.should_exit.is_set():
r = ssl_read_select(conns, 10) r = netlib.tcp.ssl_read_select(conns, 10)
for conn in r: for conn in r:
dst = server if conn == client else client dst = server if conn == client else client
@ -52,15 +52,15 @@ class RawTCPLayer(Layer):
return return
continue continue
tcp_message = TCPMessage(dst == server, buf[:size].tobytes()) tcp_message = tcp.TCPMessage(dst == server, buf[:size].tobytes())
if not self.ignore: if not self.ignore:
flow.messages.append(tcp_message) flow.messages.append(tcp_message)
self.channel.ask("tcp_message", flow) self.channel.ask("tcp_message", flow)
dst.sendall(tcp_message.content) dst.sendall(tcp_message.content)
except (socket.error, TcpException, SSL.Error) as e: except (socket.error, netlib.exceptions.TcpException, SSL.Error) as e:
if not self.ignore: if not self.ignore:
flow.error = Error("TCP connection closed unexpectedly: {}".format(repr(e))) flow.error = models.Error("TCP connection closed unexpectedly: {}".format(repr(e)))
self.channel.tell("tcp_error", flow) self.channel.tell("tcp_error", flow)
finally: finally:
if not self.ignore: if not self.ignore:

View File

@ -1,16 +1,15 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
import struct import struct
import sys import sys
from construct import ConstructError import construct
import six import six
from netlib.exceptions import InvalidCertificateException
from netlib.exceptions import TlsException
from ..contrib.tls._constructs import ClientHello import netlib.exceptions
from ..exceptions import ProtocolException, TlsProtocolException, ClientHandshakeException from mitmproxy import exceptions
from .base import Layer from mitmproxy.contrib.tls import _constructs
from mitmproxy.protocol import base
# taken from https://testssl.sh/openssl-rfc.mappping.html # taken from https://testssl.sh/openssl-rfc.mappping.html
@ -246,11 +245,11 @@ def get_client_hello(client_conn):
while len(client_hello) < client_hello_size: while len(client_hello) < client_hello_size:
record_header = client_conn.rfile.peek(offset + 5)[offset:] record_header = client_conn.rfile.peek(offset + 5)[offset:]
if not is_tls_record_magic(record_header) or len(record_header) != 5: if not is_tls_record_magic(record_header) or len(record_header) != 5:
raise TlsProtocolException('Expected TLS record, got "%s" instead.' % record_header) raise exceptions.TlsProtocolException('Expected TLS record, got "%s" instead.' % record_header)
record_size = struct.unpack("!H", record_header[3:])[0] + 5 record_size = struct.unpack("!H", record_header[3:])[0] + 5
record_body = client_conn.rfile.peek(offset + record_size)[offset + 5:] record_body = client_conn.rfile.peek(offset + record_size)[offset + 5:]
if len(record_body) != record_size - 5: if len(record_body) != record_size - 5:
raise TlsProtocolException("Unexpected EOF in TLS handshake: %s" % record_body) raise exceptions.TlsProtocolException("Unexpected EOF in TLS handshake: %s" % record_body)
client_hello += record_body client_hello += record_body
offset += record_size offset += record_size
client_hello_size = struct.unpack("!I", b'\x00' + client_hello[1:4])[0] + 4 client_hello_size = struct.unpack("!I", b'\x00' + client_hello[1:4])[0] + 4
@ -260,7 +259,7 @@ def get_client_hello(client_conn):
class TlsClientHello(object): class TlsClientHello(object):
def __init__(self, raw_client_hello): def __init__(self, raw_client_hello):
self._client_hello = ClientHello.parse(raw_client_hello) self._client_hello = _constructs.ClientHello.parse(raw_client_hello)
def raw(self): def raw(self):
return self._client_hello return self._client_hello
@ -297,21 +296,23 @@ class TlsClientHello(object):
""" """
try: try:
raw_client_hello = get_client_hello(client_conn)[4:] # exclude handshake header. raw_client_hello = get_client_hello(client_conn)[4:] # exclude handshake header.
except ProtocolException as e: except exceptions.ProtocolException as e:
raise TlsProtocolException('Cannot read raw Client Hello: %s' % repr(e)) raise exceptions.TlsProtocolException('Cannot read raw Client Hello: %s' % repr(e))
try: try:
return cls(raw_client_hello) return cls(raw_client_hello)
except ConstructError as e: except construct.ConstructError as e:
raise TlsProtocolException('Cannot parse Client Hello: %s, Raw Client Hello: %s' % raise exceptions.TlsProtocolException(
(repr(e), raw_client_hello.encode("hex"))) 'Cannot parse Client Hello: %s, Raw Client Hello: %s' %
(repr(e), raw_client_hello.encode("hex"))
)
def __repr__(self): def __repr__(self):
return "TlsClientHello( sni: %s alpn_protocols: %s, cipher_suites: %s)" % \ return "TlsClientHello( sni: %s alpn_protocols: %s, cipher_suites: %s)" % \
(self.sni, self.alpn_protocols, self.cipher_suites) (self.sni, self.alpn_protocols, self.cipher_suites)
class TlsLayer(Layer): class TlsLayer(base.Layer):
""" """
The TLS layer implements transparent TLS connections. The TLS layer implements transparent TLS connections.
@ -345,7 +346,7 @@ class TlsLayer(Layer):
# Peek into the connection, read the initial client hello and parse it to obtain SNI and ALPN values. # Peek into the connection, read the initial client hello and parse it to obtain SNI and ALPN values.
try: try:
self._client_hello = TlsClientHello.from_client_conn(self.client_conn) self._client_hello = TlsClientHello.from_client_conn(self.client_conn)
except TlsProtocolException as e: except exceptions.TlsProtocolException as e:
self.log("Cannot parse Client Hello: %s" % repr(e), "error") self.log("Cannot parse Client Hello: %s" % repr(e), "error")
# Do we need to do a server handshake now? # Do we need to do a server handshake now?
@ -490,10 +491,10 @@ class TlsLayer(Layer):
# The reason for this might be difficult to find, so we try to peek here to see if it # The reason for this might be difficult to find, so we try to peek here to see if it
# raises ann error. # raises ann error.
self.client_conn.rfile.peek(1) self.client_conn.rfile.peek(1)
except TlsException as e: except netlib.exceptions.TlsException as e:
six.reraise( six.reraise(
ClientHandshakeException, exceptions.ClientHandshakeException,
ClientHandshakeException( exceptions.ClientHandshakeException(
"Cannot establish TLS with client (sni: {sni}): {e}".format( "Cannot establish TLS with client (sni: {sni}): {e}".format(
sni=self._client_hello.sni, e=repr(e) sni=self._client_hello.sni, e=repr(e)
), ),
@ -544,7 +545,7 @@ class TlsLayer(Layer):
(tls_cert_err['depth'], tls_cert_err['errno']), (tls_cert_err['depth'], tls_cert_err['errno']),
"error") "error")
self.log("Ignoring server verification error, continuing with connection", "error") self.log("Ignoring server verification error, continuing with connection", "error")
except InvalidCertificateException as e: except netlib.exceptions.InvalidCertificateException as e:
tls_cert_err = self.server_conn.ssl_verification_error tls_cert_err = self.server_conn.ssl_verification_error
self.log( self.log(
"TLS verification failed for upstream server at depth %s with error: %s" % "TLS verification failed for upstream server at depth %s with error: %s" %
@ -552,18 +553,18 @@ class TlsLayer(Layer):
"error") "error")
self.log("Aborting connection attempt", "error") self.log("Aborting connection attempt", "error")
six.reraise( six.reraise(
TlsProtocolException, exceptions.TlsProtocolException,
TlsProtocolException("Cannot establish TLS with {address} (sni: {sni}): {e}".format( exceptions.TlsProtocolException("Cannot establish TLS with {address} (sni: {sni}): {e}".format(
address=repr(self.server_conn.address), address=repr(self.server_conn.address),
sni=self.server_sni, sni=self.server_sni,
e=repr(e), e=repr(e),
)), )),
sys.exc_info()[2] sys.exc_info()[2]
) )
except TlsException as e: except netlib.exceptions.TlsException as e:
six.reraise( six.reraise(
TlsProtocolException, exceptions.TlsProtocolException,
TlsProtocolException("Cannot establish TLS with {address} (sni: {sni}): {e}".format( exceptions.TlsProtocolException("Cannot establish TLS with {address} (sni: {sni}): {e}".format(
address=repr(self.server_conn.address), address=repr(self.server_conn.address),
sni=self.server_sni, sni=self.server_sni,
e=repr(e), e=repr(e),

View File

@ -1,8 +1,8 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
from .server import ProxyServer, DummyServer
from .config import ProxyConfig from .config import ProxyConfig
from .root_context import RootContext, Log from .root_context import RootContext, Log
from .server import ProxyServer, DummyServer
__all__ = [ __all__ = [
"ProxyServer", "DummyServer", "ProxyServer", "DummyServer",

View File

@ -1,4 +1,5 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
import collections import collections
import os import os
import re import re
@ -6,11 +7,11 @@ import re
import six import six
from OpenSSL import SSL from OpenSSL import SSL
from netlib import certutils, tcp, human from mitmproxy import platform
from netlib import certutils
from netlib import human
from netlib import tcp
from netlib.http import authentication from netlib.http import authentication
from netlib.tcp import Address, sslversion_choices
from .. import platform
CONF_BASENAME = "mitmproxy" CONF_BASENAME = "mitmproxy"
CA_DIR = "~/.mitmproxy" CA_DIR = "~/.mitmproxy"
@ -91,7 +92,7 @@ class ProxyConfig:
self.body_size_limit = body_size_limit self.body_size_limit = body_size_limit
self.mode = mode self.mode = mode
if upstream_server: if upstream_server:
self.upstream_server = ServerSpec(upstream_server[0], Address.wrap(upstream_server[1])) self.upstream_server = ServerSpec(upstream_server[0], tcp.Address.wrap(upstream_server[1]))
self.upstream_auth = upstream_auth self.upstream_auth = upstream_auth
else: else:
self.upstream_server = None self.upstream_server = None
@ -111,9 +112,9 @@ class ProxyConfig:
self.certstore.add_cert_file(spec, cert) self.certstore.add_cert_file(spec, cert)
self.openssl_method_client, self.openssl_options_client = \ self.openssl_method_client, self.openssl_options_client = \
sslversion_choices[ssl_version_client] tcp.sslversion_choices[ssl_version_client]
self.openssl_method_server, self.openssl_options_server = \ self.openssl_method_server, self.openssl_options_server = \
sslversion_choices[ssl_version_server] tcp.sslversion_choices[ssl_version_server]
if ssl_verify_upstream_cert: if ssl_verify_upstream_cert:
self.openssl_verification_mode_server = SSL.VERIFY_PEER self.openssl_verification_mode_server = SSL.VERIFY_PEER

View File

@ -1,4 +1,5 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
from .http_proxy import HttpProxy, HttpUpstreamProxy from .http_proxy import HttpProxy, HttpUpstreamProxy
from .reverse_proxy import ReverseProxy from .reverse_proxy import ReverseProxy
from .socks_proxy import Socks5Proxy from .socks_proxy import Socks5Proxy

View File

@ -1,9 +1,9 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
from ...protocol import Layer, ServerConnectionMixin from mitmproxy import protocol
class HttpProxy(Layer, ServerConnectionMixin): class HttpProxy(protocol.Layer, protocol.ServerConnectionMixin):
def __call__(self): def __call__(self):
layer = self.ctx.next_layer(self) layer = self.ctx.next_layer(self)
@ -14,7 +14,7 @@ class HttpProxy(Layer, ServerConnectionMixin):
self.disconnect() self.disconnect()
class HttpUpstreamProxy(Layer, ServerConnectionMixin): class HttpUpstreamProxy(protocol.Layer, protocol.ServerConnectionMixin):
def __init__(self, ctx, server_address): def __init__(self, ctx, server_address):
super(HttpUpstreamProxy, self).__init__(ctx, server_address=server_address) super(HttpUpstreamProxy, self).__init__(ctx, server_address=server_address)

View File

@ -1,9 +1,9 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
from ...protocol import Layer, ServerConnectionMixin from mitmproxy import protocol
class ReverseProxy(Layer, ServerConnectionMixin): class ReverseProxy(protocol.Layer, protocol.ServerConnectionMixin):
def __init__(self, ctx, server_address, server_tls): def __init__(self, ctx, server_address, server_tls):
super(ReverseProxy, self).__init__(ctx, server_address=server_address) super(ReverseProxy, self).__init__(ctx, server_address=server_address)

View File

@ -1,13 +1,13 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
from netlib import socks, tcp import netlib.exceptions
from netlib.exceptions import TcpException from mitmproxy import exceptions
from mitmproxy import protocol
from ...exceptions import Socks5ProtocolException from netlib import socks
from ...protocol import Layer, ServerConnectionMixin from netlib import tcp
class Socks5Proxy(Layer, ServerConnectionMixin): class Socks5Proxy(protocol.Layer, protocol.ServerConnectionMixin):
def __init__(self, ctx): def __init__(self, ctx):
super(Socks5Proxy, self).__init__(ctx) super(Socks5Proxy, self).__init__(ctx)
@ -51,8 +51,8 @@ class Socks5Proxy(Layer, ServerConnectionMixin):
connect_reply.to_file(self.client_conn.wfile) connect_reply.to_file(self.client_conn.wfile)
self.client_conn.wfile.flush() self.client_conn.wfile.flush()
except (socks.SocksError, TcpException) as e: except (socks.SocksError, netlib.exceptions.TcpException) as e:
raise Socks5ProtocolException("SOCKS5 mode failure: %s" % repr(e)) raise exceptions.Socks5ProtocolException("SOCKS5 mode failure: %s" % repr(e))
# https://github.com/mitmproxy/mitmproxy/issues/839 # https://github.com/mitmproxy/mitmproxy/issues/839
address_bytes = (connect_request.addr.host.encode("idna"), connect_request.addr.port) address_bytes = (connect_request.addr.host.encode("idna"), connect_request.addr.port)

View File

@ -1,11 +1,11 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
from ... import platform from mitmproxy import exceptions
from ...exceptions import ProtocolException from mitmproxy import platform
from ...protocol import Layer, ServerConnectionMixin from mitmproxy import protocol
class TransparentProxy(Layer, ServerConnectionMixin): class TransparentProxy(protocol.Layer, protocol.ServerConnectionMixin):
def __init__(self, ctx): def __init__(self, ctx):
super(TransparentProxy, self).__init__(ctx) super(TransparentProxy, self).__init__(ctx)
@ -15,7 +15,7 @@ class TransparentProxy(Layer, ServerConnectionMixin):
try: try:
self.server_conn.address = self.resolver.original_addr(self.client_conn.connection) self.server_conn.address = self.resolver.original_addr(self.client_conn.connection)
except Exception as e: except Exception as e:
raise ProtocolException("Transparent mode failure: %s" % repr(e)) raise exceptions.ProtocolException("Transparent mode failure: %s" % repr(e))
layer = self.ctx.next_layer(self) layer = self.ctx.next_layer(self)
try: try:

View File

@ -1,15 +1,13 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
import sys import sys
import six import six
from mitmproxy.exceptions import ProtocolException, TlsProtocolException import netlib.exceptions
from netlib.exceptions import TcpException from mitmproxy import exceptions
from ..protocol import ( from mitmproxy import protocol
RawTCPLayer, TlsLayer, Http1Layer, Http2Layer, is_tls_record_magic, ServerConnectionMixin, from mitmproxy.proxy import modes
UpstreamConnectLayer, TlsClientHello
)
from .modes import HttpProxy, HttpUpstreamProxy, ReverseProxy
class RootContext(object): class RootContext(object):
@ -50,53 +48,53 @@ class RootContext(object):
def _next_layer(self, top_layer): def _next_layer(self, top_layer):
try: try:
d = top_layer.client_conn.rfile.peek(3) d = top_layer.client_conn.rfile.peek(3)
except TcpException as e: except netlib.exceptions.TcpException as e:
six.reraise(ProtocolException, ProtocolException(str(e)), sys.exc_info()[2]) six.reraise(exceptions.ProtocolException, exceptions.ProtocolException(str(e)), sys.exc_info()[2])
client_tls = is_tls_record_magic(d) client_tls = protocol.is_tls_record_magic(d)
# 1. check for --ignore # 1. check for --ignore
if self.config.check_ignore: if self.config.check_ignore:
ignore = self.config.check_ignore(top_layer.server_conn.address) ignore = self.config.check_ignore(top_layer.server_conn.address)
if not ignore and client_tls: if not ignore and client_tls:
try: try:
client_hello = TlsClientHello.from_client_conn(self.client_conn) client_hello = protocol.TlsClientHello.from_client_conn(self.client_conn)
except TlsProtocolException as e: except exceptions.TlsProtocolException as e:
self.log("Cannot parse Client Hello: %s" % repr(e), "error") self.log("Cannot parse Client Hello: %s" % repr(e), "error")
else: else:
ignore = self.config.check_ignore((client_hello.sni, 443)) ignore = self.config.check_ignore((client_hello.sni, 443))
if ignore: if ignore:
return RawTCPLayer(top_layer, ignore=True) return protocol.RawTCPLayer(top_layer, ignore=True)
# 2. Always insert a TLS layer, even if there's neither client nor server tls. # 2. Always insert a TLS layer, even if there's neither client nor server tls.
# An inline script may upgrade from http to https, # An inline script may upgrade from http to https,
# in which case we need some form of TLS layer. # in which case we need some form of TLS layer.
if isinstance(top_layer, ReverseProxy): if isinstance(top_layer, modes.ReverseProxy):
return TlsLayer(top_layer, client_tls, top_layer.server_tls) return protocol.TlsLayer(top_layer, client_tls, top_layer.server_tls)
if isinstance(top_layer, ServerConnectionMixin) or isinstance(top_layer, UpstreamConnectLayer): if isinstance(top_layer, protocol.ServerConnectionMixin) or isinstance(top_layer, protocol.UpstreamConnectLayer):
return TlsLayer(top_layer, client_tls, client_tls) return protocol.TlsLayer(top_layer, client_tls, client_tls)
# 3. In Http Proxy mode and Upstream Proxy mode, the next layer is fixed. # 3. In Http Proxy mode and Upstream Proxy mode, the next layer is fixed.
if isinstance(top_layer, TlsLayer): if isinstance(top_layer, protocol.TlsLayer):
if isinstance(top_layer.ctx, HttpProxy): if isinstance(top_layer.ctx, modes.HttpProxy):
return Http1Layer(top_layer, "regular") return protocol.Http1Layer(top_layer, "regular")
if isinstance(top_layer.ctx, HttpUpstreamProxy): if isinstance(top_layer.ctx, modes.HttpUpstreamProxy):
return Http1Layer(top_layer, "upstream") return protocol.Http1Layer(top_layer, "upstream")
# 4. Check for other TLS cases (e.g. after CONNECT). # 4. Check for other TLS cases (e.g. after CONNECT).
if client_tls: if client_tls:
return TlsLayer(top_layer, True, True) return protocol.TlsLayer(top_layer, True, True)
# 4. Check for --tcp # 4. Check for --tcp
if self.config.check_tcp(top_layer.server_conn.address): if self.config.check_tcp(top_layer.server_conn.address):
return RawTCPLayer(top_layer) return protocol.RawTCPLayer(top_layer)
# 5. Check for TLS ALPN (HTTP1/HTTP2) # 5. Check for TLS ALPN (HTTP1/HTTP2)
if isinstance(top_layer, TlsLayer): if isinstance(top_layer, protocol.TlsLayer):
alpn = top_layer.client_conn.get_alpn_proto_negotiated() alpn = top_layer.client_conn.get_alpn_proto_negotiated()
if alpn == b'h2': if alpn == b'h2':
return Http2Layer(top_layer, 'transparent') return protocol.Http2Layer(top_layer, 'transparent')
if alpn == b'http/1.1': if alpn == b'http/1.1':
return Http1Layer(top_layer, 'transparent') return protocol.Http1Layer(top_layer, 'transparent')
# 6. Check for raw tcp mode # 6. Check for raw tcp mode
is_ascii = ( is_ascii = (
@ -105,10 +103,10 @@ class RootContext(object):
all(65 <= x <= 90 and 97 <= x <= 122 for x in six.iterbytes(d)) all(65 <= x <= 90 and 97 <= x <= 122 for x in six.iterbytes(d))
) )
if self.config.rawtcp and not is_ascii: if self.config.rawtcp and not is_ascii:
return RawTCPLayer(top_layer) return protocol.RawTCPLayer(top_layer)
# 7. Assume HTTP1 by default # 7. Assume HTTP1 by default
return Http1Layer(top_layer, 'transparent') return protocol.Http1Layer(top_layer, 'transparent')
def log(self, msg, level, subs=()): def log(self, msg, level, subs=()):
""" """

View File

@ -1,17 +1,18 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
import traceback
import sys
import socket import socket
import sys
import traceback
import six import six
import netlib.exceptions
from mitmproxy import exceptions
from mitmproxy import models
from mitmproxy.proxy import modes
from mitmproxy.proxy import root_context
from netlib import tcp from netlib import tcp
from netlib.exceptions import TcpException from netlib.http import http1
from netlib.http.http1 import assemble_response
from ..exceptions import ProtocolException, ServerException, ClientHandshakeException, Kill
from ..models import ClientConnection, make_error_response
from .modes import HttpUpstreamProxy, HttpProxy, ReverseProxy, TransparentProxy, Socks5Proxy
from .root_context import RootContext, Log
class DummyServer: class DummyServer:
@ -43,8 +44,8 @@ class ProxyServer(tcp.TCPServer):
super(ProxyServer, self).__init__((config.host, config.port)) super(ProxyServer, self).__init__((config.host, config.port))
except socket.error as e: except socket.error as e:
six.reraise( six.reraise(
ServerException, exceptions.ServerException,
ServerException('Error starting proxy server: ' + repr(e)), exceptions.ServerException('Error starting proxy server: ' + repr(e)),
sys.exc_info()[2] sys.exc_info()[2]
) )
self.channel = None self.channel = None
@ -67,7 +68,7 @@ class ConnectionHandler(object):
def __init__(self, client_conn, client_address, config, channel): def __init__(self, client_conn, client_address, config, channel):
self.config = config self.config = config
"""@type: mitmproxy.proxy.config.ProxyConfig""" """@type: mitmproxy.proxy.config.ProxyConfig"""
self.client_conn = ClientConnection( self.client_conn = models.ClientConnection(
client_conn, client_conn,
client_address, client_address,
None) None)
@ -76,7 +77,7 @@ class ConnectionHandler(object):
"""@type: mitmproxy.controller.Channel""" """@type: mitmproxy.controller.Channel"""
def _create_root_layer(self): def _create_root_layer(self):
root_context = RootContext( root_ctx = root_context.RootContext(
self.client_conn, self.client_conn,
self.config, self.config,
self.channel self.channel
@ -84,25 +85,25 @@ class ConnectionHandler(object):
mode = self.config.mode mode = self.config.mode
if mode == "upstream": if mode == "upstream":
return HttpUpstreamProxy( return modes.HttpUpstreamProxy(
root_context, root_ctx,
self.config.upstream_server.address self.config.upstream_server.address
) )
elif mode == "transparent": elif mode == "transparent":
return TransparentProxy(root_context) return modes.TransparentProxy(root_ctx)
elif mode == "reverse": elif mode == "reverse":
server_tls = self.config.upstream_server.scheme == "https" server_tls = self.config.upstream_server.scheme == "https"
return ReverseProxy( return modes.ReverseProxy(
root_context, root_ctx,
self.config.upstream_server.address, self.config.upstream_server.address,
server_tls server_tls
) )
elif mode == "socks5": elif mode == "socks5":
return Socks5Proxy(root_context) return modes.Socks5Proxy(root_ctx)
elif mode == "regular": elif mode == "regular":
return HttpProxy(root_context) return modes.HttpProxy(root_ctx)
elif callable(mode): # pragma: no cover elif callable(mode): # pragma: no cover
return mode(root_context) return mode(root_ctx)
else: # pragma: no cover else: # pragma: no cover
raise ValueError("Unknown proxy mode: %s" % mode) raise ValueError("Unknown proxy mode: %s" % mode)
@ -114,11 +115,11 @@ class ConnectionHandler(object):
try: try:
root_layer = self.channel.ask("clientconnect", root_layer) root_layer = self.channel.ask("clientconnect", root_layer)
root_layer() root_layer()
except Kill: except exceptions.Kill:
self.log("Connection killed", "info") self.log("Connection killed", "info")
except ProtocolException as e: except exceptions.ProtocolException as e:
if isinstance(e, ClientHandshakeException): if isinstance(e, exceptions.ClientHandshakeException):
self.log( self.log(
"Client Handshake failed. " "Client Handshake failed. "
"The client may not trust the proxy's certificate for {}.".format(e.server), "The client may not trust the proxy's certificate for {}.".format(e.server),
@ -133,9 +134,9 @@ class ConnectionHandler(object):
# we send an HTTP error response, which is both # we send an HTTP error response, which is both
# understandable by HTTP clients and humans. # understandable by HTTP clients and humans.
try: try:
error_response = make_error_response(502, repr(e)) error_response = models.make_error_response(502, repr(e))
self.client_conn.send(assemble_response(error_response)) self.client_conn.send(http1.assemble_response(error_response))
except TcpException: except netlib.exceptions.TcpException:
pass pass
except Exception: except Exception:
self.log(traceback.format_exc(), "error") self.log(traceback.format_exc(), "error")
@ -149,4 +150,4 @@ class ConnectionHandler(object):
def log(self, msg, level): def log(self, msg, level):
msg = "{}: {}".format(repr(self.client_conn.address), msg) msg = "{}: {}".format(repr(self.client_conn.address), msg)
self.channel.tell("log", Log(msg, level)) self.channel.tell("log", root_context.Log(msg, level))

View File

@ -1,8 +1,8 @@
from . import reloader
from .concurrent import concurrent
from .script import Script from .script import Script
from .script_context import ScriptContext from .script_context import ScriptContext
from .concurrent import concurrent
from ..exceptions import ScriptException from ..exceptions import ScriptException
from . import reloader
__all__ = [ __all__ = [
"Script", "Script",

View File

@ -3,6 +3,7 @@ This module provides a @concurrent decorator primitive to
offload computations from mitmproxy's main master thread. offload computations from mitmproxy's main master thread.
""" """
from __future__ import absolute_import, print_function, division from __future__ import absolute_import, print_function, division
import threading import threading

View File

@ -1,6 +1,10 @@
from __future__ import absolute_import, print_function, division
import os import os
import sys import sys
from watchdog.events import RegexMatchingEventHandler from watchdog.events import RegexMatchingEventHandler
if sys.platform == 'darwin': # pragma: no cover if sys.platform == 'darwin': # pragma: no cover
from watchdog.observers.polling import PollingObserver as Observer from watchdog.observers.polling import PollingObserver as Observer
else: else:

View File

@ -4,13 +4,15 @@ Script objects know nothing about mitmproxy or mitmproxy's API - this knowledge
by the mitmproxy-specific ScriptContext. by the mitmproxy-specific ScriptContext.
""" """
# Do not import __future__ here, this would apply transitively to the inline scripts. # Do not import __future__ here, this would apply transitively to the inline scripts.
from __future__ import absolute_import, print_function, division
import os import os
import shlex import shlex
import sys import sys
import six import six
from ..exceptions import ScriptException from mitmproxy import exceptions
class Script(object): class Script(object):
@ -41,7 +43,7 @@ class Script(object):
@staticmethod @staticmethod
def parse_command(command): def parse_command(command):
if not command or not command.strip(): if not command or not command.strip():
raise ScriptException("Empty script command.") raise exceptions.ScriptException("Empty script command.")
# Windows: escape all backslashes in the path. # Windows: escape all backslashes in the path.
if os.name == "nt": # pragma: no cover if os.name == "nt": # pragma: no cover
backslashes = shlex.split(command, posix=False)[0].count("\\") backslashes = shlex.split(command, posix=False)[0].count("\\")
@ -49,13 +51,13 @@ class Script(object):
args = shlex.split(command) # pragma: no cover args = shlex.split(command) # pragma: no cover
args[0] = os.path.expanduser(args[0]) args[0] = os.path.expanduser(args[0])
if not os.path.exists(args[0]): if not os.path.exists(args[0]):
raise ScriptException( raise exceptions.ScriptException(
("Script file not found: %s.\r\n" ("Script file not found: %s.\r\n"
"If your script path contains spaces, " "If your script path contains spaces, "
"make sure to wrap it in additional quotes, e.g. -s \"'./foo bar/baz.py' --args\".") % "make sure to wrap it in additional quotes, e.g. -s \"'./foo bar/baz.py' --args\".") %
args[0]) args[0])
elif os.path.isdir(args[0]): elif os.path.isdir(args[0]):
raise ScriptException("Not a file: %s" % args[0]) raise exceptions.ScriptException("Not a file: %s" % args[0])
return args return args
def load(self): def load(self):
@ -69,7 +71,7 @@ class Script(object):
ScriptException on failure ScriptException on failure
""" """
if self.ns is not None: if self.ns is not None:
raise ScriptException("Script is already loaded") raise exceptions.ScriptException("Script is already loaded")
script_dir = os.path.dirname(os.path.abspath(self.args[0])) script_dir = os.path.dirname(os.path.abspath(self.args[0]))
self.ns = {'__file__': os.path.abspath(self.args[0])} self.ns = {'__file__': os.path.abspath(self.args[0])}
sys.path.append(script_dir) sys.path.append(script_dir)
@ -80,8 +82,8 @@ class Script(object):
exec(code, self.ns, self.ns) exec(code, self.ns, self.ns)
except Exception: except Exception:
six.reraise( six.reraise(
ScriptException, exceptions.ScriptException,
ScriptException.from_exception_context(), exceptions.ScriptException.from_exception_context(),
sys.exc_info()[2] sys.exc_info()[2]
) )
finally: finally:
@ -107,15 +109,15 @@ class Script(object):
ScriptException if there was an exception. ScriptException if there was an exception.
""" """
if self.ns is None: if self.ns is None:
raise ScriptException("Script not loaded.") raise exceptions.ScriptException("Script not loaded.")
f = self.ns.get(name) f = self.ns.get(name)
if f: if f:
try: try:
return f(self.ctx, *args, **kwargs) return f(self.ctx, *args, **kwargs)
except Exception: except Exception:
six.reraise( six.reraise(
ScriptException, exceptions.ScriptException,
ScriptException.from_exception_context(), exceptions.ScriptException.from_exception_context(),
sys.exc_info()[2] sys.exc_info()[2]
) )
else: else:

View File

@ -2,7 +2,8 @@
The mitmproxy script context provides an API to inline scripts. The mitmproxy script context provides an API to inline scripts.
""" """
from __future__ import absolute_import, print_function, division from __future__ import absolute_import, print_function, division
from .. import contentviews
from mitmproxy import contentviews
class ScriptContext(object): class ScriptContext(object):

View File

@ -1,7 +1,8 @@
from __future__ import absolute_import from __future__ import absolute_import, print_function, division
import six import six
from typing import List, Any from typing import Any
from typing import List
import netlib.basetypes import netlib.basetypes

View File

@ -67,9 +67,10 @@ like so::
u'\u03b1' u'\u03b1'
""" """
import six
from collections import deque from collections import deque
import six
__ver_major__ = 0 __ver_major__ = 0
__ver_minor__ = 2 __ver_minor__ = 2
__ver_patch__ = 0 __ver_patch__ = 0

View File

@ -1,7 +1,8 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
import datetime import datetime
import time
import json import json
import time
import netlib.utils import netlib.utils

View File

@ -1,4 +1,4 @@
from __future__ import (absolute_import, print_function, division) from __future__ import absolute_import, print_function, division
from netlib.version import VERSION, IVERSION from netlib.version import VERSION, IVERSION

View File

@ -1,14 +1,16 @@
from __future__ import absolute_import, print_function from __future__ import absolute_import, print_function, division
import collections import collections
import tornado.ioloop
import tornado.httpserver
import sys import sys
from netlib.http import authentication import tornado.httpserver
import tornado.ioloop
from .. import flow, controller from mitmproxy import controller
from ..exceptions import FlowReadException from mitmproxy import exceptions
from . import app from mitmproxy import flow
from mitmproxy.web import app
from netlib.http import authentication
class Stop(Exception): class Stop(Exception):
@ -156,7 +158,7 @@ class WebMaster(flow.FlowMaster):
if options.rfile: if options.rfile:
try: try:
self.load_flows_file(options.rfile) self.load_flows_file(options.rfile)
except FlowReadException as v: except exceptions.FlowReadException as v:
self.add_event( self.add_event(
"Could not read flow file: %s" % v, "Could not read flow file: %s" % v,
"error" "error"

View File

@ -1,14 +1,16 @@
from __future__ import absolute_import, print_function, division
import base64
import json
import logging
import os.path import os.path
import re import re
import six import six
import tornado.web
import tornado.websocket import tornado.websocket
import logging
import json
import base64
from .. import version, filt from mitmproxy import filt
from mitmproxy import version
def _strip_content(flow_state): def _strip_content(flow_state):

View File

@ -1,6 +1,8 @@
from __future__ import absolute_import, print_function, division from __future__ import absolute_import, print_function, division
from .connections import HTTP2Protocol from .connections import HTTP2Protocol
from netlib.http.http2 import framereader
__all__ = [ __all__ = [
"HTTP2Protocol" "HTTP2Protocol",
"framereader",
] ]