/*++ Copyright (c) 1991-1997 Microsoft Corporation Module Name: //KERNEL/RAZZLE3/src/sockets/tcpcmd/icmp/icmp.c Abstract: Definitions of the ICMP Echo request API. Author: Mike Massa (mikemas) Dec 30, 1993 Revision History: Who When What -------- -------- ---------------------------------------------- mikemas 12-30-93 created RameshV 20-Jul-97 new async function IcmpSendEcho2 Notes: In the functions do_echo_req/do_echo_rep the precedence/tos bits are not used as defined RFC 1349. -- MohsinA, 30-Jul-97 --*/ #include "inc.h" #pragma hdrstop #include #include #include #include #include #include #include #include // // Constants // #define PLATFORM_NT 0x0 #define PLATFORM_VXD 0x1 #define VXD_HANDLE_VALUE 0xDFFFFFFF // // Common Global variables // DWORD Platform = 0xFFFFFFFF; // VxD external function pointers // LPWSCONTROL wsControl = NULL; __inline void CopyTDIFromSA6(TDI_ADDRESS_IP6 *To, SOCKADDR_IN6 *From) { memcpy(To, &From->sin6_port, sizeof *To); } __inline void CopySAFromTDI6(SOCKADDR_IN6 *To, TDI_ADDRESS_IP6 *From) { To->sin6_family = AF_INET6; memcpy(&To->sin6_port, From, sizeof *From); } ///////////////////////////////////////////////////////////////////////////// // // Public functions // ///////////////////////////////////////////////////////////////////////////// HANDLE WINAPI IcmpCreateFile( VOID ) /*++ Routine Description: Opens a handle on which ICMP Echo Requests can be issued. Arguments: None. Return Value: An open file handle or INVALID_HANDLE_VALUE. Extended error information is available by calling GetLastError(). Notes: This function is effectively a no-op for the VxD platform. --*/ { HANDLE IcmpHandle = INVALID_HANDLE_VALUE; if (Platform == PLATFORM_NT) { OBJECT_ATTRIBUTES objectAttributes; IO_STATUS_BLOCK ioStatusBlock; UNICODE_STRING nameString; NTSTATUS status; // // Open a Handle to the IP driver. // RtlInitUnicodeString(&nameString, DD_IP_DEVICE_NAME); InitializeObjectAttributes( &objectAttributes, &nameString, OBJ_CASE_INSENSITIVE, (HANDLE) NULL, (PSECURITY_DESCRIPTOR) NULL ); status = NtCreateFile( &IcmpHandle, GENERIC_EXECUTE, &objectAttributes, &ioStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_IF, 0, NULL, 0 ); if (!NT_SUCCESS(status)) { SetLastError(RtlNtStatusToDosError(status)); IcmpHandle = INVALID_HANDLE_VALUE; } } else { IcmpHandle = LongToHandle(VXD_HANDLE_VALUE); } return(IcmpHandle); } // IcmpCreateFile HANDLE WINAPI Icmp6CreateFile( VOID ) /*++ Routine Description: Opens a handle on which ICMPv6 Echo Requests can be issued. Arguments: None. Return Value: An open file handle or INVALID_HANDLE_VALUE. Extended error information is available by calling GetLastError(). --*/ { HANDLE IcmpHandle = INVALID_HANDLE_VALUE; if (Platform == PLATFORM_NT) { OBJECT_ATTRIBUTES objectAttributes; IO_STATUS_BLOCK ioStatusBlock; UNICODE_STRING nameString; NTSTATUS status; // // Open a Handle to the IPv6 driver. // RtlInitUnicodeString(&nameString, DD_IPV6_DEVICE_NAME); InitializeObjectAttributes( &objectAttributes, &nameString, OBJ_CASE_INSENSITIVE, (HANDLE) NULL, (PSECURITY_DESCRIPTOR) NULL ); status = NtCreateFile( &IcmpHandle, GENERIC_EXECUTE, &objectAttributes, &ioStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_IF, 0, NULL, 0 ); if (!NT_SUCCESS(status)) { SetLastError(RtlNtStatusToDosError(status)); IcmpHandle = INVALID_HANDLE_VALUE; } } else { SetLastError(ERROR_CALL_NOT_IMPLEMENTED); IcmpHandle = INVALID_HANDLE_VALUE; } return(IcmpHandle); } BOOL WINAPI IcmpCloseHandle( HANDLE IcmpHandle ) /*++ Routine Description: Closes a handle opened by IcmpCreateFile. Arguments: IcmpHandle - The handle to close. Return Value: TRUE if the handle was closed successfully, otherwise FALSE. Extended error information is available by calling GetLastError(). Notes: This function is a no-op for the VxD platform. --*/ { if (Platform == PLATFORM_NT) { NTSTATUS status; status = NtClose(IcmpHandle); if (!NT_SUCCESS(status)) { SetLastError(RtlNtStatusToDosError(status)); return(FALSE); } } return(TRUE); } // IcmpCloseHandle DWORD IcmpParseReplies( LPVOID ReplyBuffer, DWORD ReplySize ) /*++ Routine Description: Parses the reply buffer provided and returns the number of ICMP responses found. Arguments: ReplyBuffer - This must be the same buffer that was passed to IcmpSendEcho2 This is rewritten to hold an array of ICMP_ECHO_REPLY structures. (i.e. the type is PICMP_ECHO_REPLY). ReplySize - This must be the size of the above buffer. Return Value: Returns the number of ICMP responses found. If there is an errors, return value is zero. The error can be determined by a call to GetLastError. --*/ { DWORD numberOfReplies = 0; PICMP_ECHO_REPLY reply; unsigned short i; reply = ((PICMP_ECHO_REPLY) ReplyBuffer); if( NULL == reply || 0 == ReplySize ) { // // Invalid parameter passed. But we ignore this and just return # of replies =0 // return 0; } // // Convert new IP status IP_NEGOTIATING_IPSEC to IP_DEST_HOST_UNREACHABLE. // if (reply->Status == IP_NEGOTIATING_IPSEC) { reply->Status = IP_DEST_HOST_UNREACHABLE; } // // The reserved field of the first reply contains the number of replies. // numberOfReplies = reply->Reserved; reply->Reserved = 0; if (numberOfReplies == 0) { // // Internal IP error. The error code is in the first reply slot. // SetLastError(reply->Status); } else { // // Walk through the replies and convert the data offsets to user mode // pointers. // for (i=0; iData = ((UCHAR *) reply) + ((ULONG_PTR) reply->Data); reply->Options.OptionsData = ((UCHAR FAR *) reply) + ((ULONG_PTR) reply->Options.OptionsData); } } return(numberOfReplies); } // IcmpParseReplies DWORD IcmpParseReplies2( LPVOID ReplyBuffer, DWORD ReplySize ) /*++ Routine Description: Parses the reply buffer provided and returns the number of ICMP responses found. Arguments: ReplyBuffer - This must be the same buffer that was passed to IcmpSendEcho2 This is rewritten to hold an array of ICMP_ECHO_REPLY structures. (i.e. the type is PICMP_ECHO_REPLY). ReplySize - This must be the size of the above buffer. Return Value: Returns the number of ICMP responses found. If there is an errors, return value is zero. The error can be determined by a call to GetLastError. --*/ { DWORD numberOfReplies = 0; PICMP_ECHO_REPLY reply; unsigned short i; reply = ((PICMP_ECHO_REPLY) ReplyBuffer); if( NULL == reply || 0 == ReplySize ) { // // Invalid parameter passed. But we ignore this and just return # of replies =0 // return 0; } // // The reserved field of the first reply contains the number of replies. // numberOfReplies = reply->Reserved; reply->Reserved = 0; if (numberOfReplies == 0) { // // Internal IP error. The error code is in the first reply slot. // SetLastError(reply->Status); } else { // // Walk through the replies and convert the data offsets to user mode // pointers. // for (i=0; iData = ((UCHAR *) reply) + ((ULONG_PTR) reply->Data); reply->Options.OptionsData = ((UCHAR FAR *) reply) + ((ULONG_PTR) reply->Options.OptionsData); } } return(numberOfReplies); } // IcmpParseReplies DWORD WINAPI IcmpSendEcho( HANDLE IcmpHandle, IPAddr DestinationAddress, LPVOID RequestData, WORD RequestSize, PIP_OPTION_INFORMATION RequestOptions, LPVOID ReplyBuffer, DWORD ReplySize, DWORD Timeout ) /*++ Routine Description: Sends an ICMP Echo request and returns one or more replies. The call returns when the timeout has expired or the reply buffer is filled. Arguments: IcmpHandle - An open handle returned by ICMPCreateFile. DestinationAddress - The destination of the echo request. RequestData - A buffer containing the data to send in the request. RequestSize - The number of bytes in the request data buffer. RequestOptions - Pointer to the IP header options for the request. May be NULL. ReplyBuffer - A buffer to hold any replies to the request. On return, the buffer will contain an array of ICMP_ECHO_REPLY structures followed by options and data. The buffer must be large enough to hold at least one ICMP_ECHO_REPLY structure. It should be large enough to also hold 8 more bytes of data - this is the size of an ICMP error message. ReplySize - The size in bytes of the reply buffer. Timeout - The time in milliseconds to wait for replies. Return Value: Returns the number of replies received and stored in ReplyBuffer. If the return value is zero, extended error information is available via GetLastError(). --*/ { PICMP_ECHO_REQUEST requestBuffer = NULL; ULONG requestBufferSize; DWORD numberOfReplies = 0; PICMP_ECHO_REPLY reply; unsigned short i; if (ReplySize < sizeof(ICMP_ECHO_REPLY)) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return(0); } requestBufferSize = sizeof(ICMP_ECHO_REQUEST) + RequestSize; if (RequestOptions != NULL) { requestBufferSize += RequestOptions->OptionsSize; } if (requestBufferSize < ReplySize) { requestBufferSize = ReplySize; } requestBuffer = LocalAlloc(LMEM_FIXED, requestBufferSize); if (requestBuffer == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(0); } // // Initialize the input buffer. // requestBuffer->Address = DestinationAddress; requestBuffer->Timeout = Timeout; requestBuffer->DataSize = RequestSize; requestBuffer->OptionsOffset = sizeof(ICMP_ECHO_REQUEST); if (RequestOptions != NULL) { requestBuffer->OptionsValid = 1; requestBuffer->Ttl = RequestOptions->Ttl; requestBuffer->Tos = RequestOptions->Tos; requestBuffer->Flags = RequestOptions->Flags; requestBuffer->OptionsSize = RequestOptions->OptionsSize; if (RequestOptions->OptionsSize > 0) { CopyMemory( ((UCHAR *) requestBuffer) + requestBuffer->OptionsOffset, RequestOptions->OptionsData, RequestOptions->OptionsSize ); } } else { requestBuffer->OptionsValid = 0; requestBuffer->OptionsSize = 0; } requestBuffer->DataOffset = requestBuffer->OptionsOffset + requestBuffer->OptionsSize; if (RequestSize > 0) { CopyMemory( ((UCHAR *)requestBuffer) + requestBuffer->DataOffset, RequestData, RequestSize ); } if (Platform == PLATFORM_NT) { IO_STATUS_BLOCK ioStatusBlock; NTSTATUS status; HANDLE eventHandle; eventHandle = CreateEvent( NULL, // default security FALSE, // auto reset FALSE, // initially non-signalled NULL // unnamed ); if (NULL == eventHandle) { goto error_exit; } status = NtDeviceIoControlFile( IcmpHandle, // Driver handle eventHandle, // Event NULL, // APC Routine NULL, // APC context &ioStatusBlock, // Status block IOCTL_ICMP_ECHO_REQUEST, // Control code requestBuffer, // Input buffer requestBufferSize, // Input buffer size ReplyBuffer, // Output buffer ReplySize // Output buffer size ); if (status == STATUS_PENDING) { status = NtWaitForSingleObject( eventHandle, FALSE, NULL ); } CloseHandle(eventHandle); if (status != STATUS_SUCCESS) { SetLastError(RtlNtStatusToDosError(status)); goto error_exit; } } else { // // VxD Platform // DWORD status; ULONG replyBufferSize = ReplySize; status = (*wsControl)( IPPROTO_TCP, WSCNTL_TCPIP_ICMP_ECHO, requestBuffer, &requestBufferSize, ReplyBuffer, &replyBufferSize ); if (status != NO_ERROR) { SetLastError(status); goto error_exit; } } numberOfReplies = IcmpParseReplies(ReplyBuffer, ReplySize); error_exit: LocalFree(requestBuffer); return(numberOfReplies); } // IcmpSendEcho DWORD WINAPI IcmpSendEcho2( HANDLE IcmpHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, IPAddr DestinationAddress, LPVOID RequestData, WORD RequestSize, PIP_OPTION_INFORMATION RequestOptions, LPVOID ReplyBuffer, DWORD ReplySize, DWORD Timeout ) /*++ Routine Description: Sends an ICMP Echo request and the call returns either immediately (if Event or ApcRoutine is NonNULL) or returns after the specified timeout. The ReplyBuffer contains the ICMP responses, if any. Arguments: IcmpHandle - An open handle returned by ICMPCreateFile. Event - This is the event to be signalled whenever an IcmpResponse comes in. ApcRoutine - This routine would be called when the calling thread is in an alertable thread and an ICMP reply comes in. ApcContext - This optional parameter is given to the ApcRoutine when this call succeeds. DestinationAddress - The destination of the echo request. RequestData - A buffer containing the data to send in the request. RequestSize - The number of bytes in the request data buffer. RequestOptions - Pointer to the IP header options for the request. May be NULL. ReplyBuffer - A buffer to hold any replies to the request. On return, the buffer will contain an array of ICMP_ECHO_REPLY structures followed by options and data. The buffer must be large enough to hold at least one ICMP_ECHO_REPLY structure. It should be large enough to also hold 8 more bytes of data - this is the size of an ICMP error message + this should also have space for IO_STATUS_BLOCK which requires 8 or 16 bytes... ReplySize - The size in bytes of the reply buffer. Timeout - The time in milliseconds to wait for replies. This is NOT used if ApcRoutine is not NULL or if Event is not NULL. Return Value: Returns the number of replies received and stored in ReplyBuffer. If the return value is zero, extended error information is available via GetLastError(). Remarks: On NT platforms, If used Asynchronously (either ApcRoutine or Event is specified), then ReplyBuffer and ReplySize are still needed. This is where the response comes in. ICMP Response data is copied to the ReplyBuffer provided, and the caller of this function has to parse it asynchronously. The function IcmpParseReply is provided for this purpose. On non-NT platforms, Event, ApcRoutine and ApcContext are IGNORED. --*/ { PICMP_ECHO_REQUEST requestBuffer = NULL; ULONG requestBufferSize; DWORD numberOfReplies = 0; unsigned short i; BOOL Asynchronous; Asynchronous = (Platform == PLATFORM_NT && (Event || ApcRoutine)); if (ReplySize < sizeof(ICMP_ECHO_REPLY)) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return(0); } requestBufferSize = sizeof(ICMP_ECHO_REQUEST) + RequestSize; if (RequestOptions != NULL) { requestBufferSize += RequestOptions->OptionsSize; } if (requestBufferSize < ReplySize) { requestBufferSize = ReplySize; } requestBuffer = LocalAlloc(LMEM_FIXED, requestBufferSize); if (requestBuffer == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(0); } // // Initialize the input buffer. // requestBuffer->Address = DestinationAddress; requestBuffer->Timeout = Timeout; requestBuffer->DataSize = RequestSize; requestBuffer->OptionsOffset = sizeof(ICMP_ECHO_REQUEST); if (RequestOptions != NULL) { requestBuffer->OptionsValid = 1; requestBuffer->Ttl = RequestOptions->Ttl; requestBuffer->Tos = RequestOptions->Tos; requestBuffer->Flags = RequestOptions->Flags; requestBuffer->OptionsSize = RequestOptions->OptionsSize; if (RequestOptions->OptionsSize > 0) { CopyMemory( ((UCHAR *) requestBuffer) + requestBuffer->OptionsOffset, RequestOptions->OptionsData, RequestOptions->OptionsSize ); } } else { requestBuffer->OptionsValid = 0; requestBuffer->OptionsSize = 0; } requestBuffer->DataOffset = requestBuffer->OptionsOffset + requestBuffer->OptionsSize; if (RequestSize > 0) { CopyMemory( ((UCHAR *)requestBuffer) + requestBuffer->DataOffset, RequestData, RequestSize ); } if (Platform == PLATFORM_NT) { IO_STATUS_BLOCK *pioStatusBlock; NTSTATUS status; HANDLE eventHandle; // // allocate status block on the reply buffer.. // pioStatusBlock = (IO_STATUS_BLOCK*)((LPBYTE)ReplyBuffer + ReplySize); pioStatusBlock --; pioStatusBlock = ROUND_DOWN_POINTER(pioStatusBlock, ALIGN_WORST); ReplySize = (ULONG)(((LPBYTE)pioStatusBlock) - (LPBYTE)ReplyBuffer ); if( (PVOID)pioStatusBlock < ReplyBuffer || ReplySize < sizeof(ICMP_ECHO_REPLY) ) { SetLastError(ERROR_INSUFFICIENT_BUFFER); goto error_exit; } if(!Asynchronous) { // Normal synchronous. eventHandle = CreateEvent( NULL, // default security FALSE, // auto reset FALSE, // initially non-signalled NULL // unnamed ); if (NULL == eventHandle) { goto error_exit; } } else { // Asynchronous call. eventHandle = Event; // Use specified Event. } status = NtDeviceIoControlFile( IcmpHandle, // Driver handle eventHandle, // Event ApcRoutine, // APC Routine ApcContext, // APC context pioStatusBlock, // Status block IOCTL_ICMP_ECHO_REQUEST, // Control code requestBuffer, // Input buffer requestBufferSize, // Input buffer size ReplyBuffer, // Output buffer ReplySize // Output buffer size ); if (Asynchronous) { // Asynchronous calls. We cannot give any information. // We let the user do the other work. SetLastError(RtlNtStatusToDosError(status)); goto error_exit; } if (status == STATUS_PENDING) { status = NtWaitForSingleObject( eventHandle, FALSE, NULL ); } CloseHandle(eventHandle); if (status != STATUS_SUCCESS) { SetLastError(RtlNtStatusToDosError(status)); goto error_exit; } } else { // // VxD Platform // DWORD status; ULONG replyBufferSize = ReplySize; status = (*wsControl)( IPPROTO_TCP, WSCNTL_TCPIP_ICMP_ECHO, requestBuffer, &requestBufferSize, ReplyBuffer, &replyBufferSize ); if (status != NO_ERROR) { SetLastError(status); goto error_exit; } } numberOfReplies = IcmpParseReplies2(ReplyBuffer, ReplySize); error_exit: LocalFree(requestBuffer); return(numberOfReplies); } // IcmpSendEcho2 DWORD Icmp6ParseReplies( LPVOID ReplyBuffer, DWORD ReplySize ) /*++ Routine Description: Parses the reply buffer provided and returns the number of ICMPv6 responses found. Arguments: ReplyBuffer - This must be the same buffer that was passed to Icmp6SendEcho2. This is written to hold an array of ICMPV6_ECHO_REPLY structures (i.e., the type is PICMPV6_ECHO_REPLY). ReplySize - This must be the size of the above buffer. Return Value: Returns the number of ICMPv6 responses found. If there is an error, return value is zero. The error can be determined by a call to GetLastError. --*/ { PICMPV6_ECHO_REPLY reply; unsigned short i; reply = ((PICMPV6_ECHO_REPLY) ReplyBuffer); if( NULL == reply || 0 == ReplySize ) { // // Invalid parameter passed. But we ignore this and just return # of // replies =0 // return 0; } // // Convert new IP status IP_NEGOTIATING_IPSEC to IP_DEST_HOST_UNREACHABLE. // if (reply->Status == IP_NEGOTIATING_IPSEC) { reply->Status = IP_DEST_HOST_UNREACHABLE; } if ((reply->Status == IP_SUCCESS) || (reply->Status == IP_TTL_EXPIRED_TRANSIT)) { return 1; } else { // // Internal IP error. The error code is in the first reply slot. // SetLastError(reply->Status); return 0; } } DWORD WINAPI Icmp6SendEcho2( HANDLE IcmpHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, LPSOCKADDR_IN6 SourceAddress, LPSOCKADDR_IN6 DestinationAddress, LPVOID RequestData, WORD RequestSize, PIP_OPTION_INFORMATION RequestOptions, LPVOID ReplyBuffer, DWORD ReplySize, DWORD Timeout ) /*++ Routine Description: Sends an ICMPv6 Echo request and the call returns either immediately (if Event or ApcRoutine is NonNULL) or returns after the specified timeout. The ReplyBuffer contains the ICMPv6 responses, if any. Arguments: IcmpHandle - An open handle returned by ICMP6CreateFile. Event - This is the event to be signalled whenever an IcmpResponse comes in. ApcRoutine - This routine would be called when the calling thread is in an alertable thread and an ICMPv6 reply comes in. ApcContext - This optional parameter is given to the ApcRoutine when this call succeeds. DestinationAddress - The destination of the echo request. RequestData - A buffer containing the data to send in the request. RequestSize - The number of bytes in the request data buffer. RequestOptions - Pointer to the IPv6 header options for the request. May be NULL. ReplyBuffer - A buffer to hold any replies to the request. On return, the buffer will contain an array of ICMPV6_ECHO_REPLY structures followed by options and data. The buffer must be large enough to hold at least one ICMPV6_ECHO_REPLY structure. It should be large enough to also hold 8 more bytes of data - this is the size of an ICMPv6 error message + this should also have space for IO_STATUS_BLOCK which requires 8 or 16 bytes... ReplySize - The size in bytes of the reply buffer. Timeout - The time in milliseconds to wait for replies. This is NOT used if ApcRoutine is not NULL or if Event is not NULL. Return Value: Returns the number of replies received and stored in ReplyBuffer. If the return value is zero, extended error information is available via GetLastError(). Remarks: If used Asynchronously (either ApcRoutine or Event is specified), then ReplyBuffer and ReplySize are still needed. This is where the response comes in. ICMP Response data is copied to the ReplyBuffer provided, and the caller of this function has to parse it asynchronously. The function Icmp6ParseReply is provided for this purpose. --*/ { PICMPV6_ECHO_REQUEST requestBuffer = NULL; ULONG requestBufferSize; DWORD numberOfReplies = 0; unsigned short i; BOOL Asynchronous; IO_STATUS_BLOCK *pioStatusBlock; NTSTATUS status; HANDLE eventHandle; Asynchronous = (Platform == PLATFORM_NT && (Event || ApcRoutine)); if (ReplySize < sizeof(ICMPV6_ECHO_REPLY)) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return(0); } requestBufferSize = sizeof(ICMPV6_ECHO_REQUEST) + RequestSize; requestBuffer = LocalAlloc(LMEM_FIXED, requestBufferSize); if (requestBuffer == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return(0); } if (Platform != PLATFORM_NT) { SetLastError(ERROR_CALL_NOT_IMPLEMENTED); goto error_exit; } // // Initialize the input buffer. // CopyTDIFromSA6(&requestBuffer->DstAddress, DestinationAddress); CopyTDIFromSA6(&requestBuffer->SrcAddress, SourceAddress); requestBuffer->Timeout = Timeout; requestBuffer->TTL = RequestOptions->Ttl; requestBuffer->Flags = RequestOptions->Flags; if (RequestSize > 0) { CopyMemory( (UCHAR *)(requestBuffer + 1), RequestData, RequestSize ); } // // allocate status block on the reply buffer.. // pioStatusBlock = (IO_STATUS_BLOCK*)((LPBYTE)ReplyBuffer + ReplySize); pioStatusBlock --; pioStatusBlock = ROUND_DOWN_POINTER(pioStatusBlock, ALIGN_WORST); ReplySize = (ULONG)(((LPBYTE)pioStatusBlock) - (LPBYTE)ReplyBuffer ); if( (PVOID)pioStatusBlock < ReplyBuffer || ReplySize < sizeof(ICMPV6_ECHO_REPLY) ) { SetLastError(ERROR_INSUFFICIENT_BUFFER); goto error_exit; } if(!Asynchronous) { // Normal synchronous. eventHandle = CreateEvent( NULL, // default security FALSE, // auto reset FALSE, // initially non-signalled NULL // unnamed ); if (NULL == eventHandle) { goto error_exit; } } else { // Asynchronous call. eventHandle = Event; // Use specified Event. } status = NtDeviceIoControlFile( IcmpHandle, // Driver handle eventHandle, // Event ApcRoutine, // APC Routine ApcContext, // APC context pioStatusBlock, // Status block IOCTL_ICMPV6_ECHO_REQUEST, // Control code requestBuffer, // Input buffer requestBufferSize, // Input buffer size ReplyBuffer, // Output buffer ReplySize // Output buffer size ); if (Asynchronous) { // Asynchronous calls. We cannot give any information. // We let the user do the other work. SetLastError(RtlNtStatusToDosError(status)); goto error_exit; } if (status == STATUS_PENDING) { status = NtWaitForSingleObject( eventHandle, FALSE, NULL ); } CloseHandle(eventHandle); if (status != STATUS_SUCCESS) { SetLastError(RtlNtStatusToDosError(status)); goto error_exit; } numberOfReplies = Icmp6ParseReplies(ReplyBuffer, ReplySize); error_exit: LocalFree(requestBuffer); return(numberOfReplies); } // // Constants // #define PING_WAIT 1000 #define DEFAULT_TTL 32 // // Local type definitions // typedef struct icmp_local_storage { struct icmp_local_storage *Next; HANDLE IcmpHandle; LPVOID ReplyBuffer; DWORD NumberOfReplies; DWORD Status; } ICMP_LOCAL_STORAGE, *PICMP_LOCAL_STORAGE; typedef struct status_table { IP_STATUS NewStatus; int OldStatus; } STATUS_TABLE, *PSTATUS_TABLE; // // Global variables // CRITICAL_SECTION g_IcmpLock; PICMP_LOCAL_STORAGE RequestHead = NULL; STATUS_TABLE StatusTable[] = { { IP_SUCCESS, ECHO_REPLY }, { IP_DEST_NET_UNREACHABLE, DEST_UNR }, { IP_DEST_HOST_UNREACHABLE, DEST_UNR }, { IP_NEGOTIATING_IPSEC, DEST_UNR }, { IP_DEST_PROT_UNREACHABLE, DEST_UNR }, { IP_TTL_EXPIRED_TRANSIT, TIME_EXCEEDED }, { IP_TTL_EXPIRED_REASSEM, TIME_EXCEEDED }, { IP_PARAM_PROBLEM, PARAMETER_ERROR }, { IP_BAD_ROUTE, PARAMETER_ERROR }, { IP_BAD_OPTION, PARAMETER_ERROR }, { IP_BUF_TOO_SMALL, PARAMETER_ERROR }, { IP_PACKET_TOO_BIG, PARAMETER_ERROR }, { IP_BAD_DESTINATION, PARAMETER_ERROR }, { IP_GENERAL_FAILURE, POLL_FAILED } }; HANDLE STRMAPI register_icmp( void ) { HANDLE icmpHandle; icmpHandle = IcmpCreateFile(); if (icmpHandle == INVALID_HANDLE_VALUE) { SetLastError(ICMP_OPEN_ERROR); return(ICMP_ERROR); } return(icmpHandle); } // register_icmp int STRMAPI do_echo_req( HANDLE fd, long addr, char *data, int amount, char *optptr, int optlen, int df, int ttl, int tos, int precedence ) { PICMP_LOCAL_STORAGE localStorage; DWORD replySize; IP_OPTION_INFORMATION options; LPVOID replyBuffer; replySize = sizeof(ICMP_ECHO_REPLY) + amount + optlen; // // Allocate a buffer to hold the reply. // localStorage = (PICMP_LOCAL_STORAGE) LocalAlloc( LMEM_FIXED, replySize + sizeof(ICMP_LOCAL_STORAGE) ); if (localStorage == NULL) { return((int)GetLastError()); } replyBuffer = ((char *) localStorage) + sizeof(ICMP_LOCAL_STORAGE); if (ttl == 0) { options.Ttl = DEFAULT_TTL; } else { options.Ttl = (BYTE)ttl; } options.Tos = (tos << 4) | precedence; options.Flags = df ? IP_FLAG_DF : 0; options.OptionsSize = (BYTE)optlen; options.OptionsData = optptr; localStorage->NumberOfReplies = IcmpSendEcho( fd, (IPAddr) addr, data, (WORD)amount, &options, replyBuffer, replySize, PING_WAIT ); if (localStorage->NumberOfReplies == 0) { localStorage->Status = GetLastError(); } else { localStorage->Status = IP_SUCCESS; } localStorage->IcmpHandle = fd; localStorage->ReplyBuffer = replyBuffer; // // Save the reply for later retrieval. // EnterCriticalSection(&g_IcmpLock); localStorage->Next = RequestHead; RequestHead = localStorage; LeaveCriticalSection(&g_IcmpLock); return(0); } // do_echo_req int STRMAPI do_echo_rep( HANDLE fd, char *data, int amount, int *rettype, int *retttl, int *rettos, int *retprec, int *retdf, char *ropt, int *roptlen ) { PICMP_LOCAL_STORAGE localStorage, tmp; PICMP_ECHO_REPLY reply; PSTATUS_TABLE entry; DWORD status; // // Find the reply. // EnterCriticalSection(&g_IcmpLock); for ( localStorage = RequestHead, tmp = NULL; localStorage != NULL; localStorage = localStorage->Next ) { if (localStorage->IcmpHandle == fd) { if (RequestHead == localStorage) { RequestHead = localStorage->Next; } else { tmp->Next = localStorage->Next; } break; } tmp = localStorage; } LeaveCriticalSection(&g_IcmpLock); if (localStorage == NULL) { SetLastError(POLL_FAILED); return(-1); } // // Process the reply. // if (localStorage->NumberOfReplies == 0) { status = localStorage->Status; reply = NULL; } else { reply = (PICMP_ECHO_REPLY) localStorage->ReplyBuffer; status = reply->Status; } if ((status == IP_SUCCESS) && (reply != NULL)) { if (amount < reply->DataSize) { status = POLL_FAILED; goto der_error_exit; } CopyMemory(data, reply->Data, reply->DataSize); *rettype = ECHO_REPLY; } else { // // Map to the appropriate old status code & return value. // if (status < IP_STATUS_BASE) { status = POLL_FAILED; goto der_error_exit; } if (status == IP_REQ_TIMED_OUT) { status = POLL_TIMEOUT; goto der_error_exit; } for ( entry = StatusTable; entry->NewStatus != IP_GENERAL_FAILURE; entry++ ) { if (entry->NewStatus == status) { *rettype = entry->OldStatus; break; } } if (entry->NewStatus == IP_GENERAL_FAILURE) { status = POLL_FAILED; goto der_error_exit; } } if (reply != NULL) { *retdf = reply->Options.Flags ? 1 : 0; *retttl = reply->Options.Ttl; *rettos = (reply->Options.Tos & 0xf0) >> 4; *retprec = reply->Options.Tos & 0x0f; if (ropt) { if (reply->Options.OptionsSize > *roptlen) { reply->Options.OptionsSize = (BYTE)*roptlen; } *roptlen = reply->Options.OptionsSize; if (reply->Options.OptionsSize) { CopyMemory( ropt, reply->Options.OptionsData, reply->Options.OptionsSize ); } } } LocalFree(localStorage); return(0); der_error_exit: LocalFree(localStorage); SetLastError(status); return(-1); } // do_echo_rep ////////////////////////////////////////////////////////////////////////////// // // DLL entry point // ////////////////////////////////////////////////////////////////////////////// BOOL WINAPI IcmpEntryPoint( HANDLE hDll, DWORD dwReason, LPVOID lpReserved ) { OSVERSIONINFO versionInfo; PICMP_LOCAL_STORAGE entry; UNREFERENCED_PARAMETER(hDll); UNREFERENCED_PARAMETER(lpReserved); switch(dwReason) { case DLL_PROCESS_ATTACH: versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (!GetVersionEx(&versionInfo)) { return(FALSE); } // // NT 3.1 interface initialization // InitializeCriticalSection(&g_IcmpLock); if (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { HINSTANCE WSock32Dll; Platform = PLATFORM_VXD; WSock32Dll = LoadLibrary("wsock32.dll"); if (WSock32Dll == NULL) { return(FALSE); } wsControl = (LPWSCONTROL) GetProcAddress( WSock32Dll, "WsControl" ); if (wsControl == NULL) { return(FALSE); } } else if (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) { Platform = PLATFORM_NT; } else { // // Unsupported OS Version // return(FALSE); } break; case DLL_PROCESS_DETACH: // // NT 3.1 interface cleanup // DeleteCriticalSection(&g_IcmpLock); while((entry = RequestHead) != NULL) { RequestHead = RequestHead->Next; LocalFree(entry); } break; default: break; } return(TRUE); } // DllEntryPoint