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) && !traceLocation.IpAddress.StartsWith("10.") && !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.