mirror of
https://github.com/vee1e/mitmproxy.git
synced 2026-09-01 18:27:18 +00:00
transparent mode: re-use client SNI when connecting upstream (#4680)
This commit is contained in:
parent
a78069f907
commit
18ca5a6369
3 changed files with 88 additions and 3 deletions
|
|
@ -710,8 +710,22 @@ class HttpLayer(layer.Layer):
|
|||
yield from self.event_to_child(self.streams[stream_id], event)
|
||||
elif isinstance(event, events.ConnectionEvent):
|
||||
if event.connection == self.context.server and self.context.server not in self.connections:
|
||||
# We didn't do anything with this connection yet, now the peer has closed it - let's close it too!
|
||||
yield commands.CloseConnection(event.connection)
|
||||
# We didn't do anything with this connection yet, now the peer is doing something.
|
||||
if isinstance(event, events.ConnectionClosed):
|
||||
# The peer has closed it - let's close it too!
|
||||
yield commands.CloseConnection(event.connection)
|
||||
elif isinstance(event, events.DataReceived):
|
||||
# The peer has sent data. This can happen with HTTP/2 servers that already send a settings frame.
|
||||
child_layer: HttpConnection
|
||||
if self.context.server.alpn == b"h2":
|
||||
child_layer = Http2Client(self.context.fork())
|
||||
else:
|
||||
child_layer = Http1Client(self.context.fork())
|
||||
self.connections[self.context.server] = child_layer
|
||||
yield from self.event_to_child(child_layer, events.Start())
|
||||
yield from self.event_to_child(child_layer, event)
|
||||
else:
|
||||
raise AssertionError(f"Unexpected event: {event}")
|
||||
else:
|
||||
handler = self.connections[event.connection]
|
||||
yield from self.event_to_child(handler, event)
|
||||
|
|
@ -811,7 +825,12 @@ class HttpLayer(layer.Layer):
|
|||
send_connect = event.tls or self.mode != HTTPMode.upstream
|
||||
stack /= _upstream_proxy.HttpUpstreamProxy.make(context, send_connect)
|
||||
if event.tls:
|
||||
context.server.sni = event.address[0]
|
||||
# Assume that we are in transparent mode and lazily did not open a connection yet.
|
||||
# We don't want the IP (which is the address) as the upstream SNI, but the client's SNI instead.
|
||||
if self.mode == HTTPMode.transparent and event.address == self.context.server.address:
|
||||
context.server.sni = self.context.client.sni or event.address[0]
|
||||
else:
|
||||
context.server.sni = event.address[0]
|
||||
stack /= tls.ServerTLSLayer(context)
|
||||
|
||||
stack /= HttpClient(context)
|
||||
|
|
|
|||
|
|
@ -1174,3 +1174,35 @@ def test_reuse_error(tctx):
|
|||
)
|
||||
assert b"502 Bad Gateway" in error_html()
|
||||
assert b"tls verify failed" in error_html()
|
||||
|
||||
|
||||
def test_transparent_sni(tctx):
|
||||
"""Test that we keep the SNI in lazy transparent mode."""
|
||||
tctx.client.sni = "example.com"
|
||||
tctx.server.address = ("192.0.2.42", 443)
|
||||
tctx.server.tls = True
|
||||
|
||||
flow = Placeholder(HTTPFlow)
|
||||
|
||||
server = Placeholder(Server)
|
||||
assert (
|
||||
Playbook(http.HttpLayer(tctx, HTTPMode.transparent))
|
||||
>> DataReceived(tctx.client, b"GET / HTTP/1.1\r\n\r\n")
|
||||
<< http.HttpRequestHeadersHook(flow)
|
||||
>> reply()
|
||||
<< http.HttpRequestHook(flow)
|
||||
>> reply()
|
||||
<< OpenConnection(server)
|
||||
)
|
||||
assert server().address == ("192.0.2.42", 443)
|
||||
assert server().sni == "example.com"
|
||||
|
||||
|
||||
def test_original_server_disconnects(tctx):
|
||||
"""Test that we correctly handle the case where the initial server conn is just closed."""
|
||||
tctx.server.state = ConnectionState.OPEN
|
||||
assert (
|
||||
Playbook(http.HttpLayer(tctx, HTTPMode.transparent))
|
||||
>> ConnectionClosed(tctx.server)
|
||||
<< CloseConnection(tctx.server)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -693,3 +693,37 @@ class TestClient:
|
|||
>> DataReceived(tctx.server, frame_factory.build_data_frame(b"foo").serialize())
|
||||
<< SendData(tctx.server, frame_factory.build_rst_stream_frame(1, ErrorCodes.STREAM_CLOSED).serialize())
|
||||
) # important: no ResponseData event here!
|
||||
|
||||
|
||||
def test_early_server_data(tctx):
|
||||
playbook, cff = start_h2_client(tctx)
|
||||
sff = FrameFactory()
|
||||
|
||||
tctx.server.address = ("example.com", 80)
|
||||
tctx.server.state = ConnectionState.OPEN
|
||||
tctx.server.alpn = b"h2"
|
||||
|
||||
flow = Placeholder(HTTPFlow)
|
||||
server1 = Placeholder(bytes)
|
||||
server2 = Placeholder(bytes)
|
||||
assert (
|
||||
playbook
|
||||
>> DataReceived(tctx.client,
|
||||
cff.build_headers_frame(example_request_headers, flags=["END_STREAM"]).serialize())
|
||||
<< http.HttpRequestHeadersHook(flow)
|
||||
>> reply()
|
||||
<< (h := http.HttpRequestHook(flow))
|
||||
# Surprise! We get data from the server before the request hook finishes.
|
||||
>> DataReceived(tctx.server, sff.build_settings_frame({}).serialize())
|
||||
<< SendData(tctx.server, server1)
|
||||
# Request hook finishes...
|
||||
>> reply(to=h)
|
||||
<< SendData(tctx.server, server2)
|
||||
)
|
||||
assert [type(x) for x in decode_frames(server1())] == [
|
||||
hyperframe.frame.SettingsFrame,
|
||||
hyperframe.frame.SettingsFrame,
|
||||
]
|
||||
assert [type(x) for x in decode_frames(server2())] == [
|
||||
hyperframe.frame.HeadersFrame,
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue