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 _establish_tls_with_server(self): self.log("Establish TLS with server", "debug") try: alpn = None if self._client_tls: if self._client_hello.alpn_protocols: # We only support http/1.1 and h2. # If the server only supports spdy (next to http/1.1), it may select that # and mitmproxy would enter TCP passthrough mode, which we want to avoid. alpn = [ x for x in self._client_hello.alpn_protocols if not (x.startswith(b"h2-") or x.startswith(b"spdy")) ] if alpn and b"h2" in alpn and not self.config.options.http2: alpn.remove(b"h2") if self.client_conn.tls_established and self.client_conn.get_alpn_proto_negotiated( ): # If the client has already negotiated an ALP, then force the # server to use the same. This can only happen if the host gets # changed after the initial connection was established. E.g.: # * the client offers http/1.1 and h2, # * the initial host is only capable of http/1.1, # * then the first server connection negotiates http/1.1, # * but after the server_conn change, the new host offers h2 # * which results in garbage because the layers don' match. alpn = [self.client_conn.get_alpn_proto_negotiated()] # We pass through the list of ciphers send by the client, because some HTTP/2 servers # will select a non-HTTP/2 compatible cipher from our default list and then hang up # because it's incompatible with h2. :-) ciphers_server = self.config.options.ciphers_server if not ciphers_server and self._client_tls: ciphers_server = [] for id in self._client_hello.cipher_suites: if id in CIPHER_ID_NAME_MAP.keys(): ciphers_server.append(CIPHER_ID_NAME_MAP[id]) ciphers_server = ':'.join(ciphers_server) args = net_tls.client_arguments_from_options(self.config.options) args["cipher_list"] = ciphers_server self.server_conn.establish_tls(sni=self.server_sni, alpn_protos=alpn, **args) tls_cert_err = self.server_conn.ssl_verification_error if tls_cert_err is not None: self.log(str(tls_cert_err), "warn") self.log( "Ignoring server verification error, continuing with connection", "warn") except exceptions.InvalidCertificateException as e: raise exceptions.InvalidServerCertificate(str(e)) except exceptions.TlsException as e: raise exceptions.TlsProtocolException( "Cannot establish TLS with {host}:{port} (sni: {sni}): {e}". format(host=self.server_conn.address[0], port=self.server_conn.address[1], sni=self.server_sni, e=repr(e))) proto = self.alpn_for_client_connection.decode( ) if self.alpn_for_client_connection else '-' self.log("ALPN selected by server: {}".format(proto), "debug")
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 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 create_proxy_server_ssl_conn(self, tls_start: tls.TlsStartData) -> None: client = tls_start.context.client server = cast(context.Server, tls_start.conn) assert server.address if server.sni is True: server.sni = client.sni or server.address[0].encode() if not server.alpn_offers: if client.alpn_offers: if ctx.options.http2: server.alpn_offers = tuple(client.alpn_offers) else: server.alpn_offers = tuple(x for x in client.alpn_offers if x != b"h2") elif client.tls_established: # We would perfectly support HTTP/1 -> HTTP/2, but we want to keep things on the same protocol version. # There are some edge cases where we want to mirror the regular server's behavior accurately, # for example header capitalization. server.alpn_offers = [] elif ctx.options.http2: server.alpn_offers = tls.HTTP_ALPNS else: server.alpn_offers = tls.HTTP1_ALPNS # We pass through the list of ciphers send by the client, because some HTTP/2 servers # will select a non-HTTP/2 compatible cipher from our default list and then hang up # because it's incompatible with h2. if not server.cipher_list: if ctx.options.ciphers_server: server.cipher_list = ctx.options.ciphers_server.split(":") elif client.cipher_list: # We used to filter for known ciphers here, but that doesn't seem to make sense. # According to OpenSSL docs, the control string str should be universally # usable and not depend on details of the library configuration (ciphers compiled in). server.cipher_list = list(client.cipher_list) args = net_tls.client_arguments_from_options(ctx.options) client_certs = args.pop("client_certs") client_cert: Optional[str] = None if client_certs: client_certs = os.path.expanduser(client_certs) if os.path.isfile(client_certs): client_cert = client_certs else: server_name: str = (server.sni or server.address[0].encode("idna")).decode() path = os.path.join(client_certs, f"{server_name}.pem") if os.path.exists(path): client_cert = path args["cipher_list"] = ':'.join( server.cipher_list) if server.cipher_list else None ssl_ctx = net_tls.create_client_context( cert=client_cert, sni=server.sni.decode("idna") if server.sni else None, # TODO: Should pass-through here. alpn_protos=server.alpn_offers, **args) tls_start.ssl_conn = SSL.Connection(ssl_ctx) tls_start.ssl_conn.set_tlsext_host_name(server.sni) tls_start.ssl_conn.set_connect_state()
def _establish_tls_with_server(self): self.log("Establish TLS with server", "debug") try: alpn = None if self._client_tls: if self._client_hello.alpn_protocols: # We only support http/1.1 and h2. # If the server only supports spdy (next to http/1.1), it may select that # and mitmproxy would enter TCP passthrough mode, which we want to avoid. alpn = [ x for x in self._client_hello.alpn_protocols if not (x.startswith(b"h2-") or x.startswith(b"spdy")) ] if alpn and b"h2" in alpn and not self.config.options.http2: alpn.remove(b"h2") if self.client_conn.tls_established and self.client_conn.get_alpn_proto_negotiated(): # If the client has already negotiated an ALP, then force the # server to use the same. This can only happen if the host gets # changed after the initial connection was established. E.g.: # * the client offers http/1.1 and h2, # * the initial host is only capable of http/1.1, # * then the first server connection negotiates http/1.1, # * but after the server_conn change, the new host offers h2 # * which results in garbage because the layers don' match. alpn = [self.client_conn.get_alpn_proto_negotiated()] # We pass through the list of ciphers send by the client, because some HTTP/2 servers # will select a non-HTTP/2 compatible cipher from our default list and then hang up # because it's incompatible with h2. :-) ciphers_server = self.config.options.ciphers_server if not ciphers_server and self._client_tls: ciphers_server = [] for id in self._client_hello.cipher_suites: if id in CIPHER_ID_NAME_MAP.keys(): ciphers_server.append(CIPHER_ID_NAME_MAP[id]) ciphers_server = ':'.join(ciphers_server) args = net_tls.client_arguments_from_options(self.config.options) args["cipher_list"] = ciphers_server self.server_conn.establish_tls( sni=self.server_sni, alpn_protos=alpn, **args ) tls_cert_err = self.server_conn.ssl_verification_error if tls_cert_err is not None: self.log(str(tls_cert_err), "warn") self.log("Ignoring server verification error, continuing with connection", "warn") except exceptions.InvalidCertificateException as e: raise exceptions.InvalidServerCertificate(str(e)) except exceptions.TlsException as e: raise exceptions.TlsProtocolException( "Cannot establish TLS with {host}:{port} (sni: {sni}): {e}".format( host=self.server_conn.address[0], port=self.server_conn.address[1], sni=self.server_sni, e=repr(e) ) ) proto = self.alpn_for_client_connection.decode() if self.alpn_for_client_connection else '-' self.log("ALPN selected by server: {}".format(proto), "debug")