Skip to content

IP Change Monitor

December 4, 2010

I recently had a month in which my provider change my dynamic IP address 3 times.  Now, I don’t keep anything important on my home server, but I would like to know when the address changes.  So, I can manually change my GoDaddy DNS entries.  I know that DynDNS and other can automate the process, but I didn’t want to pay for the privilege, nor access my home network through DynDNS funny sounding  domain names.

So, I am going to write a small utility that will allow me to monitor my [external] IP address, notify me when it changes both with a desktop alert if I happen to be at home as well as with an email alert if I’m on the road.  I figure that this is also a good opportunity to brush up on my Windows development skill after all the time I spent developing web applications

Core Capabilities

To test current external IP address assigned to me, I call the web site www.whatismyip.com.  You can easily enough post a .Net or PHP page on an external server to provide a similar service.  WhatIsMyIp provides a specific page for automated programs like ours at http://www.whatismyip.com/automation/n09230945.asp.  That page provides the IP as a string.

To detect a change in the IP address we have to store the current address.  When our program is launched for the first time we get the current IP address.  Thereafter, we run the test on a timer (it could be once a minute, or once an hour, depends on how quickly you want to know…) and compare the results to the stored results.

To get the desktop notification I’m going to use a [hidden] form with a notification icon.  Notification icons are these little buggers like the battery indicator that show up on the toolbar.  These allow a balloon notification when the IP address finally changes, and can be hidden from view at all other times

The Windows Form Application

While our application has a minimal GUI, which allows the user to view status of IP address allocation, we want it to start with that form hidden.  However, even if we set the form to hidden it will always start visible if we use it as the main context of the Windows Forms application.

To avoid that we use an ApplicationConext derived class to run the form.  It also makes little sense to run multiple instances of the application on the same computer.  We are going to force a single instance using a mutex.  Here is what our Program.cs looks like

static class Program {
    /// <summary> /// The main entry point for the application. /// </summary> [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        // make sure no other instances of this app are running bool mutexIsAvailable = false;
        Mutex m = null;
        try {
            m = new Mutex(true, "Codejos.IPChange.Singleton");
            mutexIsAvailable = m.WaitOne(1, false); // wait only 1 ms }
        catch (AbandonedMutexException)
        {
            // don't worry about the abandonment; // the mutex only guards app instantiation mutexIsAvailable = true;
        }
        if (mutexIsAvailable)
        {
            try {
                Application.Run(new HiddenContext());
            }
            finally {
                m.ReleaseMutex();
            }
        }

    }
}

Our HiddenContext class inherits from ApplicationContext and therefore can be the main message loop of the application instead of the form.

The code for the context class is displayed below

public class HiddenContext : ApplicationContext {
    public HiddenContext()
    {        // check if the application is initialized IPAddressChange ipa = PData.DeserializeIP();
        if (ipa == null || string.IsNullOrEmpty(ipa.Address))
        {
            ProgramSettings.Initialize();
        }

        Form1 form1 = new Form1();
        form1.FormClosed += new System.Windows.Forms.FormClosedEventHandler(form1_FormClosed);
        form1.FormClosing += new FormClosingEventHandler(form1_FormClosing);
    }

    void form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        if (MessageBox.Show("Are you sure you want to stop monitoring IP changes?", "IP Change Monitor", MessageBoxButtons.YesNo) == DialogResult.No)
            e.Cancel = true;
    }

    void form1_FormClosed(object sender, System.Windows.Forms.FormClosedEventArgs e)
    {
        this.ExitThread();
    }
}

Here, we are doing a few things

  1. Read stored information
  2. Start our main form (I just kept it Form1)
  3. Wire the closing events of the form to stop the application.

The window uses IPAddressChange class to maintain IP Address and date change information.  PData class uses binary formatter to serialize and de-serialize the IP Address information to the disk.  ProgramSettings class stores the configuration information (such as email notification and reference IP address) and evokes a dialog to the user modify these settings (or set them up for the first use.)

The details of these interactions is in the attached download.

We create a simple form with a notification icon.  The form is opened (in hidden mode) as the last step of the ApplicationContext initialization.  The form has a function, triggered by a simple window time to query for the IP address

private void CheckIP()
       {
           System.Net.WebClient client = new WebClient();
           client.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)");
           Stream data = client.OpenRead(ProgramSettings.Wimi_URL);
           StreamReader input = new StreamReader(data);
           string s = input.ReadToEnd();
           input.Close();
           data.Close();
           if (CurrentAddress == null)
           {
               CurrentAddress = new IPAddressChange(s);
               PData.SerializeIP(CurrentAddress);
           }
           else if (!CurrentAddress.Equals(s))
           {
               IPChanged(s);
           }

           notifyIcon1.Text = string.Format("{0}\n{1:G}", CurrentAddress.Address, CurrentAddress.LastChanged);
       }

In this function we first call the external service with a WebClient and read the results.  CurrentAddress is IPAddressChange instance, and uses the Equality Pattern to compare to the string of the new IP address

public bool Equals(string address)
{

    if (string.IsNullOrEmpty(address))
        return false;
    return address == this.Address;
}

When we discover an address change, we trigger the IPChanged function.  As an added bonus we record the current IP Address in the Text property of the notification icon.  So, when the user hover over the icon she gets a “Tool Tip” with the current IP address and last recorded change.

Notification balloon comes up IP address change is detected

private void IPChanged(string s)
{
    NewAddress = new IPAddressChange(s);
    notifyIcon1.Text = string.Format("{0}\n{1:G}", NewAddress.Address, NewAddress.LastChanged);
    notifyIcon1.ShowBalloonTip(5000, Properties.Resources.BALOON_Title, Properties.Resources.BALOON_Text, ToolTipIcon.Warning);

    if (ProgramSettings.SendEmail)
    {
        System.Net.Mail.MailMessage msg = new MailMessage(
            ProgramSettings.EmailFrom,
            ProgramSettings.EmailTo,
            Properties.Resources.MAIL_Subject,
            string.Format(Properties.Resources.MAIL_Message, _ipAddress, _newAddress)
            );

        SmtpClient client = new SmtpClient();
        try {
            client.Send(msg);
        }

In addition there is a settings for sending email to a remote user to notify about the change.

Notice that we are not saving our changes just yet.  We want the alert to keep going out every time the timer runs until the problem is fixed. The user indicates to stop the alerts by checking a box on the dialog and clicking OK.

private void btnOK_Click(object sender, EventArgs e)
 {
     if (IsChanged.Checked)
     {
         // save new IP Address IsChanged.Checked = false;
         PData.SerializeIP(NewAddress);
     }
     this.Hide();
 }

This form becomes active when the user clicks on the notification icon

private void notifyIcon1_Click(object sender, EventArgs e)
 {
     this.Show();
     this.Activate();
 }

Conclusion

In this article we went over the major steps to create a monitor for external IP changes.  We made use of a custom ApplicationContext object to run a form hidden and show only a notification icon to the user.

Obvious improvements to add a right-click access to all functions and make sure that mail goes out only once.  Also, we can add FTP and twitter alerts as a reaction to changed address.

Download Source Code

Advertisements
Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: