reactos/dll/win32/iphlpapi/icmp.c
Victor Perevertkin dd2ff41dfc [IPHLPAPI] Make icmp functions use IOCTL_ICMP_ECHO_REQUEST from tcpip.sys
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>
2020-04-07 05:32:40 +03:00

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;
}