def configure(self, updated): opts = ctx.options if opts.add_upstream_certs_to_client_chain and not opts.upstream_cert: raise exceptions.OptionsError( "The no-upstream-cert and add-upstream-certs-to-client-chain " "options are mutually exclusive. If no-upstream-cert is enabled " "then the upstream certificate is not retrieved before generating " "the client certificate chain." ) if "body_size_limit" in updated and opts.body_size_limit: try: opts._processed["body_size_limit"] = human.parse_size( opts.body_size_limit ) except ValueError as e: raise exceptions.OptionsError( "Invalid body size limit specification: %s" % opts.body_size_limit ) if "mode" in updated: mode = opts.mode if mode.startswith("reverse:") or mode.startswith("upstream:"): try: server_spec.parse_with_mode(mode) except ValueError as e: raise exceptions.OptionsError(str(e)) from e elif mode == "transparent": if not platform.original_addr: raise exceptions.OptionsError( "Transparent mode not supported on this platform." ) elif mode not in ["regular", "socks5"]: raise exceptions.OptionsError( "Invalid mode specification: %s" % mode )
def configure(self, updated): opts = ctx.options if opts.add_upstream_certs_to_client_chain and not opts.upstream_cert: raise exceptions.OptionsError( "The no-upstream-cert and add-upstream-certs-to-client-chain " "options are mutually exclusive. If no-upstream-cert is enabled " "then the upstream certificate is not retrieved before generating " "the client certificate chain." ) if opts.add_upstream_certs_to_client_chain and not opts.ssl_insecure: raise exceptions.OptionsError( "The verify-upstream-cert requires certificate verification to be disabled. " "If upstream certificates are verified then extra upstream certificates are " "not available for inclusion to the client chain." ) if "body_size_limit" in updated: try: human.parse_size(opts.body_size_limit) except ValueError as e: raise exceptions.OptionsError( "Invalid body size limit specification: %s" % opts.body_size_limit ) if "mode" in updated: mode = opts.mode if mode.startswith("reverse:") or mode.startswith("upstream:"): try: server_spec.parse_with_mode(mode) except ValueError as e: raise exceptions.OptionsError(str(e)) from e elif mode == "transparent": if not platform.original_addr: raise exceptions.OptionsError( "Transparent mode not supported on this platform." ) elif mode not in ["regular", "socks5"]: raise exceptions.OptionsError( "Invalid mode specification: %s" % mode ) if "client_certs" in updated: if opts.client_certs: client_certs = os.path.expanduser(opts.client_certs) if not os.path.exists(client_certs): raise exceptions.OptionsError( "Client certificate path does not exist: {}".format(opts.client_certs) )
def _change_reverse_host(self, f): """ When we load flows in reverse proxy mode, we adjust the target host to the reverse proxy destination for all flows we load. This makes it very easy to replay saved flows against a different host. """ if self.options.mode.startswith("reverse:"): _, upstream_spec = server_spec.parse_with_mode(self.options.mode) f.request.host, f.request.port = upstream_spec.address f.request.scheme = upstream_spec.scheme
def _change_reverse_host(self, f): """ When we load flows in reverse proxy mode, we adjust the target host to the reverse proxy destination for all flows we load. This makes it very easy to replay saved flows against a different host. """ if self.options.mode.startswith("reverse:"): _, upstream_spec = server_spec.parse_with_mode(self.options.mode) f.request.host, f.request.port = upstream_spec.address f.request.scheme = upstream_spec.scheme
def configure(self, options: moptions.Options, updated: Any) -> None: if options.add_upstream_certs_to_client_chain and not options.ssl_insecure: raise exceptions.OptionsError( "The verify-upstream-cert requires certificate verification to be disabled. " "If upstream certificates are verified then extra upstream certificates are " "not available for inclusion to the client chain.") if options.ssl_insecure: self.openssl_verification_mode_server = SSL.VERIFY_NONE else: self.openssl_verification_mode_server = SSL.VERIFY_PEER self.check_ignore = HostMatcher(options.ignore_hosts) self.check_tcp = HostMatcher(options.tcp_hosts) self.openssl_method_client, self.openssl_options_client = \ tcp.sslversion_choices[options.ssl_version_client] self.openssl_method_server, self.openssl_options_server = \ tcp.sslversion_choices[options.ssl_version_server] certstore_path = os.path.expanduser(options.cadir) if not os.path.exists(os.path.dirname(certstore_path)): raise exceptions.OptionsError( "Certificate Authority parent directory does not exist: %s" % os.path.dirname(options.cadir)) self.certstore = certs.CertStore.from_store(certstore_path, CONF_BASENAME) if options.client_certs: client_certs = os.path.expanduser(options.client_certs) if not os.path.exists(client_certs): raise exceptions.OptionsError( "Client certificate path does not exist: %s" % options.client_certs) self.client_certs = client_certs for c in options.certs: parts = c.split("=", 1) if len(parts) == 1: parts = ["*", parts[0]] cert = os.path.expanduser(parts[1]) if not os.path.exists(cert): raise exceptions.OptionsError( "Certificate file does not exist: %s" % cert) try: self.certstore.add_cert_file(parts[0], cert) except crypto.Error: raise exceptions.OptionsError( "Invalid certificate format: %s" % cert) m = options.mode if m.startswith("upstream:") or m.startswith("reverse:"): _, spec = server_spec.parse_with_mode(options.mode) self.upstream_server = spec
def configure(self, updated): opts = ctx.options if opts.add_upstream_certs_to_client_chain and not opts.upstream_cert: raise exceptions.OptionsError( "The no-upstream-cert and add-upstream-certs-to-client-chain " "options are mutually exclusive. If no-upstream-cert is enabled " "then the upstream certificate is not retrieved before generating " "the client certificate chain.") if opts.add_upstream_certs_to_client_chain and not opts.ssl_insecure: raise exceptions.OptionsError( "The verify-upstream-cert requires certificate verification to be disabled. " "If upstream certificates are verified then extra upstream certificates are " "not available for inclusion to the client chain.") if "body_size_limit" in updated: try: human.parse_size(opts.body_size_limit) except ValueError as e: raise exceptions.OptionsError( "Invalid body size limit specification: %s" % opts.body_size_limit) if "mode" in updated: mode = opts.mode if mode.startswith("reverse:") or mode.startswith("upstream:"): try: server_spec.parse_with_mode(mode) except ValueError as e: raise exceptions.OptionsError(str(e)) from e elif mode == "transparent": if not platform.original_addr: raise exceptions.OptionsError( "Transparent mode not supported on this platform.") elif mode not in ["regular", "socks5"]: raise exceptions.OptionsError( "Invalid mode specification: %s" % mode) if "client_certs" in updated: if opts.client_certs: client_certs = os.path.expanduser(opts.client_certs) if not os.path.exists(client_certs): raise exceptions.OptionsError( "Client certificate path does not exist: {}".format( opts.client_certs))
def load_flow(self, f): """ Loads a flow """ if isinstance(f, http.HTTPFlow): if self.options.mode.startswith("reverse:"): _, upstream_spec = server_spec.parse_with_mode(self.options.mode) f.request.host, f.request.port = upstream_spec.address f.request.scheme = upstream_spec.scheme f.reply = controller.DummyReply() for e, o in eventsequence.iterate(f): self.addons.handle_lifecycle(e, o)
def _handle_event(self, event: events.Event): if isinstance(event, events.Start): yield from self.event_to_child( self.connections[self.context.client], event) if self.mode is HTTPMode.upstream: self.context.server.via = server_spec.parse_with_mode( self.context.options.mode)[1] elif isinstance(event, events.CommandCompleted): stream = self.command_sources.pop(event.command) yield from self.event_to_child(stream, event) elif isinstance(event, events.MessageInjected): # For injected messages we pass the HTTP stacks entirely and directly address the stream. try: conn = self.connections[event.flow.server_conn] except KeyError: # We have a miss for the server connection, which means we're looking at a connection object # that is tunneled over another connection (for example: over an upstream HTTP proxy). # We now take the stream associated with the client connection. That won't work for HTTP/2, # but it's good enough for HTTP/1. conn = self.connections[event.flow.client_conn] if isinstance(conn, HttpStream): stream_id = conn.stream_id else: # We reach to the end of the connection's child stack to get the HTTP/1 client layer, # which tells us which stream we are dealing with. conn = conn.context.layers[-1] assert isinstance(conn, Http1Connection) assert conn.stream_id stream_id = conn.stream_id yield from self.event_to_child(self.streams[stream_id], event) elif isinstance(event, events.ConnectionEvent): if event.connection == self.context.server and self.context.server not in self.connections: # We didn't do anything with this connection yet, now the peer is doing something. if isinstance(event, events.ConnectionClosed): # The peer has closed it - let's close it too! yield commands.CloseConnection(event.connection) elif isinstance(event, events.DataReceived): # The peer has sent data. This can happen with HTTP/2 servers that already send a settings frame. child_layer: HttpConnection if self.context.server.alpn == b"h2": child_layer = Http2Client(self.context.fork()) else: child_layer = Http1Client(self.context.fork()) self.connections[self.context.server] = child_layer yield from self.event_to_child(child_layer, events.Start()) yield from self.event_to_child(child_layer, event) else: raise AssertionError(f"Unexpected event: {event}") else: handler = self.connections[event.connection] yield from self.event_to_child(handler, event) else: raise AssertionError(f"Unexpected event: {event}")
def load_flow(self, f): """ Loads a flow """ if isinstance(f, http.HTTPFlow): if self.options.mode.startswith("reverse:"): _, upstream_spec = server_spec.parse_with_mode(self.options.mode) f.request.host, f.request.port = upstream_spec.address f.request.scheme = upstream_spec.scheme f.reply = controller.DummyReply() for e, o in eventsequence.iterate(f): self.addons.handle_lifecycle(e, o)
def _handle_event(self, event: events.Event) -> layer.CommandGenerator[None]: spec = server_spec.parse_with_mode(self.context.options.mode)[1] self.context.server.address = spec.address if spec.scheme not in ("http", "tcp"): if not self.context.options.keep_host_header: self.context.server.sni = spec.address[0] self.child_layer = tls.ServerTLSLayer(self.context) else: self.child_layer = layer.NextLayer(self.context) yield from self.finish_start()
def configure(self, updated): opts = ctx.options if opts.add_upstream_certs_to_client_chain and not opts.upstream_cert: raise exceptions.OptionsError( "add_upstream_certs_to_client_chain requires the upstream_cert option to be enabled." ) if "body_size_limit" in updated: if opts.body_size_limit: # pragma: no cover ctx.log.warn( "body_size_limit is currently nonfunctioning, " "see https://github.com/mitmproxy/mitmproxy/issues/4348") try: human.parse_size(opts.body_size_limit) except ValueError: raise exceptions.OptionsError( "Invalid body size limit specification: %s" % opts.body_size_limit) if "mode" in updated: mode = opts.mode if mode.startswith("reverse:") or mode.startswith("upstream:"): try: server_spec.parse_with_mode(mode) except ValueError as e: raise exceptions.OptionsError(str(e)) from e elif mode == "transparent": if not platform.original_addr: raise exceptions.OptionsError( "Transparent mode not supported on this platform.") elif mode not in ["regular", "socks5"]: raise exceptions.OptionsError( "Invalid mode specification: %s" % mode) if "client_certs" in updated: if opts.client_certs: client_certs = os.path.expanduser(opts.client_certs) if not os.path.exists(client_certs): raise exceptions.OptionsError( f"Client certificate path does not exist: {opts.client_certs}" )
def __init__(self, flow: http.HTTPFlow, options: Options) -> None: client = flow.client_conn.copy() client.state = ConnectionState.OPEN context = Context(client, options) context.server = Server((flow.request.host, flow.request.port)) context.server.tls = flow.request.scheme == "https" if options.mode.startswith("upstream:"): context.server.via = server_spec.parse_with_mode(options.mode)[1] super().__init__(context) self.layer = layers.HttpLayer(context, HTTPMode.transparent) self.layer.connections[client] = MockServer(flow, context.fork()) self.flow = flow self.done = asyncio.Event()
async def load_flow(self, f): """ Loads a flow """ if isinstance(f, http.HTTPFlow): if self.options.mode.startswith("reverse:"): # When we load flows in reverse proxy mode, we adjust the target host to # the reverse proxy destination for all flows we load. This makes it very # easy to replay saved flows against a different host. _, upstream_spec = server_spec.parse_with_mode(self.options.mode) f.request.host, f.request.port = upstream_spec.address f.request.scheme = upstream_spec.scheme for e in eventsequence.iterate(f): await self.addons.handle_lifecycle(e)
def configure(self, options: moptions.Options, updated: typing.Any) -> None: if options.allow_hosts and options.ignore_hosts: raise exceptions.OptionsError("--ignore-hosts and --allow-hosts are mutually " "exclusive; please choose one.") if options.ignore_hosts: self.check_filter = HostMatcher("ignore", options.ignore_hosts) elif options.allow_hosts: self.check_filter = HostMatcher("allow", options.allow_hosts) else: self.check_filter = HostMatcher(False) if "tcp_hosts" in updated: self.check_tcp = HostMatcher("tcp", options.tcp_hosts) certstore_path = os.path.expanduser(options.confdir) if not os.path.exists(os.path.dirname(certstore_path)): raise exceptions.OptionsError( "Certificate Authority parent directory does not exist: %s" % os.path.dirname(certstore_path) ) key_size = options.key_size self.certstore = certs.CertStore.from_store( certstore_path, moptions.CONF_BASENAME, key_size ) for c in options.certs: parts = c.split("=", 1) if len(parts) == 1: parts = ["*", parts[0]] cert = os.path.expanduser(parts[1]) if not os.path.exists(cert): raise exceptions.OptionsError( "Certificate file does not exist: %s" % cert ) try: self.certstore.add_cert_file(parts[0], cert) except crypto.Error: raise exceptions.OptionsError( "Invalid certificate format: %s" % cert ) m = options.mode if m.startswith("upstream:") or m.startswith("reverse:"): _, spec = server_spec.parse_with_mode(options.mode) self.upstream_server = spec
def _handle_event(self, event: events.Event): if isinstance(event, events.Start): yield from self.event_to_child(self.connections[self.context.client], event) if self.mode is HTTPMode.upstream: self.context.server.via = server_spec.parse_with_mode(self.context.options.mode)[1] elif isinstance(event, events.CommandCompleted): stream = self.command_sources.pop(event.command) yield from self.event_to_child(stream, event) elif isinstance(event, events.ConnectionEvent): if event.connection == self.context.server and self.context.server not in self.connections: # We didn't do anything with this connection yet, now the peer has closed it - let's close it too! yield commands.CloseConnection(event.connection) else: handler = self.connections[event.connection] yield from self.event_to_child(handler, event) else: raise AssertionError(f"Unexpected event: {event}")
def configure(self, options: moptions.Options, updated: typing.Any) -> None: if "ignore_hosts" in updated: self.check_ignore = HostMatcher(options.ignore_hosts) if "tcp_hosts" in updated: self.check_tcp = HostMatcher(options.tcp_hosts) certstore_path = os.path.expanduser(options.cadir) if not os.path.exists(os.path.dirname(certstore_path)): raise exceptions.OptionsError( "Certificate Authority parent directory does not exist: %s" % os.path.dirname(certstore_path) ) self.certstore = certs.CertStore.from_store( certstore_path, CONF_BASENAME ) for c in options.certs: parts = c.split("=", 1) if len(parts) == 1: parts = ["*", parts[0]] cert = os.path.expanduser(parts[1]) if not os.path.exists(cert): raise exceptions.OptionsError( "Certificate file does not exist: %s" % cert ) try: self.certstore.add_cert_file(parts[0], cert) except crypto.Error: raise exceptions.OptionsError( "Invalid certificate format: %s" % cert ) m = options.mode if m.startswith("upstream:") or m.startswith("reverse:"): _, spec = server_spec.parse_with_mode(options.mode) self.upstream_server = spec
def configure(self, options: moptions.Options, updated: typing.Any) -> None: if "ignore_hosts" in updated: self.check_ignore = HostMatcher(options.ignore_hosts) if "tcp_hosts" in updated: self.check_tcp = HostMatcher(options.tcp_hosts) certstore_path = os.path.expanduser(options.confdir) if not os.path.exists(os.path.dirname(certstore_path)): raise exceptions.OptionsError( "Certificate Authority parent directory does not exist: %s" % os.path.dirname(certstore_path) ) self.certstore = certs.CertStore.from_store( certstore_path, CONF_BASENAME ) for c in options.certs: parts = c.split("=", 1) if len(parts) == 1: parts = ["*", parts[0]] cert = os.path.expanduser(parts[1]) if not os.path.exists(cert): raise exceptions.OptionsError( "Certificate file does not exist: %s" % cert ) try: self.certstore.add_cert_file(parts[0], cert) except crypto.Error: raise exceptions.OptionsError( "Invalid certificate format: %s" % cert ) m = options.mode if m.startswith("upstream:") or m.startswith("reverse:"): _, spec = server_spec.parse_with_mode(options.mode) self.upstream_server = spec
def replay(self, f): # pragma: no cover f.live = True r = f.request bsl = human.parse_size(self.options.body_size_limit) authority_backup = r.authority server = None try: f.response = None # If we have a channel, run script hooks. request_reply = self.channel.ask("request", f) if isinstance(request_reply, http.HTTPResponse): f.response = request_reply if not f.response: # In all modes, we directly connect to the server displayed if self.options.mode.startswith("upstream:"): server_address = server_spec.parse_with_mode( self.options.mode)[1].address server = connections.ServerConnection(server_address) server.connect() if r.scheme == "https": connect_request = http.make_connect_request( (r.data.host, r.port)) server.wfile.write( http1.assemble_request(connect_request)) server.wfile.flush() resp = http1.read_response(server.rfile, connect_request, body_size_limit=bsl) if resp.status_code != 200: raise exceptions.ReplayException( "Upstream server refuses CONNECT request") server.establish_tls( sni=f.server_conn.sni, **tls.client_arguments_from_options(self.options)) r.authority = b"" else: r.authority = hostport(r.scheme, r.host, r.port) else: server_address = (r.host, r.port) server = connections.ServerConnection(server_address) server.connect() if r.scheme == "https": server.establish_tls( sni=f.server_conn.sni, **tls.client_arguments_from_options(self.options)) r.authority = "" server.wfile.write(http1.assemble_request(r)) server.wfile.flush() r.timestamp_start = r.timestamp_end = time.time() if f.server_conn: f.server_conn.close() f.server_conn = server f.response = http1.read_response(server.rfile, r, body_size_limit=bsl) response_reply = self.channel.ask("response", f) if response_reply == exceptions.Kill: raise exceptions.Kill() except (exceptions.ReplayException, exceptions.NetlibException) as e: f.error = flow.Error(str(e)) self.channel.ask("error", f) except exceptions.Kill: self.channel.tell("log", log.LogEntry(flow.Error.KILLED_MESSAGE, "info")) except Exception as e: self.channel.tell("log", log.LogEntry(repr(e), "error")) finally: r.authority = authority_backup f.live = False if server and server.connected(): server.finish() server.close()
def configure(self, options: moptions.Options, updated: typing.Any) -> None: if options.add_upstream_certs_to_client_chain and not options.ssl_insecure: raise exceptions.OptionsError( "The verify-upstream-cert requires certificate verification to be disabled. " "If upstream certificates are verified then extra upstream certificates are " "not available for inclusion to the client chain." ) if options.ssl_insecure: self.openssl_verification_mode_server = SSL.VERIFY_NONE else: self.openssl_verification_mode_server = SSL.VERIFY_PEER if "ignore_hosts" in updated: self.check_ignore = HostMatcher(options.ignore_hosts) if "tcp_hosts" in updated: self.check_tcp = HostMatcher(options.tcp_hosts) self.openssl_method_client, self.openssl_options_client = \ tls.VERSION_CHOICES[options.ssl_version_client] self.openssl_method_server, self.openssl_options_server = \ tls.VERSION_CHOICES[options.ssl_version_server] certstore_path = os.path.expanduser(options.cadir) if not os.path.exists(os.path.dirname(certstore_path)): raise exceptions.OptionsError( "Certificate Authority parent directory does not exist: %s" % os.path.dirname(options.cadir) ) self.certstore = certs.CertStore.from_store( certstore_path, CONF_BASENAME ) if options.client_certs: client_certs = os.path.expanduser(options.client_certs) if not os.path.exists(client_certs): raise exceptions.OptionsError( "Client certificate path does not exist: %s" % options.client_certs ) self.client_certs = client_certs for c in options.certs: parts = c.split("=", 1) if len(parts) == 1: parts = ["*", parts[0]] cert = os.path.expanduser(parts[1]) if not os.path.exists(cert): raise exceptions.OptionsError( "Certificate file does not exist: %s" % cert ) try: self.certstore.add_cert_file(parts[0], cert) except crypto.Error: raise exceptions.OptionsError( "Invalid certificate format: %s" % cert ) m = options.mode if m.startswith("upstream:") or m.startswith("reverse:"): _, spec = server_spec.parse_with_mode(options.mode) self.upstream_server = spec
def run(self): r = self.f.request bsl = self.options._processed.get("body_size_limit") first_line_format_backup = r.first_line_format server = None try: self.f.response = None # If we have a channel, run script hooks. if self.channel: request_reply = self.channel.ask("request", self.f) if isinstance(request_reply, http.HTTPResponse): self.f.response = request_reply if not self.f.response: # In all modes, we directly connect to the server displayed if self.options.mode.startswith("upstream:"): server_address = server_spec.parse_with_mode( self.options.mode)[1].address server = connections.ServerConnection( server_address, (self.options.listen_host, 0)) server.connect() if r.scheme == "https": connect_request = http.make_connect_request( (r.data.host, r.port)) server.wfile.write( http1.assemble_request(connect_request)) server.wfile.flush() resp = http1.read_response(server.rfile, connect_request, body_size_limit=bsl) if resp.status_code != 200: raise exceptions.ReplayException( "Upstream server refuses CONNECT request") server.establish_ssl(self.options.client_certs, sni=self.f.server_conn.sni) r.first_line_format = "relative" else: r.first_line_format = "absolute" else: server_address = (r.host, r.port) server = connections.ServerConnection( server_address, (self.options.listen_host, 0)) server.connect() if r.scheme == "https": server.establish_ssl(self.options.client_certs, sni=self.f.server_conn.sni) r.first_line_format = "relative" server.wfile.write(http1.assemble_request(r)) server.wfile.flush() if self.f.server_conn: self.f.server_conn.close() self.f.server_conn = server self.f.response = http.HTTPResponse.wrap( http1.read_response(server.rfile, r, body_size_limit=bsl)) if self.channel: response_reply = self.channel.ask("response", self.f) if response_reply == exceptions.Kill: raise exceptions.Kill() except (exceptions.ReplayException, exceptions.NetlibException) as e: self.f.error = flow.Error(str(e)) if self.channel: self.channel.ask("error", self.f) except exceptions.Kill: # Kill should only be raised if there's a channel in the # first place. self.channel.tell("log", log.LogEntry("Connection killed", "info")) except Exception as e: self.channel.tell("log", log.LogEntry(repr(e), "error")) finally: r.first_line_format = first_line_format_backup self.f.live = False if server.connected(): server.finish()
def replay(self, f): # pragma: no cover f.live = True r = f.request bsl = human.parse_size(self.options.body_size_limit) first_line_format_backup = r.first_line_format server = None global new, cur_cycle, cur_group try: f.response = None # If we have a channel, run script hooks. request_reply = self.channel.ask("request", f) if isinstance(request_reply, http.HTTPResponse): f.response = request_reply if not f.response: # In all modes, we directly connect to the server displayed if self.options.mode.startswith("upstream:"): server_address = server_spec.parse_with_mode( self.options.mode)[1].address server = connections.ServerConnection(server_address) server.connect() if r.scheme == "https": connect_request = http.make_connect_request( (r.data.host, r.port)) server.wfile.write( http1.assemble_request(connect_request)) server.wfile.flush() resp = http1.read_response(server.rfile, connect_request, body_size_limit=bsl) if resp.status_code != 200: raise exceptions.ReplayException( "Upstream server refuses CONNECT request") server.establish_tls( sni=f.server_conn.sni, **tls.client_arguments_from_options(self.options)) r.first_line_format = "relative" else: r.first_line_format = "absolute" else: server_address = (r.host, r.port) server = connections.ServerConnection(server_address) server.connect() if r.scheme == "https": server.establish_tls( sni=f.server_conn.sni, **tls.client_arguments_from_options(self.options)) r.first_line_format = "relative" server.wfile.write(http1.assemble_request(r)) server.wfile.flush() if f.server_conn: f.server_conn.close() f.server_conn = server f.response = http.HTTPResponse.wrap( http1.read_response(server.rfile, r, body_size_limit=bsl)) response_reply = self.channel.ask("response", f) #new.append(f) #record the response cur_cycle[cur_group] = f if response_reply == exceptions.Kill: raise exceptions.Kill() except (exceptions.ReplayException, exceptions.NetlibException) as e: f.error = flow.Error(str(e)) self.channel.ask("error", f) except exceptions.Kill: self.channel.tell("log", log.LogEntry("Connection killed", "info")) except Exception as e: self.channel.tell("log", log.LogEntry(repr(e), "error")) finally: r.first_line_format = first_line_format_backup f.live = False if server.connected(): server.finish() server.close()
def run(self): r = self.f.request bsl = human.parse_size(self.options.body_size_limit) first_line_format_backup = r.first_line_format server = None try: self.f.response = None # If we have a channel, run script hooks. if self.channel: request_reply = self.channel.ask("request", self.f) if isinstance(request_reply, http.HTTPResponse): self.f.response = request_reply if not self.f.response: # In all modes, we directly connect to the server displayed if self.options.mode.startswith("upstream:"): server_address = server_spec.parse_with_mode(self.options.mode)[1].address server = connections.ServerConnection(server_address, (self.options.listen_host, 0)) server.connect() if r.scheme == "https": connect_request = http.make_connect_request((r.data.host, r.port)) server.wfile.write(http1.assemble_request(connect_request)) server.wfile.flush() resp = http1.read_response( server.rfile, connect_request, body_size_limit=bsl ) if resp.status_code != 200: raise exceptions.ReplayException("Upstream server refuses CONNECT request") server.establish_ssl( self.options.client_certs, sni=self.f.server_conn.sni ) r.first_line_format = "relative" else: r.first_line_format = "absolute" else: server_address = (r.host, r.port) server = connections.ServerConnection( server_address, (self.options.listen_host, 0) ) server.connect() if r.scheme == "https": server.establish_ssl( self.options.client_certs, sni=self.f.server_conn.sni ) r.first_line_format = "relative" server.wfile.write(http1.assemble_request(r)) server.wfile.flush() if self.f.server_conn: self.f.server_conn.close() self.f.server_conn = server self.f.response = http.HTTPResponse.wrap( http1.read_response( server.rfile, r, body_size_limit=bsl ) ) if self.channel: response_reply = self.channel.ask("response", self.f) if response_reply == exceptions.Kill: raise exceptions.Kill() except (exceptions.ReplayException, exceptions.NetlibException) as e: self.f.error = flow.Error(str(e)) if self.channel: self.channel.ask("error", self.f) except exceptions.Kill: # Kill should only be raised if there's a channel in the # first place. self.channel.tell( "log", log.LogEntry("Connection killed", "info") ) except Exception as e: self.channel.tell( "log", log.LogEntry(repr(e), "error") ) finally: r.first_line_format = first_line_format_backup self.f.live = False if server.connected(): server.finish()
def replay(self, f): # pragma: no cover f.live = True r = f.request bsl = human.parse_size(self.options.body_size_limit) first_line_format_backup = r.first_line_format server = None try: f.response = None # If we have a channel, run script hooks. request_reply = self.channel.ask("request", f) if isinstance(request_reply, http.HTTPResponse): f.response = request_reply if not f.response: # In all modes, we directly connect to the server displayed if self.options.mode.startswith("upstream:"): server_address = server_spec.parse_with_mode(self.options.mode)[1].address server = connections.ServerConnection(server_address) server.connect() if r.scheme == "https": connect_request = http.make_connect_request((r.data.host, r.port)) server.wfile.write(http1.assemble_request(connect_request)) server.wfile.flush() resp = http1.read_response( server.rfile, connect_request, body_size_limit=bsl ) if resp.status_code != 200: raise exceptions.ReplayException( "Upstream server refuses CONNECT request" ) server.establish_tls( sni=f.server_conn.sni, **tls.client_arguments_from_options(self.options) ) r.first_line_format = "relative" else: r.first_line_format = "absolute" else: server_address = (r.host, r.port) server = connections.ServerConnection(server_address) server.connect() if r.scheme == "https": server.establish_tls( sni=f.server_conn.sni, **tls.client_arguments_from_options(self.options) ) r.first_line_format = "relative" server.wfile.write(http1.assemble_request(r)) server.wfile.flush() r.timestamp_start = r.timestamp_end = time.time() if f.server_conn: f.server_conn.close() f.server_conn = server f.response = http.HTTPResponse.wrap( http1.read_response(server.rfile, r, body_size_limit=bsl) ) response_reply = self.channel.ask("response", f) if response_reply == exceptions.Kill: raise exceptions.Kill() except (exceptions.ReplayException, exceptions.NetlibException) as e: f.error = flow.Error(str(e)) self.channel.ask("error", f) except exceptions.Kill: self.channel.tell("log", log.LogEntry("Connection killed", "info")) except Exception as e: self.channel.tell("log", log.LogEntry(repr(e), "error")) finally: r.first_line_format = first_line_format_backup f.live = False if server.connected(): server.finish() server.close()
def test_parse_with_mode(): assert server_spec.parse_with_mode("m:example.com") == ("m", ("https", ("example.com", 443))) with pytest.raises(ValueError): server_spec.parse_with_mode("moo")