mirror of
https://github.com/mitmproxy/mitmproxy.git
synced 2024-11-27 23:30:28 +00:00
[quic] fix next layer handling
This commit is contained in:
parent
201f03082a
commit
97ca30ce6f
@ -314,15 +314,21 @@ class NextLayer:
|
||||
if scheme in ("udp", "dtls"):
|
||||
return layers.UDPLayer(context)
|
||||
elif scheme == "http3":
|
||||
if isinstance(context.layers[-1], layers.ClientQuicLayer):
|
||||
return layers.HttpLayer(context, HTTPMode.transparent)
|
||||
else:
|
||||
return layers.ClientQuicLayer(context)
|
||||
elif scheme == "quic":
|
||||
# if the client supports QUIC, we use QUIC raw layer,
|
||||
# otherwise we only use the QUIC datagram only
|
||||
return (
|
||||
layers.RawQuicLayer(context)
|
||||
if isinstance(context.layers[-1], layers.ClientQuicLayer) else
|
||||
layers.UDPLayer(context)
|
||||
)
|
||||
if isinstance(context.layers[-1], layers.ClientQuicLayer):
|
||||
# the client supports QUIC, use raw layer
|
||||
return layers.RawQuicLayer(context)
|
||||
elif data_client:
|
||||
# we have received data, which was not a handshake, use UDP
|
||||
# on the client, and send datagrams over QUIC to the server
|
||||
return layers.UDPLayer(context)
|
||||
else:
|
||||
# wait for client data to make a decision
|
||||
return None
|
||||
elif scheme == "dns":
|
||||
return layers.DNSLayer(context)
|
||||
else:
|
||||
|
@ -5,6 +5,7 @@ from pathlib import Path
|
||||
import ssl
|
||||
from typing import Any, Optional, TypedDict
|
||||
|
||||
from aioquic.h3.connection import H3_ALPN
|
||||
from aioquic.tls import CipherSuite
|
||||
from OpenSSL import SSL, crypto
|
||||
from mitmproxy import certs, ctx, exceptions, connection, tls
|
||||
@ -360,7 +361,8 @@ class TlsConfig:
|
||||
if client.alpn_offers:
|
||||
server.alpn_offers = tuple(client.alpn_offers)
|
||||
else:
|
||||
server.alpn_offers = []
|
||||
# aioquic fails if no ALPN is offered, so use H3
|
||||
server.alpn_offers = tuple(alpn.encode("ascii") for alpn in H3_ALPN)
|
||||
|
||||
if not server.cipher_list and ctx.options.ciphers_server:
|
||||
server.cipher_list = ctx.options.ciphers_server.split(":")
|
||||
|
@ -7,7 +7,7 @@ from typing import Optional
|
||||
from mitmproxy import connection
|
||||
from mitmproxy.proxy import commands, events, layer
|
||||
from mitmproxy.proxy.commands import StartHook
|
||||
from mitmproxy.proxy.layers import dns, quic, tls, udp
|
||||
from mitmproxy.proxy.layers import quic, tls
|
||||
from mitmproxy.proxy.mode_specs import ReverseMode
|
||||
from mitmproxy.proxy.utils import expect
|
||||
|
||||
@ -68,12 +68,8 @@ class ReverseProxy(DestinationKnown):
|
||||
if not self.context.options.keep_host_header:
|
||||
self.context.server.sni = spec.address[0]
|
||||
self.child_layer = tls.ServerTLSLayer(self.context)
|
||||
elif spec.scheme == "udp":
|
||||
self.child_layer = udp.UDPLayer(self.context)
|
||||
elif spec.scheme == "http" or spec.scheme == "tcp":
|
||||
elif spec.scheme in ("tcp", "http", "udp", "dns"):
|
||||
self.child_layer = layer.NextLayer(self.context)
|
||||
elif spec.scheme == "dns":
|
||||
self.child_layer = dns.DNSLayer(self.context)
|
||||
else:
|
||||
raise AssertionError(spec.scheme) # pragma: no cover
|
||||
|
||||
|
@ -1057,8 +1057,8 @@ class ClientQuicLayer(QuicLayer):
|
||||
return False, f"Cannot parse ClientHello: {str(e)} ({data.hex()})"
|
||||
|
||||
# copy the client hello information
|
||||
self.context.client.sni = client_hello.sni
|
||||
self.context.client.alpn_offers = client_hello.alpn_protocols
|
||||
self.conn.sni = client_hello.sni
|
||||
self.conn.alpn_offers = client_hello.alpn_protocols
|
||||
|
||||
# check with addons what we shall do
|
||||
tls_clienthello = ClientHelloData(self.context, client_hello)
|
||||
@ -1068,7 +1068,7 @@ class ClientQuicLayer(QuicLayer):
|
||||
if tls_clienthello.ignore_connection:
|
||||
self.conn = self.tunnel_connection = connection.Client(
|
||||
("ignore-conn", 0), ("ignore-conn", 0), time.time(),
|
||||
transport_protocol="udp", proxy_mode=self.context.client.proxy_mode
|
||||
transport_protocol="udp"
|
||||
)
|
||||
|
||||
# we need to replace the server layer as well, if there is one
|
||||
|
@ -289,7 +289,13 @@ class TestNextLayer:
|
||||
layers.modes.ReverseProxy(ctx),
|
||||
layers.ServerQuicLayer(ctx),
|
||||
]
|
||||
assert isinstance(nl._next_layer(ctx, b"", b""), layers.UDPLayer)
|
||||
assert nl._next_layer(ctx, b"", b"") is None
|
||||
assert isinstance(nl._next_layer(ctx, b"notahandshake", b""), layers.UDPLayer)
|
||||
ctx.layers = [
|
||||
layers.modes.ReverseProxy(ctx),
|
||||
layers.ServerQuicLayer(ctx),
|
||||
]
|
||||
assert isinstance(nl._next_layer(ctx, quic_client_hello, b""), layers.ClientQuicLayer)
|
||||
|
||||
def test_next_layer_reverse_http3_mode(self):
|
||||
nl = NextLayer()
|
||||
@ -301,8 +307,8 @@ class TestNextLayer:
|
||||
ctx.layers = [
|
||||
layers.modes.ReverseProxy(ctx),
|
||||
layers.ServerQuicLayer(ctx),
|
||||
layers.ClientQuicLayer(ctx),
|
||||
]
|
||||
assert isinstance(nl._next_layer(ctx, b"notahandshakebutignore", b""), layers.ClientQuicLayer)
|
||||
decision = nl._next_layer(ctx, b"", b"")
|
||||
assert isinstance(decision, layers.HttpLayer)
|
||||
assert decision.mode is HTTPMode.transparent
|
||||
|
@ -161,6 +161,8 @@ def test_reverse_dns(tctx):
|
||||
assert (
|
||||
Playbook(modes.ReverseProxy(tctx), hooks=False)
|
||||
>> DataReceived(tctx.client, tflow.tdnsreq().packed)
|
||||
<< NextLayerHook(Placeholder(NextLayer))
|
||||
>> reply_next_layer(layers.DNSLayer)
|
||||
<< layers.dns.DnsRequestHook(f)
|
||||
>> reply(None)
|
||||
<< OpenConnection(server)
|
||||
@ -202,9 +204,11 @@ def test_udp(tctx: Context):
|
||||
Playbook(modes.ReverseProxy(tctx))
|
||||
<< OpenConnection(tctx.server)
|
||||
>> reply(None)
|
||||
>> DataReceived(tctx.client, b"test-input")
|
||||
<< NextLayerHook(Placeholder(NextLayer))
|
||||
>> reply_next_layer(layers.UDPLayer)
|
||||
<< udp.UdpStartHook(flow)
|
||||
>> reply()
|
||||
>> DataReceived(tctx.client, b"test-input")
|
||||
<< udp.UdpMessageHook(flow)
|
||||
>> reply()
|
||||
<< SendData(tctx.server, b"test-input")
|
||||
|
Loading…
Reference in New Issue
Block a user