Basic .NET (C#) Traceroute library

Spending the day researching all things DNS, I eventually came upon Jim Scott's post on C# Traceroute using .net framework. After a bit of tweaking, I've got something that I like a bit more, because I really want to know what the IP address means.

The code for the assembly and console application are included below. Written against .NET Framework 4 (in Visual Studio 2010), but if you change the String.IsNullOrWhiteSpace() reference, you should be able to compile this in 3.5.

Trace.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.NetworkInformation;
using System.Diagnostics;

namespace JamesRSkemp.Traceroute {
	public class TraceLocation {
		/// <summary>
		/// Hop number in a particular trace.
		/// </summary>
		public int Hop { get; set; }
		/// <summary>
		/// Time in milliseconds.
		/// </summary>
		public long Time { get; set; }
		/// <summary>
		/// IP address returned.
		/// </summary>
		public String IpAddress { get; set; }
	}

	public class Trace {
		/// <summary>
		/// Given an ip address or domain name, follow the trace path.
		///
		/// Idea and majority of the code from Jim Scott - http://coding.infoconex.com/post/C-Traceroute-using-net-framework.aspx
		/// </summary>
		/// <param name="ipAddressOrHostName">IP address or domain name to trace.</param>
		/// <param name="maximumHops">Maximum number of hops before quitting.</param>
		/// <returns>List of TraceLocation.</returns>
		public static List<TraceLocation> Traceroute(string ipAddressOrHostName, int maximumHops) {
			if (maximumHops < 1 || maximumHops > 100) {
				maximumHops = 30;
			}

			IPAddress ipAddress = Dns.GetHostEntry(ipAddressOrHostName).AddressList[0];

			List<TraceLocation> traceLocations = new List<TraceLocation>();

			using (Ping pingSender = new Ping()) {
				PingOptions pingOptions = new PingOptions();
				Stopwatch stopWatch = new Stopwatch();
				byte[] bytes = new byte[32];
				pingOptions.DontFragment = true;
				pingOptions.Ttl = 1;

				for (int i = 1; i < maximumHops + 1; i++) {
					TraceLocation traceLocation = new TraceLocation();

					stopWatch.Reset();
					stopWatch.Start();
					PingReply pingReply = pingSender.Send(
						ipAddress,
						5000,
						new byte[32], pingOptions);
					stopWatch.Stop();

					traceLocation.Hop = i;
					traceLocation.Time = stopWatch.ElapsedMilliseconds;
					if (pingReply.Address != null) {
						traceLocation.IpAddress = pingReply.Address.ToString();
					}

					traceLocations.Add(traceLocation);
					traceLocation = null;

					if (pingReply.Status == IPStatus.Success) {
						break;
					}
					pingOptions.Ttl++;
				}
			}
			return traceLocations;
		}
	}
}

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.NetworkInformation;
using System.Diagnostics;

namespace DnsConsoleApp {
	class Program {
		static void Main(string[] args) {
			List domainNames = new List();
			domainNames.Add("strivinglife.com");

			foreach (String domainName in domainNames) {
				Console.WriteLine("***" + domainName + "***");

				foreach (JamesRSkemp.Traceroute.TraceLocation traceLocation in JamesRSkemp.Traceroute.Trace.Traceroute(domainName)) {
					Console.Write(traceLocation.Hop + "	");
					Console.Write(traceLocation.Time + "ms	");
					Console.Write(traceLocation.IpAddress + "	");
					if (!String.IsNullOrWhiteSpace(traceLocation.IpAddress) &amp;&amp; !traceLocation.IpAddress.StartsWith("10.") &amp;&amp; !traceLocation.IpAddress.StartsWith("192.")) {
						try {
							Console.WriteLine(Dns.GetHostEntry(traceLocation.IpAddress).HostName.ToString());
						} catch (Exception ex) {
							Console.WriteLine(ex.Message);
						}
					} else {
						Console.WriteLine();
					}
				}
				Console.ReadKey();
			}
		}
	}
}

Next steps

It would probably make sense to allow the application to accept domain names/IPs on the fly, and what an IP resolves to could be cached.

Comments and etcetera welcome.