I have been writing a custom subclass of NETransparentProxyProvider. Here is what I do to handle NEAppProxyUDPFlow.
(1) Return true in method handleNewUDPFlow(_:initialRemoteEndpoint:) and retain flow object
(2) Open flow
open(withLocalEndpoint:completionHandler:)
(3) Read datagrams
readDatagrams(completionHandler: @escaping ([Data]?, [NWEndpoint]?, Error?) -> Void)
(4) Create NWConnection object wait for it to be in ready state
(5) Send data from step 3
send(content: Data?, contentContext: NWConnection.ContentContext = .defaultMessage, isComplete: Bool = true, completion: NWConnection.SendCompletion)
(6) Listen for the response
receiveMessage(completion: @escaping (Data?, NWConnection.ContentContext?, Bool, NWError?) -> Void)
(7) Write a response to the flow
writeDatagrams(_ datagrams: [Data], sentBy remoteEndpoints: [NWEndpoint], completionHandler: @escaping (Error?) -> Void)
The scheme above works.
One of the questions I have is when to close the flow? The first case is when the datagrams and remoteEndpoints arrays are non-nil but are empty in readDatagrams callback
But how about the UDP server response? rfc768 spec is pretty short https://datatracker.ietf.org/doc/html/rfc768 And there is no response as such in UDP. The server extracts the source port and source address from the packet and may or may not send data to that socket. Theoretically, it can send multiple replies to the same socket How can I know that no more data is expected to be received in NWConnection to close the connection and release the flow? The receive message callback can only tell us that that one datagram has been delivered Can I not close the flow at all?
So, I think I understood how things work. Since the transport layer(my transparent proxy) can't decide if more data is coming from the remote side of the connection or not it should not do that. And rely on the app layer which can.
From practical tests, readDatagrams callback will not return empty the datagrams and remote endpoints arrays(EOF sign) until the app that opened socket(flow) is satisfied with the number of datagrams received from the remote side.
So, to summarize:
- Close connection and flow in case of errors
- Close flow as per documentation to readDatagrams function:
If the datagrams and remoteEndpoints arrays are non-nil but are empty, then no more datagrams can be subsequently read from the flow.