mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2025-02-02 00:05:27 +00:00
move ClientHello and related functions to mitmproxy.net.tls
This commit is contained in:
parent
b7db304dde
commit
fc80aa562e
@ -2,15 +2,20 @@
|
|||||||
# then add options to disable certain methods
|
# then add options to disable certain methods
|
||||||
# https://bugs.launchpad.net/pyopenssl/+bug/1020632/comments/3
|
# https://bugs.launchpad.net/pyopenssl/+bug/1020632/comments/3
|
||||||
import binascii
|
import binascii
|
||||||
|
import io
|
||||||
import os
|
import os
|
||||||
|
import struct
|
||||||
import threading
|
import threading
|
||||||
import typing
|
import typing
|
||||||
from ssl import match_hostname, CertificateError
|
from ssl import match_hostname, CertificateError
|
||||||
|
|
||||||
import certifi
|
import certifi
|
||||||
from OpenSSL import SSL
|
from OpenSSL import SSL
|
||||||
|
from kaitaistruct import KaitaiStream
|
||||||
|
|
||||||
from mitmproxy import exceptions, certs
|
from mitmproxy import exceptions, certs
|
||||||
|
from mitmproxy.contrib.kaitaistruct import tls_client_hello
|
||||||
|
from mitmproxy.net import check
|
||||||
|
|
||||||
BASIC_OPTIONS = (
|
BASIC_OPTIONS = (
|
||||||
SSL.OP_CIPHER_SERVER_PREFERENCE
|
SSL.OP_CIPHER_SERVER_PREFERENCE
|
||||||
@ -189,7 +194,7 @@ def _create_ssl_context(
|
|||||||
def create_client_context(
|
def create_client_context(
|
||||||
cert: str = None,
|
cert: str = None,
|
||||||
sni: str = None,
|
sni: str = None,
|
||||||
address: str=None,
|
address: str = None,
|
||||||
verify: int = SSL.VERIFY_NONE,
|
verify: int = SSL.VERIFY_NONE,
|
||||||
**sslctx_kwargs
|
**sslctx_kwargs
|
||||||
) -> SSL.Context:
|
) -> SSL.Context:
|
||||||
@ -338,3 +343,121 @@ def create_server_context(
|
|||||||
SSL._lib.SSL_CTX_set_tmp_dh(context._context, dhparams)
|
SSL._lib.SSL_CTX_set_tmp_dh(context._context, dhparams)
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
def is_tls_record_magic(d):
|
||||||
|
"""
|
||||||
|
Returns:
|
||||||
|
True, if the passed bytes start with the TLS record magic bytes.
|
||||||
|
False, otherwise.
|
||||||
|
"""
|
||||||
|
d = d[:3]
|
||||||
|
|
||||||
|
# TLS ClientHello magic, works for SSLv3, TLSv1.0, TLSv1.1, TLSv1.2
|
||||||
|
# http://www.moserware.com/2009/06/first-few-milliseconds-of-https.html#client-hello
|
||||||
|
return (
|
||||||
|
len(d) == 3 and
|
||||||
|
d[0] == 0x16 and
|
||||||
|
d[1] == 0x03 and
|
||||||
|
0x0 <= d[2] <= 0x03
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_client_hello(client_conn):
|
||||||
|
"""
|
||||||
|
Peek into the socket and read all records that contain the initial client hello message.
|
||||||
|
|
||||||
|
client_conn:
|
||||||
|
The :py:class:`client connection <mitmproxy.connections.ClientConnection>`.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The raw handshake packet bytes, without TLS record header(s).
|
||||||
|
"""
|
||||||
|
client_hello = b""
|
||||||
|
client_hello_size = 1
|
||||||
|
offset = 0
|
||||||
|
while len(client_hello) < client_hello_size:
|
||||||
|
record_header = client_conn.rfile.peek(offset + 5)[offset:]
|
||||||
|
if not is_tls_record_magic(record_header) or len(record_header) != 5:
|
||||||
|
raise exceptions.TlsProtocolException(
|
||||||
|
'Expected TLS record, got "%s" instead.' % record_header)
|
||||||
|
record_size = struct.unpack("!H", record_header[3:])[0] + 5
|
||||||
|
record_body = client_conn.rfile.peek(offset + record_size)[offset + 5:]
|
||||||
|
if len(record_body) != record_size - 5:
|
||||||
|
raise exceptions.TlsProtocolException(
|
||||||
|
"Unexpected EOF in TLS handshake: %s" % record_body)
|
||||||
|
client_hello += record_body
|
||||||
|
offset += record_size
|
||||||
|
client_hello_size = struct.unpack("!I", b'\x00' + client_hello[1:4])[0] + 4
|
||||||
|
return client_hello
|
||||||
|
|
||||||
|
|
||||||
|
class ClientHello:
|
||||||
|
|
||||||
|
def __init__(self, raw_client_hello):
|
||||||
|
self._client_hello = tls_client_hello.TlsClientHello(
|
||||||
|
KaitaiStream(io.BytesIO(raw_client_hello)))
|
||||||
|
|
||||||
|
def raw(self):
|
||||||
|
return self._client_hello
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cipher_suites(self):
|
||||||
|
return self._client_hello.cipher_suites.cipher_suites
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sni(self):
|
||||||
|
if self._client_hello.extensions:
|
||||||
|
for extension in self._client_hello.extensions.extensions:
|
||||||
|
is_valid_sni_extension = (
|
||||||
|
extension.type == 0x00 and
|
||||||
|
len(extension.body.server_names) == 1 and
|
||||||
|
extension.body.server_names[0].name_type == 0 and
|
||||||
|
check.is_valid_host(extension.body.server_names[0].host_name)
|
||||||
|
)
|
||||||
|
if is_valid_sni_extension:
|
||||||
|
return extension.body.server_names[0].host_name.decode("idna")
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def alpn_protocols(self):
|
||||||
|
if self._client_hello.extensions:
|
||||||
|
for extension in self._client_hello.extensions.extensions:
|
||||||
|
if extension.type == 0x10:
|
||||||
|
return list(x.name for x in extension.body.alpn_protocols)
|
||||||
|
return []
|
||||||
|
|
||||||
|
@property
|
||||||
|
def extensions(self) -> typing.List[typing.Tuple[int, bytes]]:
|
||||||
|
ret = []
|
||||||
|
if self._client_hello.extensions:
|
||||||
|
for extension in self._client_hello.extensions.extensions:
|
||||||
|
body = getattr(extension, "_raw_body", extension.body)
|
||||||
|
ret.append((extension.type, body))
|
||||||
|
return ret
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_client_conn(cls, client_conn) -> "ClientHello":
|
||||||
|
"""
|
||||||
|
Peek into the connection, read the initial client hello and parse it to obtain ALPN values.
|
||||||
|
client_conn:
|
||||||
|
The :py:class:`client connection <mitmproxy.connections.ClientConnection>`.
|
||||||
|
Returns:
|
||||||
|
:py:class:`client hello <mitmproxy.net.tls.ClientHello>`.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
raw_client_hello = get_client_hello(client_conn)[4:] # exclude handshake header.
|
||||||
|
except exceptions.ProtocolException as e:
|
||||||
|
raise exceptions.TlsProtocolException('Cannot read raw Client Hello: %s' % repr(e))
|
||||||
|
|
||||||
|
try:
|
||||||
|
return cls(raw_client_hello)
|
||||||
|
except EOFError as e:
|
||||||
|
raise exceptions.TlsProtocolException(
|
||||||
|
'Cannot parse Client Hello: %s, Raw Client Hello: %s' %
|
||||||
|
(repr(e), raw_client_hello.encode("hex"))
|
||||||
|
)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "ClientHello(sni: %s, alpn_protocols: %s, cipher_suites: %s)" % \
|
||||||
|
(self.sni, self.alpn_protocols, self.cipher_suites)
|
||||||
|
@ -36,13 +36,11 @@ from .http1 import Http1Layer
|
|||||||
from .http2 import Http2Layer
|
from .http2 import Http2Layer
|
||||||
from .websocket import WebSocketLayer
|
from .websocket import WebSocketLayer
|
||||||
from .rawtcp import RawTCPLayer
|
from .rawtcp import RawTCPLayer
|
||||||
from .tls import TlsClientHello
|
|
||||||
from .tls import TlsLayer
|
from .tls import TlsLayer
|
||||||
from .tls import is_tls_record_magic
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Layer", "ServerConnectionMixin",
|
"Layer", "ServerConnectionMixin",
|
||||||
"TlsLayer", "is_tls_record_magic", "TlsClientHello",
|
"TlsLayer",
|
||||||
"UpstreamConnectLayer",
|
"UpstreamConnectLayer",
|
||||||
"HttpLayer",
|
"HttpLayer",
|
||||||
"Http1Layer",
|
"Http1Layer",
|
||||||
|
@ -1,14 +1,9 @@
|
|||||||
import struct
|
|
||||||
from typing import Optional # noqa
|
from typing import Optional # noqa
|
||||||
from typing import Union
|
from typing import Union
|
||||||
import io
|
|
||||||
|
|
||||||
from kaitaistruct import KaitaiStream
|
|
||||||
from mitmproxy import exceptions
|
from mitmproxy import exceptions
|
||||||
from mitmproxy.contrib.kaitaistruct import tls_client_hello
|
from mitmproxy.net import tls as net_tls
|
||||||
from mitmproxy.proxy.protocol import base
|
from mitmproxy.proxy.protocol import base
|
||||||
from mitmproxy.net import check
|
|
||||||
|
|
||||||
|
|
||||||
# taken from https://testssl.sh/openssl-rfc.mappping.html
|
# taken from https://testssl.sh/openssl-rfc.mappping.html
|
||||||
CIPHER_ID_NAME_MAP = {
|
CIPHER_ID_NAME_MAP = {
|
||||||
@ -200,7 +195,6 @@ CIPHER_ID_NAME_MAP = {
|
|||||||
0x080080: 'RC4-64-MD5',
|
0x080080: 'RC4-64-MD5',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# We manually need to specify this, otherwise OpenSSL may select a non-HTTP2 cipher by default.
|
# We manually need to specify this, otherwise OpenSSL may select a non-HTTP2 cipher by default.
|
||||||
# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=apache-2.2.15&openssl=1.0.2&hsts=yes&profile=old
|
# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=apache-2.2.15&openssl=1.0.2&hsts=yes&profile=old
|
||||||
DEFAULT_CLIENT_CIPHERS = (
|
DEFAULT_CLIENT_CIPHERS = (
|
||||||
@ -216,114 +210,7 @@ DEFAULT_CLIENT_CIPHERS = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def is_tls_record_magic(d):
|
|
||||||
"""
|
|
||||||
Returns:
|
|
||||||
True, if the passed bytes start with the TLS record magic bytes.
|
|
||||||
False, otherwise.
|
|
||||||
"""
|
|
||||||
d = d[:3]
|
|
||||||
|
|
||||||
# TLS ClientHello magic, works for SSLv3, TLSv1.0, TLSv1.1, TLSv1.2
|
|
||||||
# http://www.moserware.com/2009/06/first-few-milliseconds-of-https.html#client-hello
|
|
||||||
return (
|
|
||||||
len(d) == 3 and
|
|
||||||
d[0] == 0x16 and
|
|
||||||
d[1] == 0x03 and
|
|
||||||
0x0 <= d[2] <= 0x03
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_client_hello(client_conn):
|
|
||||||
"""
|
|
||||||
Peek into the socket and read all records that contain the initial client hello message.
|
|
||||||
|
|
||||||
client_conn:
|
|
||||||
The :py:class:`client connection <mitmproxy.connections.ClientConnection>`.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The raw handshake packet bytes, without TLS record header(s).
|
|
||||||
"""
|
|
||||||
client_hello = b""
|
|
||||||
client_hello_size = 1
|
|
||||||
offset = 0
|
|
||||||
while len(client_hello) < client_hello_size:
|
|
||||||
record_header = client_conn.rfile.peek(offset + 5)[offset:]
|
|
||||||
if not is_tls_record_magic(record_header) or len(record_header) != 5:
|
|
||||||
raise exceptions.TlsProtocolException('Expected TLS record, got "%s" instead.' % record_header)
|
|
||||||
record_size = struct.unpack("!H", record_header[3:])[0] + 5
|
|
||||||
record_body = client_conn.rfile.peek(offset + record_size)[offset + 5:]
|
|
||||||
if len(record_body) != record_size - 5:
|
|
||||||
raise exceptions.TlsProtocolException("Unexpected EOF in TLS handshake: %s" % record_body)
|
|
||||||
client_hello += record_body
|
|
||||||
offset += record_size
|
|
||||||
client_hello_size = struct.unpack("!I", b'\x00' + client_hello[1:4])[0] + 4
|
|
||||||
return client_hello
|
|
||||||
|
|
||||||
|
|
||||||
class TlsClientHello:
|
|
||||||
|
|
||||||
def __init__(self, raw_client_hello):
|
|
||||||
self._client_hello = tls_client_hello.TlsClientHello(KaitaiStream(io.BytesIO(raw_client_hello)))
|
|
||||||
|
|
||||||
def raw(self):
|
|
||||||
return self._client_hello
|
|
||||||
|
|
||||||
@property
|
|
||||||
def cipher_suites(self):
|
|
||||||
return self._client_hello.cipher_suites.cipher_suites
|
|
||||||
|
|
||||||
@property
|
|
||||||
def sni(self):
|
|
||||||
if self._client_hello.extensions:
|
|
||||||
for extension in self._client_hello.extensions.extensions:
|
|
||||||
is_valid_sni_extension = (
|
|
||||||
extension.type == 0x00 and
|
|
||||||
len(extension.body.server_names) == 1 and
|
|
||||||
extension.body.server_names[0].name_type == 0 and
|
|
||||||
check.is_valid_host(extension.body.server_names[0].host_name)
|
|
||||||
)
|
|
||||||
if is_valid_sni_extension:
|
|
||||||
return extension.body.server_names[0].host_name.decode("idna")
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def alpn_protocols(self):
|
|
||||||
if self._client_hello.extensions:
|
|
||||||
for extension in self._client_hello.extensions.extensions:
|
|
||||||
if extension.type == 0x10:
|
|
||||||
return list(x.name for x in extension.body.alpn_protocols)
|
|
||||||
return []
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_client_conn(cls, client_conn):
|
|
||||||
"""
|
|
||||||
Peek into the connection, read the initial client hello and parse it to obtain ALPN values.
|
|
||||||
client_conn:
|
|
||||||
The :py:class:`client connection <mitmproxy.connections.ClientConnection>`.
|
|
||||||
Returns:
|
|
||||||
:py:class:`client hello <mitmproxy.proxy.protocol.tls.TlsClientHello>`.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
raw_client_hello = get_client_hello(client_conn)[4:] # exclude handshake header.
|
|
||||||
except exceptions.ProtocolException as e:
|
|
||||||
raise exceptions.TlsProtocolException('Cannot read raw Client Hello: %s' % repr(e))
|
|
||||||
|
|
||||||
try:
|
|
||||||
return cls(raw_client_hello)
|
|
||||||
except EOFError as e:
|
|
||||||
raise exceptions.TlsProtocolException(
|
|
||||||
'Cannot parse Client Hello: %s, Raw Client Hello: %s' %
|
|
||||||
(repr(e), raw_client_hello.encode("hex"))
|
|
||||||
)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "TlsClientHello( sni: %s alpn_protocols: %s, cipher_suites: %s)" % \
|
|
||||||
(self.sni, self.alpn_protocols, self.cipher_suites)
|
|
||||||
|
|
||||||
|
|
||||||
class TlsLayer(base.Layer):
|
class TlsLayer(base.Layer):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The TLS layer implements transparent TLS connections.
|
The TLS layer implements transparent TLS connections.
|
||||||
|
|
||||||
@ -334,13 +221,13 @@ class TlsLayer(base.Layer):
|
|||||||
the server connection.
|
the server connection.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, ctx, client_tls, server_tls, custom_server_sni = None):
|
def __init__(self, ctx, client_tls, server_tls, custom_server_sni=None):
|
||||||
super().__init__(ctx)
|
super().__init__(ctx)
|
||||||
self._client_tls = client_tls
|
self._client_tls = client_tls
|
||||||
self._server_tls = server_tls
|
self._server_tls = server_tls
|
||||||
|
|
||||||
self._custom_server_sni = custom_server_sni
|
self._custom_server_sni = custom_server_sni
|
||||||
self._client_hello = None # type: Optional[TlsClientHello]
|
self._client_hello = None # type: Optional[net_tls.ClientHello]
|
||||||
|
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
"""
|
"""
|
||||||
@ -355,7 +242,7 @@ class TlsLayer(base.Layer):
|
|||||||
if self._client_tls:
|
if self._client_tls:
|
||||||
# Peek into the connection, read the initial client hello and parse it to obtain SNI and ALPN values.
|
# Peek into the connection, read the initial client hello and parse it to obtain SNI and ALPN values.
|
||||||
try:
|
try:
|
||||||
self._client_hello = TlsClientHello.from_client_conn(self.client_conn)
|
self._client_hello = net_tls.ClientHello.from_client_conn(self.client_conn)
|
||||||
except exceptions.TlsProtocolException as e:
|
except exceptions.TlsProtocolException as e:
|
||||||
self.log("Cannot parse Client Hello: %s" % repr(e), "error")
|
self.log("Cannot parse Client Hello: %s" % repr(e), "error")
|
||||||
|
|
||||||
@ -414,7 +301,7 @@ class TlsLayer(base.Layer):
|
|||||||
if self._server_tls and not self.server_conn.tls_established:
|
if self._server_tls and not self.server_conn.tls_established:
|
||||||
self._establish_tls_with_server()
|
self._establish_tls_with_server()
|
||||||
|
|
||||||
def set_server_tls(self, server_tls: bool, sni: Union[str, None, bool]=None) -> None:
|
def set_server_tls(self, server_tls: bool, sni: Union[str, None, bool] = None) -> None:
|
||||||
"""
|
"""
|
||||||
Set the TLS settings for the next server connection that will be established.
|
Set the TLS settings for the next server connection that will be established.
|
||||||
This function will not alter an existing connection.
|
This function will not alter an existing connection.
|
||||||
@ -519,8 +406,10 @@ class TlsLayer(base.Layer):
|
|||||||
# We only support http/1.1 and h2.
|
# We only support http/1.1 and h2.
|
||||||
# If the server only supports spdy (next to http/1.1), it may select that
|
# 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.
|
# and mitmproxy would enter TCP passthrough mode, which we want to avoid.
|
||||||
alpn = [x for x in self._client_hello.alpn_protocols if
|
alpn = [
|
||||||
not (x.startswith(b"h2-") or x.startswith(b"spdy"))]
|
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:
|
if alpn and b"h2" in alpn and not self.config.options.http2:
|
||||||
alpn.remove(b"h2")
|
alpn.remove(b"h2")
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from mitmproxy import log
|
from mitmproxy import log
|
||||||
from mitmproxy import exceptions
|
from mitmproxy import exceptions
|
||||||
|
from mitmproxy.net import tls
|
||||||
from mitmproxy.proxy import protocol
|
from mitmproxy.proxy import protocol
|
||||||
from mitmproxy.proxy import modes
|
from mitmproxy.proxy import modes
|
||||||
from mitmproxy.proxy.protocol import http
|
from mitmproxy.proxy.protocol import http
|
||||||
@ -45,14 +46,14 @@ class RootContext:
|
|||||||
d = top_layer.client_conn.rfile.peek(3)
|
d = top_layer.client_conn.rfile.peek(3)
|
||||||
except exceptions.TcpException as e:
|
except exceptions.TcpException as e:
|
||||||
raise exceptions.ProtocolException(str(e))
|
raise exceptions.ProtocolException(str(e))
|
||||||
client_tls = protocol.is_tls_record_magic(d)
|
client_tls = tls.is_tls_record_magic(d)
|
||||||
|
|
||||||
# 1. check for --ignore
|
# 1. check for --ignore
|
||||||
if self.config.check_ignore:
|
if self.config.check_ignore:
|
||||||
ignore = self.config.check_ignore(top_layer.server_conn.address)
|
ignore = self.config.check_ignore(top_layer.server_conn.address)
|
||||||
if not ignore and client_tls:
|
if not ignore and client_tls:
|
||||||
try:
|
try:
|
||||||
client_hello = protocol.TlsClientHello.from_client_conn(self.client_conn)
|
client_hello = tls.ClientHello.from_client_conn(self.client_conn)
|
||||||
except exceptions.TlsProtocolException as e:
|
except exceptions.TlsProtocolException as e:
|
||||||
self.log("Cannot parse Client Hello: %s" % repr(e), "error")
|
self.log("Cannot parse Client Hello: %s" % repr(e), "error")
|
||||||
else:
|
else:
|
||||||
@ -76,10 +77,10 @@ class RootContext:
|
|||||||
# if the user manually sets a scheme for connect requests, we use this to decide if we
|
# if the user manually sets a scheme for connect requests, we use this to decide if we
|
||||||
# want TLS or not.
|
# want TLS or not.
|
||||||
if top_layer.connect_request.scheme:
|
if top_layer.connect_request.scheme:
|
||||||
tls = top_layer.connect_request.scheme == "https"
|
server_tls = top_layer.connect_request.scheme == "https"
|
||||||
else:
|
else:
|
||||||
tls = client_tls
|
server_tls = client_tls
|
||||||
return protocol.TlsLayer(top_layer, client_tls, tls)
|
return protocol.TlsLayer(top_layer, client_tls, server_tls)
|
||||||
|
|
||||||
# 3. In Http Proxy mode and Upstream Proxy mode, the next layer is fixed.
|
# 3. In Http Proxy mode and Upstream Proxy mode, the next layer is fixed.
|
||||||
if isinstance(top_layer, protocol.TlsLayer):
|
if isinstance(top_layer, protocol.TlsLayer):
|
||||||
|
@ -53,3 +53,27 @@ class TestTLSInvalid:
|
|||||||
|
|
||||||
with pytest.raises(exceptions.TlsException, match="ALPN error"):
|
with pytest.raises(exceptions.TlsException, match="ALPN error"):
|
||||||
tls.create_client_context(alpn_select="foo", alpn_select_callback="bar")
|
tls.create_client_context(alpn_select="foo", alpn_select_callback="bar")
|
||||||
|
|
||||||
|
|
||||||
|
class TestClientHello:
|
||||||
|
def test_no_extensions(self):
|
||||||
|
data = bytes.fromhex(
|
||||||
|
"03015658a756ab2c2bff55f636814deac086b7ca56b65058c7893ffc6074f5245f70205658a75475103a152637"
|
||||||
|
"78e1bb6d22e8bbd5b6b0a3a59760ad354e91ba20d353001a0035002f000a000500040009000300060008006000"
|
||||||
|
"61006200640100"
|
||||||
|
)
|
||||||
|
c = tls.ClientHello(data)
|
||||||
|
assert c.sni is None
|
||||||
|
assert c.alpn_protocols == []
|
||||||
|
|
||||||
|
def test_extensions(self):
|
||||||
|
data = bytes.fromhex(
|
||||||
|
"03033b70638d2523e1cba15f8364868295305e9c52aceabda4b5147210abc783e6e1000022c02bc02fc02cc030"
|
||||||
|
"cca9cca8cc14cc13c009c013c00ac014009c009d002f0035000a0100006cff0100010000000010000e00000b65"
|
||||||
|
"78616d706c652e636f6d0017000000230000000d00120010060106030501050304010403020102030005000501"
|
||||||
|
"00000000001200000010000e000c02683208687474702f312e3175500000000b00020100000a00080006001d00"
|
||||||
|
"170018"
|
||||||
|
)
|
||||||
|
c = tls.ClientHello(data)
|
||||||
|
assert c.sni == 'example.com'
|
||||||
|
assert c.alpn_protocols == [b'h2', b'http/1.1']
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
from mitmproxy.proxy.protocol.tls import TlsClientHello
|
|
||||||
|
|
||||||
|
|
||||||
class TestClientHello:
|
|
||||||
|
|
||||||
def test_no_extensions(self):
|
|
||||||
data = bytes.fromhex(
|
|
||||||
"03015658a756ab2c2bff55f636814deac086b7ca56b65058c7893ffc6074f5245f70205658a75475103a152637"
|
|
||||||
"78e1bb6d22e8bbd5b6b0a3a59760ad354e91ba20d353001a0035002f000a000500040009000300060008006000"
|
|
||||||
"61006200640100"
|
|
||||||
)
|
|
||||||
c = TlsClientHello(data)
|
|
||||||
assert c.sni is None
|
|
||||||
assert c.alpn_protocols == []
|
|
||||||
|
|
||||||
def test_extensions(self):
|
|
||||||
data = bytes.fromhex(
|
|
||||||
"03033b70638d2523e1cba15f8364868295305e9c52aceabda4b5147210abc783e6e1000022c02bc02fc02cc030"
|
|
||||||
"cca9cca8cc14cc13c009c013c00ac014009c009d002f0035000a0100006cff0100010000000010000e00000b65"
|
|
||||||
"78616d706c652e636f6d0017000000230000000d00120010060106030501050304010403020102030005000501"
|
|
||||||
"00000000001200000010000e000c02683208687474702f312e3175500000000b00020100000a00080006001d00"
|
|
||||||
"170018"
|
|
||||||
)
|
|
||||||
c = TlsClientHello(data)
|
|
||||||
assert c.sni == 'example.com'
|
|
||||||
assert c.alpn_protocols == [b'h2', b'http/1.1']
|
|
Loading…
Reference in New Issue
Block a user