Bug 15152 - iOS WCF Web service host name resolution problem after changing networks
Summary: iOS WCF Web service host name resolution problem after changing networks
Status: NEW
Alias: None
Product: Class Libraries
Classification: Mono
Component: WCF assemblies (show other bugs)
Version: unspecified
Hardware: PC Mac OS
: --- normal
Target Milestone: Untriaged
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2013-10-02 18:57 UTC by Jon Goldberger [MSFT]
Modified: 2014-01-30 09:45 UTC (History)
6 users (show)

See Also:
Tags:
Is this bug a regression?: ---
Last known good build:


Attachments

Description Jon Goldberger [MSFT] 2013-10-02 18:57:40 UTC
I have an app that connects to a WCF service; that works fine in general. I have encountered a situation though where the app is unable to reconnect. The situation is:


(1) I have the app running on my iPad and the app connects via a host name to the WCF server on the office Wi-Fi; that works fine.

(2) I go somewhere else with the iPad with the app in the suspended state (i.e. without forcefully terminating the app). After opening the app again, this time connected to the Internet, it can no longer reconnect. The app connects again only after terminating and re-starting the app.

The difference between (1) and (2) is that the same host name resolves to different IP addresses inside the office on the office Wi-Fi and outside the office on the Internet.

I already re-create the endpoint and pass it to a new client before attempting a WCF service call, i.e.:

var endpointAddress = new EndpointAddress("http://hostName:1234/serviceName")
var client = new ServiceClient(binding, endpointAddress);

But it looks like as if the host name is never resolved again and I did not find a way to force the app to resolve the host name to the new / different IP address.

Is there a way to reset the host name/IP address association? Am I missing something else?
==============================================================================================
It would be good to understand what the WCF client does with a new EndpointAddress when it is created like so:

var endpointAddress = new EndpointAddress("http://hostName:1234/serviceName")
var client = new ServiceClient(binding, endpointAddress);

I.e. does it only perform a DNS lookup once per lifetime of the app? Or is there a timeout for retry and I’ve just not been patient enough?

So far I have observed the problem without proving that the resolved IP address remains the same; that is currently my assumption. Is there a method I could call to confirm that the resolved IP address indeed remains the same when changing networks? This would be the test case for the problem I suppose.
============================================================================================================
Comment 2 Sebastien Pouliot 2013-10-02 19:56:48 UTC
That's not really iOS specific (in the sense that there's not iOS specific code) and may be better filed in the more general Mono / Class Libraries.

From what I know .NET has ServicePointManager.DnsRefreshTimeout [1] but it's not implemented in Mono.

ServicePointManager.MaxServicePointIdleTime [2] is implemented (default to 15 minutes) but I do not known if WCF uses ServicePointManager (or if the Dns cache might still be in effect).

c.c. Martin as he knows more about WCF and the networking stack

[1] http://msdn.microsoft.com/en-us/library/system.net.servicepointmanager.dnsrefreshtimeout.aspx
[2] http://msdn.microsoft.com/en-us/library/system.net.servicepointmanager.maxservicepointidletime.aspx
Comment 4 Jon Goldberger [MSFT] 2014-01-15 19:46:56 UTC
From case 57518:
"I'm developing an (iOS.Xamarin)app that connects to REST, WebDAV and WCF
services via VPN using System.Net.HttpWebRequest and
System.ServiceModel.ClientBase<> classes.

VPN is starting automatically. To start VPN I'm calling StartWWAN(new
Uri(serviceUrl)). E.g:



// start VPN

Runtime.StartWWAN(new Uri("http://serverName"));



// setup request

var webRequest = WebRequest.Create(new
Uri("http://serverName:port/serviceName")) as HttpWebRequest;

webRequest.Method = "GET";

webRequest.AllowAutoRedirect = false;

webRequest.ContentLength = 0;

webRequest.Headers.Set("Pragma", "no-cache");



// setup service credentials

:



// request data

var webResponse = (HttpWebResponse)webRequest.GetResponse();

var reader = new StreamReader(webResponse.GetResponseStream());

var content = reader.ReadToEnd();





Sometimes StartWWAN(new Uri(serviceUrl)) does not start VPN and wrong
service IP is cached in mono. It remains cached until user unload
application or restart iPad.

After wrong IP address is cached there is no way to connect to server by
name inside VPN.



I found two bugs in bugzilla.xamarin.com related to my issue.



<https://bugzilla.xamarin.com/show_bug.cgi?id=11424>;
https://bugzilla.xamarin.com/show_bug.cgi?id=11424 

<https://bugzilla.xamarin.com/show_bug.cgi?id=15152>;
https://bugzilla.xamarin.com/show_bug.cgi?id=15152



Are there any workaround to reset DNS cache in mono or disable it at all?"
Comment 5 Andrey Beltiukov 2014-01-16 03:20:54 UTC
Version information for app with VPN problems

=== Xamarin Studio ===

Version 4.0.12 (build 3)
Installation UUID: 63e65309-d2a1-425e-a033-2dbdf229f771
Runtime:
	Mono 3.2.3 ((no/8d3b4b7)
	GTK 2.24.20
	GTK# (2.12.0.0)
	Package version: 302030000

=== Apple Developer Tools ===

Xcode 5.0.2 (3335.32)
Build 5A3005

=== Xamarin.iOS ===

Version: 7.0.1.4 (Business Edition)
Hash: 4cfca2f
Branch: 
Build date: 2013-20-09 23:14:32-0400

=== Xamarin.Android ===

Not Installed

=== Xamarin.Mac ===

Xamarin.Mac: Not Installed

=== Build Information ===

Release ID: 400120003
Git revision: 593d7acb1cb78ceeeb482d5133cf1fe514467e39
Build date: 2013-08-07 20:30:53+0000
Xamarin addins: 25a0858b281923e666b09259ad4746b774e0a873
Comment 6 Andrey Beltiukov 2014-01-17 06:23:48 UTC
I forced recycling of datapoints at some moments to workaround this bug.

static void ForceRecycleServicePoints()
{
    var servicePoints = (IDictionary)(typeof(ServicePointManager).GetField("servicePoints", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.GetField).GetValue(null));

    lock (servicePoints)
    {
        var toRemove =
            servicePoints
            .Cast<DictionaryEntry>()
            .Where(de => ((ServicePoint)de.Value).CurrentConnections == 0)
            .Select(de => de.Key)
            .ToList();

        foreach (var removing in toRemove)
        {
            servicePoints.Remove(removing);
        }
    }
}
Comment 7 Andrey Beltiukov 2014-01-17 06:26:15 UTC
I forced recycling of *ServicePoints*
Comment 8 Andrey Beltiukov 2014-01-30 09:45:26 UTC
Code above caused some problems and I changed it to

private static void ResetHosts()
{
    var servicePoints = (IDictionary)(typeof(ServicePointManager).GetField("servicePoints", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.GetField).GetValue(null));

    lock (servicePoints)
    {
        var toRemove =
            servicePoints
            .Cast<DictionaryEntry>()
            .Where(de => ((ServicePoint)de.Value).CurrentConnections == 0)
            .Select(de => de.Value)
            .ToList();

        foreach (var removing in toRemove)
        {
            var hostLock = typeof(ServicePoint).GetField("hostE", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance).GetValue(removing);

            lock (hostLock)
            {
                typeof(ServicePoint).GetField("host", BindingFlags.NonPublic | BindingFlags.SetField | BindingFlags.Instance).SetValue(removing, null);
            }
        }
    }
}

Note You need to log in before you can comment on or make changes to this bug.