Question

To do a trace route we can read in the Indy documentation

"For Traceroute application, send ping echo requests with increased TTL values"

The problem is that the TTL property is protected and can't be set. Is this just another bug or do I really have to define a new class to make the TTL property public?

However, I did a new class (including the Ping work around):

class TPing : public TIdIcmpClient {

  public:
    __property TTL;

    __fastcall TPing(TComponent* Owner) : TIdIcmpClient(Owner) {};
    __fastcall Ping(unsigned int Id = 0) {
      AnsiString Proxy = StringOfChar('X',PacketSize);
      TIdIcmpClient::Ping(Proxy,Id);
    }

};

If I set TTL to 5 and call Ping to google.com (I have checked that there is 6 TTL to google.com from my location).

So a TTL of 5 will generate an ICMP timeout message and according to the documentation, the last IP will be returned. But instead I get IP 0.0.0.0. This is the member values of AReplyStatus in the OnReply(TComponent *ASender, const TReplyStatus *AReplyStatus) callback.

FByteReceived    0,
FFromIpAddress   { u"0.0.0.0" },
FToIpAddress     { u"0.0.0.0" },
FMsgType         '\0',
FMsgCode         '\0',
FSequenceId      3490U(0x0DA2),
FMsRoundTripTime 109,
FTimeToLive      '\0',
FReplyStatus     2 /* rsTimeOut */,
FPacketNumber    0,
FHostName        { NULL },
FMsg             { NULL },
FRedirectTo      { NULL }

If I change the TTL to 6 everything works as expected (google.com answers) and I get rsEcho in return.

So to clarify the question:
How can I do a traceroute (Incrementing TTL) to collect all the router IP addresses along the way?

Was it helpful?

Solution

I have done some poking around and managed to do a trace with the Win32 API instead of Indy. The Win32 API is quite straight forward and has a function for just ICMP Echo: IcmpSendEcho.

You need to use the optional RequestOptions to set the TTL from 1 and increase for each call until you either reach your hop limit or get a success back.

The Indy component just doesn't seem to return on errors, only on a success or timeout (ms not TTL).

I have boiled down my code to a minimum (not tested although).

IP_OPTION_INFORMATION Options;

IPAddr           Host;   // Host address to ping
char             SendData[32] = "Echo";
HANDLE           hIcmp;
LPVOID           ReplyBuffer;
DWORD            ReplySize;
PICMP_ECHO_REPLY pEchoReply;
int              TTL = 0;
int              Timeout = 5000; // 5 seconds timeout
bool             done = false;

// Prepare ICMP operation
Host = inet_addr("74.125.232.95"); // Google server
hIcmp = IcmpCreateFile();

if (hIcmp == INVALID_HANDLE_VALUE) {
  return psICMPFailure;
}

// Prepare ISMP reply buffer
ReplySize   = sizeof(ICMP_ECHO_REPLY) + 32;
ReplyBuffer = (VOID*) malloc(ReplySize);

while (!done && TTL++<30) {

  // Prepare options
  Options.Ttl = TTL;
  Options.Tos = 0;
  Options.Flags = IP_FLAG_DF;
  Options.OptionsSize = 0;
  Options.OptionsData = NULL;

  // PING
  IcmpSendEcho(hIcmp,Host,SendData,sizeof(SendData),&Options,ReplyBuffer,ReplySize, Timeout);

  // get result
  pEchoReply = (PICMP_ECHO_REPLY)ReplyBuffer;

  // Do something like adding the Host IP to a list

  // Check ICMP status
  done = (pEchoReply->Status == 0);
}

// Cleanup
IcmpCloseHandle(hIcmp);
free(ReplyBuffer);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top