import argparse import crochet import pappyproxy import shlex import sys from pappyproxy.colors import Colors, Styles, path_formatter, host_color, scode_color, verb_color from pappyproxy.util import PappyException, remove_color, confirm, load_reqlist, Capturing from pappyproxy.macros import InterceptMacro from pappyproxy.requestcache import RequestCache from pappyproxy.session import Session from pappyproxy.pappy import session from pappyproxy.plugin import add_intercepting_macro, remove_intercepting_macro, add_to_history from pappyproxy.http import async_submit_requests, Request from twisted.internet import defer from twisted.enterprise import adbapi class PrintStreamInterceptMacro(InterceptMacro): """ Intercepting macro that prints requests and responses as they go through the proxy """ def __init__(self): InterceptMacro.__init__(self) self.name = 'Pappy Interceptor Macro' self.intercept_requests = False self.intercept_responses = False self.async_req = False self.async_rsp = False def __repr__(self): return "" @staticmethod def _print_request(req): s = verb_color(req.verb)+'> '+req.verb+' '+Colors.ENDC s += req.url_color s += ', len=' + str(len(req.body)) print s sys.stdout.flush() @staticmethod def _print_response(req): response_code = str(req.response.response_code) + \ ' ' + req.response.response_text s = scode_color(response_code) s += '< ' s += response_code s += Colors.ENDC s += ' ' s += req.url_color s += ', len=' + str(len(req.response.body)) print s sys.stdout.flush() def mangle_request(self, request): PrintStreamInterceptMacro._print_request(request) return request def mangle_response(self, request): PrintStreamInterceptMacro._print_response(request) return request.response @crochet.wait_for(timeout=None) @defer.inlineCallbacks def clrmem(line): """ Delete all in-memory only requests Usage: clrmem """ to_delete = list(pappyproxy.http.Request.cache.inmem_reqs) for r in to_delete: try: yield r.deep_delete() except PappyException as e: print str(e) def gencerts(line): """ Generate CA cert and private CA file Usage: gencerts [/path/to/put/certs/in] """ dest_dir = line or pappyproxy.pappy.session.config.cert_dir message = "This will overwrite any existing certs in %s. Are you sure?" % dest_dir if not confirm(message, 'n'): return False print "Generating certs to %s" % dest_dir pappyproxy.proxy.generate_ca_certs(dest_dir) def log(line): """ Display the log in real time. Honestly it probably doesn't work. Usage: log [verbosity (default is 1)] verbosity=1: Show connections as they're made/lost, some additional info verbosity=3: Show full requests/responses as they are processed by the proxy """ try: verbosity = int(line.strip()) except: verbosity = 1 pappyproxy.pappy.session.config.debug_verbosity = verbosity raw_input() pappyproxy.pappy.session.config.debug_verbosity = 0 @crochet.wait_for(timeout=None) @defer.inlineCallbacks def save(line): args = shlex.split(line) reqids = args[0] reqs = yield load_reqlist(reqids) for req in reqs: yield req.async_deep_save() @crochet.wait_for(timeout=None) @defer.inlineCallbacks def export(line): """ Write the full request/response of a request/response to a file. Usage: export [req|rsp] """ args = shlex.split(line) if len(args) < 2: print 'Requires req/rsp and and request id(s)' defer.returnValue(None) if args[0] not in ('req', 'rsp'): raise PappyException('Request or response not specified') reqs = yield load_reqlist(args[1]) for req in reqs: try: if args[0] == 'req': fname = 'req_%s.txt'%req.reqid with open(fname, 'w') as f: f.write(req.full_request) print 'Full request written to %s' % fname elif args[0] == 'rsp': fname = 'rsp_%s.txt'%req.reqid with open(fname, 'w') as f: f.write(req.full_response) print 'Full response written to %s' % fname except PappyException as e: print 'Unable to export %s: %s' % (req.reqid, e) @crochet.wait_for(timeout=None) @defer.inlineCallbacks def merge_datafile(line): """ Add all the requests/responses from another data file to the current one """ def set_text_factory(conn): conn.text_factory = str line = line.strip() other_dbpool = adbapi.ConnectionPool("sqlite3", line, check_same_thread=False, cp_openfun=set_text_factory, cp_max=1) try: count = 0 other_cache = RequestCache(cust_dbpool=other_dbpool) yield other_cache.load_ids() for req_d in other_cache.req_it(): count += 1 req = yield req_d r = req.copy() yield r.async_deep_save() print 'Added %d requests' % count finally: other_dbpool.close() def watch_proxy(line): print 'Watching proxy... press ENTER to exit' macro = PrintStreamInterceptMacro() macro.intercept_requests = True macro.intercept_responses = True try: add_intercepting_macro('pappy_watch_proxy', macro) raw_input() finally: try: remove_intercepting_macro('pappy_watch_proxy') except PappyException: pass def run_without_color(line): with Capturing() as output: session.cons.onecmd(line.strip()) print remove_color(output.val) def version(line): import pappyproxy print pappyproxy.__version__ @crochet.wait_for(timeout=180.0) @defer.inlineCallbacks def submit(line): """ Resubmit some requests, optionally with modified headers and cookies. Usage: submit reqids [-h] [-m] [-u] [-p] [-c [COOKIES [COOKIES ...]]] [-d [HEADERS [HEADERS ...]]] """ parser = argparse.ArgumentParser(prog="submit", usage=submit.__doc__) parser.add_argument('reqids') parser.add_argument('-m', '--inmem', action='store_true', help='Store resubmitted requests in memory without storing them in the data file') parser.add_argument('-u', '--unique', action='store_true', help='Only resubmit one request per endpoint (different URL parameters are different endpoints)') parser.add_argument('-p', '--uniquepath', action='store_true', help='Only resubmit one request per endpoint (ignoring URL parameters)') parser.add_argument('-c', '--cookies', nargs='*', help='Apply a cookie to requests before submitting') parser.add_argument('-d', '--headers', nargs='*', help='Apply a header to requests before submitting') args = parser.parse_args(shlex.split(line)) headers = {} cookies = {} if args.headers: for h in args.headers: k, v = h.split('=', 1) headers[k] = v if args.cookies: for c in args.cookies: k, v = c.split('=', 1) cookies[k] = v if args.unique and args.uniquepath: raise PappyException('Both -u and -p cannot be given as arguments') newsession = Session(cookie_vals=cookies, header_vals=headers) reqs = yield load_reqlist(args.reqids) if args.unique or args.uniquepath: endpoints = set() new_reqs = [] for r in reqs: if args.unique: s = r.url else: s = r.path if not s in endpoints: new_reqs.append(r.copy()) endpoints.add(s) reqs = new_reqs else: reqs = [r.copy() for r in reqs] for req in reqs: newsession.apply_req(req) conf_message = "You're about to submit %d requests, continue?" % len(reqs) if not confirm(conf_message): defer.returnValue(None) for r in reqs: r.tags.add('resubmitted') if args.inmem: yield async_submit_requests(reqs) for req in reqs: add_to_history(req) else: yield async_submit_requests(reqs, save=True) def load_cmds(cmd): cmd.set_cmds({ 'clrmem': (clrmem, None), 'gencerts': (gencerts, None), 'sv': (save, None), 'export': (export, None), 'log': (log, None), 'merge': (merge_datafile, None), 'nocolor': (run_without_color, None), 'watch': (watch_proxy, None), 'version': (version, None), 'submit': (submit, None) }) cmd.add_aliases([ #('rpy', ''), ])