【发布时间】:2018-06-28 16:30:30
【问题描述】:
我正在尝试使用 Windows 提供的 IcmpSendEcho 函数发送 ping 请求,如文档中的 here 所述。
在大多数情况下,这似乎很简单,但是,我无法理解如何设置和使用调用者提供的回显请求缓冲区(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() 解析时表示“一般故障”。
我还看到,文档中的示例代码不包括回复缓冲区中为“ICMP 错误消息”指定的 8 个字节(并且已经看到 speculation that 8 is incorrect for 64-bit platforms) - 我不确定这是否会影响问题.
如果我改用ICMP_ECHO_REPLY 进行ReplySize 计算,IcmpSendEcho 将不再因任何有效负载大小而失败,但似乎仍不清楚该函数在内部实际使用了哪个函数。即,ReplyBuffer 是写成ICMP_ECHO_REPLY 或ICMP_ECHO_REPLY32 的数组吗?由于 once 必须通过转换指针类型来访问缓冲区,因此使用错误的类型可能会默默地错位并破坏其上的所有操作。
那么应该如何正确设置回复缓冲区呢?应该使用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);
}
【问题讨论】:
-
您应该始终使用
ICMP_ECHO_REPLY -
报告这是一个文档错误,请参阅:github.com/MicrosoftDocs/cpp-docs/issues/315
标签: c++ winapi visual-c++ windows-10-desktop win64