mitmproxy/libpathod/pathoc.py

228 lines
8.1 KiB
Python
Raw Normal View History

2014-10-24 01:01:34 +00:00
import sys
import os
2014-03-07 02:21:34 +00:00
from netlib import tcp, http, certutils
import netlib.utils
2012-10-04 21:30:32 +00:00
import language, utils
import OpenSSL.crypto
class PathocError(Exception): pass
class SSLInfo:
def __init__(self, certchain, cipher):
self.certchain, self.cipher = certchain, cipher
class Response:
def __init__(self, httpversion, status_code, msg, headers, content, sslinfo):
self.httpversion, self.status_code, self.msg = httpversion, status_code, msg
self.headers, self.content = headers, content
self.sslinfo = sslinfo
def __repr__(self):
return "Response(%s - %s)"%(self.status_code, self.msg)
class Pathoc(tcp.TCPClient):
2014-03-02 02:13:56 +00:00
def __init__(self, address, ssl=None, sni=None, sslversion=4, clientcert=None, ciphers=None):
tcp.TCPClient.__init__(self, address)
self.settings = dict(
staticdir = os.getcwd(),
unconstrained_file_access = True,
)
2013-01-03 21:37:26 +00:00
self.ssl, self.sni = ssl, sni
self.clientcert = clientcert
self.sslversion = utils.SSLVERSIONS[sslversion]
self.ciphers = ciphers
2013-01-03 21:37:26 +00:00
def http_connect(self, connect_to):
self.wfile.write(
'CONNECT %s:%s HTTP/1.1\r\n'%tuple(connect_to) +
'\r\n'
)
self.wfile.flush()
l = self.rfile.readline()
if not l:
raise PathocError("Proxy CONNECT failed")
parsed = http.parse_response_line(l)
if not parsed[1] == 200:
raise PathocError("Proxy CONNECT failed: %s - %s"%(parsed[1], parsed[2]))
http.read_headers(self.rfile)
def connect(self, connect_to=None):
"""
connect_to: A (host, port) tuple, which will be connected to with an
HTTP CONNECT request.
"""
2013-01-03 21:37:26 +00:00
tcp.TCPClient.connect(self)
if connect_to:
self.http_connect(connect_to)
self.sslinfo = None
2013-01-03 21:37:26 +00:00
if self.ssl:
try:
self.convert_to_ssl(
sni=self.sni,
cert=self.clientcert,
method=self.sslversion,
cipher_list = self.ciphers
)
2013-01-03 21:37:26 +00:00
except tcp.NetLibError, v:
raise PathocError(str(v))
self.sslinfo = SSLInfo(
self.connection.get_peer_cert_chain(),
self.get_current_cipher()
)
def request(self, spec):
"""
Return an (httpversion, code, msg, headers, content) tuple.
2012-10-04 21:30:32 +00:00
May raise language.ParseException, netlib.http.HttpError or
language.FileAccessDenied.
"""
2012-10-04 21:30:32 +00:00
r = language.parse_request(self.settings, spec)
language.serve(r, self.wfile, self.settings, self.address.host)
self.wfile.flush()
2014-07-21 12:08:09 +00:00
ret = list(http.read_response(self.rfile, r.method.string(), None))
ret.append(self.sslinfo)
return Response(*ret)
def _show_summary(self, fp, httpversion, code, msg, headers, content):
print >> fp, "<< %s %s: %s bytes"%(code, utils.xrepr(msg), len(content))
def _show(self, fp, header, data, hexdump):
if hexdump:
print >> fp, "%s (hex dump):"%header
for line in netlib.utils.hexdump(data):
print >> fp, "\t%s %s %s"%line
else:
print >> fp, "%s (unprintables escaped):"%header
print >> fp, netlib.utils.cleanBin(data)
def print_request(self, spec, showreq, showresp, explain, showssl, hexdump, ignorecodes, ignoretimeout, fp=sys.stdout):
"""
Performs a series of requests, and prints results to the specified
file descriptor.
spec: A request specification
showreq: Print requests
showresp: Print responses
explain: Print request explanation
showssl: Print info on SSL connection
hexdump: When printing requests or responses, use hex dump output
ignorecodes: Sequence of return codes to ignore
Returns True if we have a non-ignored response.
"""
try:
2012-10-04 21:30:32 +00:00
r = language.parse_request(self.settings, spec)
except language.ParseException, v:
print >> fp, "Error parsing request spec: %s"%v.msg
print >> fp, v.marked()
return
2012-10-04 21:30:32 +00:00
except language.FileAccessDenied, v:
print >> fp, "File access error: %s"%v
return
if explain:
r = r.freeze(self.settings, self.address.host)
resp, req = None, None
if showreq:
self.wfile.start_log()
2012-10-04 21:30:32 +00:00
if showresp:
self.rfile.start_log()
try:
req = language.serve(r, self.wfile, self.settings, self.address.host)
self.wfile.flush()
2014-07-21 12:08:09 +00:00
resp = http.read_response(self.rfile, r.method.string(), None)
except http.HttpError, v:
2014-07-21 12:08:09 +00:00
print >> fp, "<< HTTP Error:", v.message
except tcp.NetLibTimeout:
if ignoretimeout:
return
print >> fp, "<<", "Timeout"
except tcp.NetLibDisconnect: # pragma: nocover
print >> fp, "<<", "Disconnect"
if req:
if ignorecodes and resp and resp[1] in ignorecodes:
return
if explain:
print >> fp, ">> Spec:", r.spec()
if showreq:
self._show(fp, ">> Request", self.wfile.get_log(), hexdump)
if showresp:
self._show(fp, "<< Response", self.rfile.get_log(), hexdump)
else:
if resp:
self._show_summary(fp, *resp)
if self.sslinfo:
print >> fp, "Cipher: %s, %s bit, %s"%self.sslinfo.cipher
print >> fp, "SSL certificate chain:\n"
for i in self.sslinfo.certchain:
print >> fp, "\tSubject: ",
for cn in i.get_subject().get_components():
print >> fp, "%s=%s"%cn,
print >> fp
print >> fp, "\tIssuer: ",
for cn in i.get_issuer().get_components():
print >> fp, "%s=%s"%cn,
print >> fp
print >> fp, "\tVersion: %s"%i.get_version()
2014-10-24 01:01:34 +00:00
print >> fp, "\tValidity: %s - %s"%(i.get_notBefore(),i.get_notAfter())
print >> fp, "\tSerial: %s"%i.get_serial_number()
print >> fp, "\tAlgorithm: %s"%i.get_signature_algorithm()
pk = i.get_pubkey()
types = {
OpenSSL.crypto.TYPE_RSA: "RSA",
OpenSSL.crypto.TYPE_DSA: "DSA"
}
t = types.get(pk.type(), "Uknown")
print >> fp, "\tPubkey: %s bit %s"%(pk.bits(), t)
2014-03-07 02:21:34 +00:00
s = certutils.SSLCert(i)
if s.altnames:
print >> fp, "\tSANs:", " ".join(s.altnames)
print >> fp
return True
2014-10-24 04:12:54 +00:00
def main(args):
try:
for i in range(args.repeat):
p = Pathoc(
(args.host, args.port),
ssl=args.ssl,
sni=args.sni,
sslversion=args.sslversion,
clientcert=args.clientcert,
ciphers=args.ciphers
)
try:
p.connect(args.connect_to)
except (tcp.NetLibError, PathocError), v:
print >> sys.stderr, str(v)
sys.exit(1)
if args.timeout:
p.settimeout(args.timeout)
for spec in args.request:
ret = p.print_request(
spec,
showreq=args.showreq,
showresp=args.showresp,
explain=args.explain,
showssl=args.showssl,
hexdump=args.hexdump,
ignorecodes=args.ignorecodes,
ignoretimeout=args.ignoretimeout
)
sys.stdout.flush()
if ret and args.oneshot:
sys.exit(0)
except KeyboardInterrupt:
pass