IP Adress Change Notifications

December 26, 2012

TL;DR

To manage the complexity of frequently changing dynamic IP addresses, the author developed a monitoring system to automatically detect and notify the user of any changes.

  • A simple WebAPI service was implemented to reliably retrieve the current public IP address.
  • A dedicated console application runs on a scheduled basis to check the IP and compares it against the previously recorded address.
  • When a change is detected, the system automatically sends an email notification detailing both the new and old IP addresses.

I've always had some sort of server at my house, for various reasons, but one thing that I never have to go along with those servers is a static IP address. Either they are way too expensive and not worth it for something that I am not making any money from, or they are completely unavailable. One day, hopefully my ISP will provide them for something other than a huge monthly business package, but that is probably wishful thinking.

Because I have servers, and this dynamic IP address, every once in a while my IP address changes. It's much less frequent than I thought it would be, but it still happens from time to time. And since it doesn't happen often, it's usually one of those things I don't notice until I am away from home and trying to access something on one of my servers, with no way to check the new IP address. I also try to keep some DNS records updated with the address, for easy reference.

So, my solution was to make a simple WebAPI service that simply returns the clients IP address, and then to have a simple application that consumes that service and keeps track of the addresses that are returned. Once a change is found, it will shoot out an e-mail to me with the new address and a reference to the old address. Then I can update my DNS, etc... No more unknown address changes. And if it changes while I'm out of the house, I will still be able to get access to my systems.

The WebAPI is very simple, just a Get() method with some very simple IP logic in it.

public class IpPingController : ApiController { public string Get() { string ipAddr = HttpContext.Current.Request.UserHostAddress; if(string.IsNullOrEmpty(ipAddr)) { object property; Request.Properties.TryGetValue(typeof(RemoteEndpointMessageProperty).FullName, out property); RemoteEndpointMessageProperty remoteProperty = property as RemoteEndpointMessageProperty; if (remoteProperty != null) { ipAddr = remoteProperty.Address; } } return ipAddr; } }

You can test this service by hitting this address in your browser, https://www.santsys.com/api/ipping/.

The application is a very simple console application that I run using windows scheduled tasks. I have it setup to run every 2 hours.

private static Configuration _cfg = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); public static Configuration Config { get { return _cfg; } } public static string ApiAddress { get { return ConfigurationManager.AppSettings["IpPingAddress"]; } } public static string PreviousIpAddress { get { return ConfigurationManager.AppSettings["PreviousIpAddress"]; } set { _cfg.AppSettings.Settings["PreviousIpAddress"].Value = value; } } public static string LastUpdate { get { return ConfigurationManager.AppSettings["LastCheck"]; } set { _cfg.AppSettings.Settings["LastCheck"].Value = value; } } public static string SmtpHost { get { return ConfigurationManager.AppSettings["SmtpHost"]; } } public static int SmtpPort { get { string port = ConfigurationManager.AppSettings["SmtpPort"]; int iPort; if (int.TryParse(port, out iPort)) { return iPort; } return 25; } } public static string SmtpUser { get { return ConfigurationManager.AppSettings["SmtpUser"]; } } public static string SmtpPassword { get { return ConfigurationManager.AppSettings["SmtpPassword"]; } } public static string SmtpFrom { get { return ConfigurationManager.AppSettings["SmtpFrom"]; } } public static string SmtpTo { get { return ConfigurationManager.AppSettings["SmtpTo"]; } }

static void Main(string[] args) { try { DoWork();

#if DEBUG Console.Write("\r\nPress any key to exit."); Console.ReadKey(); #endif } catch (Exception ex) { Console.WriteLine("{0}\r\n\r\n{1}", ex.Message, ex.StackTrace); } }

public static void DoWork() { Version ver = Assembly.GetAssembly(typeof(Program)).GetName().Version; Console.WriteLine("IpPing v{0}.{1} build {2}", ver.Major, ver.Minor, ver.Revision); Console.WriteLine("\r\nLast Run: {0} @ {1}", PreviousIpAddress, LastUpdate); Console.Write("\r\n");

if (string.IsNullOrEmpty(ApiAddress)) { Console.WriteLine("Error: No Api Address found in configuration. (IpPingAddress)"); return; }

HttpClient client = new HttpClient(); client.BaseAddress = new Uri(ApiAddress);

// Add an Accept header for JSON format. client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/json"));

HttpResponseMessage response = client.GetAsync("api/IpPing/").Result; if (response.IsSuccessStatusCode) { var ipAddrInfo = response.Content.ReadAsAsync<string>().Result;

Task emailUpdate = null;

// if the IP Address has not changed if (PreviousIpAddress != ipAddrInfo) { Console.WriteLine("New IP address detected.\r\n\tOld Address:{0}\r\n\tNew Address:{1}\r\n\r\nSending notification.", PreviousIpAddress, ipAddrInfo); emailUpdate = SendUpdateEmail(ipAddrInfo, PreviousIpAddress); PreviousIpAddress = ipAddrInfo; }

LastUpdate = DateTime.Now.ToString(); Config.Save(ConfigurationSaveMode.Modified); ConfigurationManager.RefreshSection("appSettings"); // wait for the email to send, if one is being sent if (emailUpdate != null) { emailUpdate.Wait(); } } else { Console.WriteLine("Error: {0} ({1})", (int)response.StatusCode, response.ReasonPhrase); } }

public static Task SendUpdateEmail(string newIp, string oldIp) { return Task.Factory.StartNew(() => { try { SmtpClient smtpClient = new SmtpClient(SmtpHost, SmtpPort); if (!string.IsNullOrEmpty(SmtpUser) && !string.IsNullOrEmpty(SmtpPassword)) { smtpClient.UseDefaultCredentials = false; smtpClient.Credentials = new NetworkCredential(SmtpUser, SmtpPassword); } MailMessage msg = new MailMessage(SmtpFrom, SmtpTo); msg.IsBodyHtml = true; msg.Priority = MailPriority.High; msg.Headers.Add("X-IP-PING", "Automated message from IP PING"); msg.Headers.Add("X-IP-PING-ADDRESS", newIp); msg.Subject = "Ip Address Changed!"; msg.Body = "<style type=\"text/css\">body, p { color: #555; font-size: 12px; font-family: Arial; } a, a:link, a:visited { color: #486db5; text-decoration:none;}</style>" + "<p style=\"color: #555;font-size: 12px; font-family: Arial;\">This is a notification from Ip Ping. Your IP Address has changed.</p>" + "<p style=\"color: #555;font-size: 12px; font-family: Arial;\">Your new IP Address is <b style=\"font-size: 14px; color: #000;\">" + newIp + "</b></p>" + "<p style=\"color: #555;font-size: 12px; font-family: Arial;\">Your old IP Address was " + (string.IsNullOrEmpty(oldIp) ? "(blank)" : oldIp) + "</p>" + "<br /><br /><p style=\"color: #666;font-size: 10px; font-family: Arial;\">© " + DateTime.Now.Year.ToString() + " Santomieri Systems - <a href=\"https://www.santsys.com/\">www.santsys.com</a></p>";

smtpClient.Send(msg); } catch (Exception ex) { Console.WriteLine("{0}\r\n\r\n{1}", ex.Message, ex.StackTrace); } }); }

That's pretty much it. Feel free to use this if it will help you out. I know it solved a lot of issues for me.

Tags: