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