トップ回答者
64bit環境下でIcmpSendEchoに渡すReplyBufferのサイズについて

質問
-
現在Win32 APIのIcmpSendEcho (http://msdn.microsoft.com/ja-jp/library/windows/desktop/aa366050%28v=vs.85%29.aspx)を使用して、
他のホストにping要求を送信するプログラムを作成しております。ドキュメントを見ると、64bitでコンパイルする場合、ReplyBufferにはsizeof(ICMP_ECHO_REPLY32)+リクエストデータサイズ+8を確保しろとあります。
しかし、実際はドキュメント通り指定すると、対象ホストがネットワークに接続しているにも関わらず、戻り値が0で、GetLastErrorの値が11050で返るようです。
ReplyBufferにsizeof(ICMP_ECHO_REPLY)+リクエストデータサイズ+8とすると、想定通り成功し、ICMP_ECHO_REPLY.Statusにも0が入るようです。
また、ICMP_ECHO_REPLY32を使う場合でも、更に4バイト余分に確保すれば、レスポンスは返ってくるようです。
しかし、その場合でも、VisualStudio上で確認すると、ICMP_ECHO_REPLY32.Options.OptionsDataに不正なポインタ値が格納されているようです。私のデバッグ環境では、0x40が格納されていました。実際はリプライバッファーにどの位のバッファを確保すればいいのか、また、どのような値を期待すればいいのか、ご教示お願いできますでしょうか?
回答
-
XXX32という命名の構造体や、そのフィールドに使われているPOINTER_32マクロ(__ptr32修飾子)は、64bit環境において64bitネイティブプロセスが32bitのWOW64プロセスまたは32bitドライバー(64bit環境での32bitドライバーは一般的ではありませんが)と直接通信するために用意されたバージョンだと思います。おそらくMSDNライブラリの説明中の「64-bit platform」というのもそのことを指しているのだと思われます。
IcmpSendEcho function (Windows)
#include <cstdio> #include <vector> #include <winsock2.h> #include <iphlpapi.h> #include <icmpapi.h> #include <conio.h> #pragma comment(lib, "iphlpapi.lib") #pragma comment(lib, "ws2_32.lib") int main() { auto hIcmp = ::IcmpCreateFile(); if (hIcmp == INVALID_HANDLE_VALUE) { printf("Failed to create ICMP stream handle."); return -1; } { printf("sizeof(void*) = %Iu\n", sizeof(void*)); printf("sizeof(void* __ptr64) = %Iu\n", sizeof(void* __ptr64)); // void* POINTER_64 printf("sizeof(void* __ptr32) = %Iu\n", sizeof(void* __ptr32)); // void* POINTER_32 printf("sizeof(ICMP_ECHO_REPLY) = %Iu\n", sizeof(ICMP_ECHO_REPLY)); printf("sizeof(IP_OPTION_INFORMATION) = %Iu\n", sizeof(IP_OPTION_INFORMATION)); #ifdef _WIN64 printf("sizeof(ICMP_ECHO_REPLY32) = %Iu\n", sizeof(ICMP_ECHO_REPLY32)); printf("sizeof(IP_OPTION_INFORMATION32) = %Iu\n", sizeof(IP_OPTION_INFORMATION32)); #endif printf("ERROR_NOT_ENOUGH_MEMORY = %lu\n", ERROR_NOT_ENOUGH_MEMORY); printf("ERROR_INVALID_PARAMETER = %lu\n", ERROR_INVALID_PARAMETER); printf("ERROR_INSUFFICIENT_BUFFER = %lu\n", ERROR_INSUFFICIENT_BUFFER); printf("ERROR_NOT_SUPPORTED = %lu\n", ERROR_NOT_SUPPORTED); printf("IP_BUF_TOO_SMALL = %lu\n", IP_BUF_TOO_SMALL); printf("IP_GENERAL_FAILURE = %lu\n", IP_GENERAL_FAILURE); } const wchar_t SendingData[] = L"My ICMP sending data"; const DWORD requestBufLen = sizeof(SendingData); const DWORD icmpErrorMessageBufferSize = 8; const DWORD replyBufLen = sizeof(ICMP_ECHO_REPLY) + sizeof(SendingData) + icmpErrorMessageBufferSize; std::vector<byte> requestBuffer(requestBufLen); std::vector<byte> replyBuffer(replyBufLen); memcpy_s(&requestBuffer[0], requestBuffer.size(), SendingData, sizeof(SendingData)); printf("\n"); printf("Sending...\n"); const DWORD timeoutValMillisec = 1000; const auto dwRetVal = ::IcmpSendEcho(hIcmp, inet_addr("127.0.0.1"), &requestBuffer[0], requestBufLen, nullptr, &replyBuffer[0], replyBufLen, timeoutValMillisec); if (dwRetVal != 0) { auto pIcmpEchoReply = reinterpret_cast<const ICMP_ECHO_REPLY*>(&replyBuffer[0]); printf("Received %lu messages.\n", dwRetVal); printf("\n"); printf("Status: %lu\n", pIcmpEchoReply->Status); printf("RoundTripTime: %lu\n", pIcmpEchoReply->RoundTripTime); printf("DataSize: %hu\n", pIcmpEchoReply->DataSize); printf("Data: 0x%p\n", pIcmpEchoReply->Data); auto receivedMessage = static_cast<const wchar_t*>(pIcmpEchoReply->Data); wprintf(L"Messasge: %s\n", receivedMessage); } else { const auto lastError = ::GetLastError(); printf("Failed to call IcmpSendEcho().\n"); printf("Error: %lu\n", lastError); } ::IcmpCloseHandle(hIcmp); hIcmp = nullptr; puts("Press any..."); _getch(); return 0; }
状況や要求仕様にも依存しますが、通常のアプリケーションプログラムにおいて、32bit版と64bit版とでコードを分けなければいけないようなことはまずない(普通はWin32 API側で吸収してくれる)はずなので、64bit版でもICMP_ECHO_REPLYやIP_OPTION_INFORMATIONを使うのが妥当ではないでしょうか。
- 編集済み sygh 2014年8月9日 15:37
- 回答としてマーク yusuke.ito 2014年8月11日 7:17
すべての返信
-
XXX32という命名の構造体や、そのフィールドに使われているPOINTER_32マクロ(__ptr32修飾子)は、64bit環境において64bitネイティブプロセスが32bitのWOW64プロセスまたは32bitドライバー(64bit環境での32bitドライバーは一般的ではありませんが)と直接通信するために用意されたバージョンだと思います。おそらくMSDNライブラリの説明中の「64-bit platform」というのもそのことを指しているのだと思われます。
IcmpSendEcho function (Windows)
#include <cstdio> #include <vector> #include <winsock2.h> #include <iphlpapi.h> #include <icmpapi.h> #include <conio.h> #pragma comment(lib, "iphlpapi.lib") #pragma comment(lib, "ws2_32.lib") int main() { auto hIcmp = ::IcmpCreateFile(); if (hIcmp == INVALID_HANDLE_VALUE) { printf("Failed to create ICMP stream handle."); return -1; } { printf("sizeof(void*) = %Iu\n", sizeof(void*)); printf("sizeof(void* __ptr64) = %Iu\n", sizeof(void* __ptr64)); // void* POINTER_64 printf("sizeof(void* __ptr32) = %Iu\n", sizeof(void* __ptr32)); // void* POINTER_32 printf("sizeof(ICMP_ECHO_REPLY) = %Iu\n", sizeof(ICMP_ECHO_REPLY)); printf("sizeof(IP_OPTION_INFORMATION) = %Iu\n", sizeof(IP_OPTION_INFORMATION)); #ifdef _WIN64 printf("sizeof(ICMP_ECHO_REPLY32) = %Iu\n", sizeof(ICMP_ECHO_REPLY32)); printf("sizeof(IP_OPTION_INFORMATION32) = %Iu\n", sizeof(IP_OPTION_INFORMATION32)); #endif printf("ERROR_NOT_ENOUGH_MEMORY = %lu\n", ERROR_NOT_ENOUGH_MEMORY); printf("ERROR_INVALID_PARAMETER = %lu\n", ERROR_INVALID_PARAMETER); printf("ERROR_INSUFFICIENT_BUFFER = %lu\n", ERROR_INSUFFICIENT_BUFFER); printf("ERROR_NOT_SUPPORTED = %lu\n", ERROR_NOT_SUPPORTED); printf("IP_BUF_TOO_SMALL = %lu\n", IP_BUF_TOO_SMALL); printf("IP_GENERAL_FAILURE = %lu\n", IP_GENERAL_FAILURE); } const wchar_t SendingData[] = L"My ICMP sending data"; const DWORD requestBufLen = sizeof(SendingData); const DWORD icmpErrorMessageBufferSize = 8; const DWORD replyBufLen = sizeof(ICMP_ECHO_REPLY) + sizeof(SendingData) + icmpErrorMessageBufferSize; std::vector<byte> requestBuffer(requestBufLen); std::vector<byte> replyBuffer(replyBufLen); memcpy_s(&requestBuffer[0], requestBuffer.size(), SendingData, sizeof(SendingData)); printf("\n"); printf("Sending...\n"); const DWORD timeoutValMillisec = 1000; const auto dwRetVal = ::IcmpSendEcho(hIcmp, inet_addr("127.0.0.1"), &requestBuffer[0], requestBufLen, nullptr, &replyBuffer[0], replyBufLen, timeoutValMillisec); if (dwRetVal != 0) { auto pIcmpEchoReply = reinterpret_cast<const ICMP_ECHO_REPLY*>(&replyBuffer[0]); printf("Received %lu messages.\n", dwRetVal); printf("\n"); printf("Status: %lu\n", pIcmpEchoReply->Status); printf("RoundTripTime: %lu\n", pIcmpEchoReply->RoundTripTime); printf("DataSize: %hu\n", pIcmpEchoReply->DataSize); printf("Data: 0x%p\n", pIcmpEchoReply->Data); auto receivedMessage = static_cast<const wchar_t*>(pIcmpEchoReply->Data); wprintf(L"Messasge: %s\n", receivedMessage); } else { const auto lastError = ::GetLastError(); printf("Failed to call IcmpSendEcho().\n"); printf("Error: %lu\n", lastError); } ::IcmpCloseHandle(hIcmp); hIcmp = nullptr; puts("Press any..."); _getch(); return 0; }
状況や要求仕様にも依存しますが、通常のアプリケーションプログラムにおいて、32bit版と64bit版とでコードを分けなければいけないようなことはまずない(普通はWin32 API側で吸収してくれる)はずなので、64bit版でもICMP_ECHO_REPLYやIP_OPTION_INFORMATIONを使うのが妥当ではないでしょうか。
- 編集済み sygh 2014年8月9日 15:37
- 回答としてマーク yusuke.ito 2014年8月11日 7:17