Hello, I am looking for help debugging a UDP Flow Copier in the context of a TransparentProxyProvider Network System Extension. My SysEx has been handling TCP flows reliably for months but extending it to handle UDP flows including DNS has been problematic. With extensive logging I am able to observe the DNS flow as follows (see diagram attached separately): 1. DNS requests are intercepted by the SysEx and pushed it up to my proxy app. 2. The proxy app forwards the request back to the SysEx where it is sent to the network through a BSD socket. 3. The request and corresponding response can be observed in Wireshark. 4. The SysEx intercepts the response and pushes it up to my proxy app. 5. The proxy app forwards the response back to the SysEx where the flow copier writes it to the flow that originated the DNS request. The final write to the flow copier causes my TransparentProxySystem extension to remove itself by disabling the filter configuration. I’m attaching an excerpt from the ULS log below. Each proxy flow has a sequential endpoint ID so I have only included lines for flow ID: 1. “->” means outbound and “<-“ means inbound. ➜ ~ grep "ID: 1 " /Users/peter.sichel/Library/CloudStorage/OneDrive-DtexSystems\,Inc/Desktop/DNS\ response\ through\ proxy.txt -> ID: 1 pushStatusToApp - localEndpoint: 10.255.254.154:0 -> ID: 1 pushStatusToApp - Notify the app, JSON, status is connect -> ID: 1 pushStatusTo MitM proxy App - success -> ID: 1 pushDataToApp data size: 43 -> ID: 1 pushDataToApp: Notify the app, JSON: 318 bytes DTNetworkTracker postSendToEndpoint: calling sendFilteredUdpData -> ID: 1 and data count: 43 DTNetworkTracker DTNetT -- IPCConnection - sendFilteredUdpData -> ID: 1 with data count: 43 DTNetworkTracker DTNetT -- IPCConnection - sendFilteredUdpData -> ID: 1 calling providerProxy.sendFilteredUdpData IPCConnection.sendFilteredUdpData -> ID: 1 data count: 43 PassThroughProviderCore.sendFilteredUdpData -> ID: 1 data count: 43 PassThroughProviderCore.sendFilteredUdpData -> ID: 1 sending 43 bytes -> ID: 1 pushDataTo MitM proxy App - success DTNetworkTracker DTNetT -- IPCConnection - providerProxy.sendFilteredUdpData -> ID: 1 succeeded <- ID: 1 pushDataToApp data size: 99 <- ID: 1 pushDataToApp: Notify the app, JSON: 389 bytes DTNetworkTracker postReceiveToEndpoint: calling sendFilteredUdpData <- ID: 1 and data count: 99 DTNetworkTracker DTNetT -- IPCConnection - sendFilteredUdpData <- ID: 1 with data count: 99 DTNetworkTracker DTNetT -- IPCConnection - sendFilteredUdpData <- ID: 1 calling providerProxy.sendFilteredUdpData IPCConnection.sendFilteredUdpData <- ID: 1 data count: 99 PassThroughProviderCore.sendFilteredUdpData <- ID: 1 data count: 99 PassThroughProviderCore.sendFilteredUdpData <- ID: 1 writing 99 bytes to flow localEndpoint:Optional(10.255.254.154:63056) DTNetworkTracker DTNetT -- IPCConnection - providerProxy.sendFilteredUdpData <- ID: 1 succeeded <- ID: 1 pushDataTo MitM proxy App - success PassThroughProviderCore.sendFilteredUdpData <- ID: 1 did write to flow ➜ ~ At this point the extension fails with these logged lines: DTNetT -- will start system extension mode DTNetT -- IPCConnection Starting XPC listener for mach service APAZN4S39Z.com.example.apple-samplecode.DTNetworkTracker.DTNetworkTrackerExtension DTNetT -- will start dispatch main The offending code is this block that attempts to send the filtered UDP data back to the original application flow: copier.flow.writeDatagrams([data], sentBy: endpoints) { errorQ in self.queue.async { switch errorQ { case nil: self.logger.debug("PassThroughProviderCore.sendFilteredUdpData \(dArrow, privacy: .public) ID: \(endpointId) did write to flow") completionHandler(nil) case let error?: self.logger.error("PassThroughProviderCore.sendFilteredUdpData \(dArrow, privacy: .public) ID: \(endpointId) writer received error: \(error.localizedDescription, privacy: .public)") completionHandler(error) } } } Commenting out these few lines allows the extension to continue running but without forwarding DNS responses back to the original requester. Tens of DNS responses like this are partially processed before the SysEx overflows: PassThroughProviderCore.sendFilteredUdpData <- ID: 32 writing 90 bytes to flow localEndpoint:Optional(10.255.254.154:62159) I was concerned I might be calling “copier.flow.writeDatagrams()” improperly but the completionHandler is invoked without error as shown in the log. The comment describing the method parameters conflicts with the actual code as shown here: /** * @method writeDatagrams:sentByEndpoint:completionHandler: * @discussion Write datagrams to the flow. * @param datagrams An array of NSData objects containing the data to be written. * @param remoteEndpoints The source endpoints of the datagrams. * @param completionHandler A block that will be executed when the datagrams have been written to the corresponding socket's receive buffer. */ @available(macOS, introduced: 10.11, deprecated: 15.0) open func writeDatagrams(_ datagrams: [Data], sentBy remoteEndpoints: [NWEndpoint], completionHandler: @escaping ((any Error)?) -> Void) @param datagrams is an array of Data, not NSData suggesting this might have changed since introduced in 10.11 . My question remains how to writeDatagrams() to the original flow without disabling my TransparentProxyProvider SysEx. Kind regards, - Peter