mirror of
https://github.com/reactos/reactos.git
synced 2024-12-03 16:33:37 +08:00
dd2ff41dfc
This adds missing features like using events and APCs within IcmpSendEcho2 functions and others. CORE-10742 CORE-14411 Co-authored-by: Tim Crawford <crawfxrd@gmail.com>
553 lines
14 KiB
C
553 lines
14 KiB
C
/*
|
|
* PROJECT: ReactOS IP Helper API
|
|
* LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
|
|
* PURPOSE: ICMP functions
|
|
* COPYRIGHT: 2016 Tim Crawford (crawfxrd@gmail.com)
|
|
* 2019 Victor Perevertkin (victor.perevertkin@reactos.org)
|
|
*/
|
|
|
|
#include "iphlpapi_private.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(iphlpapi);
|
|
|
|
HANDLE
|
|
WINAPI
|
|
Icmp6CreateFile(void)
|
|
{
|
|
HANDLE IcmpFile;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\Ip6");
|
|
NTSTATUS Status;
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&DeviceName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtCreateFile(
|
|
&IcmpFile,
|
|
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));
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
return IcmpFile;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
Icmp6ParseReplies(
|
|
_In_ LPVOID ReplyBuffer,
|
|
_In_ DWORD ReplySize)
|
|
{
|
|
PICMPV6_ECHO_REPLY pEcho;
|
|
|
|
if (ReplyBuffer == NULL || ReplySize == 0)
|
|
return 0;
|
|
|
|
pEcho = (PICMPV6_ECHO_REPLY)ReplyBuffer;
|
|
|
|
// XXX: MSDN also says IP_TTL_EXPIRED_TRANSIT.
|
|
if (pEcho->Status == IP_SUCCESS)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
SetLastError(pEcho->Status);
|
|
return 0;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
Icmp6SendEcho2(
|
|
_In_ HANDLE IcmpHandle,
|
|
_In_opt_ HANDLE Event,
|
|
_In_opt_ PIO_APC_ROUTINE ApcRoutine,
|
|
_In_opt_ PVOID ApcContext,
|
|
_In_ struct sockaddr_in6 *SourceAddress,
|
|
_In_ struct sockaddr_in6 *DestinationAddress,
|
|
_In_ LPVOID RequestData,
|
|
_In_ WORD RequestSize,
|
|
_In_ PIP_OPTION_INFORMATION RequestOptions,
|
|
_Out_ LPVOID ReplyBuffer,
|
|
_In_ DWORD ReplySize,
|
|
_In_ DWORD Timeout)
|
|
{
|
|
HANDLE hEvent;
|
|
PIO_STATUS_BLOCK IoStatusBlock;
|
|
PVOID InputBuffer;
|
|
ULONG InputBufferLength;
|
|
//ULONG OutputBufferLength;
|
|
PICMPV6_ECHO_REQUEST Request;
|
|
NTSTATUS Status;
|
|
|
|
InputBufferLength = sizeof(ICMPV6_ECHO_REQUEST) + RequestSize;
|
|
|
|
if (ReplySize < sizeof(ICMPV6_ECHO_REPLY) + sizeof(IO_STATUS_BLOCK))
|
|
{
|
|
SetLastError(IP_BUF_TOO_SMALL);
|
|
return 0;
|
|
}
|
|
|
|
// IO_STATUS_BLOCK will be stored inside ReplyBuffer (in the end)
|
|
// that's because the function may return before device request ends
|
|
IoStatusBlock = (PIO_STATUS_BLOCK)((PUCHAR)ReplyBuffer + ReplySize - sizeof(IO_STATUS_BLOCK));
|
|
ReplySize -= sizeof(IO_STATUS_BLOCK);
|
|
|
|
InputBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, InputBufferLength);
|
|
if (InputBuffer == NULL)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return 0;
|
|
}
|
|
|
|
Request = (PICMPV6_ECHO_REQUEST)InputBuffer;
|
|
|
|
Request->DestinationAddress.sin6_port = DestinationAddress->sin6_port;
|
|
Request->DestinationAddress.sin6_flowinfo = DestinationAddress->sin6_flowinfo;
|
|
CopyMemory(&(Request->DestinationAddress.sin6_addr), &(DestinationAddress->sin6_addr), sizeof(Request->DestinationAddress.sin6_addr));
|
|
Request->DestinationAddress.sin6_scope_id = DestinationAddress->sin6_scope_id;
|
|
|
|
Request->SourceAddress.sin6_port = SourceAddress->sin6_port;
|
|
Request->SourceAddress.sin6_flowinfo = SourceAddress->sin6_flowinfo;
|
|
CopyMemory(&(Request->SourceAddress.sin6_addr), &(SourceAddress->sin6_addr), sizeof(Request->SourceAddress.sin6_addr));
|
|
Request->SourceAddress.sin6_scope_id = SourceAddress->sin6_scope_id;
|
|
|
|
// XXX: What is this and why is it sometimes 0x72?
|
|
Request->Unknown1 = 0x72;
|
|
|
|
Request->Timeout = Timeout;
|
|
Request->Ttl = RequestOptions->Ttl;
|
|
Request->Flags = RequestOptions->Flags;
|
|
|
|
if (RequestSize > 0)
|
|
{
|
|
CopyMemory((PBYTE)InputBuffer + sizeof(ICMPV6_ECHO_REQUEST), RequestData, RequestSize);
|
|
}
|
|
|
|
if (Event == NULL && ApcRoutine == NULL)
|
|
{
|
|
hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
|
|
}
|
|
else
|
|
{
|
|
hEvent = Event;
|
|
}
|
|
|
|
Status = NtDeviceIoControlFile(
|
|
IcmpHandle,
|
|
hEvent,
|
|
ApcRoutine,
|
|
ApcContext,
|
|
IoStatusBlock,
|
|
IOCTL_ICMP_ECHO_REQUEST,
|
|
InputBuffer,
|
|
InputBufferLength,
|
|
ReplyBuffer,
|
|
ReplySize); // TODO: Determine how Windows calculates OutputBufferLength.
|
|
|
|
if (Event != NULL || ApcRoutine != NULL)
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
HeapFree(GetProcessHeap(), 0, InputBuffer);
|
|
return 0;
|
|
}
|
|
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
Status = NtWaitForSingleObject(hEvent, FALSE, NULL);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = IoStatusBlock->Status;
|
|
}
|
|
}
|
|
|
|
CloseHandle(hEvent);
|
|
HeapFree(GetProcessHeap(), 0, InputBuffer);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
return 0;
|
|
}
|
|
|
|
Status = ((PICMPV6_ECHO_REPLY)ReplyBuffer)->Status;
|
|
if (Status != IP_SUCCESS)
|
|
{
|
|
SetLastError(Status);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
IcmpCloseHandle(
|
|
_In_ HANDLE IcmpHandle)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
Status = NtClose(IcmpHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HANDLE
|
|
WINAPI
|
|
IcmpCreateFile(void)
|
|
{
|
|
HANDLE IcmpFile;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\Ip");
|
|
NTSTATUS Status;
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&DeviceName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtCreateFile(
|
|
&IcmpFile,
|
|
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));
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
return IcmpFile;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
IcmpParseReplies(
|
|
_In_ LPVOID ReplyBuffer,
|
|
_In_ DWORD ReplySize)
|
|
{
|
|
PICMP_ECHO_REPLY pEcho;
|
|
DWORD nReplies;
|
|
|
|
if (ReplyBuffer == NULL || ReplySize == 0)
|
|
return 0;
|
|
|
|
// TODO: Handle ReplyBuffer having more than 1 ICMP_ECHO_REPLY.
|
|
|
|
pEcho = (PICMP_ECHO_REPLY)ReplyBuffer;
|
|
|
|
if (pEcho->Reserved == 0)
|
|
{
|
|
SetLastError(pEcho->Status);
|
|
}
|
|
|
|
nReplies = pEcho->Reserved;
|
|
pEcho->Reserved = 0;
|
|
|
|
return nReplies;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
IcmpSendEcho2(
|
|
_In_ HANDLE IcmpHandle,
|
|
_In_opt_ HANDLE Event,
|
|
_In_opt_ PIO_APC_ROUTINE ApcRoutine,
|
|
_In_opt_ PVOID ApcContext,
|
|
_In_ IPAddr DestinationAddress,
|
|
_In_ LPVOID RequestData,
|
|
_In_ WORD RequestSize,
|
|
_In_opt_ PIP_OPTION_INFORMATION RequestOptions,
|
|
_Out_ LPVOID ReplyBuffer,
|
|
_In_ DWORD ReplySize,
|
|
_In_ DWORD Timeout)
|
|
{
|
|
HANDLE hEvent;
|
|
PIO_STATUS_BLOCK IoStatusBlock;
|
|
PVOID InputBuffer;
|
|
PICMP_ECHO_REQUEST Request;
|
|
DWORD nReplies;
|
|
NTSTATUS Status;
|
|
|
|
if (ReplySize < sizeof(ICMP_ECHO_REPLY) + sizeof(IO_STATUS_BLOCK))
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return 0;
|
|
}
|
|
|
|
if (ReplySize < RequestSize + sizeof(ICMP_ECHO_REPLY))
|
|
{
|
|
SetLastError(IP_GENERAL_FAILURE);
|
|
return 0;
|
|
}
|
|
|
|
// IO_STATUS_BLOCK will be stored inside ReplyBuffer (in the end)
|
|
// that's because the function may return before device request ends
|
|
IoStatusBlock = (PIO_STATUS_BLOCK)((PUCHAR)ReplyBuffer + ReplySize - sizeof(IO_STATUS_BLOCK));
|
|
ReplySize -= sizeof(IO_STATUS_BLOCK);
|
|
|
|
InputBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ReplySize);
|
|
if (InputBuffer == NULL)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return 0;
|
|
}
|
|
|
|
Request = (PICMP_ECHO_REQUEST)InputBuffer;
|
|
Request->Address = DestinationAddress;
|
|
Request->Timeout = Timeout;
|
|
Request->OptionsOffset = sizeof(ICMP_ECHO_REQUEST);
|
|
Request->DataOffset = sizeof(ICMP_ECHO_REQUEST);
|
|
|
|
if (RequestOptions != NULL)
|
|
{
|
|
Request->HasOptions = TRUE;
|
|
Request->Ttl = RequestOptions->Ttl;
|
|
Request->Tos = RequestOptions->Tos;
|
|
Request->Flags = RequestOptions->Flags;
|
|
|
|
if (RequestOptions->OptionsSize > 0)
|
|
{
|
|
Request->OptionsSize = RequestOptions->OptionsSize;
|
|
Request->DataOffset += Request->OptionsSize;
|
|
|
|
CopyMemory(
|
|
(PUCHAR)InputBuffer + sizeof(ICMP_ECHO_REQUEST),
|
|
RequestOptions->OptionsData,
|
|
Request->OptionsSize);
|
|
}
|
|
}
|
|
|
|
if (RequestSize > 0)
|
|
{
|
|
Request->DataSize = RequestSize;
|
|
CopyMemory((PUCHAR)InputBuffer + Request->DataOffset, RequestData, RequestSize);
|
|
}
|
|
|
|
if (Event == NULL && ApcRoutine == NULL)
|
|
{
|
|
hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
|
|
}
|
|
else
|
|
{
|
|
hEvent = Event;
|
|
}
|
|
|
|
Status = NtDeviceIoControlFile(
|
|
IcmpHandle,
|
|
hEvent,
|
|
ApcRoutine,
|
|
ApcContext,
|
|
IoStatusBlock,
|
|
IOCTL_ICMP_ECHO_REQUEST,
|
|
InputBuffer,
|
|
ReplySize,
|
|
ReplyBuffer,
|
|
ReplySize); // TODO: Determine how Windows calculates OutputBufferLength.
|
|
|
|
// If called asynchronously, return for the caller to handle.
|
|
if (Event != NULL || ApcRoutine != NULL)
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
HeapFree(GetProcessHeap(), 0, InputBuffer);
|
|
return 0;
|
|
}
|
|
|
|
// Otherwise handle it like IcmpSendEcho.
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
Status = NtWaitForSingleObject(hEvent, FALSE, NULL);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = IoStatusBlock->Status;
|
|
}
|
|
}
|
|
|
|
CloseHandle(hEvent);
|
|
HeapFree(GetProcessHeap(), 0, InputBuffer);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
return 0;
|
|
}
|
|
|
|
Status = ((PICMP_ECHO_REPLY)ReplyBuffer)->Status;
|
|
if (Status != IP_SUCCESS)
|
|
{
|
|
SetLastError(Status);
|
|
}
|
|
|
|
nReplies = ((PICMP_ECHO_REPLY)ReplyBuffer)->Reserved;
|
|
((PICMP_ECHO_REPLY)ReplyBuffer)->Reserved = 0;
|
|
|
|
return nReplies;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
IcmpSendEcho(
|
|
_In_ HANDLE IcmpHandle,
|
|
_In_ IPAddr DestinationAddress,
|
|
_In_ LPVOID RequestData,
|
|
_In_ WORD RequestSize,
|
|
_In_opt_ PIP_OPTION_INFORMATION RequestOptions,
|
|
_Out_ LPVOID ReplyBuffer,
|
|
_In_ DWORD ReplySize,
|
|
_In_ DWORD Timeout)
|
|
{
|
|
HANDLE hEvent;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PVOID InputBuffer;
|
|
ULONG InputBufferLength;
|
|
PICMP_ECHO_REQUEST Request;
|
|
DWORD nReplies;
|
|
NTSTATUS Status;
|
|
|
|
if (Timeout == 0 || Timeout == (DWORD)-1)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
if (ReplySize < sizeof(ICMP_ECHO_REPLY))
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return 0;
|
|
}
|
|
|
|
if (ReplySize < RequestSize + sizeof(ICMP_ECHO_REPLY))
|
|
{
|
|
SetLastError(IP_GENERAL_FAILURE);
|
|
return 0;
|
|
}
|
|
|
|
InputBufferLength = sizeof(ICMP_ECHO_REQUEST) + RequestSize;
|
|
if (RequestOptions != NULL)
|
|
InputBufferLength += RequestOptions->OptionsSize;
|
|
|
|
if (InputBufferLength < ReplySize)
|
|
InputBufferLength = ReplySize;
|
|
|
|
InputBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, InputBufferLength);
|
|
if (InputBuffer == NULL)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return 0;
|
|
}
|
|
|
|
Request = (PICMP_ECHO_REQUEST)InputBuffer;
|
|
Request->Address = DestinationAddress;
|
|
Request->Timeout = Timeout;
|
|
Request->OptionsOffset = sizeof(ICMP_ECHO_REQUEST);
|
|
Request->DataOffset = sizeof(ICMP_ECHO_REQUEST);
|
|
|
|
if (RequestOptions != NULL)
|
|
{
|
|
Request->HasOptions = TRUE;
|
|
Request->Ttl = RequestOptions->Ttl;
|
|
Request->Tos = RequestOptions->Tos;
|
|
Request->Flags = RequestOptions->Flags;
|
|
|
|
if (RequestOptions->OptionsSize > 0)
|
|
{
|
|
Request->OptionsSize = RequestOptions->OptionsSize;
|
|
Request->DataOffset += Request->OptionsSize;
|
|
|
|
CopyMemory(
|
|
(PUCHAR)InputBuffer + sizeof(ICMP_ECHO_REQUEST),
|
|
RequestOptions->OptionsData,
|
|
Request->OptionsSize);
|
|
}
|
|
}
|
|
|
|
if (RequestSize > 0)
|
|
{
|
|
Request->DataSize = RequestSize;
|
|
CopyMemory((PUCHAR)InputBuffer + Request->DataOffset, RequestData, RequestSize);
|
|
}
|
|
|
|
hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
|
|
if (hEvent == NULL)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, InputBuffer);
|
|
return 0;
|
|
}
|
|
|
|
Status = NtDeviceIoControlFile(
|
|
IcmpHandle,
|
|
hEvent,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
IOCTL_ICMP_ECHO_REQUEST,
|
|
InputBuffer,
|
|
InputBufferLength,
|
|
ReplyBuffer,
|
|
ReplySize);
|
|
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
Status = NtWaitForSingleObject(hEvent, FALSE, NULL);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = IoStatusBlock.Status;
|
|
}
|
|
}
|
|
|
|
CloseHandle(hEvent);
|
|
HeapFree(GetProcessHeap(), 0, InputBuffer);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
return 0;
|
|
}
|
|
|
|
Status = ((PICMP_ECHO_REPLY)ReplyBuffer)->Status;
|
|
if (Status != IP_SUCCESS)
|
|
{
|
|
SetLastError(Status);
|
|
}
|
|
|
|
nReplies = ((PICMP_ECHO_REPLY)ReplyBuffer)->Reserved;
|
|
((PICMP_ECHO_REPLY)ReplyBuffer)->Reserved = 0;
|
|
|
|
return nReplies;
|
|
}
|