C# 4.5.2 framework HttpClient.GetAsync() method works fine on Windows 10 when system is not using VPN.
When VPN is connected HttpClient.GetAsync() call to the same address just blocks until it times out. Both Edge and Chrome have no issues accessing that same address.
Is there a way to see what is happening? What is HttpClient doing differently?
Update: Got some interesting clues by calling Dns.GetHostEntry()
. Without VPN this call returned only IPv4 addresses that all could be connected to. With VPN client connected Dns.GetHostEntry()
returned additional IPv6 addresses at the top of the list. Connection to all IPv6 addresses timed out but all IPv4 ones still worked OK. Now is there a way to figure out without trying to connect which addresses work and which ones do not?
2 Answers
Answers 1
In my experience, this sounds like a VPN / firewall issue to me. One quick thing to toggle in windows is under you VPN adapter properties, try unchecking "Use default gateway on remote network" - I know it sounds like a long shot but have had this problem in the past...
Answers 2
Have to answer this myself as this problem has a simple cause but very confusing symptoms.
The root cause:
DNS reports only IPv4 addresses for the host when system is not connected to VPN. All IPv4 addresses are usable.
When VPN connection is active DNS returns IPv6 addresses in addition to IPv4. IPv4 addresses are still accessible but IPv6 are not.
The cause of such invalid network configuration is still a mystery that deserves its own separate post.
Confusing part:
Some apps work no matter what VPN connection status is.
"But web browser can connect to the same host with or without VPN." True. Browsers may use Happy eyeballs approach attempting to connect using both IPv4 and IPv6 at the same time.
"But my old app has not problems connecting." Also true. Some older and not so old apps use IPv4 protocol by default. Support for IPv6 or IPv4+IPv6 has to be explicitly implemented.
"But it works sometimes". This happens when VPN connections are not reliable. It leads to all sorts of solutions that are mere coincidences.
What exactly is happening:
HttpClient.GetAsync() uses default DNS resolution and can connect using both IPv4 and IPv6 addresses. It does not discriminate and there is no direct way to influence protocol selection. If DNS returns inaccessible address then HttpClient may use that invalid address to connect resulting in timeout.
Possible workarounds:
The best: ask IT to fix IPv6 DNS issues. DNS should not report inaccessible addresses.
Good: implement Happy eyeballs approach. Connect to both IPv6 and IPv4 host addresses using numeric IP instead of automatic resolution using host name.
OK: Always connect to IPv4 using numeric IP.
Here is the piece of code that shows how to connect to a specific IP address:
// Get DNS entries for the host. var hostEntry = Dns.GetHostEntry(uri.Host); // Get IPv4 address var ip4 = hostEntry.AddressList.First(addr => addr.AddressFamily == AddressFamily.InterNetwork); // Build URI with numeric IPv4 var uriBuilderIP4 = new UriBuilder(uri); uriBuilderIP4.Host = ip4.ToString()); var uri4 = uriBuilder4.Uri; // Get IPv6 address var ip6 = hostEntry.AddressList.First(addr => addr.AddressFamily == AddressFamily.InterNetworkV6); // Build URI with numeric IPv6 var uriBuilderIP6 = new UriBuilder(uri); uriBuilderIP6.Host = $"[{ip6}]"; var uri6 = uriBuilder6.Uri;
For HTTPS connections numeric addresses work only with "host" header with the name of the host (not an IP address) in it. Here is the way to add it.
var client = new HttpClient(); // Add "host" header with real host name e.g. stackoverflow.com client.DefaultRequestHeaders.Add("Host", uri.Host);
0 comments:
Post a Comment