Post

Replies

Boosts

Views

Activity

Reply to UDP TransparentProxyProvider
With more testing I've learned there are a few reasons packets might not be intercepted. Only outbound requests (so called connections) are intercepted. Sequencing and timing when enabling a SysEx are also important. After rewriting my UDP flow copier to use NWConnection with ".udp" and debugging I found a working combination.
2w
Reply to UDP TransparentProxyProvider
I still want to add a UDP receive capability within my test case but since packets are being sent from an external shell script to avoid the proxy provider context the typical send and receive on the same socket from inside my test case won't work. Since I'm sending to an echo server I need to know the send port or receive on the sending socket. Testing UDP through a SysEx seems like kind of a hassle. If I knew exactly what criteria were preventing UDP packets generated from within my test case from being proxied I could hopefully find an easier work around. It's not the bundle ID since each component (App, SysEx, and Tests have their own). The mystery continues...
3w
Reply to UDP TransparentProxyProvider
I extracted my NWConnection send UDP code into a tiny separate program and see the packets being intercepted. I suspect the problem was running the code in the context of a test case with the same bundle ID and signature as the SysEx. In a SysEx context it appears the TransparentProxyProvider uses the identity of the sender to avoid intercepting its own packets. I updated my test case to call out to shell script to send UDP echo datagrams to work around the problem. I miss Mentat Portable Streams :-)
3w
Reply to QNE2TransparentProxyMac sample code
With my UDP Flow Copier working as demonstrated by the fact that it is proxying DNS traffic successfully, I am finally writing tests to verify UDP packet filtering. I'm sending packets to a public UDP echo server and reading the response successfully. In my initial testing however the TransparentProxyProvider System Extension is not intercepting my UDP traffic. handleNewUDPFlow() is being called for DNS but not for my test case UDP echo sends and receives. I've tried sending UDP with both GCDAsyncSocket and connection = NWConnection(host: host, port: port, using: .udp) Is there some other criteria for UDP datagrams to be intercepted? Google search suggests this might be a known issue for connected or async UDP sockets.
Aug ’25
Reply to QNE2TransparentProxyMac sample code
Per the updated description the packets are getting out to the wire. The problem I'm still seeing is with writeDatagrams() back to the original flow that's disabling my TransparentProxyProvider. I've included a bit of detail including the line of code that's causing the failure. Scouring the system log I found this: default 15:48:52.258383-0400 DTNetworkTracker Last disconnect error for DTNetworkTracker changed from "The VPN session failed because an internal error occurred." to "none". My question now is what's causing this failure or how to write datagrams back to the original NEAppProxyUDPFlow flow safely? Thanks!
Aug ’25
Reply to QNE2TransparentProxyMac sample code
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. A detailed problem description is attached. I am prepared to open a DTS support incident (on request) but do not have a "focussed example project" since testing System Extensions is not easily focussed into a small example. The problem is consistently reproducible per the detailed logging provided. Please see the attached problem description and diagram. Thank you Problem description with logs and code snippet
Jul ’25
Reply to QNE2TransparentProxyMac sample code
I've been assuming the network extension mechanism is aware of the network stack order so that when I send a UDP datagram from within my extension it enters the outbound network stack below my extension. Perhaps this is an unwarrented assumption dating back to the STREAMs network plugin architecture. Do I need to tag or otherwise identify my outgoing UDP datagram(s) so I can ignore them in a subsequent call to handleNewUDPFlow(_ flow:)?
Jul ’25
Reply to QNE2TransparentProxyMac sample code
I still seem to be missing something. I updated my UDP flow copier to use an NWUDPSession based on the initial remote endpoint. When I try to send a datagram to the actual remote destination, it's somehow being intercepted by my proxy (as if originating from an app) instead of going out over the wire. The send is a simple call like this: udpSession?.writeDatagram(messageData) I'd welcome any ideas for what might be missing or what to try next. Thanks!
Jul ’25
Reply to QNE2TransparentProxyMac sample code
So I have developed a UDP flow copier using BSD Sockets (via GCDAsyncUDPSocket) which appears to be sending and receiving UDP datagrams but I am struggling to get DNS querries to resolve. I can see in Wireshark UDP DNS queries are not getting out to the wire.  Internal logging suggests packets written to a normal UDP socket are being intercepted by our transparent proxy (SysEx) and routed back to our app. Do I need to use an In-Provider API or respect some property to send packets to their actual destination? The TCP Flow Copier uses an NWConnection which doesn’t fit the UDP flow semantics. One idea I tried exploring is to have my flow copier not handle DNS or broadcast traffic but I don’t see an easy way to do this. There are two ways to control what flows you want to handle. One is the provider settings that let you specify an array of included and excluded NENetworkRules. The excluded networks take precedence so if you exclude a network it will bypass your proxy regardless of any included networks. When I try to exclude traffic for port 53 (DNS) I am confronted with the following error message: startProxy - did not start, error: NETunnelProviderErrorDomain / 1 53 cannot be specified as the port for transparent proxy network rules. Create a NENetworkRule object with a domain endpoint to divert DNS queries for that domain to the provider. If the intent of not allowing port 53 is to avoid proxying all DNS traffic the restriction seems to be preventing the result it was intended to achieve. The other way to control what flows you want to handle is in the handleNewFlow method: override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool but this also fails to meet the need because FOR NEAppProxyUDPFlow there is no remote endpoint information available. Still looking for a workable path or example.
Jul ’25
Reply to QNE2TransparentProxyMac sample code
I’m still debugging my UdpFlowCopier and could use some guidance as some aspects are confusing. I need to copy datagrams between a provider flow object and an actual UDP socket or “connection”. Can you help me understand the difference between a NetworkExtension.NWEndpoint and Network.NWEndpoint and where I would need to use one versus the other? Following the TCP example, the PassThroughProviderCore calls handleNewUDPFlow(_ flow: NEAppProxyUDPFlow). There’s no remote endpoint but I can retrieve the local endpoint as guard let nwEndpoint = flow.localEndpoint?.nwEndpoint else { return } I’m trying to open a UDP listener on this endpoint as follows but it mostly returns failed to create listener on port 0 private func handleStart() -> State { let port = portForEndpoint(self.localEndpoint) ?? 0 logger.debug("UDPFlowCopier - handleStart copier \(self.osLogID) port \(port.rawValue, privacy: .public)") let params = NWParameters.udp params.allowFastOpen = true self.listener = try? NWListener(using: params, on: port) self.listener?.stateUpdateHandler = { update in switch update { case .ready: self.isReady = true //self.processEvent(.didOpenConnection) case .failed, .cancelled: // Announce we are no longer able to listen self.listening = false self.isReady = false logger.debug("UDPFlowCopier - copier \(self.osLogID) failed to create listener on port \(port.rawValue, privacy: .public)") self.stop() default: () } } self.listener?.newConnectionHandler = { connection in self.createConnection(connection: connection) } self.listener?.start(queue: self.queue) return .openingConnection } Can you offer any insight? Thanks
Jul ’25
Reply to QNE2TransparentProxyMac sample code
In func handleNewTCPFlow(_ flow: NEAppProxyTCPFlow) we create a connection as: let connection = NWConnection(to: flow.remoteEndpoint.nwEndpoint, using: .tcp) When converting this to UDP, the NEAppProxyUDPFlow doesn't have a flow.remoteEndpoint so it's unclear to me where I should get this from. Presumably each datagram has a destination which defines which flow it belongs to.
Jun ’25
Reply to QNE2TransparentProxyMac sample code
Thanks for getting back to me. I'm afraid my poor editing above confused the issue. The project claims it is based on the QNE2TransparentProxyMac and I've been working on it for a while to finish TLS inspection and filtering. The previous developer had moved on before I joined the project so I don't have whatever code he might have received. I want to add support for UDP and noticed it's not enabled in the Network Extension and there's no UDP flow copier. Is there some transparent proxy example that includes support for UDP? I'm following the TCP example to implement UDP support but would prefer not to reinvent it if it already exists. Thanks. Peter
Jun ’25