diff --git a/CHANGELOG.md b/CHANGELOG.md index 6378beae4..3c1ffd9db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,8 @@ ([#7130](https://github.com/mitmproxy/mitmproxy/pull/7130), @catap) - Fix of measurement unit in HAR import, duration is in milliseconds ([#7179](https://github.com/mitmproxy/mitmproxy/pull/7179), @dstd) +- `Connection.tls_version` now is `QUICv1` instead of `QUIC` for QUIC. + ([#7201](https://github.com/mitmproxy/mitmproxy/pull/7201), @mhils) - Add support for full mTLS with client certs between client and mitmproxy. ([#7175](https://github.com/mitmproxy/mitmproxy/pull/7175), @Kriechi) diff --git a/mitmproxy/addons/dumper.py b/mitmproxy/addons/dumper.py index 1fdfe8bcf..5964f0e7f 100644 --- a/mitmproxy/addons/dumper.py +++ b/mitmproxy/addons/dumper.py @@ -347,7 +347,7 @@ class Dumper: if self.match(f): message = f.messages[-1] direction = "->" if message.from_client else "<-" - if f.client_conn.tls_version == "QUIC": + if f.client_conn.tls_version == "QUICv1": if f.type == "tcp": quic_type = "stream" else: diff --git a/mitmproxy/connection.py b/mitmproxy/connection.py index dfa474d0d..7215a7a79 100644 --- a/mitmproxy/connection.py +++ b/mitmproxy/connection.py @@ -27,6 +27,18 @@ class ConnectionState(Flag): TransportProtocol = Literal["tcp", "udp"] +# https://docs.openssl.org/master/man3/SSL_get_version/#return-values +TlsVersion = Literal[ + "SSLv3", + "TLSv1", + "TLSv1.1", + "TLSv1.2", + "TLSv1.3", + "DTLSv0.9", + "DTLSv1", + "DTLSv1.2", + "QUICv1", +] # practically speaking we may have IPv6 addresses with flowinfo and scope_id, # but type checking isn't good enough to properly handle tuple unions. @@ -104,7 +116,7 @@ class Connection(serializable.SerializableDataclass, metaclass=ABCMeta): """The active cipher name as returned by OpenSSL's `SSL_CIPHER_get_name`.""" cipher_list: Sequence[str] = () """Ciphers accepted by the proxy server on this connection.""" - tls_version: str | None = None + tls_version: TlsVersion | None = None """The active TLS version.""" sni: str | None = None """ diff --git a/mitmproxy/io/compat.py b/mitmproxy/io/compat.py index 75e601654..d4e22ad26 100644 --- a/mitmproxy/io/compat.py +++ b/mitmproxy/io/compat.py @@ -424,6 +424,15 @@ def convert_19_20(data): return data +def convert_20_21(data): + data["version"] = 21 + if data["client_conn"]["tls_version"] == "QUIC": + data["client_conn"]["tls_version"] = "QUICv1" + if data["server_conn"]["tls_version"] == "QUIC": + data["server_conn"]["tls_version"] = "QUICv1" + return data + + def _convert_dict_keys(o: Any) -> Any: if isinstance(o, dict): return {strutils.always_str(k): _convert_dict_keys(v) for k, v in o.items()} @@ -488,6 +497,7 @@ converters = { 17: convert_17_18, 18: convert_18_19, 19: convert_19_20, + 20: convert_20_21, } diff --git a/mitmproxy/proxy/layers/quic/_stream_layers.py b/mitmproxy/proxy/layers/quic/_stream_layers.py index 2d2bdb9c0..3f460dc44 100644 --- a/mitmproxy/proxy/layers/quic/_stream_layers.py +++ b/mitmproxy/proxy/layers/quic/_stream_layers.py @@ -216,7 +216,7 @@ class QuicLayer(tunnel.TunnelLayer): self.conn.certificate_list = [certs.Cert(cert) for cert in all_certs] assert self.quic.tls.key_schedule self.conn.cipher = self.quic.tls.key_schedule.cipher_suite.name - self.conn.tls_version = "QUIC" + self.conn.tls_version = "QUICv1" # log the result and report the success to addons if self.debug: diff --git a/mitmproxy/proxy/layers/tls.py b/mitmproxy/proxy/layers/tls.py index 6c0f9473f..bf253fbda 100644 --- a/mitmproxy/proxy/layers/tls.py +++ b/mitmproxy/proxy/layers/tls.py @@ -1,5 +1,6 @@ import struct import time +import typing from collections.abc import Iterator from dataclasses import dataclass from logging import DEBUG @@ -11,6 +12,7 @@ from OpenSSL import SSL from mitmproxy import certs from mitmproxy import connection +from mitmproxy.connection import TlsVersion from mitmproxy.net.tls import starts_like_dtls_record from mitmproxy.net.tls import starts_like_tls_record from mitmproxy.proxy import commands @@ -377,7 +379,9 @@ class TLSLayer(tunnel.TunnelLayer): self.conn.timestamp_tls_setup = time.time() self.conn.alpn = self.tls.get_alpn_proto_negotiated() self.conn.cipher = self.tls.get_cipher_name() - self.conn.tls_version = self.tls.get_protocol_version_name() + self.conn.tls_version = typing.cast( + TlsVersion, self.tls.get_protocol_version_name() + ) if self.debug: yield commands.Log( f"{self.debug}[tls] tls established: {self.conn}", DEBUG diff --git a/mitmproxy/tools/console/common.py b/mitmproxy/tools/console/common.py index 651e22317..28f4714a5 100644 --- a/mitmproxy/tools/console/common.py +++ b/mitmproxy/tools/console/common.py @@ -762,7 +762,7 @@ def format_flow( duration = f.messages[-1].timestamp - f.client_conn.timestamp_start else: duration = None - if f.client_conn.tls_version == "QUIC": + if f.client_conn.tls_version == "QUICv1": protocol = "quic" else: protocol = f.type diff --git a/mitmproxy/version.py b/mitmproxy/version.py index 5e84f10e8..d9ea06e61 100644 --- a/mitmproxy/version.py +++ b/mitmproxy/version.py @@ -7,7 +7,7 @@ MITMPROXY = "mitmproxy " + VERSION # Serialization format version. This is displayed nowhere, it just needs to be incremented by one # for each change in the file format. -FLOW_FORMAT_VERSION = 20 +FLOW_FORMAT_VERSION = 21 def get_dev_version() -> str: diff --git a/test/mitmproxy/addons/test_dumper.py b/test/mitmproxy/addons/test_dumper.py index e9b6a92bc..9db3bd1cb 100644 --- a/test/mitmproxy/addons/test_dumper.py +++ b/test/mitmproxy/addons/test_dumper.py @@ -306,7 +306,7 @@ def test_quic(): d = dumper.Dumper(sio) with taddons.context(d): f = tflow.ttcpflow() - f.client_conn.tls_version = "QUIC" + f.client_conn.tls_version = "QUICv1" # TODO: This should not be metadata, this should be typed attributes. f.metadata["quic_stream_id_client"] = 1 f.metadata["quic_stream_id_server"] = 1 @@ -314,7 +314,7 @@ def test_quic(): assert "quic stream 1" in sio.getvalue() f2 = tflow.tudpflow() - f2.client_conn.tls_version = "QUIC" + f2.client_conn.tls_version = "QUICv1" # TODO: This should not be metadata, this should be typed attributes. f2.metadata["quic_stream_id_client"] = 1 f2.metadata["quic_stream_id_server"] = 1 diff --git a/test/mitmproxy/data/dumpfile-19.mitm b/test/mitmproxy/data/dumpfile-19.mitm new file mode 100644 index 000000000..480dc1030 --- /dev/null +++ b/test/mitmproxy/data/dumpfile-19.mitm @@ -0,0 +1,2252 @@ +131541:9:websocket;0:~8:response;126314:6:reason;0:,11:status_code;3:200#13:timestamp_end;17:1726927927.799734^15:timestamp_start;18:1726927927.7027874^8:trailers;0:~7:content;125959: + + QUIC | Cloudflare + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + +
+ +
+ + Industries + + + + +
+ + + + + + +
+ +
+ +
+ +
+ +
+
+ +
+ +
+ + Develop + + + + +
+ + + + + +
+ + Connect + + + + +
+ + +
+
+
+ + + + + +
+ +
+ + + + + +
+
+ +
+ +
+
+
+
+
+

Does my browser support HTTP/3 & QUIC?

+ +

When loading this page from Cloudflare's edge network, your browser used HTTP/3.

+

This page is HTTP/3 & QUIC enabled. Try reloading a few times to spring it into action.

+

Not all HTTP clients have HTTP/3 & QUIC support configured. See our documentation for more information about how to check and configure your favorite client such as Chrome, Firefox or curl.

+
+ +
+ +
+ +
+
+ +
+
+
+
+

Available for all Cloudflare zones

+ +

Cloudflare's QUIC & HTTP/3 is generally available to all zones. You can control whether it is enabled or disabled using a toggle on the Network tab of your dashboard.

+ +
+ +
+ +
+ +
+
+ + +
+
+
+
+ +
+ +
+

New to QUIC?

+ +

QUIC is a new transport protocol being developed in the Internet Engineering Task Force (IETF). It offers reliability, security and multiplexing by default.

+

HTTP/3 is a new version of HTTP that sits on top of QUIC. It leverages the new transport features to fix performance problems such as Head-of-Line blocking. This enables web pages to load faster, especially over troublesome networks.

+

If you’re new to QUIC and need to learn more about the protocol, the following resources will help you gain a better understanding.

+ +
+
+
+
+ + +
+
+
+
+ +
+ +
+

Faster handshakes

+ +

QUIC handshakes are faster by design when compared to the equivalent TCP & TLS.

+

Since QUIC uses TLS 1.3, it can benefit from zero roundtrip time (0-RTT) connection resumption. Check out our 0-RTT blogpost to understand more about this feature.

+ +
+
+
+
+ +
+
+
+
+

Powered by delicious quiche

+ +

Quiche is Cloudflare's own open-source implementation of the QUIC and HTTP/3 protocols written in Rust.

+

The following articles provide some background.

+ + + +
+ +
+ +
+ +
+
+ +
+
+
+
+

Continuing developments

+ +

Cloudflare will continue to make updates to its QUIC implementation as the IETF makes progress towards finalizing the protocol standard.

+

Since launching QUIC & HTTP/3 support we've continued to measure performance and deploy optimisations such as new Congestion Control algorithms.

+ +
+ +
+ +
+ +
+
+ + +
+
+
+
+

Get started

+ + + +
+
+
+
+ + + + +
+ + + + + +,7:headers;171:40:4:date,29:Sat, 21 Sep 2024 14:12:09 GMT,]28:12:content-type,9:text/html,]27:14:content-length,6:125959,]23:6:server,10:cloudflare,]33:6:cf-ray,20:8c6aa6058e1730db-FRA,]]12:http_version;6:HTTP/3,}7:request;318:4:path;1:/,9:authority;19:cloudflare-quic.com,6:scheme;5:https,6:method;3:GET,4:port;3:443#4:host;19:cloudflare-quic.com;13:timestamp_end;18:1726927927.6633708^15:timestamp_start;18:1726927927.6628444^8:trailers;0:~7:content;0:,7:headers;51:28:10:user-agent,10:curl/8.9.1,]15:6:accept,3:*/*,]]12:http_version;6:HTTP/3,}6:backup;0:~17:timestamp_created;18:1726927927.6629763^7:comment;0:;8:metadata;0:}6:marked;0:;9:is_replay;0:~11:intercepted;5:false!11:server_conn;4129:3:via;0:~19:timestamp_tcp_setup;0:~7:address;29:19:cloudflare-quic.com;3:443#]19:timestamp_tls_setup;17:1726927927.652231^13:timestamp_end;0:~15:timestamp_start;18:1726927927.5914717^3:sni;19:cloudflare-quic.com;11:tls_version;4:QUIC;11:cipher_list;0:]6:cipher;18:AES_256_GCM_SHA384;11:alpn_offers;5:2:h3,]4:alpn;2:h3,16:certificate_list;3610:1359:-----BEGIN CERTIFICATE----- +MIIDvTCCA2OgAwIBAgIQfhh2L/oNtKINNqCqBxC8uTAKBggqhkjOPQQDAjA7MQsw +CQYDVQQGEwJVUzEeMBwGA1UEChMVR29vZ2xlIFRydXN0IFNlcnZpY2VzMQwwCgYD +VQQDEwNXRTEwHhcNMjQwOTAzMDYwNzEyWhcNMjQxMjAyMDYwNzExWjAeMRwwGgYD +VQQDExNjbG91ZGZsYXJlLXF1aWMuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD +QgAEF2huuLoPM1zpmb7/VxTIoogVQlgutJM06dXnB8r7pvCmkAhuqyR/dkL/5cF8 +pxLW3OIVysPQQQpRwj1Lm6iC3KOCAmQwggJgMA4GA1UdDwEB/wQEAwIHgDATBgNV +HSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSh9T+DF7Tm +xFzZEpIqjbqdWYaKRjAfBgNVHSMEGDAWgBSQd5I1Z8T/qMyp5nvZgHl7zJP5ODBe +BggrBgEFBQcBAQRSMFAwJwYIKwYBBQUHMAGGG2h0dHA6Ly9vLnBraS5nb29nL3Mv +d2UxL2ZoZzAlBggrBgEFBQcwAoYZaHR0cDovL2kucGtpLmdvb2cvd2UxLmNydDA1 +BgNVHREELjAsghNjbG91ZGZsYXJlLXF1aWMuY29tghUqLmNsb3VkZmxhcmUtcXVp +Yy5jb20wEwYDVR0gBAwwCjAIBgZngQwBAgEwNgYDVR0fBC8wLTAroCmgJ4YlaHR0 +cDovL2MucGtpLmdvb2cvd2UxL0pXVzNHajU2WmQ0LmNybDCCAQUGCisGAQQB1nkC +BAIEgfYEgfMA8QB2AHb/iD8KtvuVUcJhzPWHujS0pM27KdxoQgqf5mdMWjp0AAAB +kba1foQAAAQDAEcwRQIgHOTcOeZUJQc+kBVHwJHIqbvMnmx2D84nAFEfVv1x1EgC +IQDa0znSfzpHAIJ1/x8eWwUbgOK5e7Hn13R1DR057im4PgB3ANq2v2s/tbYin5vC +u1xr6HCRcWy7UYSFNL2kPTBI1/urAAABkba1fpEAAAQDAEgwRgIhAPjK86OWjZ/U +VrtJoGh2O00SgwW+4m13T1zuILLzYp0gAiEA075qC++r7e/KBafw69zsJ45JZF+i +N0LjWhF4WZ4yHYswCgYIKoZIzj0EAwIDSAAwRQIhALnaJ0FCYUhqQHSvCWgmBkXw +xqLr9zTDFVsHDm2jACBrAiAlovujZazaEb4n/GMSSL6BUM9VLeBZiUN0patcRTg2 +0A== +-----END CERTIFICATE----- +,969:-----BEGIN CERTIFICATE----- +MIICnzCCAiWgAwIBAgIQf/MZd5csIkp2FV0TttaF4zAKBggqhkjOPQQDAzBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjQwHhcNMjMxMjEzMDkwMDAwWhcNMjkwMjIwMTQw +MDAwWjA7MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVR29vZ2xlIFRydXN0IFNlcnZp +Y2VzMQwwCgYDVQQDEwNXRTEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARvzTr+ +Z1dHTCEDhUDCR127WEcPQMFcF4XGGTfn1XzthkubgdnXGhOlCgP4mMTG6J7/EFmP +LCaY9eYmJbsPAvpWo4H+MIH7MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggr +BgEFBQcDAQYIKwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU +kHeSNWfE/6jMqeZ72YB5e8yT+TgwHwYDVR0jBBgwFoAUgEzW63T/STaj1dj8tT7F +avCUHYwwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzAChhhodHRwOi8vaS5wa2ku +Z29vZy9yNC5jcnQwKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL2MucGtpLmdvb2cv +ci9yNC5jcmwwEwYDVR0gBAwwCjAIBgZngQwBAgEwCgYIKoZIzj0EAwMDaAAwZQIx +AOcCq1HW90OVznX+0RGU1cxAQXomvtgM8zItPZCuFQ8jSBJSjz5keROv9aYsAm5V +sQIwJonMaAFi54mrfhfoFNZEfuNMSQ6/bIBiNLiyoX46FohQvKeIoJ99cx7sUkFN +7uJW +-----END CERTIFICATE----- +,1265:-----BEGIN CERTIFICATE----- +MIIDejCCAmKgAwIBAgIQf+UwvzMTQ77dghYQST2KGzANBgkqhkiG9w0BAQsFADBX +MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEQMA4GA1UE +CxMHUm9vdCBDQTEbMBkGA1UEAxMSR2xvYmFsU2lnbiBSb290IENBMB4XDTIzMTEx +NTAzNDMyMVoXDTI4MDEyODAwMDA0MlowRzELMAkGA1UEBhMCVVMxIjAgBgNVBAoT +GUdvb2dsZSBUcnVzdCBTZXJ2aWNlcyBMTEMxFDASBgNVBAMTC0dUUyBSb290IFI0 +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE83Rzp2iLYK5DuDXFgTB7S0md+8Fhzube +Rr1r1WEYNa5A3XP3iZEwWus87oV8okB2O6nGuEfYKueSkWpz6bFyOZ8pn6KY019e +WIZlD6GEZQbR3IvJx3PIjGov5cSr0R2Ko4H/MIH8MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUgEzW63T/STaj1dj8tT7FavCUHYwwHwYDVR0jBBgwFoAUYHtmGkUN +l8qJUC99BM00qP/8/UswNgYIKwYBBQUHAQEEKjAoMCYGCCsGAQUFBzAChhpodHRw +Oi8vaS5wa2kuZ29vZy9nc3IxLmNydDAtBgNVHR8EJjAkMCKgIKAehhxodHRwOi8v +Yy5wa2kuZ29vZy9yL2dzcjEuY3JsMBMGA1UdIAQMMAowCAYGZ4EMAQIBMA0GCSqG +SIb3DQEBCwUAA4IBAQAYQrsPBtYDh5bjP2OBDwmkoWhIDDkic574y04tfzHpn+cJ +odI2D4SseesQ6bDrarZ7C30ddLibZatoKiws3UL9xnELz4ct92vID24FfVbiI1hY ++SW6FoVHkNeWIP0GCbaM4C6uVdF5dTUsMVs/ZbzNnIdCp5Gxmx5ejvEau8otR/Cs +kGN+hr/W5GvT1tMBjgWKZ1i4//emhA1JG1BbPzoLJQvyEotc03lXjTaCzv8mEbep +8RqZ7a2CPsgRbuvTPBwcOMBBmuFeU88+FSBX6+7iP0il8b4Z0QFqIwwMHfs/L6K1 +vepuoxtGzi4CZ68zJpiq1UvSqTbFJjtbD4seiMHl +-----END CERTIFICATE----- +,]3:tls;4:true!5:error;0:~18:transport_protocol;3:udp;2:id;36:e47ee853-3b4f-4c26-9efe-2f0597c917ea;8:sockname;27:15:192.168.178.113;5:62652#]8:peername;21:11:104.22.8.38;3:443#]}11:client_conn;473:10:proxy_mode;36:reverse:https://cloudflare-quic.com/;8:mitmcert;0:~19:timestamp_tls_setup;18:1726927927.6583664^13:timestamp_end;0:~15:timestamp_start;17:1726927927.585847^3:sni;9:localhost;11:tls_version;4:QUIC;11:cipher_list;0:]6:cipher;18:AES_256_GCM_SHA384;11:alpn_offers;5:2:h3,]4:alpn;2:h3,16:certificate_list;0:]3:tls;4:true!5:error;0:~18:transport_protocol;3:udp;2:id;36:14d997f0-eb0b-4e39-958b-6ba688d9c705;8:sockname;12:2:::;4:8080#]8:peername;14:3:::1;5:62648#]}5:error;0:~2:id;36:82091aa1-7003-4aa6-9d00-d1e6ad69af03;4:type;4:http;7:version;2:20#} \ No newline at end of file diff --git a/test/mitmproxy/io/test_compat.py b/test/mitmproxy/io/test_compat.py index 35b11d619..744a14610 100644 --- a/test/mitmproxy/io/test_compat.py +++ b/test/mitmproxy/io/test_compat.py @@ -13,6 +13,7 @@ from mitmproxy import io ["dumpfile-7-websocket.mitm", "https://echo.websocket.org/", 6], ["dumpfile-7.mitm", "https://example.com/", 2], ["dumpfile-10.mitm", "https://example.com/", 1], + ["dumpfile-19.mitm", "https://cloudflare-quic.com/", 1], ], ) def test_load(tdata, dumpfile, url, count): diff --git a/web/src/js/flow/utils.ts b/web/src/js/flow/utils.ts index a5ba3b35f..5921137dc 100644 --- a/web/src/js/flow/utils.ts +++ b/web/src/js/flow/utils.ts @@ -193,7 +193,7 @@ export const canReplay = (flow: Flow): boolean => { export const getIcon = (flow: Flow): string => { if (flow.type !== "http") { - if (flow.client_conn.tls_version === "QUIC") { + if (flow.client_conn.tls_version === "QUICv1") { return `resource-icon-quic`; } return `resource-icon-${flow.type}`;