windows-nt/Source/XPSP1/NT/net/tcpip/apis/iphlpapi/dll/icmp.c

1515 lines
40 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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 <align.h>
#include <icmp.h>
#include <icmpapi.h>
#include <icmpif.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <wscntl.h>
#include <ntddip6.h>
//
// 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; i<numberOfReplies; i++, reply++) {
reply->Data = ((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; i<numberOfReplies; i++, reply++) {
reply->Data = ((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