What does it do?
DynDNS is one of many services that can associate a domain name with an IP Address. A domain name is just a user friendly alias for an IP address. In situations where the IP addresses given out by ISPs (know as the WAN IP) are not always the same (dynamic versus static), it is not possible to find the device over the Internet without knowing the latest address given to it. A DynDNS client is a software that identifies changes to a WAN IP, and re-associates a pre-determined domain name with the new IP. The domain name can then be used to find the device over the Internet. Because 3G/GPRS connections always have dynamic IPs (at least in Australia), a DynDNS type service is needed to access a device over the Internet.
*Note – Many 3G/GPRS connections also have what are called ‘Private IPs’. You will need to find a provider that will allow your SIM to have a ‘Public IP’. In Australia, Optus works with no modifications, whist Telstra will provide the service if you are prepared to spend several hours on the phone searching for someone who knows what you’re on about.
Uses
- Use a Pocket PC as a gateway to control systems (security/irrigation/lighting etc.).
- Use a GPS enabled Pocket PC to track a vehicle’s location at all times.
- Use with ‘VNC’ to access employee PDAs for remote assistance.
- Many more …
Background
I decided to post this article for several reasons. I could not find any reference to a DynDNS updater for Pocket PCs when I developed this code. The Code Project has been of tremendous use to me over the past couple of years, and I am keen to contribute something back.
Achieving the end goal involved negotiating a few hurdles I had not foreseen. I work as an engineer developing software to interface with control systems, and have spent the last few years focused on the use of Pocket PCs not only as devices to remotely access controllers, but also as the ‘brains’ in distributed control systems.
The Pocket PC offers great value for money as a control/logging device. Although it does not provide onboard I/O (many Wi-Fi I/O devices available), for around $500 AU, you get a device with:
- Real time clock
- Battery backup
- Local GUI for user interaction
- Wi-Fi and Bluetooth connectivity
- 3G/GPRS connectivity
- SMS alarms
- Device designed for low power consumption
- Rich programming environment with .NET CF
Requirements
To use CompactDDNS, you will need:
- A DynDNS account and at least one host created
- (Free for up to 5 hosts)
- A Pocket PC phone with WM5 or WM6
- (Might work with PPC2003)
- A 3G/GPRS enabled SIM card with a ‘Public IP’
- Visual Studio 2005 with the relevant Pocket PC SDK
Features
As well as a DynDNS updater, CompactDDNS can also edit the appropriate Registry key for your 3G/GPRS connection to keep it permanently connected.
Finally, I have included the EXE, but not the source code for my in-house VNC server for Pocket PCs. This VNC server works well, but is yet to have authentication implemented. You can choose to launch the VNC server from CompactDDNS. I am happy for the Code Project community to use this VNC server in non-commercial applications.
The Code
In developing professional applications for Pocket PCs, I make extensive use of the OpenNetCF Framework, and would recommend anyone who is serious about development using .NET CF to do likewise.
In order to post this project, I have reworked my code to not use the OpenNetCF Framework. For instance, to find the WAN IP of my SIM, I usually use the Adaptors
class provided in the OpenNetCF.Net assembly. However to bypass this requirement, I have used the ‘WhatsMyIP’ web service.
If you have access to the OpenNetCF Framework, I have included an alternate ‘modMain’ that includes the creation of a notify icon. Enabling this feature will enable you to hide and then show the CompactDDNS app. To use this feature, add a reference to the OpenNetCF.Windows.Forms assembly and set the ‘ControlBox
’ property of the main form to true
(enabling you to hide the form).
The basic updater code first loads a configuration (including the last update info) from an XML file, then - if the updater is enabled - it retrieves the WAN IP using the ‘WhatsMyIP’ web service.
The retrieved IP is then compared with the last update IP from the settings file, and only if the IP has changed does it use the new WAN IP to update the DynDNS service. Once updated, the application periodically loops through the available IP addresses on the device to ensure that the last updated IP is still available. If the last updated IP is no longer available, the updater once again tries the ‘WhatsMyIP’ web service for the new WAN IP (if connected), and so on and so forth.
Although the code I used for doing the DynDNS update resides in the ‘PhoneLib’ project and was written in C#, I have produced the equivalent code for VB, and it can be found commented out in the ‘modGlobals’ file.
Here is the function that does the DynDNS update:
public static string UpdateDDNS(string Host, string Ip, string UserID,
string Password, string UserAgent)
{
string ResponseText = "Failed";
System.Net.HttpWebRequest Request = null;
System.Net.NetworkCredential myCred =
new System.Net.NetworkCredential(UserID,
Password);
SetDDNS_Prov(UserID, Password);
string url = "http://" + UserID + ":"+ Password +
"@members.dyndns.org/nic/update?hostname=" + Host + "&myip=" +
Ip + "&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG HTTP/1.0:8245";
Debug.WriteLine("url =" + url);
try
{
Request = (System.Net.HttpWebRequest)
System.Net.HttpWebRequest.Create(url);
Request.UserAgent = UserAgent;
Request.Proxy = System.Net.GlobalProxySelection.GetEmptyWebProxy();
Request.Method = "GET";
Request.Credentials = myCred;
System.Net.HttpWebResponse HttpWResponse = (
System.Net.HttpWebResponse) Request.GetResponse();
if (HttpWResponse != null)
{
System.IO.Stream stm = HttpWResponse.GetResponseStream();
System.IO.StreamReader sr = new System.IO.StreamReader(stm);
ResponseText = sr.ReadToEnd();
stm.Close();
sr.Close();
Request = null;
HttpWResponse = null;
stm = null;
sr = null;
}
}
catch (Exception ex)
{
Debug.WriteLine("Update Failed - " + ex.Message);
}
return ResponseText;
}
And, here is the function for the mapping provisions:
public static string SetDDNS_Prov(string UserID , string Password )
{
System.Xml.XmlDocument d= new System.Xml.XmlDocument();
String provString = "<wap-provisioningdoc>";
provString += "<characteristic type='CM_Mappings'>";
provString += "<characteristic type='900'>"; provString += "<parm name='Pattern' value='*://" + UserID + ":" + Password +
provString += "<parm name='Network'
value='{436EF144-B4FB-4863-A041-8F905A62C572}'/>";
provString += "</characteristic>";
provString += "</characteristic>";
provString += "</wap-provisioningdoc>";
provString = provString.Replace("'", Convert.ToChar(34).ToString());
Debug.WriteLine("provString = " + provString);
d.LoadXml(provString);
try
{
System.Xml.XmlDocument d2 =
Microsoft.WindowsMobile.Configuration.ConfigurationManager.ProcessConfiguration(
d, true);
return d2.OuterXml;
}
catch (Exception ex)
{
Debug.WriteLine("Error caught at SetDDNS_Prov - " + ex.Message);
}
return "failed";
}
And the same functions in VB:
Public Function UpdateDDNS(ByRef Host As String, ByRef Ip As String, _
ByVal UserID As String, ByVal Password As String) As String
Dim myCred As New Net.NetworkCredential(UserID, Password)
Dim url As String = "http://" & UserID & ":" & _
Password & "@members.dyndns.org/nic/update" & _
"?hostname=" + Host + "&myip=" + Ip + "&wildcard" & _
"=NOCHG&mx=NOCHG&backmx=NOCHG HTTP/1.0:8245"
Dim Request As Net.HttpWebRequest
Dim ResponseText As String = "Failed - Could not connect"
Try
SetDDNS_Prov(UserID, Password)
Request = CType(System.Net.HttpWebRequest.Create(url), _
System.Net.HttpWebRequest)
Request.UserAgent = "Open Control-Compact Watchdog-v1.0"
Request.Proxy = System.Net.GlobalProxySelection.GetEmptyWebProxy()
Request.Method = "GET"
Request.Credentials = myCred
Dim HttpWResponse As Net.HttpWebResponse = Nothing
Try
HttpWResponse = CType(Request.GetResponse(), _
System.Net.HttpWebResponse)
Catch e As Exception
Debug.WriteLine("Inner Exception Caught" & _
" at UpdateDDNS" & e.Message)
End Try
If Not HttpWResponse Is Nothing Then
Dim strm As IO.Stream = HttpWResponse.GetResponseStream()
Dim sr As IO.StreamReader = New IO.StreamReader(strm)
ResponseText = sr.ReadToEnd()
strm.Close()
sr.Close()
Request = Nothing
HttpWResponse = Nothing
strm = Nothing
sr = Nothing
End If
Catch ex As Exception
OnEx(ex)
End Try
Return ResponseText
End Function
Public Sub SetDDNS_Prov(ByVal UserID As String, ByVal Password As String)
Dim d As System.Xml.XmlDocument = New System.Xml.XmlDocument()
Dim provString As String = "<wap-provisioningdoc>"
provString += "<characteristic type='CM_Mappings'>"
provString += "<characteristic type='900'>"
provString += "<parm name='Pattern' value='*://" & UserID & ":" &
Password & "@members.dyndns.org/*'/>"
provString += "<parm name='Network'
value='{436EF144-B4FB-4863-A041-8F905A62C572}'/>"
provString += "</characteristic>"
provString += "</characteristic>"
provString += "</wap-provisioningdoc>"
provString = provString.Replace("'", Chr(34))
Debug.WriteLine("provString = " & provString)
Try
d.LoadXml(provString)
Dim d2 As System.Xml.XmlDocument = _
Microsoft.WindowsMobile.Configuration._
ConfigurationManager.ProcessConfiguration(d, True)
Debug.WriteLine("d2 = " & d2.InnerXml)
Catch ex As Exception
OnEx(ex)
End Try
Problems Encountered
Aside from having to use Net.HttpWebRequest
and Net.HttpWebResponse
to implement the web service requests, the basic code to do the DynDNS update worked almost the first go.
Then, for no apparent reason, it stopped working about a week later. It took me another 24 hours to find the cause of the problem, and then another two days to find a suitable solution.
I found that because I was debugging over Wi-Fi, my Pocket PC in fact had two Internet connections: one via 3G/GPRS, and another via my ADSL Router. Because I was using the OpenNetCF libraries to report the WAN IP of my SIM, I assumed the HttpWebRequest
was also taking this route.
But, what I discovered was that the HttpWebRequest
had always been using my ADSL connection over WiFi for the DDNS updates, and the day it stopped working, I had my ADSL modem off.
Having discovered the cause of the problem, I set out to find a solution. I was aware that the Pocket PC has a ‘Connection Manager’ that tries to determine the best connection to use for any network request.
It seemed logical to me that if the ‘Connection Manager’ found an Internet connection via the Wi-Fi, then this is likely to be cheaper for data transfer and hence a sensible choice. What did not seem logical was that if there was no Internet connection available via Wi-Fi, why the requests were not being made via the 3G/GPRS connection.
After much Googling, I discovered that the ‘Connection Manager’ uses mapping templates to match against the requested URL to determine the most appropriate connection to use. For a reason I never quite got to the bottom of (because I found the solution), the Connection Manager always wanted to use my ‘Work Network’ for the format of the URL for the DDNS update.
After more Googling, I found that by means of ‘WAP Provisioning Files’, I could add extra mappings of higher priority that included the template for the DDS update URL. After creating these extra mappings, everything worked fine.
I don’t wish to go into WAP Provisioning in detail here, however it is worth noting that can be very useful in the customization of Pocket PCs for specific tasks.
I have spent a reasonable amount of time modifying my code to work without OpenNetCF, and so shall leave this post at this point. If there proves to be a lot of interest in this article, I’ll endeavor to do a follow up article that goes into the code in more detail (I’ll also comment my code).
Hoping you found this article useful.
History
- 09/01/09 - Posted v. 1.0.0.
- 10/01/09 - Changed the
UserAgent
parameter for the 'UpdateDDNS
' function called in doDDNS_Upate
to "Code Project-Compact DDNS-v1.0". I had accidently left it with my product name. I should have mentioned the user agent in the article. If you uploaded the earlier version, can you change it to the above string, or use your own in the format "Company-Product-Version'? I'll include more detail on the DynDNS code in the next article.