zhuhaow / NEKit

A toolkit for Network Extension Framework
https://zhuhaow.github.io/NEKit
BSD 3-Clause "New" or "Revised" License
2.84k stars 670 forks source link

Packets being tunneled through PacketTunnelProvider.packetFlow v.s. requests made to HTTP proxy server. #202

Closed saoudrizwan closed 6 years ago

saoudrizwan commented 6 years ago

Hello, I implemented a GCDHTTPProxyServer as my PacketTunnelProvider's HTTP proxy server (kind of like Specht) and I didn't have to implement packetFlow's readPackets/writePackets handlers. I was able to make requests through Safari and other apps just fine. However I noticed that raw sockets didn't get sent/read properly (e.g. through FaceTime) so after implementing packetFlow.readPackets/.writePackets cycle, the raw sockets seemed to have started working and apps like FaceTime work now (e.g. streaming video/audio.) My question is - does the PacketTunnelProvider's settings HTTP proxy only handle HTTP/HTTPS requests, while all other 'raw socket packets' are handled by packetFlow? Do they not work together? I noticed that when my device is idle, it is constantly reading UDP packets from Apple's servers, while not going through my HTTP proxy server. Any help would be greatly appreciated!

zhuhaow commented 6 years ago

My question is - does the PacketTunnelProvider's settings HTTP proxy only handle HTTP/HTTPS requests, while all other 'raw socket packets' are handled by packetFlow? Do they not work together?

They are two different things working at different levels. HTTP proxy only works with HTTP protocol. As modern (a.k.a every) HTTP proxy supports CONNECT command which will act as a tunnel forwarding data stream back and forth to support HTTPS, we can use HTTP proxy supporting CONNECT as a stream proxy. In other way, we can ensemble TCP packets back to the origin data flow and send them with the HTTP proxy. However, every modern OS obeys the protocol specification faithfully and only uses HTTP proxy as proxy for HTTP requests.

As I just said, they work at different levels, so it is not bypassed because OS thinks it is already proxied. When you create a packetFlow you create a virtual network device (utunx) that you can route IP packets to. In your case I assume you route everything except for a few internal subnets, among them, usually are 127.0.0.1/8 which is the localhost, this means the data sending to the proxy at localhost (I'm assuming you start a proxy listening at 127.0.0.1) will not be route to the utunx but to lo0, that's why it won't be captured by the packetFlow. If you then connect to remote servers without setting the output device, it will go to utunx which will be captured by packetFlow. So if you want to connect to remote directly, you have to "protect" the socket. The NWTCPConnection from Network extension does exactly this.

I noticed that when my device is idle, it is constantly reading UDP packets from Apple's servers, while not going through my HTTP proxy server.

As I just explained, there is no way to send UDP packets through HTTP proxy server, even if you rebel against the specification. You can use SOCKS5 server to proxy that (given the server enables it), but the OS still won't send UDP packets though SOCKS5 server even if you set them in the system settings since it would cause serious problems if users don't understand what they are doing.

saoudrizwan commented 6 years ago

In other way, we can resemble TCP packets back to the origin data flow and send them with the HTTP proxy. However, every modern OS obeys the protocol specification faithfully and only uses HTTP proxy as proxy for HTTP requests.

Ahh, so it is up to the OS to determine if certain packets should be sent to the HTTP proxy if an HTTP request specifically was made. But if a different application layer protocol, like RTP for video streaming or VOIP for media traffic, was used then the OS would not send it through the proxy.

I assume you route everything except for a few internal subnets, among them, usually are 127.0.0.1/8 which is the localhost, this means the data sending to the proxy at localhost will not be route to the utunx but to lo0, that's why it won't be captured by the packetFlow.

You are exactly correct! I set the included routes to [NEIPv4Route.default()], which according to this thread does not include the traffic to local host or traffic bound to a specific interface. So any traffic sent to my HTTP proxy (AKA local host) will not be routed through my PacketTunnelProvider! Makes perfect sense!

So if you want to connect to remote directly, you have to "protect" the socket. The NWTCPConnection from Network extension does exactly this.

Do you mean using createTCPConnectionToEndpoint:enableTLS:TLSParameters:delegate: method to tunnel packets from packetFlow.readPackets to a tunnel server, and writing packets received from tunnel server to packetFlow.writePackets? What do you mean by "protect"? I am using your TCPStack class to handle the packetFlow, and set its .proxyServer property to the GCDHTTPProxyServer. So all TCP traffic is first routed through my packetFlow and then sent through the HTTP proxy server. So now the only thing not going through the HTTP proxy is UDP packets (using UDPDirectStack), since it is sent directly to the destination, correct?

Thank you so much for the explanation, your responses to all the issues here and in the Specht repos have helped me wrap my head around all the lower level networking involved in NetworkExtensions tremendously.

zhuhaow commented 6 years ago

Ahh, so it is up to the OS to determine if certain packets should be sent the the HTTP proxy if an HTTP request specifically was made. But if a different application layer protocol, like RTP for video streaming or VOIP for media traffic, was used then the OS would not send it through the proxy.

Do not confuse anything relating to HTTP with packets, they works at different level and there is nothing requires HTTP to even transmit by packets.

The simpler answer is, OS decides what to do.

A more precise answer would be, it depends on what API you call. As I'm not an iOS/Mac developer, I'm not sure about the hierarchy here, but the principle holds. Let's say you want to make a HTTP request, you don't create a socket and serialize the request yourself, instead you call NSURLSession, which internally create a NSSocket or CFSocket to send the request and get the response back. The NSURLSession here (or some other class it depends on) will read the system configuration, decide whether to use a proxy to send the request and handle negotiation with the proxy server.

So will app using other protocol uses proxy? It depends on how they are implemented.

Do you mean using createTCPConnectionToEndpoint:enableTLS:TLSParameters:delegate: method to tunnel packets from packetFlow.readPackets to a tunnel server, and writing packets received from tunnel server to packetFlow.writePackets? What do you mean by "protect"?

You will be working with data stream with NWTCPConnection, not packets. You need to transform the packets back into the stream and then break the received stream data into packets. "protect" means don't go through the route table matching logic.

I am using your TCPStack class to handle the packetFlow, and set its .proxyServer property to the GCDHTTPProxyServer. So all traffic is first routed through my packetFlow and then sent through the HTTP proxy server. So now the only thing not going through the HTTP proxy is UDP packets (using UDPDirectStack), since it is sent directly to the destination, correct?

Seems like so.

saoudrizwan commented 6 years ago

A more precise answer would be, it depends on what API you call. you call NSURLSession, which internally create a NSSocket or CFSocket to send the request and get the response back. The NSURLSession here (or some other class it depends on) will read the system configuration, decide whether to use a proxy to send the request and handle negotiation with the proxy server.

Ahhh, so that's how the system determines where to route packets! That clarifies things.

I'm not an iOS/Mac developer

You know much more about iOS and Mac development than most iOS/Mac developers I know :^)

Thank you again @zhuhaow, you've been very helpful!