From 476428a0de631949cde8e7ffced34f14de443b32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=A1=D0=BA?= =?UTF-8?q?=D0=BE=D1=80=D0=B1=D0=BE=D0=B2=D0=B5=D0=BD=D0=BA=D0=BE?= Date: Wed, 14 May 2025 22:22:29 +0300 Subject: [PATCH 1/2] ISSUE-7675 Fix connection leaks --- .../network-extension/FlowExtensions.swift | 30 ++++++++++++------- .../TransparentProxyProvider.swift | 3 ++ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/mitmproxy-macos/redirector/network-extension/FlowExtensions.swift b/mitmproxy-macos/redirector/network-extension/FlowExtensions.swift index ea35acac..5d12d244 100644 --- a/mitmproxy-macos/redirector/network-extension/FlowExtensions.swift +++ b/mitmproxy-macos/redirector/network-extension/FlowExtensions.swift @@ -17,6 +17,7 @@ extension NEAppProxyTCPFlow { self.outboundCopier(conn) } else { // log.debug("outbound copier: error copying: \(String(describing: error), privacy: .public)") + self.closeConnection(conn, error) } })) } else { @@ -24,10 +25,9 @@ extension NEAppProxyTCPFlow { "outbound copier end: \(String(describing: data), privacy: .public) \(String(describing: error), privacy: .public)" ) conn.send(content: nil, isComplete: true, completion: .contentProcessed({ error in - conn.cancel() // log.debug("outbound copier: sent end.") + self.closeConnection(conn, error) })) - self.closeWriteWithError(error) } } } @@ -40,12 +40,14 @@ extension NEAppProxyTCPFlow { self.write(data) { error in if error == nil { self.inboundCopier(conn) + } else { + self.closeConnection(conn, error) } } case (_, true, _): - self.closeReadWithError(error) + self.closeConnection(conn, error) default: - self.closeReadWithError(error) + self.closeConnection(conn, error) log.info( "inbound copier error=\(String(describing: error), privacy: .public) isComplete=\(String(describing: isComplete), privacy: .public)" ) @@ -61,8 +63,9 @@ extension NEAppProxyUDPFlow { func readDatagrams() async throws -> ([Data], [NetworkExtension.NWEndpoint]) { return try await withCheckedThrowingContinuation { continuation in readDatagrams { datagrams, endpoints, error in - if let error = error { + if let error { continuation.resume(throwing: error) + return } guard let datagrams = datagrams, let endpoints = endpoints else { fatalError("No error, but also no datagrams") @@ -94,10 +97,10 @@ extension NEAppProxyUDPFlow { try await conn.send(ipc: message) } } + self.closeConnection(conn, nil) } catch { log.error("Error in outbound UDP copier: \(String(describing: error), privacy: .public)") - self.closeWriteWithError(error) - conn.cancel() + self.closeConnection(conn, error) } } } @@ -108,18 +111,25 @@ extension NEAppProxyUDPFlow { while true { // log.debug("UDP inbound: receiving...") guard let packet = try await conn.receive(ipc: MitmproxyIpc_UdpPacket.self) else { - self.closeReadWithError(nil) break } // log.debug("UDP inbound: received packet.: \(String(describing: packet), privacy: .public)") let endpoint = NWHostEndpoint(address: packet.remoteAddress) try await self.writeDatagrams([packet.data], sentBy: [endpoint]) } + self.closeConnection(conn, nil) } catch { log.error("Error in inbound UDP copier: \(String(describing: error), privacy: .public)") - self.closeReadWithError(error) - conn.cancel() + self.closeConnection(conn, error) } } } } + +extension NEAppProxyFlow { + func closeConnection(_ conn: NWConnection, _ error: Error?) { + conn.cancel() + self.closeReadWithError(error) + self.closeWriteWithError(error) + } +} diff --git a/mitmproxy-macos/redirector/network-extension/TransparentProxyProvider.swift b/mitmproxy-macos/redirector/network-extension/TransparentProxyProvider.swift index c11d9a96..2718d22b 100644 --- a/mitmproxy-macos/redirector/network-extension/TransparentProxyProvider.swift +++ b/mitmproxy-macos/redirector/network-extension/TransparentProxyProvider.swift @@ -32,9 +32,11 @@ class TransparentProxyProvider: NETransparentProxyProvider { switch state { case .failed(.posix(.ENETDOWN)): log.debug("control channel closed, stopping proxy.") + control.forceCancel() self.cancelProxyWithError(.none) case .failed(let err): log.error("control channel failed: \(err, privacy: .public)") + control.forceCancel() self.cancelProxyWithError(err) default: break @@ -48,6 +50,7 @@ class TransparentProxyProvider: NETransparentProxyProvider { } } catch { log.error("Error on control channel: \(String(describing: error), privacy: .public)") + control.forceCancel() self.cancelProxyWithError(error) } } From 1f39c09533aecdc84327b3742ffef29796041371 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Thu, 29 May 2025 15:20:52 +0200 Subject: [PATCH 2/2] update CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a83feeb4..7cbd5f2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## Unreleased: mitmproxy_rs next +- Fix a bug where macOS local capture mode wouldn't clean up connections and stop working after a while. + The changes may break half-closed TCP streams. ## 29 April 2025: mitmproxy_rs 0.12.3