Я пытаюсь отправить запрос ping с помощью функции IcmpSendEcho
, предоставляемой Windows, как описано здесь .
По большей части это кажется простым, однако у меня возникают проблемы с пониманием того, как должен быть установлен и использован буфер эхо-запросов, предоставляемый вызывающим абонентом (параметры LPVOID ReplyBuffer
и DWORD ReplySize
). В документации говорится следующее:
ReplyBuffer
[...]
Буфер для хранения любых ответов на эхо-запрос. По возвращении буфер содержит массив структур ICMP_ECHO_REPLY, за которыми следуют параметры и данные для ответов. Буфер должен быть достаточно большим, чтобы вместить хотя бы одну структуру ICMP_ECHO_REPLY плюс байты данных RequestSize.
На 64-битной платформе по возвращении буфер содержит массив структур ICMP_ECHO_REPLY32, за которыми следуют параметры и данные для ответов.
и
ReplySize
[...]
Выделенный размер в байтах буфера ответов. Буфер должен быть достаточно большим, чтобы содержать хотя бы одну структуру ICMP_ECHO_REPLY плюс байты данных RequestSize. На 64-битной платформе буфер должен быть достаточно большим, чтобы вместить хотя бы одну структуру ICMP_ECHO_REPLY32 плюс байты данных RequestSize.
Этот буфер также должен быть достаточно большим, чтобы содержать еще 8 байтов данных (размер сообщения об ошибке ICMP).
Я занимаюсь разработкой 64-разрядной системы и версии Windows и нацеливаюсь на нее, поэтому, насколько я понимаю из документации, мне нужно использовать ICMP_ECHO_REPLY32
для расчета размера буфера, т. Е .:
ReplySize >= sizeof(ICMP_ECHO_REPLY32) + RequestSize + 8
Однако при тестировании IcmpSendEcho
всегда мгновенно завершается с ошибкой с возвращаемым значением 0.
Для RequestSize < 4
, GetLastError()
возвращает ERROR_INVALID_PARAMETER
, что, как говорится в документации, указывает, что «параметр ReplySize задает значение, которое меньше размера структуры ICMP_ECHO_REPLY или ICMP_ECHO_REPLY32».
Для RequestSize >= 4
, GetLastError
возвращает недокументированное значение, которое при анализе с GetIpErrorString()
указывает на «Общий сбой».
Я также вижу, что пример кода в документации исключает 8 байтов в буфере ответа, который указан как требуется для «сообщения об ошибке ICMP» (и видел предположение, что 8 неверно для 64-битных платформ ) - Я не уверен, влияет ли это на проблему.
Если я вместо этого использую ICMP_ECHO_REPLY
для вычисления ReplySize
, IcmpSendEcho
больше не завершается ошибкой для любого размера полезной нагрузки, однако все еще кажется неясным, какая функция на самом деле использует внутренне. То есть ReplyBuffer
записывается как массив ICMP_ECHO_REPLY
или ICMP_ECHO_REPLY32
? Так как однажды должен получить доступ к буферу через приведение типа указателя, использование неправильного типа может молча сместить и исказить все операции над ним.
Так как правильно настроить буфер ответов? Нужно ли использовать ICMP_ECHO_REPLY
или ICMP_ECHO_REPLY32
?
Это тестовый код, с которым я работал:
#include <WS2tcpip.h>
#include <Windows.h>
#include <iphlpapi.h>
#include <IcmpAPI.h>
#pragma comment(lib, "Iphlpapi.lib")
#pragma comment(lib, "Ws2_32.lib")
int main()
{
// Create the ICMP context.
HANDLE icmp_handle = IcmpCreateFile();
if (icmp_handle == INVALID_HANDLE_VALUE) {
throw;
}
// Parse the destination IP address.
IN_ADDR dest_ip{};
if (1 != InetPtonA(AF_INET, "<IP here>", &dest_ip)) {
throw;
}
// Payload to send.
constexpr WORD payload_size = 1;
unsigned char payload[payload_size]{};
// Reply buffer for exactly 1 echo reply, payload data, and 8 bytes for ICMP error message.
constexpr DWORD reply_buf_size = sizeof(ICMP_ECHO_REPLY32) + payload_size + 8;
unsigned char reply_buf[reply_buf_size]{};
// Make the echo request.
DWORD reply_count = IcmpSendEcho(icmp_handle, dest_ip.S_un.S_addr,
payload, payload_size, NULL, reply_buf, reply_buf_size, 10000);
// Return value of 0 indicates failure, try to get error info.
if (reply_count == 0) {
auto e = GetLastError();
// Some documented error codes from IcmpSendEcho docs.
switch (e) {
case ERROR_INSUFFICIENT_BUFFER:
throw;
case ERROR_INVALID_PARAMETER:
throw;
case ERROR_NOT_ENOUGH_MEMORY:
throw;
case ERROR_NOT_SUPPORTED:
throw;
case IP_BUF_TOO_SMALL:
throw;
}
// Try to get an error message for all other error codes.
DWORD buf_size = 1000;
WCHAR buf[1000];
GetIpErrorString(e, buf, &buf_size);
throw;
}
// Close ICMP context.
IcmpCloseHandle(icmp_handle);
}