Как отследить маршрут с Indy10 TIdIcmpClient?
Чтобы сделать трассировку маршрута мы можем прочитать в документации Indy
"Для приложения Traceroute отправляйте эхо-запросы ping с увеличенными значениями TTL"
Проблема в том, что свойство TTL защищено и не может быть установлено. Это просто еще одна ошибка или мне действительно нужно определить новый класс, чтобы сделать свойство TTL общедоступным?
Тем не менее, я сделал новый класс (включая обход Ping):
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);
}
};
Если я установлю TTL на 5 и вызову Ping на google.com (я проверил, что на моем сайте есть 6 TTL для google.com).
Таким образом, значение TTL, равное 5, сгенерирует сообщение об истечении времени ICMP, и в соответствии с документацией будет возвращен последний IP-адрес. Но вместо этого я получаю IP 0.0.0.0
, Это значения членов AReplyStatus
в OnReply(TComponent *ASender, const TReplyStatus *AReplyStatus)
Перезвоните.
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 }
Если я изменю TTL на 6, все будет работать как положено (ответы google.com), и я получу взамен rsEcho.
Итак, чтобы уточнить вопрос:
Как я могу сделать traceroute (увеличение TTL), чтобы собрать все IP-адреса маршрутизатора по пути?
1 ответ
Я немного покопался и сумел провести трассировку с помощью Win32 API вместо Indy. Win32 API довольно прост и имеет функцию только для ICMP Echo: IcmpSendEcho.
Вам необходимо использовать необязательные параметры RequestOptions, чтобы установить TTL от 1 и увеличивать для каждого вызова, пока вы не достигнете своего предела прыжка или не добьетесь успеха.
Компонент Indy, похоже, просто не возвращает ошибок, только в случае успеха или тайм-аута (мс не TTL).
Я свел мой код к минимуму (хотя не проверял).
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);