Code Repo    |     RSS
MD's Technical Sharing



Wednesday, May 14, 2014

Limited Internet connection issues when using VPN connections on Windows

As a frequent user of VPN (Virtual Private Network) for personal purposes, using both free software such as Hamachi and paid VPN service providers, I often encounter no issues accessing the Internet  with a VPN session connected, except for the unavoidable decrease in bandwidth. However, recently when connecting to a remote network at office via a VPN session to perform some troubleshooting, I noticed that the wireless connection status on Windows quickly became "limited" as soon as the VPN session was started:


Despite this, the VPN still worked fine and provided access to the internal network, but not the Internet. According to my system administrator, the network is secure and therefore has no route to the Internet so the behavior is somewhat expected as Windows automatically gives the VPN connection highest priority thus prohibiting access to the Internet. After much research, I found an apparently simple solution for this in the advanced IPv4 settings of the VPN connection:


By disabling "Use default gateway on remote network" and re-establishing the VPN connection, I noticed that the wireless connection status was no longer "limited" and Internet access suddenly became available: 


The VPN session was also established with the following details from ipconfig:

   IPv4 Address. . . . . . . . . . . : 192.168.20.200
   DNS Servers . . . . . . . . . . . : 192.168.20.1


Some quick tests by pinging to the DNS server at 192.168.20.1 and a few other machines on the network showed that both the VPN connection and Internet access now worked fine after the setting change.

That was it, just a simple setting change, or so I thought. Unfortunately it was not that simple. Another issue came when I realized I could not access 11.1.11.10, another computer on the network, which I used to be able to before the setting change. Pinging the computer simply received no reply. Turning on "Use default gateway on remote network" and the server became accessible again.

Why is this the case? After spending another few more hours researching, the answer is found in this article from Microsoft. Apparently this behavior is due to the characteristics of VPN connections on Windows. The major relevant points from the article are summarized below:
  1. By default, a newly created VPN connection on Windows will have "Use default gateway on remote network" enabled, which sends all network traffic on your computer through the VPN connection. 
  2. If there are multiple established VPN connections with the option "Use default gateway on remote network" checked, Windows will automatically pick the network with highest priority. The priority is usually defined automatically, but can be changed by unchecking "Automatic metric" and specifying a manual metric value.
  3. For each established VPN connection, a default route will be added for network based on a single class-based network ID, unless "Disable class based route addition" is checked, in which case no default routes will be added.
How does this explain the behavior I encountered? The answer lies in the automatic addition of default route. When the computer routes all traffic to the VPN connection (e.g. "Use default gateway on remote network" is checked), all requests will be routed through the VPN default gateway and thus I was able to access 192.168.20.1 and 11.1.11.10, despite not being able to access the Internet (since the VPN network is isolated). However, when the VPN does not have traffic priority (e.g. "Use default gateway on remote network" is unchecked), requests will be routed through the wireless connection, unless there is a specific route from the target IP address to the VPN default gateway.

In my case, the DNS server IP address (192.168.20.1) is within the single class network covered by the default route and is therefore automatically accessible. However, IP 11.1.11.10 is on a different network class and not covered by the default route, causing its requests to terminate on the wireless connection default gateway and failed.

The solution is to manually add a route that cover the path to 11.1.11.10 and other computers not on the same network class which I intend to access. This can be done using the route command. You may need to run Command Prompt as administrator to execute the route addition:

route add 11.0.0.0 mask 255.0.0.0 192.168.20.1 metric 1

With this, you can ping the 192.168.x.x network, 11.1.11.10 and any other 11.x.x.x IP addresses with no issues, while at the same time still able to access the Internet.

Just for educational purposes, if "Use default gateway on remote network" is unchecked and "Disable class based route addition" is checked, access to 192.168.x.x will also failed unless a manual route is added:

route add 192.0.0.0 mask 255.0.0.0 192.168.20.1 metric 1

Interestingly, during my research, I found many forum posts claiming this to be a bug with Windows networking stack. Microsoft also published a hotfix here, although this is intended for cases where Internet access is still limited despite the VPN providing full Internet access, and not for my cases where the VPN indeed has no route to the Internet. Most answers I found (even on MSDN) simply suggested unchecking "Use default gateway on remote network", which result in replies saying that the internal network is inaccessible following the changes, defeating the purposes of the VPN! Of course this will be the case unless the behavior of default route addition is understood.

Also, although the route command supports a -p parameter to add a persistent route which will still remain after a reboot, I have found its usage to be buggy. Firstly, the presumably persistent route will still be removed after reboot and needs to be re-added. Secondly, when the route is re-added, there will be an error message "The route addition failed: The object already exists" even though the route effectively does not exist. This may indeed be a Windows networking stack bug, or might perhaps be a problem with my network settings.

Similar to Windows, for each VPN connection on Mac OS X, a similar checkbox called "Send all traffic over VPN connection" is provided:


Unlike Windows, the default setting for this option is unchecked so all traffic will by default go though all available network connections in the order of priority specified in the Set Services Order window:

During my testing, the default route for the VPN connection on Mac OS X did not cover the entire single class network like on Windows, so we'll need to add the route manually to access the servers that we want to (unless "Send all traffic over VPN connection" is checked):

route -n add 192.0.0.0/8 192.168.20.1

This command will need to be prefixed with sudo or otherwise run as superuser (via su for example) to be executed successfully.
Read More »

Thursday, October 18, 2012

Using SOAP web service in an iOS application

I recently needed to connect to SOAP web services written in .NET/PHP from one of my iOS applications when I realized that is there is no built in support for SOAP messages (although iOS5 and above has support for JSON via the NSJSONSerialization class). Obviously I could decide to construct the SOAP XML messages manually and POST them to the web service via NSURLRequest, I soon realized that this approach is not feasible due to the many (50+) web services that my application needs to use.

Fortunately I find wsdl2objc, an Objective-C code-generator for SOAP web services which automatically parses the web service WSDL definition and generates the appropriate classes to send a request and parse the response. 

The tool is a simple Mac OS app that asks you for the location to your web service WSDL definition, which can be a URL or a local file, and where to put the generated code:




Supply the required information and press Parse WSDL. The tool should quickly finish and give you the resulting source code files, which look similar to below:



Among the files, MyService.h and MyService.m contain the definition and implementation of  your web service based on the WSDL file. The rest of the files are supporting classes to parse the XML messages. If you have more than one WSDL file, run the generator for each WSDL and include all the generated service class files in your project. The supporting classes only need to be included once.

You should have no issues compiling the generated code and make a simple web service call using the sample code given on the wsdl2objc home page. However, depending on the web service, you may run into any of the following problems: 

1. The web service does not receive the parameters passed in properly, or, for if the service is written in Java, exception "Cannot find dispatch method for {serviceURL}/{serviceName}" will be thrown on the server.
This is because the namespace attribute, xmlns, is missing from the SOAP body envelope. To fix this, locate the method:

- (NSString *)serializedFormUsingHeaderElements:(NSDictionary *)headerElements bodyElements:(NSDictionary *)bodyElements

Look for the following block of code

if((headerElements != nil) && ([headerElements count] > 0)) {
        xmlNodePtr headerNode = xmlNewDocNode(doc, soapEnvelopeNs, (const xmlChar*)"Header", NULL);
        xmlAddChild(root, headerNode);
       
        for(NSString *key in [headerElements allKeys]) {
            id header = [headerElements objectForKey:key];
            xmlAddChild(headerNode, [header xmlNodeForDoc:doc elementName:key]);
        }
    }


and add the following code just below the above code:

xmlNewNs(root, (const xmlChar*)"http://localhost/MyService", (const xmlChar*)"ns1");

Remember to modify the URL to point to the correct namespace of your web service. ns1 in the above code specifies the namespace that each of the web service method must adhere to. Some web services may not require this, but for those that require, failing to provide may result in issue (2) below.

2. Simple values (integers, strings) are received property by the web service, but complex values (custom types) are received as blank
This is because the web service methods do not specify the namespace (ns1) it belongs to. Fixing this requires manual modification of MyService.m for each of the web service in the WSDL file. Assuming your service is called MyService, look for the following line:

NSMutableDictionary *bodyElements = nil;
bodyElements = [NSMutableDictionary dictionary];
if(callerParams != nil) [bodyElements setObject:callerParams forKey:@"MyService"];

Replace the last line with

if(callerParams != nil) [bodyElements setObject:callerParams forKey:@"ns1:MyService"];

3. Special characters such as ampersand will not be received on the server
This is because the generated code forgets to escape the ampersand character, as required by the SOAP protocol. To fix this, open USAdditions.m, locate the xmlNodeForDoc method, and replace the method code with the following:


There is a reason why the last block of code is a colorful image :). Had it been text, Google Blogger will try to unescape the ampersand character (the withString part), resulting in wrong code.

With all the above changes, the generated code should be compatible with most SOAP web services.
Read More »