TraceRoute and Ping in C#
Asked Answered
D

7

34

Does anyone have C# code handy for doing a ping and traceroute to a target computer? I am looking for a pure code solution, not what I'm doing now, which is invoking the ping.exe and tracert.exe program and parsing the output. I would like something more robust.

Durkheim answered 27/9, 2008 at 0:29 Comment(0)
S
15

Although the Base Class Library includes Ping, the BCL does not include any tracert functionality.

However, a quick search reveals two open-source attempts, the first in C# the second in C++:

Sagerman answered 27/9, 2008 at 2:46 Comment(0)
U
59

Given that I had to write a TraceRoute class today I figured I might as well share the source code.

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

namespace Answer
{  
  public class TraceRoute
  {
    private const string Data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";

    public static IEnumerable<IPAddress> GetTraceRoute(string hostNameOrAddress)
    {
      return GetTraceRoute(hostNameOrAddress, 1);
    }
    private static IEnumerable<IPAddress> GetTraceRoute(string hostNameOrAddress, int ttl)
    {
      Ping pinger = new Ping();
      PingOptions pingerOptions = new PingOptions(ttl, true);
      int timeout = 10000;
      byte[] buffer = Encoding.ASCII.GetBytes(Data);
      PingReply reply = default(PingReply);

      reply = pinger.Send(hostNameOrAddress, timeout, buffer, pingerOptions);

      List<IPAddress> result = new List<IPAddress>();
      if (reply.Status == IPStatus.Success)
      {
        result.Add(reply.Address);
      }
      else if (reply.Status == IPStatus.TtlExpired || reply.Status == IPStatus.TimedOut)
      {
        //add the currently returned address if an address was found with this TTL
        if (reply.Status == IPStatus.TtlExpired) result.Add(reply.Address);
        //recurse to get the next address...
        IEnumerable<IPAddress> tempResult = default(IEnumerable<IPAddress>);
        tempResult = GetTraceRoute(hostNameOrAddress, ttl + 1);
        result.AddRange(tempResult);
      }
      else
      {
        //failure 
      }

      return result;
    }
  }
}

And a VB version for anyone that wants/needs it

Public Class TraceRoute
    Private Const Data As String = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

    Public Shared Function GetTraceRoute(ByVal hostNameOrAddress As String) As IEnumerable(Of IPAddress)
        Return GetTraceRoute(hostNameOrAddress, 1)
    End Function
    Private Shared Function GetTraceRoute(ByVal hostNameOrAddress As String, ByVal ttl As Integer) As IEnumerable(Of IPAddress)
        Dim pinger As Ping = New Ping
        Dim pingerOptions As PingOptions = New PingOptions(ttl, True)
        Dim timeout As Integer = 10000
        Dim buffer() As Byte = Encoding.ASCII.GetBytes(Data)
        Dim reply As PingReply

        reply = pinger.Send(hostNameOrAddress, timeout, buffer, pingerOptions)

        Dim result As List(Of IPAddress) = New List(Of IPAddress)
        If reply.Status = IPStatus.Success Then
            result.Add(reply.Address)
        ElseIf reply.Status = IPStatus.TtlExpired Then
            'add the currently returned address
            result.Add(reply.Address)
            'recurse to get the next address...
            Dim tempResult As IEnumerable(Of IPAddress)
            tempResult = GetTraceRoute(hostNameOrAddress, ttl + 1)
            result.AddRange(tempResult)
        Else
            'failure 
        End If

        Return result
    End Function
End Class
Ultraism answered 22/4, 2010 at 4:0 Comment(2)
Cool solution. Minor point but since you used an IEnumerable<> return, you might consider doing yield return instead of populating the list.Cloying
If anyone stumbles upon this, it has some issues, including that it has the potential to never return and spin forever. please look at https://mcmap.net/q/433632/-traceroute-and-ping-in-cParasol
P
24

This implementation is simple, lazy (properly enumerable) and it will not go on searching forever (maxTTL) like some of the other answers.

public static IEnumerable<IPAddress> GetTraceRoute(string hostname)
{
    // following are similar to the defaults in the "traceroute" unix command.
    const int timeout = 10000;
    const int maxTTL = 30;
    const int bufferSize = 32;

    byte[] buffer = new byte[bufferSize];
    new Random().NextBytes(buffer);

    using (var pinger = new Ping())
    {
        for (int ttl = 1; ttl <= maxTTL; ttl++)
        {
            PingOptions options = new PingOptions(ttl, true);
            PingReply reply = pinger.Send(hostname, timeout, buffer, options);

            // we've found a route at this ttl
            if (reply.Status == IPStatus.Success || reply.Status == IPStatus.TtlExpired)
                yield return reply.Address;

            // if we reach a status other than expired or timed out, we're done searching or there has been an error
            if (reply.Status != IPStatus.TtlExpired && reply.Status != IPStatus.TimedOut)
                break;
        }
    }
}
Parasol answered 8/8, 2017 at 9:59 Comment(3)
How about disposing the pinger object ?Phthisis
Can you show us the implementation of this code? ie: how to call it.Blackout
@FlyingV: You call the function, and the routes are returned. var routes = GetTraceRoute("google.com");Parasol
S
15

Although the Base Class Library includes Ping, the BCL does not include any tracert functionality.

However, a quick search reveals two open-source attempts, the first in C# the second in C++:

Sagerman answered 27/9, 2008 at 2:46 Comment(0)
T
8

For the ping part, take a look at the Ping class on MSDN.

Tabitha answered 27/9, 2008 at 0:33 Comment(1)
The following code example demonstrates using the Ping class synchronously. See Ping class.Perturb
M
2

This is the most efficient way I could think of. Please vote it up if you like it so others can benefit.

    using System;
    using System.Collections.Generic;
    using System.Net.NetworkInformation;

    namespace NetRouteAnalysis
    {
        class Program
        {
            static void Main(string[] args)
            {
                var route = TraceRoute.GetTraceRoute("8.8.8.8")

                foreach (var step in route)
                {
                    Console.WriteLine($"{step.Address,-20} {step.Status,-20} \t{step.RoundtripTime} ms");
                }
            }
        }

        public static class TraceRoute
        {
            public static IEnumerable<PingReply> GetTraceRoute(string hostnameOrIp)
            {
                // Initial variables
                var limit = 1000;
                var buffer = new byte[32];
                var pingOpts = new PingOptions(1, true);
                var ping = new Ping();               

                // Result holder.
                PingReply result = null;

                do
                {
                    result = ping.Send(hostnameOrIp, 4000, buffer, pingOpts);
                    pingOpts = new PingOptions(pingOpts.Ttl + 1, pingOpts.DontFragment);

                    if (result.Status != IPStatus.TimedOut)
                    {
                        yield return result;
                    }
                }
                while (result.Status != IPStatus.Success && pingOpts.Ttl < limit);
            }
        }       
    }
Miracle answered 21/1, 2021 at 13:8 Comment(0)
M
1

Ping: We can use the Ping class built into the .NET Framework.

Instantiate a Ping and subscribe to the PingCompleted event:

Ping pingSender = new Ping();
pingSender.PingCompleted += PingCompletedCallback;

Add code to configure and action the ping, e.g.:

string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
string who = "www.google.com";
AutoResetEvent waiter = new AutoResetEvent(false);
int timeout = 12000;

PingOptions options = new PingOptions(64, true);

pingSender.SendAsync(who, timeout, buffer, options, waiter);

Add a PingCompletedEventHandler:

public static void PingCompletedCallback(object sender, PingCompletedEventArgs e)
{
    ... Do stuff here
}

Code-dump of a full working example, based on MSDN's example:

public static void Main(string[] args)
{
    string who = "www.google.com";
    AutoResetEvent waiter = new AutoResetEvent(false);

    Ping pingSender = new Ping();

    // When the PingCompleted event is raised,
    // the PingCompletedCallback method is called.
    pingSender.PingCompleted += PingCompletedCallback;

    // Create a buffer of 32 bytes of data to be transmitted.
    string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
    byte[] buffer = Encoding.ASCII.GetBytes(data);

    // Wait 12 seconds for a reply.
    int timeout = 12000;

    // Set options for transmission:
    // The data can go through 64 gateways or routers
    // before it is destroyed, and the data packet
    // cannot be fragmented.
    PingOptions options = new PingOptions(64, true);

    Console.WriteLine("Time to live: {0}", options.Ttl);
    Console.WriteLine("Don't fragment: {0}", options.DontFragment);

    // Send the ping asynchronously.
    // Use the waiter as the user token.
    // When the callback completes, it can wake up this thread.
    pingSender.SendAsync(who, timeout, buffer, options, waiter);

    // Prevent this example application from ending.
    // A real application should do something useful
    // when possible.
    waiter.WaitOne();
    Console.WriteLine("Ping example completed.");
}

public static void PingCompletedCallback(object sender, PingCompletedEventArgs e)
{
    // If the operation was canceled, display a message to the user.
    if (e.Cancelled)
    {
        Console.WriteLine("Ping canceled.");

        // Let the main thread resume. 
        // UserToken is the AutoResetEvent object that the main thread 
        // is waiting for.
        ((AutoResetEvent)e.UserState).Set();
    }

    // If an error occurred, display the exception to the user.
    if (e.Error != null)
    {
        Console.WriteLine("Ping failed:");
        Console.WriteLine(e.Error.ToString());

        // Let the main thread resume. 
        ((AutoResetEvent)e.UserState).Set();
    }

    Console.WriteLine($"Roundtrip Time: {e.Reply.RoundtripTime}");

    // Let the main thread resume.
    ((AutoResetEvent)e.UserState).Set();
}
Matrices answered 12/3, 2018 at 13:50 Comment(0)
P
0

As am improvement to Scotts code answer above, I found that his solution doesn't work if the route tapers off into nothing before reaching the destination - it never returns. A better solution with at least a partial route could be this (which I've tested and it works well). You can change the '20' in the for loop to something bigger or smaller or try to detect if it's taking too long if you want to control the number of iterations some other way. Full credit to Scott for the original code - thanks.

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

    ...

    public static void TraceRoute(string hostNameOrAddress)
    {
        for (int i = 1; i < 20; i++)
        {
            IPAddress ip = GetTraceRoute(hostNameOrAddress, i);
            if(ip == null)
            {
                break;
            }
            Console.WriteLine(ip.ToString());
        }
    }

    private static IPAddress GetTraceRoute(string hostNameOrAddress, int ttl)
    {
        const string Data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
        Ping pinger = new Ping();
        PingOptions pingerOptions = new PingOptions(ttl, true);
        int timeout = 10000;
        byte[] buffer = Encoding.ASCII.GetBytes(Data);
        PingReply reply = default(PingReply);

        reply = pinger.Send(hostNameOrAddress, timeout, buffer, pingerOptions);

        List<IPAddress> result = new List<IPAddress>();
        if (reply.Status == IPStatus.Success || reply.Status == IPStatus.TtlExpired)
        {
            return reply.Address;
        }
        else
        {
            return null;
        }
    }
Personnel answered 8/3, 2017 at 23:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.