def _open_socket_for_connection_via_http_proxy( server_location: ServerNetworkLocation, network_timeout: int ) -> socket.socket: assert server_location.http_proxy_settings try: sock = socket.create_connection( (server_location.http_proxy_settings.hostname, server_location.http_proxy_settings.port), timeout=network_timeout, ) # Send a CONNECT request with the host we want to tunnel to proxy_authorization_header = server_location.http_proxy_settings.proxy_authorization_header if proxy_authorization_header is None: sock.send(f"CONNECT {server_location.hostname}:{server_location.port} HTTP/1.1\r\n\r\n".encode("utf-8")) else: sock.send( ( f"CONNECT {server_location.hostname}:{server_location.port} HTTP/1.1\r\n" f"Proxy-Authorization: Basic {proxy_authorization_header}\r\n\r\n" ).encode("utf-8") ) http_response = HttpResponseParser.parse_from_socket(sock) except socket.timeout: raise _ConnectionToHttpProxyTimedOut() except ConnectionError: raise _HttpProxyRejectedConnection("The HTTP proxy rejected the connection") except socket.error: raise _ConnectionToHttpProxyFailed() # Check if the proxy was able to connect to the host if http_response.status != 200: raise _HttpProxyRejectedConnection("The HTTP proxy rejected the CONNECT request") return sock
def _retrieve_and_analyze_http_response( server_info: ServerConnectivityInfo) -> HttpHeadersScanResult: # Send HTTP requests until we no longer received an HTTP redirection, but allow only 4 redirections max redirections_count = 0 next_location_path: Optional[str] = "/" while next_location_path and redirections_count < 4: ssl_connection = server_info.get_preconfigured_tls_connection() try: # Perform the TLS handshake ssl_connection.connect() # Send an HTTP GET request to the server ssl_connection.ssl_client.write( HttpRequestGenerator.get_request( host=server_info.network_configuration. tls_server_name_indication, path=next_location_path)) http_response = HttpResponseParser.parse_from_ssl_connection( ssl_connection.ssl_client) finally: ssl_connection.close() if http_response.version == 9: # HTTP 0.9 => Probably not an HTTP response raise ValueError("Server did not return an HTTP response") # Handle redirection if there is one next_location_path = _detect_http_redirection( http_response=http_response, server_host_name=server_info.network_configuration. tls_server_name_indication, server_port=server_info.server_location.port, ) redirections_count += 1 # Parse and return each header return HttpHeadersScanResult( strict_transport_security_header=_parse_hsts_header_from_http_response( http_response), public_key_pins_header=_parse_hpkp_header_from_http_response( http_response), public_key_pins_report_only_header= _parse_hpkp_report_only_header_from_http_response(http_response), expect_ct_header=_parse_expect_ct_header_from_http_response( http_response), )
def _retrieve_and_analyze_http_response( server_info: ServerConnectivityInfo) -> HttpHeadersScanResult: # Perform the TLS handshake ssl_connection = server_info.get_preconfigured_tls_connection() try: ssl_connection.connect() # Send an HTTP GET request to the server ssl_connection.ssl_client.write( HttpRequestGenerator.get_request( host=server_info.network_configuration. tls_server_name_indication)) # We do not follow redirections because the security headers must be set on the first page according to # https://hstspreload.appspot.com/: # "If you are serving an additional redirect from your HTTPS site, that redirect must still have the HSTS # header (rather than the page it redirects to)." http_response = HttpResponseParser.parse_from_ssl_connection( ssl_connection.ssl_client) finally: ssl_connection.close() if http_response.version == 9: # HTTP 0.9 => Probably not an HTTP response raise ValueError("Server did not return an HTTP response") # Parse and return each header return HttpHeadersScanResult( strict_transport_security_header=_parse_hsts_header_from_http_response( http_response), public_key_pins_header=_parse_hpkp_header_from_http_response( http_response), public_key_pins_report_only_header= _parse_hpkp_report_only_header_from_http_response(http_response), expect_ct_header=_parse_expect_ct_header_from_http_response( http_response), )
def _retrieve_and_analyze_http_response(server_info: ServerConnectivityInfo) -> HttpHeadersScanResult: # Send HTTP requests until we no longer received an HTTP redirection, but allow only 4 redirections max _logger.info(f"Retrieving HTTP headers from {server_info}") redirections_count = 0 next_location_path: Optional[str] = "/" http_error_trace = None while next_location_path and redirections_count < 4: _logger.info(f"Sending HTTP request to {next_location_path}") http_path_redirected_to = next_location_path # Perform the TLS handshake ssl_connection = server_info.get_preconfigured_tls_connection() ssl_connection.connect() try: # Send an HTTP GET request to the server ssl_connection.ssl_client.write( HttpRequestGenerator.get_request( host=server_info.network_configuration.tls_server_name_indication, path=next_location_path ) ) http_response = HttpResponseParser.parse_from_ssl_connection(ssl_connection.ssl_client) except (OSError, NotAValidHttpResponseError, SslError) as e: # The server closed/rejected the connection, or didn't return a valid HTTP response http_error_trace = TracebackException.from_exception(e) finally: ssl_connection.close() if http_error_trace: break # Handle redirection if there is one next_location_path = _detect_http_redirection( http_response=http_response, server_host_name=server_info.network_configuration.tls_server_name_indication, server_port=server_info.server_location.port, ) redirections_count += 1 # Prepare the results initial_http_request = HttpRequestGenerator.get_request( host=server_info.network_configuration.tls_server_name_indication, path="/" ).decode("ascii") if http_error_trace: # If the server errored when receiving an HTTP request, return the error as the result return HttpHeadersScanResult( http_request_sent=initial_http_request, http_error_trace=http_error_trace, http_path_redirected_to=None, strict_transport_security_header=None, expect_ct_header=None, ) else: # If no HTTP error happened, parse and return each header return HttpHeadersScanResult( http_request_sent=initial_http_request, http_path_redirected_to=http_path_redirected_to, http_error_trace=None, strict_transport_security_header=_parse_hsts_header_from_http_response(http_response), expect_ct_header=_parse_expect_ct_header_from_http_response(http_response), )