1 /* 2 * ICMP 3 * 4 * Francois Gouget, 1999, based on the work of 5 * RW Hall, 1999, based on public domain code PING.C by Mike Muus (1983) 6 * and later works (c) 1989 Regents of Univ. of California - see copyright 7 * notice at end of source-code. 8 * Copyright 2015 Sebastian Lackner 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public 12 * License as published by the Free Software Foundation; either 13 * version 2.1 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with this library; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 23 */ 24 25 /* Future work: 26 * - Systems like FreeBSD don't seem to support the IP_TTL option and maybe others. 27 * But using IP_HDRINCL and building the IP header by hand might work. 28 * - Not all IP options are supported. 29 * - Are ICMP handles real handles, i.e. inheritable and all? There might be some 30 * more work to do here, including server side stuff with synchronization. 31 * - This API should probably be thread safe. Is it really? 32 * - Using the winsock functions has not been tested. 33 */ 34 35 #include "iphlpapi_private.h" 36 37 /* Set up endianness macros for the ip and ip_icmp BSD headers */ 38 #ifndef BIG_ENDIAN 39 #define BIG_ENDIAN 4321 40 #endif 41 #ifndef LITTLE_ENDIAN 42 #define LITTLE_ENDIAN 1234 43 #endif 44 #ifndef BYTE_ORDER 45 #ifdef WORDS_BIGENDIAN 46 #define BYTE_ORDER BIG_ENDIAN 47 #else 48 #define BYTE_ORDER LITTLE_ENDIAN 49 #endif 50 #endif /* BYTE_ORDER */ 51 52 #define u_int16_t WORD 53 #define u_int32_t DWORD 54 55 /* These are BSD headers. We use these here because they are needed on 56 * libc5 Linux systems. On other platforms they are usually simply more 57 * complete than the native stuff, and cause less portability problems 58 * so we use them anyway. 59 */ 60 #include "ip.h" 61 #include "ip_icmp.h" 62 63 64 WINE_DEFAULT_DEBUG_CHANNEL(icmp); 65 WINE_DECLARE_DEBUG_CHANNEL(winediag); 66 67 68 typedef struct { 69 int sid; 70 IP_OPTION_INFORMATION default_opts; 71 } icmp_t; 72 73 #define IP_OPTS_UNKNOWN 0 74 #define IP_OPTS_DEFAULT 1 75 #define IP_OPTS_CUSTOM 2 76 77 /* The sequence number is unique process wide, so that all threads 78 * have a distinct sequence number. 79 */ 80 static LONG icmp_sequence=0; 81 82 static int in_cksum(u_short *addr, int len) 83 { 84 int nleft=len; 85 u_short *w = addr; 86 int sum = 0; 87 u_short answer = 0; 88 89 while (nleft > 1) { 90 sum += *w++; 91 nleft -= 2; 92 } 93 94 if (nleft == 1) { 95 *(u_char *)(&answer) = *(u_char *)w; 96 sum += answer; 97 } 98 99 sum = (sum >> 16) + (sum & 0xffff); 100 sum += (sum >> 16); 101 answer = ~sum; 102 return(answer); 103 } 104 105 106 107 /* 108 * Exported Routines. 109 */ 110 111 /*********************************************************************** 112 * Icmp6CreateFile (IPHLPAPI.@) 113 */ 114 HANDLE WINAPI Icmp6CreateFile(VOID) 115 { 116 icmp_t* icp; 117 int sid; 118 #ifdef __REACTOS__ 119 WSADATA wsaData; 120 121 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != ERROR_SUCCESS) 122 { 123 ERR_(winediag)("Failed to use ICMPV6 (network ping), this requires special permissions.\n"); 124 return INVALID_HANDLE_VALUE; 125 } 126 #endif 127 128 sid=socket(AF_INET6,SOCK_RAW,IPPROTO_ICMPV6); 129 #ifndef __REACTOS__ 130 if (sid < 0) 131 { 132 /* Mac OS X supports non-privileged ICMP via SOCK_DGRAM type. */ 133 sid=socket(AF_INET6,SOCK_DGRAM,IPPROTO_ICMPV6); 134 } 135 #endif 136 if (sid < 0) { 137 ERR_(winediag)("Failed to use ICMPV6 (network ping), this requires special permissions.\n"); 138 SetLastError(ERROR_ACCESS_DENIED); 139 return INVALID_HANDLE_VALUE; 140 } 141 142 icp=HeapAlloc(GetProcessHeap(), 0, sizeof(*icp)); 143 if (icp==NULL) { 144 #ifdef __REACTOS__ 145 closesocket(sid); 146 WSACleanup(); 147 #else 148 close(sid); 149 #endif 150 SetLastError(IP_NO_RESOURCES); 151 return INVALID_HANDLE_VALUE; 152 } 153 icp->sid=sid; 154 icp->default_opts.OptionsSize=IP_OPTS_UNKNOWN; 155 return (HANDLE)icp; 156 } 157 158 159 /*********************************************************************** 160 * Icmp6SendEcho2 (IPHLPAPI.@) 161 */ 162 DWORD WINAPI Icmp6SendEcho2( 163 HANDLE IcmpHandle, 164 HANDLE Event, 165 PIO_APC_ROUTINE ApcRoutine, 166 PVOID ApcContext, 167 struct sockaddr_in6* SourceAddress, 168 struct sockaddr_in6* DestinationAddress, 169 LPVOID RequestData, 170 WORD RequestSize, 171 PIP_OPTION_INFORMATION RequestOptions, 172 LPVOID ReplyBuffer, 173 DWORD ReplySize, 174 DWORD Timeout 175 ) 176 { 177 FIXME("(%p, %p, %p, %p, %p, %p, %p, %d, %p, %p, %d, %d): stub\n", IcmpHandle, Event, 178 ApcRoutine, ApcContext, SourceAddress, DestinationAddress, RequestData, 179 RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout); 180 SetLastError(ERROR_CALL_NOT_IMPLEMENTED); 181 return 0; 182 } 183 184 185 /*********************************************************************** 186 * IcmpCreateFile (IPHLPAPI.@) 187 */ 188 HANDLE WINAPI IcmpCreateFile(VOID) 189 { 190 #ifndef __REACTOS__ 191 static int once; 192 #endif 193 icmp_t* icp; 194 int sid; 195 #ifdef __REACTOS__ 196 WSADATA wsaData; 197 198 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != ERROR_SUCCESS) 199 { 200 ERR_(winediag)("Failed to use ICMPV6 (network ping), this requires special permissions.\n"); 201 return INVALID_HANDLE_VALUE; 202 } 203 #endif 204 sid=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP); 205 #ifdef __REACTOS__ 206 if (sid < 0) { 207 ERR_(winediag)("Failed to use ICMP (network ping), this requires special permissions.\n"); 208 SetLastError(ERROR_ACCESS_DENIED); 209 return INVALID_HANDLE_VALUE; 210 } 211 #else 212 if (sid < 0) 213 { 214 /* Mac OS X supports non-privileged ICMP via SOCK_DGRAM type. */ 215 sid=socket(AF_INET,SOCK_DGRAM,IPPROTO_ICMP); 216 } 217 if (sid < 0 && !once++) { 218 FIXME_(winediag)("Failed to use ICMP (network ping), this requires special permissions.\n"); 219 FIXME_(winediag)("Falling back to system 'ping' command as a workaround.\n"); 220 } 221 #endif 222 223 icp=HeapAlloc(GetProcessHeap(), 0, sizeof(*icp)); 224 if (icp==NULL) { 225 #ifdef __REACTOS__ 226 closesocket(sid); 227 WSACleanup(); 228 #else 229 if (sid >= 0) close(sid); 230 #endif 231 SetLastError(IP_NO_RESOURCES); 232 return INVALID_HANDLE_VALUE; 233 } 234 icp->sid=sid; 235 icp->default_opts.OptionsSize=IP_OPTS_UNKNOWN; 236 return (HANDLE)icp; 237 } 238 239 240 /*********************************************************************** 241 * IcmpCloseHandle (IPHLPAPI.@) 242 */ 243 BOOL WINAPI IcmpCloseHandle(HANDLE IcmpHandle) 244 { 245 icmp_t* icp=(icmp_t*)IcmpHandle; 246 // REACTOS: Added a check for NULL handle, CORE-10707 247 if (IcmpHandle==INVALID_HANDLE_VALUE || IcmpHandle==NULL) { 248 /* FIXME: in fact win98 seems to ignore the handle value !!! */ 249 SetLastError(ERROR_INVALID_HANDLE); 250 return FALSE; 251 } 252 253 #ifdef __REACTOS__ 254 shutdown(icp->sid,2); 255 #else 256 if (icp->sid >= 0) close(icp->sid); 257 #endif 258 HeapFree(GetProcessHeap (), 0, icp); 259 #ifdef __REACTOS__ 260 WSACleanup(); 261 #endif 262 return TRUE; 263 } 264 265 #ifndef __REACTOS__ 266 static DWORD system_icmp( 267 IPAddr DestinationAddress, 268 LPVOID RequestData, 269 WORD RequestSize, 270 PIP_OPTION_INFORMATION RequestOptions, 271 LPVOID ReplyBuffer, 272 DWORD ReplySize, 273 DWORD Timeout 274 ) 275 { 276 #ifdef HAVE_FORK 277 ICMP_ECHO_REPLY *reply = ReplyBuffer; 278 char ntoa_buffer[16]; /* 4*3 digits + 3 '.' + 1 '\0' */ 279 char size_buffer[6]; /* 5 digits + '\0' */ 280 char tos_buffer[4]; /* 3 digits + '\0' */ 281 char ttl_buffer[4]; /* 3 digits + '\0' */ 282 char time_buffer[11]; /* 10 digits + '\0' */ 283 int i, pos, res, status, argc; 284 const char *argv[20]; 285 struct in_addr addr; 286 int pipe_out[2]; 287 pid_t pid, wpid; 288 char *ptr, *eol; 289 char buf[127]; 290 291 /* Assemble the ping commandline */ 292 argc = 0; 293 argv[argc++] = "ping"; 294 argv[argc++] = "-c"; /* only send a single ping */ 295 argv[argc++] = "1"; 296 argv[argc++] = "-n"; /* numeric output only */ 297 argv[argc++] = "-s"; /* request size */ 298 sprintf(size_buffer, "%u", (RequestSize >= 16) ? RequestSize : 16); 299 argv[argc++] = size_buffer; 300 argv[argc++] = "-W"; /* timeout */ 301 #ifdef __linux__ 302 /* The linux 'ping' utlity expects a time in seconds */ 303 Timeout = (Timeout + 999) / 1000; 304 #endif 305 sprintf(time_buffer, "%u", Timeout); 306 argv[argc++] = time_buffer; 307 308 if (RequestOptions) 309 { 310 #ifdef __linux__ 311 argv[argc++] = "-Q"; /* tos option */ 312 #else 313 argv[argc++] = "-z"; /* tos option */ 314 #endif 315 sprintf(tos_buffer, "%u", RequestOptions->Tos); 316 argv[argc++] = tos_buffer; 317 #ifdef __linux__ 318 /* TTL can only be specified for multicast addresses on FreeBSD/MacOS */ 319 argv[argc++] = "-t"; /* ttl option */ 320 sprintf(ttl_buffer, "%u", RequestOptions->Ttl); 321 argv[argc++] = ttl_buffer; 322 #endif 323 } 324 325 addr.s_addr = DestinationAddress; 326 if (!(ptr = inet_ntoa(addr))) 327 { 328 SetLastError(ERROR_INVALID_PARAMETER); 329 return 0; 330 } 331 strcpy(ntoa_buffer, ptr); 332 argv[argc++] = ntoa_buffer; 333 argv[argc] = NULL; 334 335 /* Dump commandline for debugging purposes */ 336 TRACE("Ping commandline: "); 337 for (i = 0; i < argc; i++) 338 { 339 TRACE("%s ", debugstr_a(argv[i])); 340 } 341 TRACE("\n"); 342 343 /* Prefill the reply struct with fallback values */ 344 memset(reply, 0, sizeof(*reply)); 345 reply->Address = DestinationAddress; 346 reply->RoundTripTime = 40; 347 reply->Options.Ttl = 120; 348 349 /* Create communication pipes */ 350 #ifdef HAVE_PIPE2 351 if (pipe2(pipe_out, O_CLOEXEC) < 0) 352 #endif 353 { 354 if (pipe(pipe_out) < 0) 355 { 356 SetLastError(ERROR_OUTOFMEMORY); 357 return 0; 358 } 359 fcntl(pipe_out[0], F_SETFD, FD_CLOEXEC); 360 fcntl(pipe_out[1], F_SETFD, FD_CLOEXEC); 361 } 362 363 /* Fork child process */ 364 pid = fork(); 365 if (pid == -1) 366 { 367 close(pipe_out[0]); 368 close(pipe_out[1]); 369 SetLastError(ERROR_OUTOFMEMORY); 370 return 0; 371 } 372 373 /* Child process */ 374 if (pid == 0) 375 { 376 static char lang_env[] = "LANG=C"; 377 378 dup2(pipe_out[1], 1); 379 close(pipe_out[0]); 380 close(pipe_out[1]); 381 close(0); 382 close(2); 383 384 putenv(lang_env); 385 execvp(argv[0], (char **)argv); 386 _exit(1); 387 } 388 389 close(pipe_out[1]); 390 391 /* Wait for child and read output */ 392 pos = 0; 393 do 394 { 395 if (pos >= sizeof(buf) - 1) 396 { 397 ERR("line too long, dropping buffer\n"); 398 pos = 0; 399 } 400 401 /* read next block */ 402 do 403 { 404 res = read(pipe_out[0], &buf[pos], (sizeof(buf) - 1) - pos); 405 } 406 while (res < 0 && errno == EINTR); 407 if (res < 0) 408 { 409 ERR("read failed: %s\n", strerror(errno)); 410 break; 411 } 412 413 pos += res; 414 while (pos) 415 { 416 eol = memchr(buf, '\n', pos); 417 if (!eol) break; 418 *eol = 0; 419 420 TRACE("Received line: %s\n", debugstr_a(buf)); 421 422 /* Interpret address */ 423 if ((ptr = strstr(buf, "from "))) 424 { 425 int a, b, c, d; 426 if (sscanf(ptr + 5, "%u.%u.%u.%u", &a, &b, &c, &d) >= 4) 427 { 428 reply->Address = a | (b << 8) | (c << 16) | (d << 24); 429 addr.s_addr = reply->Address; 430 TRACE("Got address %s\n", inet_ntoa(addr)); 431 } 432 } 433 434 /* Interpret ttl */ 435 if ((ptr = strstr(buf, "ttl="))) 436 { 437 int val; 438 if (sscanf(ptr + 4, "%u", &val) >= 1) 439 { 440 reply->Options.Ttl = val; 441 TRACE("Got ttl %u\n", val); 442 } 443 } 444 445 /* Interpret time */ 446 if ((ptr = strstr(buf, "time="))) 447 { 448 float val; 449 if (sscanf(ptr + 5, "%f", &val) >= 1) 450 { 451 reply->RoundTripTime = (unsigned int)(val + 0.5); 452 TRACE("Got rtt = %u\n", reply->RoundTripTime); 453 } 454 } 455 456 memmove(buf, eol + 1, pos - (eol + 1 - buf)); 457 pos -= (eol + 1 - buf); 458 } 459 } 460 while (res > 0); 461 close(pipe_out[0]); 462 463 /* reap the child process */ 464 do 465 { 466 wpid = waitpid(pid, &status, 0); 467 } 468 while (wpid < 0 && errno == EINTR); 469 470 /* fill out remaining struct fields */ 471 if (wpid >= 0 && WIFEXITED(status) && WEXITSTATUS(status) == 0) 472 { 473 if (ReplySize < RequestSize + sizeof(*reply)) 474 { 475 reply->Status = IP_BUF_TOO_SMALL; 476 reply->DataSize = 0; 477 reply->Data = NULL; 478 } 479 else 480 { 481 reply->Status = 0; 482 reply->DataSize = RequestSize; 483 reply->Data = (char *)reply + sizeof(*reply); 484 memcpy(reply->Data, RequestData, RequestSize); 485 } 486 return 1; 487 } 488 489 SetLastError(IP_REQ_TIMED_OUT); 490 return 0; 491 #else 492 ERR("no fork support on this platform\n"); 493 SetLastError(ERROR_NOT_SUPPORTED); 494 return 0; 495 #endif 496 } 497 #endif 498 499 BOOL 500 GetIPv4ByIndex( 501 _In_ DWORD Index, 502 _Out_ IPAddr * Address 503 ) 504 { 505 PMIB_IPADDRTABLE pIpAddrTable; 506 ULONG dwSize = 0; 507 BOOL result = FALSE; 508 509 if (GetIpAddrTable(NULL, &dwSize, FALSE) != ERROR_INSUFFICIENT_BUFFER) 510 { 511 return result; 512 } 513 pIpAddrTable = HeapAlloc(GetProcessHeap(), 0, dwSize); 514 515 if (GetIpAddrTable(pIpAddrTable, &dwSize, FALSE) == NO_ERROR) 516 { 517 INT i; 518 519 for (i = 0; i < (*pIpAddrTable).dwNumEntries; i++) 520 { 521 if ((*pIpAddrTable).table[i].dwIndex == Index) 522 { 523 *Address = (IPAddr)(*pIpAddrTable).table[i].dwAddr; 524 result = TRUE; 525 break; 526 } 527 } 528 } 529 HeapFree(GetProcessHeap(), 0, pIpAddrTable); 530 return result; 531 } 532 533 /*********************************************************************** 534 * IcmpSendEcho (IPHLPAPI.@) 535 */ 536 DWORD WINAPI IcmpSendEcho( 537 HANDLE IcmpHandle, 538 IPAddr DestinationAddress, 539 LPVOID RequestData, 540 WORD RequestSize, 541 PIP_OPTION_INFORMATION RequestOptions, 542 LPVOID ReplyBuffer, 543 DWORD ReplySize, 544 DWORD Timeout 545 ) 546 { 547 icmp_t* icp=(icmp_t*)IcmpHandle; 548 unsigned char* reqbuf; 549 int reqsize; 550 551 struct icmp_echo_reply* ier; 552 struct ip* ip_header; 553 struct icmp* icmp_header; 554 char* endbuf; 555 int ip_header_len; 556 int maxlen; 557 #ifdef __REACTOS__ 558 fd_set fdr; 559 struct timeval timeout; 560 #else 561 struct pollfd fdr; 562 #endif 563 DWORD send_time,recv_time; 564 struct sockaddr_in addr; 565 socklen_t addrlen; 566 unsigned short id,seq,cksum; 567 int res; 568 569 if (IcmpHandle==INVALID_HANDLE_VALUE) { 570 /* FIXME: in fact win98 seems to ignore the handle value !!! */ 571 SetLastError(ERROR_INVALID_HANDLE); 572 return 0; 573 } 574 575 if (ReplySize<sizeof(ICMP_ECHO_REPLY)) { 576 SetLastError(ERROR_INVALID_PARAMETER); 577 return 0; 578 } 579 580 if (ReplySize-RequestSize<sizeof(ICMP_ECHO_REPLY)) { 581 SetLastError(IP_GENERAL_FAILURE); 582 return 0; 583 } 584 585 if (!ReplyBuffer) { 586 SetLastError(ERROR_INVALID_PARAMETER); 587 return 0; 588 } 589 590 if (Timeout == 0 || Timeout == -1) { 591 SetLastError(ERROR_INVALID_PARAMETER); 592 return 0; 593 } 594 /* check the request size against SO_MAX_MSG_SIZE using getsockopt */ 595 596 if (!DestinationAddress) { 597 SetLastError(ERROR_INVALID_NETNAME); 598 return 0; 599 } 600 601 #ifndef __REACTOS__ 602 if (icp->sid < 0) { 603 WARN("using system ping command since SOCK_RAW was not supported.\n"); 604 return system_icmp(DestinationAddress, RequestData, RequestSize, 605 RequestOptions, ReplyBuffer, ReplySize, Timeout); 606 #endif 607 608 /* Prepare the request */ 609 #ifdef __REACTOS__ 610 id = GetCurrentProcessId() & 0xFFFF; 611 #else 612 id=getpid() & 0xFFFF; 613 #endif 614 seq=InterlockedIncrement(&icmp_sequence) & 0xFFFF; 615 616 reqsize=ICMP_MINLEN; 617 if (RequestData && RequestSize > 0) 618 reqsize += RequestSize; 619 reqbuf=HeapAlloc(GetProcessHeap(), 0, reqsize); 620 if (reqbuf==NULL) { 621 SetLastError(ERROR_OUTOFMEMORY); 622 return 0; 623 } 624 625 icmp_header=(struct icmp*)reqbuf; 626 icmp_header->icmp_type=ICMP_ECHO; 627 icmp_header->icmp_code=0; 628 icmp_header->icmp_cksum=0; 629 icmp_header->icmp_id=id; 630 icmp_header->icmp_seq=seq; 631 if (RequestData && RequestSize > 0) 632 memcpy(reqbuf+ICMP_MINLEN, RequestData, RequestSize); 633 icmp_header->icmp_cksum=cksum=in_cksum((u_short*)reqbuf,reqsize); 634 635 addr.sin_family=AF_INET; 636 addr.sin_addr.s_addr=DestinationAddress; 637 addr.sin_port=0; 638 639 if (RequestOptions!=NULL) { 640 int val; 641 if (icp->default_opts.OptionsSize==IP_OPTS_UNKNOWN) { 642 socklen_t len; 643 /* Before we mess with the options, get the default values */ 644 len=sizeof(val); 645 getsockopt(icp->sid,IPPROTO_IP,IP_TTL,(char *)&val,&len); 646 icp->default_opts.Ttl=val; 647 648 len=sizeof(val); 649 getsockopt(icp->sid,IPPROTO_IP,IP_TOS,(char *)&val,&len); 650 icp->default_opts.Tos=val; 651 /* FIXME: missing: handling of IP 'flags', and all the other options */ 652 } 653 654 val=RequestOptions->Ttl; 655 setsockopt(icp->sid,IPPROTO_IP,IP_TTL,(char *)&val,sizeof(val)); 656 val=RequestOptions->Tos; 657 setsockopt(icp->sid,IPPROTO_IP,IP_TOS,(char *)&val,sizeof(val)); 658 /* FIXME: missing: handling of IP 'flags', and all the other options */ 659 660 icp->default_opts.OptionsSize=IP_OPTS_CUSTOM; 661 } else if (icp->default_opts.OptionsSize==IP_OPTS_CUSTOM) { 662 int val; 663 664 /* Restore the default options */ 665 val=icp->default_opts.Ttl; 666 setsockopt(icp->sid,IPPROTO_IP,IP_TTL,(char *)&val,sizeof(val)); 667 val=icp->default_opts.Tos; 668 setsockopt(icp->sid,IPPROTO_IP,IP_TOS,(char *)&val,sizeof(val)); 669 /* FIXME: missing: handling of IP 'flags', and all the other options */ 670 671 icp->default_opts.OptionsSize=IP_OPTS_DEFAULT; 672 } 673 674 /* Get ready for receiving the reply 675 * Do it before we send the request to minimize the risk of introducing delays 676 */ 677 #ifdef __REACTOS__ 678 FD_ZERO(&fdr); 679 FD_SET(icp->sid,&fdr); 680 timeout.tv_sec=Timeout/1000; 681 timeout.tv_usec=(Timeout % 1000)*1000; 682 #else 683 fdr.fd = icp->sid; 684 fdr.events = POLLIN; 685 #endif 686 addrlen=sizeof(addr); 687 ier=ReplyBuffer; 688 endbuf=((char *) ReplyBuffer)+ReplySize; 689 maxlen=sizeof(struct ip)+ICMP_MINLEN+RequestSize; 690 691 /* Send the packet */ 692 TRACE("Sending %d bytes (RequestSize=%d) to %s\n", reqsize, RequestSize, inet_ntoa(addr.sin_addr)); 693 #if 0 694 if (TRACE_ON(icmp)){ 695 unsigned char* buf=(unsigned char*)reqbuf; 696 int i; 697 printf("Output buffer:\n"); 698 for (i=0;i<reqsize;i++) 699 printf("%2x,", buf[i]); 700 printf("\n"); 701 } 702 #endif 703 704 send_time = GetTickCount(); 705 res=sendto(icp->sid, (const char*)reqbuf, reqsize, 0, (struct sockaddr*)&addr, sizeof(addr)); 706 HeapFree(GetProcessHeap (), 0, reqbuf); 707 if (res<0) { 708 DWORD dwBestIfIndex; 709 IPAddr IP4Addr; 710 711 ZeroMemory(&ier->Address, sizeof(ier->Address)); 712 713 if (GetBestInterface(addr.sin_addr.s_addr, &dwBestIfIndex) == NO_ERROR && 714 GetIPv4ByIndex(dwBestIfIndex, &IP4Addr)) 715 { 716 memcpy(&ier->Address, &IP4Addr, sizeof(IP4Addr)); 717 } 718 719 if (WSAGetLastError()==WSAEMSGSIZE) 720 ier->Status = IP_PACKET_TOO_BIG; 721 else { 722 switch (WSAGetLastError()) { 723 case WSAENETUNREACH: 724 ier->Status = IP_DEST_NET_UNREACHABLE; 725 break; 726 case WSAEHOSTUNREACH: 727 ier->Status = IP_DEST_HOST_UNREACHABLE; 728 break; 729 default: 730 TRACE("unknown error: errno=%d\n",WSAGetLastError()); 731 ier->Status = IP_GENERAL_FAILURE; 732 ZeroMemory(&ier->Address, sizeof(ier->Address)); 733 } 734 } 735 return 1; 736 } 737 738 /* Get the reply */ 739 ip_header=HeapAlloc(GetProcessHeap(), 0, maxlen); 740 ip_header_len=0; /* because gcc was complaining */ 741 #ifdef __REACTOS__ 742 while ((res=select(icp->sid+1,&fdr,NULL,NULL,&timeout))>0) { 743 #else 744 while (poll(&fdr,1,Timeout)>0) { 745 #endif 746 recv_time = GetTickCount(); 747 res=recvfrom(icp->sid, (char*)ip_header, maxlen, 0, (struct sockaddr*)&addr,(int*)&addrlen); 748 TRACE("received %d bytes from %s\n",res, inet_ntoa(addr.sin_addr)); 749 ier->Status=IP_REQ_TIMED_OUT; 750 if (res < 0) 751 break; 752 753 /* Check whether we should ignore this packet */ 754 if ((ip_header->ip_p==IPPROTO_ICMP) && (res>=sizeof(struct ip)+ICMP_MINLEN)) { 755 ip_header_len=ip_header->ip_hl << 2; 756 icmp_header=(struct icmp*)(((char*)ip_header)+ip_header_len); 757 TRACE("received an ICMP packet of type,code=%d,%d\n",icmp_header->icmp_type,icmp_header->icmp_code); 758 if (icmp_header->icmp_type==ICMP_ECHOREPLY) { 759 if ((icmp_header->icmp_id==id) && (icmp_header->icmp_seq==seq)) 760 ier->Status=IP_SUCCESS; 761 } else { 762 switch (icmp_header->icmp_type) { 763 case ICMP_UNREACH: 764 switch (icmp_header->icmp_code) { 765 case ICMP_UNREACH_HOST: 766 #ifdef ICMP_UNREACH_HOST_UNKNOWN 767 case ICMP_UNREACH_HOST_UNKNOWN: 768 #endif 769 #ifdef ICMP_UNREACH_ISOLATED 770 case ICMP_UNREACH_ISOLATED: 771 #endif 772 #ifdef ICMP_UNREACH_HOST_PROHIB 773 case ICMP_UNREACH_HOST_PROHIB: 774 #endif 775 #ifdef ICMP_UNREACH_TOSHOST 776 case ICMP_UNREACH_TOSHOST: 777 #endif 778 ier->Status=IP_DEST_HOST_UNREACHABLE; 779 break; 780 case ICMP_UNREACH_PORT: 781 ier->Status=IP_DEST_PORT_UNREACHABLE; 782 break; 783 case ICMP_UNREACH_PROTOCOL: 784 ier->Status=IP_DEST_PROT_UNREACHABLE; 785 break; 786 case ICMP_UNREACH_SRCFAIL: 787 ier->Status=IP_BAD_ROUTE; 788 break; 789 default: 790 ier->Status=IP_DEST_NET_UNREACHABLE; 791 } 792 break; 793 case ICMP_TIMXCEED: 794 if (icmp_header->icmp_code==ICMP_TIMXCEED_REASS) 795 ier->Status=IP_TTL_EXPIRED_REASSEM; 796 else 797 ier->Status=IP_TTL_EXPIRED_TRANSIT; 798 break; 799 case ICMP_PARAMPROB: 800 ier->Status=IP_PARAM_PROBLEM; 801 break; 802 case ICMP_SOURCEQUENCH: 803 ier->Status=IP_SOURCE_QUENCH; 804 break; 805 } 806 if (ier->Status!=IP_REQ_TIMED_OUT) { 807 struct ip* rep_ip_header; 808 struct icmp* rep_icmp_header; 809 /* The ICMP header size of all the packets we accept is the same */ 810 rep_ip_header=(struct ip*)(((char*)icmp_header)+ICMP_MINLEN); 811 rep_icmp_header=(struct icmp*)(((char*)rep_ip_header)+(rep_ip_header->ip_hl << 2)); 812 813 /* Make sure that this is really a reply to our packet */ 814 if (ip_header_len+ICMP_MINLEN+(rep_ip_header->ip_hl << 2)+ICMP_MINLEN>ip_header->ip_len) { 815 ier->Status=IP_REQ_TIMED_OUT; 816 } else if ((rep_icmp_header->icmp_type!=ICMP_ECHO) || 817 (rep_icmp_header->icmp_code!=0) || 818 (rep_icmp_header->icmp_id!=id) || 819 /* windows doesn't check this checksum, else tracert */ 820 /* behind a Linux 2.2 masquerading firewall would fail*/ 821 /* (rep_icmp_header->icmp_cksum!=cksum) || */ 822 (rep_icmp_header->icmp_seq!=seq)) { 823 /* This was not a reply to one of our packets after all */ 824 TRACE("skipping type,code=%d,%d id,seq=%d,%d cksum=%d\n", 825 rep_icmp_header->icmp_type,rep_icmp_header->icmp_code, 826 rep_icmp_header->icmp_id,rep_icmp_header->icmp_seq, 827 rep_icmp_header->icmp_cksum); 828 TRACE("expected type,code=8,0 id,seq=%d,%d cksum=%d\n", 829 id,seq, 830 cksum); 831 ier->Status=IP_REQ_TIMED_OUT; 832 } 833 } 834 } 835 } 836 837 if (ier->Status==IP_REQ_TIMED_OUT) { 838 /* This packet was not for us. 839 * Decrease the timeout so that we don't enter an endless loop even 840 * if we get flooded with ICMP packets that are not for us. 841 */ 842 #ifdef __REACTOS__ 843 int t = Timeout - (recv_time - send_time); 844 if (t < 0) t = 0; 845 timeout.tv_sec = t / 1000; 846 timeout.tv_usec = (t % 1000) * 1000; 847 FD_ZERO(&fdr); 848 FD_SET(icp->sid, &fdr); 849 #else 850 DWORD t = (recv_time - send_time); 851 if (Timeout > t) Timeout -= t; 852 else Timeout = 0; 853 #endif 854 continue; 855 } else { 856 /* This is a reply to our packet */ 857 memcpy(&ier->Address,&ip_header->ip_src,sizeof(IPAddr)); 858 /* Status is already set */ 859 ier->RoundTripTime= recv_time - send_time; 860 ier->DataSize=res-ip_header_len-ICMP_MINLEN; 861 ier->Reserved=0; 862 ier->Data=endbuf-ier->DataSize; 863 memmove(ier->Data,((char*)ip_header)+ip_header_len+ICMP_MINLEN,ier->DataSize); 864 ier->Options.Ttl=ip_header->ip_ttl; 865 ier->Options.Tos=ip_header->ip_tos; 866 ier->Options.Flags=ip_header->ip_off >> 13; 867 ier->Options.OptionsSize=ip_header_len-sizeof(struct ip); 868 if (ier->Options.OptionsSize!=0) { 869 ier->Options.OptionsData=(unsigned char *) ier->Data-ier->Options.OptionsSize; 870 /* FIXME: We are supposed to rearrange the option's 'source route' data */ 871 memmove(ier->Options.OptionsData,((char*)ip_header)+ip_header_len,ier->Options.OptionsSize); 872 endbuf=(char*)ier->Options.OptionsData; 873 } else { 874 ier->Options.OptionsData=NULL; 875 endbuf=ier->Data; 876 } 877 878 /* Prepare for the next packet */ 879 ier++; 880 881 /* Check out whether there is more but don't wait this time */ 882 #ifdef __REACTOS__ 883 timeout.tv_sec=0; 884 timeout.tv_usec=0; 885 #else 886 Timeout=0; 887 #endif 888 } 889 #ifdef __REACTOS__ 890 FD_ZERO(&fdr); 891 FD_SET(icp->sid,&fdr); 892 #endif 893 } 894 HeapFree(GetProcessHeap(), 0, ip_header); 895 res=ier-(ICMP_ECHO_REPLY*)ReplyBuffer; 896 if (res==0) 897 { 898 ier->Status = IP_REQ_TIMED_OUT; 899 SetLastError(IP_REQ_TIMED_OUT); 900 } 901 TRACE("received %d replies\n",res); 902 return res; 903 } 904 905 /*********************************************************************** 906 * IcmpSendEcho2 (IPHLPAPI.@) 907 */ 908 DWORD WINAPI IcmpSendEcho2( 909 HANDLE IcmpHandle, 910 HANDLE Event, 911 PIO_APC_ROUTINE ApcRoutine, 912 PVOID ApcContext, 913 IPAddr DestinationAddress, 914 LPVOID RequestData, 915 WORD RequestSize, 916 PIP_OPTION_INFORMATION RequestOptions, 917 LPVOID ReplyBuffer, 918 DWORD ReplySize, 919 DWORD Timeout 920 ) 921 { 922 TRACE("(%p, %p, %p, %p, %08x, %p, %d, %p, %p, %d, %d): stub\n", IcmpHandle, 923 Event, ApcRoutine, ApcContext, DestinationAddress, RequestData, 924 RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout); 925 926 if (Event) 927 { 928 FIXME("unsupported for events\n"); 929 return 0; 930 } 931 if (ApcRoutine) 932 { 933 FIXME("unsupported for APCs\n"); 934 return 0; 935 } 936 return IcmpSendEcho(IcmpHandle, DestinationAddress, RequestData, 937 RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout); 938 } 939 940 /*********************************************************************** 941 * IcmpSendEcho2Ex (IPHLPAPI.@) 942 */ 943 DWORD WINAPI IcmpSendEcho2Ex( 944 HANDLE IcmpHandle, 945 HANDLE Event, 946 PIO_APC_ROUTINE ApcRoutine, 947 PVOID ApcContext, 948 IPAddr SourceAddress, 949 IPAddr DestinationAddress, 950 LPVOID RequestData, 951 WORD RequestSize, 952 PIP_OPTION_INFORMATION RequestOptions, 953 LPVOID ReplyBuffer, 954 DWORD ReplySize, 955 DWORD Timeout 956 ) 957 { 958 TRACE("(%p, %p, %p, %p, %08x, %08x, %p, %d, %p, %p, %d, %d): stub\n", IcmpHandle, 959 Event, ApcRoutine, ApcContext, SourceAddress, DestinationAddress, RequestData, 960 RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout); 961 962 if (Event) 963 { 964 FIXME("unsupported for events\n"); 965 return 0; 966 } 967 if (ApcRoutine) 968 { 969 FIXME("unsupported for APCs\n"); 970 return 0; 971 } 972 if (SourceAddress) 973 { 974 FIXME("unsupported for source addresses\n"); 975 return 0; 976 } 977 978 return IcmpSendEcho(IcmpHandle, DestinationAddress, RequestData, 979 RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout); 980 } 981 982 /* 983 * Copyright (c) 1989 The Regents of the University of California. 984 * All rights reserved. 985 * 986 * This code is derived from software contributed to Berkeley by 987 * Mike Muuss. 988 * 989 * Redistribution and use in source and binary forms, with or without 990 * modification, are permitted provided that the following conditions 991 * are met: 992 * 1. Redistributions of source code must retain the above copyright 993 * notice, this list of conditions and the following disclaimer. 994 * 2. Redistributions in binary form must reproduce the above copyright 995 * notice, this list of conditions and the following disclaimer in the 996 * documentation and/or other materials provided with the distribution. 997 * 3. Neither the name of the University nor the names of its contributors 998 * may be used to endorse or promote products derived from this software 999 * without specific prior written permission. 1000 * 1001 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 1002 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1003 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1004 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 1005 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1006 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1007 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 1008 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 1009 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 1010 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 1011 * SUCH DAMAGE. 1012 * 1013 */ 1014