xref: /reactos/dll/win32/iphlpapi/icmp.c (revision 019f21ee)
1 /*
2  * PROJECT:         ReactOS IP Helper API
3  * LICENSE:         LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:         ICMP functions
5  * COPYRIGHT:       2016 Tim Crawford (crawfxrd@gmail.com)
6  *                  2019 Victor Perevertkin (victor.perevertkin@reactos.org)
7  */
8 
9 #include "iphlpapi_private.h"
10 
11 WINE_DEFAULT_DEBUG_CHANNEL(iphlpapi);
12 
13 HANDLE
14 WINAPI
15 Icmp6CreateFile(void)
16 {
17     HANDLE IcmpFile;
18     OBJECT_ATTRIBUTES ObjectAttributes;
19     IO_STATUS_BLOCK IoStatusBlock;
20     UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\Ip6");
21     NTSTATUS Status;
22 
23     InitializeObjectAttributes(
24         &ObjectAttributes,
25         &DeviceName,
26         OBJ_CASE_INSENSITIVE,
27         NULL,
28         NULL);
29 
30     Status = NtCreateFile(
31         &IcmpFile,
32         GENERIC_EXECUTE,
33         &ObjectAttributes,
34         &IoStatusBlock,
35         NULL,
36         FILE_ATTRIBUTE_NORMAL,
37         FILE_SHARE_READ | FILE_SHARE_WRITE,
38         FILE_OPEN_IF,
39         0,
40         NULL,
41         0);
42 
43     if (!NT_SUCCESS(Status))
44     {
45         SetLastError(RtlNtStatusToDosError(Status));
46         return INVALID_HANDLE_VALUE;
47     }
48 
49     return IcmpFile;
50 }
51 
52 DWORD
53 WINAPI
54 Icmp6ParseReplies(
55     _In_ LPVOID ReplyBuffer,
56     _In_ DWORD  ReplySize)
57 {
58     PICMPV6_ECHO_REPLY pEcho;
59 
60     if (ReplyBuffer == NULL || ReplySize == 0)
61         return 0;
62 
63     pEcho = (PICMPV6_ECHO_REPLY)ReplyBuffer;
64 
65     // XXX: MSDN also says IP_TTL_EXPIRED_TRANSIT.
66     if (pEcho->Status == IP_SUCCESS)
67     {
68         return 1;
69     }
70 
71     SetLastError(pEcho->Status);
72     return 0;
73 }
74 
75 DWORD
76 WINAPI
77 Icmp6SendEcho2(
78     _In_     HANDLE                 IcmpHandle,
79     _In_opt_ HANDLE                 Event,
80     _In_opt_ PIO_APC_ROUTINE        ApcRoutine,
81     _In_opt_ PVOID                  ApcContext,
82     _In_     struct sockaddr_in6    *SourceAddress,
83     _In_     struct sockaddr_in6    *DestinationAddress,
84     _In_     LPVOID                 RequestData,
85     _In_     WORD                   RequestSize,
86     _In_     PIP_OPTION_INFORMATION RequestOptions,
87     _Out_    LPVOID                 ReplyBuffer,
88     _In_     DWORD                  ReplySize,
89     _In_     DWORD                  Timeout)
90 {
91     HANDLE hEvent;
92     PIO_STATUS_BLOCK IoStatusBlock;
93     PVOID InputBuffer;
94     ULONG InputBufferLength;
95     //ULONG OutputBufferLength;
96     PICMPV6_ECHO_REQUEST Request;
97     NTSTATUS Status;
98 
99     InputBufferLength = sizeof(ICMPV6_ECHO_REQUEST) + RequestSize;
100 
101     if (ReplySize < sizeof(ICMPV6_ECHO_REPLY) + sizeof(IO_STATUS_BLOCK))
102     {
103         SetLastError(IP_BUF_TOO_SMALL);
104         return 0;
105     }
106 
107     // IO_STATUS_BLOCK will be stored inside ReplyBuffer (in the end)
108     // that's because the function may return before device request ends
109     IoStatusBlock = (PIO_STATUS_BLOCK)((PUCHAR)ReplyBuffer + ReplySize - sizeof(IO_STATUS_BLOCK));
110     ReplySize -= sizeof(IO_STATUS_BLOCK);
111 
112     InputBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, InputBufferLength);
113     if (InputBuffer == NULL)
114     {
115         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
116         return 0;
117     }
118 
119     Request = (PICMPV6_ECHO_REQUEST)InputBuffer;
120 
121     Request->DestinationAddress.sin6_port = DestinationAddress->sin6_port;
122     Request->DestinationAddress.sin6_flowinfo = DestinationAddress->sin6_flowinfo;
123     CopyMemory(&(Request->DestinationAddress.sin6_addr), &(DestinationAddress->sin6_addr), sizeof(Request->DestinationAddress.sin6_addr));
124     Request->DestinationAddress.sin6_scope_id = DestinationAddress->sin6_scope_id;
125 
126     Request->SourceAddress.sin6_port = SourceAddress->sin6_port;
127     Request->SourceAddress.sin6_flowinfo = SourceAddress->sin6_flowinfo;
128     CopyMemory(&(Request->SourceAddress.sin6_addr), &(SourceAddress->sin6_addr), sizeof(Request->SourceAddress.sin6_addr));
129     Request->SourceAddress.sin6_scope_id = SourceAddress->sin6_scope_id;
130 
131     // XXX: What is this and why is it sometimes 0x72?
132     Request->Unknown1 = 0x72;
133 
134     Request->Timeout = Timeout;
135     Request->Ttl = RequestOptions->Ttl;
136     Request->Flags = RequestOptions->Flags;
137 
138     if (RequestSize > 0)
139     {
140         CopyMemory((PBYTE)InputBuffer + sizeof(ICMPV6_ECHO_REQUEST), RequestData, RequestSize);
141     }
142 
143     if (Event == NULL && ApcRoutine == NULL)
144     {
145         hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
146     }
147     else
148     {
149         hEvent = Event;
150     }
151 
152     Status = NtDeviceIoControlFile(
153         IcmpHandle,
154         hEvent,
155         ApcRoutine,
156         ApcContext,
157         IoStatusBlock,
158         IOCTL_ICMP_ECHO_REQUEST,
159         InputBuffer,
160         InputBufferLength,
161         ReplyBuffer,
162         ReplySize);         // TODO: Determine how Windows calculates OutputBufferLength.
163 
164     if (Event != NULL || ApcRoutine != NULL)
165     {
166         SetLastError(RtlNtStatusToDosError(Status));
167         HeapFree(GetProcessHeap(), 0, InputBuffer);
168         return 0;
169     }
170 
171     if (Status == STATUS_PENDING)
172     {
173         Status = NtWaitForSingleObject(hEvent, FALSE, NULL);
174 
175         if (NT_SUCCESS(Status))
176         {
177             Status = IoStatusBlock->Status;
178         }
179     }
180 
181     CloseHandle(hEvent);
182     HeapFree(GetProcessHeap(), 0, InputBuffer);
183 
184     if (!NT_SUCCESS(Status))
185     {
186         SetLastError(RtlNtStatusToDosError(Status));
187         return 0;
188     }
189 
190     Status = ((PICMPV6_ECHO_REPLY)ReplyBuffer)->Status;
191     if (Status != IP_SUCCESS)
192     {
193         SetLastError(Status);
194         return 0;
195     }
196 
197     return 1;
198 }
199 
200 BOOL
201 WINAPI
202 IcmpCloseHandle(
203     _In_ HANDLE IcmpHandle)
204 {
205     NTSTATUS Status;
206 
207     Status = NtClose(IcmpHandle);
208     if (!NT_SUCCESS(Status))
209     {
210         SetLastError(RtlNtStatusToDosError(Status));
211         return FALSE;
212     }
213 
214     return TRUE;
215 }
216 
217 HANDLE
218 WINAPI
219 IcmpCreateFile(void)
220 {
221     HANDLE IcmpFile;
222     OBJECT_ATTRIBUTES ObjectAttributes;
223     IO_STATUS_BLOCK IoStatusBlock;
224     UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\Ip");
225     NTSTATUS Status;
226 
227     InitializeObjectAttributes(
228         &ObjectAttributes,
229         &DeviceName,
230         OBJ_CASE_INSENSITIVE,
231         NULL,
232         NULL);
233 
234     Status = NtCreateFile(
235         &IcmpFile,
236         GENERIC_EXECUTE,
237         &ObjectAttributes,
238         &IoStatusBlock,
239         NULL,
240         FILE_ATTRIBUTE_NORMAL,
241         FILE_SHARE_READ | FILE_SHARE_WRITE,
242         FILE_OPEN_IF,
243         0,
244         NULL,
245         0);
246 
247     if (!NT_SUCCESS(Status))
248     {
249         SetLastError(RtlNtStatusToDosError(Status));
250         return INVALID_HANDLE_VALUE;
251     }
252 
253     return IcmpFile;
254 }
255 
256 DWORD
257 WINAPI
258 IcmpParseReplies(
259     _In_ LPVOID ReplyBuffer,
260     _In_ DWORD  ReplySize)
261 {
262     PICMP_ECHO_REPLY pEcho;
263     DWORD nReplies;
264 
265     if (ReplyBuffer == NULL || ReplySize == 0)
266         return 0;
267 
268     // TODO: Handle ReplyBuffer having more than 1 ICMP_ECHO_REPLY.
269 
270     pEcho = (PICMP_ECHO_REPLY)ReplyBuffer;
271 
272     if (pEcho->Reserved == 0)
273     {
274         SetLastError(pEcho->Status);
275     }
276 
277     nReplies = pEcho->Reserved;
278     pEcho->Reserved = 0;
279 
280     return nReplies;
281 }
282 
283 DWORD
284 WINAPI
285 IcmpSendEcho2(
286     _In_     HANDLE                 IcmpHandle,
287     _In_opt_ HANDLE                 Event,
288     _In_opt_ PIO_APC_ROUTINE        ApcRoutine,
289     _In_opt_ PVOID                  ApcContext,
290     _In_     IPAddr                 DestinationAddress,
291     _In_     LPVOID                 RequestData,
292     _In_     WORD                   RequestSize,
293     _In_opt_ PIP_OPTION_INFORMATION RequestOptions,
294     _Out_    LPVOID                 ReplyBuffer,
295     _In_     DWORD                  ReplySize,
296     _In_     DWORD                  Timeout)
297 {
298     HANDLE hEvent;
299     PIO_STATUS_BLOCK IoStatusBlock;
300     PVOID InputBuffer;
301     PICMP_ECHO_REQUEST Request;
302     DWORD nReplies;
303     NTSTATUS Status;
304 
305     if (ReplySize < sizeof(ICMP_ECHO_REPLY) + sizeof(IO_STATUS_BLOCK))
306     {
307         SetLastError(ERROR_INSUFFICIENT_BUFFER);
308         return 0;
309     }
310 
311     if (ReplySize < RequestSize + sizeof(ICMP_ECHO_REPLY))
312     {
313         SetLastError(IP_GENERAL_FAILURE);
314         return 0;
315     }
316 
317     // IO_STATUS_BLOCK will be stored inside ReplyBuffer (in the end)
318     // that's because the function may return before device request ends
319     IoStatusBlock = (PIO_STATUS_BLOCK)((PUCHAR)ReplyBuffer + ReplySize - sizeof(IO_STATUS_BLOCK));
320     ReplySize -= sizeof(IO_STATUS_BLOCK);
321 
322     InputBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ReplySize);
323     if (InputBuffer == NULL)
324     {
325         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
326         return 0;
327     }
328 
329     Request = (PICMP_ECHO_REQUEST)InputBuffer;
330     Request->Address = DestinationAddress;
331     Request->Timeout = Timeout;
332     Request->OptionsOffset = sizeof(ICMP_ECHO_REQUEST);
333     Request->DataOffset = sizeof(ICMP_ECHO_REQUEST);
334 
335     if (RequestOptions != NULL)
336     {
337         Request->HasOptions = TRUE;
338         Request->Ttl = RequestOptions->Ttl;
339         Request->Tos = RequestOptions->Tos;
340         Request->Flags = RequestOptions->Flags;
341 
342         if (RequestOptions->OptionsSize > 0)
343         {
344             Request->OptionsSize = RequestOptions->OptionsSize;
345             Request->DataOffset += Request->OptionsSize;
346 
347             CopyMemory(
348                 (PUCHAR)InputBuffer + sizeof(ICMP_ECHO_REQUEST),
349                 RequestOptions->OptionsData,
350                 Request->OptionsSize);
351         }
352     }
353 
354     if (RequestSize > 0)
355     {
356         Request->DataSize = RequestSize;
357         CopyMemory((PUCHAR)InputBuffer + Request->DataOffset, RequestData, RequestSize);
358     }
359 
360     if (Event == NULL && ApcRoutine == NULL)
361     {
362         hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
363     }
364     else
365     {
366         hEvent = Event;
367     }
368 
369     Status = NtDeviceIoControlFile(
370         IcmpHandle,
371         hEvent,
372         ApcRoutine,
373         ApcContext,
374         IoStatusBlock,
375         IOCTL_ICMP_ECHO_REQUEST,
376         InputBuffer,
377         ReplySize,
378         ReplyBuffer,
379         ReplySize);         // TODO: Determine how Windows calculates OutputBufferLength.
380 
381     // If called asynchronously, return for the caller to handle.
382     if (Event != NULL || ApcRoutine != NULL)
383     {
384         SetLastError(RtlNtStatusToDosError(Status));
385         HeapFree(GetProcessHeap(), 0, InputBuffer);
386         return 0;
387     }
388 
389     // Otherwise handle it like IcmpSendEcho.
390     if (Status == STATUS_PENDING)
391     {
392         Status = NtWaitForSingleObject(hEvent, FALSE, NULL);
393 
394         if (NT_SUCCESS(Status))
395         {
396             Status = IoStatusBlock->Status;
397         }
398     }
399 
400     CloseHandle(hEvent);
401     HeapFree(GetProcessHeap(), 0, InputBuffer);
402 
403     if (!NT_SUCCESS(Status))
404     {
405         SetLastError(RtlNtStatusToDosError(Status));
406         return 0;
407     }
408 
409     Status = ((PICMP_ECHO_REPLY)ReplyBuffer)->Status;
410     if (Status != IP_SUCCESS)
411     {
412         SetLastError(Status);
413     }
414 
415     nReplies = ((PICMP_ECHO_REPLY)ReplyBuffer)->Reserved;
416     ((PICMP_ECHO_REPLY)ReplyBuffer)->Reserved = 0;
417 
418     return nReplies;
419 }
420 
421 DWORD
422 WINAPI
423 IcmpSendEcho(
424     _In_     HANDLE                 IcmpHandle,
425     _In_     IPAddr                 DestinationAddress,
426     _In_     LPVOID                 RequestData,
427     _In_     WORD                   RequestSize,
428     _In_opt_ PIP_OPTION_INFORMATION RequestOptions,
429     _Out_    LPVOID                 ReplyBuffer,
430     _In_     DWORD                  ReplySize,
431     _In_     DWORD                  Timeout)
432 {
433     HANDLE hEvent;
434     IO_STATUS_BLOCK IoStatusBlock;
435     PVOID InputBuffer;
436     ULONG InputBufferLength;
437     PICMP_ECHO_REQUEST Request;
438     DWORD nReplies;
439     NTSTATUS Status;
440 
441     if (Timeout == 0 || Timeout == (DWORD)-1)
442     {
443         SetLastError(ERROR_INVALID_PARAMETER);
444         return 0;
445     }
446 
447     if (ReplySize < sizeof(ICMP_ECHO_REPLY))
448     {
449         SetLastError(ERROR_INSUFFICIENT_BUFFER);
450         return 0;
451     }
452 
453     if (ReplySize < RequestSize + sizeof(ICMP_ECHO_REPLY))
454     {
455         SetLastError(IP_GENERAL_FAILURE);
456         return 0;
457     }
458 
459     InputBufferLength = sizeof(ICMP_ECHO_REQUEST) + RequestSize;
460     if (RequestOptions != NULL)
461         InputBufferLength += RequestOptions->OptionsSize;
462 
463     if (InputBufferLength < ReplySize)
464         InputBufferLength = ReplySize;
465 
466     InputBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, InputBufferLength);
467     if (InputBuffer == NULL)
468     {
469         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
470         return 0;
471     }
472 
473     Request = (PICMP_ECHO_REQUEST)InputBuffer;
474     Request->Address = DestinationAddress;
475     Request->Timeout = Timeout;
476     Request->OptionsOffset = sizeof(ICMP_ECHO_REQUEST);
477     Request->DataOffset = sizeof(ICMP_ECHO_REQUEST);
478 
479     if (RequestOptions != NULL)
480     {
481         Request->HasOptions = TRUE;
482         Request->Ttl = RequestOptions->Ttl;
483         Request->Tos = RequestOptions->Tos;
484         Request->Flags = RequestOptions->Flags;
485 
486         if (RequestOptions->OptionsSize > 0)
487         {
488             Request->OptionsSize = RequestOptions->OptionsSize;
489             Request->DataOffset += Request->OptionsSize;
490 
491             CopyMemory(
492                 (PUCHAR)InputBuffer + sizeof(ICMP_ECHO_REQUEST),
493                 RequestOptions->OptionsData,
494                 Request->OptionsSize);
495         }
496     }
497 
498     if (RequestSize > 0)
499     {
500         Request->DataSize = RequestSize;
501         CopyMemory((PUCHAR)InputBuffer + Request->DataOffset, RequestData, RequestSize);
502     }
503 
504     hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
505     if (hEvent == NULL)
506     {
507         HeapFree(GetProcessHeap(), 0, InputBuffer);
508         return 0;
509     }
510 
511     Status = NtDeviceIoControlFile(
512         IcmpHandle,
513         hEvent,
514         NULL,
515         NULL,
516         &IoStatusBlock,
517         IOCTL_ICMP_ECHO_REQUEST,
518         InputBuffer,
519         InputBufferLength,
520         ReplyBuffer,
521         ReplySize);
522 
523     if (Status == STATUS_PENDING)
524     {
525         Status = NtWaitForSingleObject(hEvent, FALSE, NULL);
526 
527         if (NT_SUCCESS(Status))
528         {
529             Status = IoStatusBlock.Status;
530         }
531     }
532 
533     CloseHandle(hEvent);
534     HeapFree(GetProcessHeap(), 0, InputBuffer);
535 
536     if (!NT_SUCCESS(Status))
537     {
538         SetLastError(RtlNtStatusToDosError(Status));
539         return 0;
540     }
541 
542     Status = ((PICMP_ECHO_REPLY)ReplyBuffer)->Status;
543     if (Status != IP_SUCCESS)
544     {
545         SetLastError(Status);
546     }
547 
548     nReplies = ((PICMP_ECHO_REPLY)ReplyBuffer)->Reserved;
549     ((PICMP_ECHO_REPLY)ReplyBuffer)->Reserved = 0;
550 
551     return nReplies;
552 }
553