mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-27 02:24:18 +00:00
parent
049d253a83
commit
eb2264e91a
@ -27,7 +27,7 @@ import six
|
|||||||
|
|
||||||
from netlib.odict import ODict
|
from netlib.odict import ODict
|
||||||
from netlib import encoding
|
from netlib import encoding
|
||||||
import netlib.utils
|
from netlib.utils import clean_bin, hexdump, urldecode, multipartdecode, parse_content_type
|
||||||
|
|
||||||
from . import utils
|
from . import utils
|
||||||
from .exceptions import ContentViewException
|
from .exceptions import ContentViewException
|
||||||
@ -121,12 +121,14 @@ class ViewAuto(View):
|
|||||||
headers = metadata.get("headers", {})
|
headers = metadata.get("headers", {})
|
||||||
ctype = headers.get("content-type")
|
ctype = headers.get("content-type")
|
||||||
if ctype:
|
if ctype:
|
||||||
ct = netlib.utils.parse_content_type(ctype) if ctype else None
|
ct = parse_content_type(ctype) if ctype else None
|
||||||
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 utils.isXML(data):
|
||||||
return get("XML")(data, **metadata)
|
return get("XML")(data, **metadata)
|
||||||
|
if utils.isMostlyBin(data):
|
||||||
|
return get("Hex")(data)
|
||||||
return get("Raw")(data)
|
return get("Raw")(data)
|
||||||
|
|
||||||
|
|
||||||
@ -146,7 +148,7 @@ class ViewHex(View):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _format(data):
|
def _format(data):
|
||||||
for offset, hexa, s in netlib.utils.hexdump(data):
|
for offset, hexa, s in hexdump(data):
|
||||||
yield [
|
yield [
|
||||||
("offset", offset + " "),
|
("offset", offset + " "),
|
||||||
("text", hexa + " "),
|
("text", hexa + " "),
|
||||||
@ -251,7 +253,7 @@ class ViewURLEncoded(View):
|
|||||||
content_types = ["application/x-www-form-urlencoded"]
|
content_types = ["application/x-www-form-urlencoded"]
|
||||||
|
|
||||||
def __call__(self, data, **metadata):
|
def __call__(self, data, **metadata):
|
||||||
d = netlib.utils.urldecode(data)
|
d = urldecode(data)
|
||||||
return "URLEncoded form", format_dict(ODict(d))
|
return "URLEncoded form", format_dict(ODict(d))
|
||||||
|
|
||||||
|
|
||||||
@ -268,7 +270,7 @@ class ViewMultipart(View):
|
|||||||
|
|
||||||
def __call__(self, data, **metadata):
|
def __call__(self, data, **metadata):
|
||||||
headers = metadata.get("headers", {})
|
headers = metadata.get("headers", {})
|
||||||
v = netlib.utils.multipartdecode(headers, data)
|
v = multipartdecode(headers, data)
|
||||||
if v:
|
if v:
|
||||||
return "Multipart form", self._format(v)
|
return "Multipart form", self._format(v)
|
||||||
|
|
||||||
@ -519,6 +521,21 @@ def get(name):
|
|||||||
return i
|
return i
|
||||||
|
|
||||||
|
|
||||||
|
def safe_to_print(lines, encoding="utf8"):
|
||||||
|
"""
|
||||||
|
Wraps a content generator so that each text portion is a *safe to print* unicode string.
|
||||||
|
"""
|
||||||
|
for line in lines:
|
||||||
|
clean_line = []
|
||||||
|
for (style, text) in line:
|
||||||
|
try:
|
||||||
|
text = clean_bin(text.decode(encoding, "strict"))
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
text = clean_bin(text).decode(encoding, "strict")
|
||||||
|
clean_line.append((style, text))
|
||||||
|
yield clean_line
|
||||||
|
|
||||||
|
|
||||||
def get_content_view(viewmode, data, **metadata):
|
def get_content_view(viewmode, data, **metadata):
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
@ -527,6 +544,7 @@ def get_content_view(viewmode, data, **metadata):
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A (description, content generator) tuple.
|
A (description, content generator) tuple.
|
||||||
|
In contrast to calling the views directly, text is always safe-to-print unicode.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ContentViewException, if the content view threw an error.
|
ContentViewException, if the content view threw an error.
|
||||||
@ -556,4 +574,4 @@ def get_content_view(viewmode, data, **metadata):
|
|||||||
msg.append("Couldn't parse: falling back to Raw")
|
msg.append("Couldn't parse: falling back to Raw")
|
||||||
else:
|
else:
|
||||||
msg.append(ret[0])
|
msg.append(ret[0])
|
||||||
return " ".join(msg), ret[1]
|
return " ".join(msg), safe_to_print(ret[1])
|
||||||
|
@ -57,12 +57,8 @@ class Options(object):
|
|||||||
setattr(self, i, None)
|
setattr(self, i, None)
|
||||||
|
|
||||||
|
|
||||||
_contentview_auto = contentviews.get("Auto")
|
|
||||||
_contentview_raw = contentviews.get("Raw")
|
|
||||||
|
|
||||||
|
|
||||||
class DumpMaster(flow.FlowMaster):
|
class DumpMaster(flow.FlowMaster):
|
||||||
def __init__(self, server, options, outfile=sys.stdout):
|
def __init__(self, server, options, outfile=None):
|
||||||
flow.FlowMaster.__init__(self, server, flow.State())
|
flow.FlowMaster.__init__(self, server, flow.State())
|
||||||
self.outfile = outfile
|
self.outfile = outfile
|
||||||
self.o = options
|
self.o = options
|
||||||
@ -91,7 +87,7 @@ class DumpMaster(flow.FlowMaster):
|
|||||||
if options.outfile:
|
if options.outfile:
|
||||||
path = os.path.expanduser(options.outfile[0])
|
path = os.path.expanduser(options.outfile[0])
|
||||||
try:
|
try:
|
||||||
f = file(path, options.outfile[1])
|
f = open(path, options.outfile[1])
|
||||||
self.start_stream(f, self.filt)
|
self.start_stream(f, self.filt)
|
||||||
except IOError as v:
|
except IOError as v:
|
||||||
raise DumpError(v.strerror)
|
raise DumpError(v.strerror)
|
||||||
@ -185,7 +181,7 @@ class DumpMaster(flow.FlowMaster):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
type, lines = contentviews.get_content_view(
|
type, lines = contentviews.get_content_view(
|
||||||
_contentview_auto,
|
contentviews.get("Auto"),
|
||||||
message.body,
|
message.body,
|
||||||
headers=message.headers
|
headers=message.headers
|
||||||
)
|
)
|
||||||
@ -193,7 +189,7 @@ class DumpMaster(flow.FlowMaster):
|
|||||||
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(
|
||||||
_contentview_raw,
|
contentviews.get("Raw"),
|
||||||
message.body,
|
message.body,
|
||||||
headers=message.headers
|
headers=message.headers
|
||||||
)
|
)
|
||||||
@ -206,17 +202,19 @@ class DumpMaster(flow.FlowMaster):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def colorful(line):
|
def colorful(line):
|
||||||
yield " " # we can already indent here
|
yield u" " # we can already indent here
|
||||||
for (style, text) in line:
|
for (style, text) in line:
|
||||||
yield click.style(text, **styles.get(style, {}))
|
yield click.style(text, **styles.get(style, {}))
|
||||||
|
|
||||||
if self.o.flow_detail == 3:
|
if self.o.flow_detail == 3:
|
||||||
lines_to_echo = itertools.islice(lines, contentviews.VIEW_CUTOFF)
|
lines_to_echo = itertools.islice(lines, 70)
|
||||||
else:
|
else:
|
||||||
lines_to_echo = lines
|
lines_to_echo = lines
|
||||||
|
|
||||||
content = "\r\n".join(
|
lines_to_echo = list(lines_to_echo)
|
||||||
"".join(colorful(line)) for line in lines_to_echo
|
|
||||||
|
content = u"\r\n".join(
|
||||||
|
u"".join(colorful(line)) for line in lines_to_echo
|
||||||
)
|
)
|
||||||
|
|
||||||
self.echo(content)
|
self.echo(content)
|
||||||
@ -302,6 +300,7 @@ class DumpMaster(flow.FlowMaster):
|
|||||||
if f.error:
|
if f.error:
|
||||||
self.echo(" << {}".format(f.error.msg), bold=True, fg="red")
|
self.echo(" << {}".format(f.error.msg), bold=True, fg="red")
|
||||||
|
|
||||||
|
if self.outfile:
|
||||||
self.outfile.flush()
|
self.outfile.flush()
|
||||||
|
|
||||||
def _process_flow(self, f):
|
def _process_flow(self, f):
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from __future__ import (absolute_import, print_function, division)
|
from __future__ import (absolute_import, print_function, division)
|
||||||
import itertools
|
import itertools
|
||||||
import sys
|
import sys
|
||||||
|
import traceback
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
@ -384,9 +385,13 @@ class HttpLayer(Layer):
|
|||||||
return
|
return
|
||||||
|
|
||||||
except (HttpErrorConnClosed, NetLibError, HttpError, ProtocolException) as e:
|
except (HttpErrorConnClosed, NetLibError, HttpError, ProtocolException) as e:
|
||||||
|
error_propagated = False
|
||||||
if flow.request and not flow.response:
|
if flow.request and not flow.response:
|
||||||
flow.error = Error(repr(e))
|
flow.error = Error(str(e))
|
||||||
self.channel.ask("error", flow)
|
self.channel.ask("error", flow)
|
||||||
|
self.log(traceback.format_exc(), "debug")
|
||||||
|
error_propagated = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.send_response(make_error_response(
|
self.send_response(make_error_response(
|
||||||
getattr(e, "code", 502),
|
getattr(e, "code", 502),
|
||||||
@ -394,6 +399,8 @@ class HttpLayer(Layer):
|
|||||||
))
|
))
|
||||||
except NetLibError:
|
except NetLibError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if not error_propagated:
|
||||||
if isinstance(e, ProtocolException):
|
if isinstance(e, ProtocolException):
|
||||||
six.reraise(ProtocolException, e, sys.exc_info()[2])
|
six.reraise(ProtocolException, e, sys.exc_info()[2])
|
||||||
else:
|
else:
|
||||||
|
@ -7,7 +7,7 @@ import sys
|
|||||||
from OpenSSL import SSL
|
from OpenSSL import SSL
|
||||||
|
|
||||||
from netlib.tcp import NetLibError, ssl_read_select
|
from netlib.tcp import NetLibError, ssl_read_select
|
||||||
from netlib.utils import cleanBin
|
from netlib.utils import clean_bin
|
||||||
from ..exceptions import ProtocolException
|
from ..exceptions import ProtocolException
|
||||||
from .base import Layer
|
from .base import Layer
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ class RawTCPLayer(Layer):
|
|||||||
direction = "-> tcp -> {}".format(repr(self.server_conn.address))
|
direction = "-> tcp -> {}".format(repr(self.server_conn.address))
|
||||||
else:
|
else:
|
||||||
direction = "<- tcp <- {}".format(repr(self.server_conn.address))
|
direction = "<- tcp <- {}".format(repr(self.server_conn.address))
|
||||||
data = cleanBin(buf[:size].tobytes())
|
data = clean_bin(buf[:size].tobytes())
|
||||||
self.log(
|
self.log(
|
||||||
"{}\r\n{}".format(direction, data),
|
"{}\r\n{}".format(direction, data),
|
||||||
"info"
|
"info"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from __future__ import absolute_import
|
from __future__ import (absolute_import, print_function, division)
|
||||||
import os
|
import os
|
||||||
import datetime
|
import datetime
|
||||||
import re
|
import re
|
||||||
@ -30,15 +30,16 @@ def isBin(s):
|
|||||||
"""
|
"""
|
||||||
for i in s:
|
for i in s:
|
||||||
i = ord(i)
|
i = ord(i)
|
||||||
if i < 9:
|
if i < 9 or 13 < i < 32 or 126 < i:
|
||||||
return True
|
|
||||||
elif i > 13 and i < 32:
|
|
||||||
return True
|
|
||||||
elif i > 126:
|
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def isMostlyBin(s):
|
||||||
|
s = s[:100]
|
||||||
|
return sum(isBin(ch) for ch in s)/len(s) > 0.3
|
||||||
|
|
||||||
|
|
||||||
def isXML(s):
|
def isXML(s):
|
||||||
for i in s:
|
for i in s:
|
||||||
if i in "\n \t":
|
if i in "\n \t":
|
||||||
|
Loading…
Reference in New Issue
Block a user