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 /*********************************************************************** 500 * IcmpSendEcho (IPHLPAPI.@) 501 */ 502 DWORD WINAPI IcmpSendEcho( 503 HANDLE IcmpHandle, 504 IPAddr DestinationAddress, 505 LPVOID RequestData, 506 WORD RequestSize, 507 PIP_OPTION_INFORMATION RequestOptions, 508 LPVOID ReplyBuffer, 509 DWORD ReplySize, 510 DWORD Timeout 511 ) 512 { 513 icmp_t* icp=(icmp_t*)IcmpHandle; 514 unsigned char* reqbuf; 515 int reqsize; 516 517 struct icmp_echo_reply* ier; 518 struct ip* ip_header; 519 struct icmp* icmp_header; 520 char* endbuf; 521 int ip_header_len; 522 int maxlen; 523 #ifdef __REACTOS__ 524 fd_set fdr; 525 struct timeval timeout; 526 #else 527 struct pollfd fdr; 528 #endif 529 DWORD send_time,recv_time; 530 struct sockaddr_in addr; 531 socklen_t addrlen; 532 unsigned short id,seq,cksum; 533 int res; 534 535 if (IcmpHandle==INVALID_HANDLE_VALUE) { 536 /* FIXME: in fact win98 seems to ignore the handle value !!! */ 537 SetLastError(ERROR_INVALID_HANDLE); 538 return 0; 539 } 540 541 if (ReplySize<sizeof(ICMP_ECHO_REPLY)) { 542 SetLastError(ERROR_INVALID_PARAMETER); 543 return 0; 544 } 545 546 if (ReplySize-RequestSize<sizeof(ICMP_ECHO_REPLY)) { 547 SetLastError(IP_GENERAL_FAILURE); 548 return 0; 549 } 550 551 if (!ReplyBuffer) { 552 SetLastError(ERROR_INVALID_PARAMETER); 553 return 0; 554 } 555 556 if (Timeout == 0 || Timeout == -1) { 557 SetLastError(ERROR_INVALID_PARAMETER); 558 return 0; 559 } 560 /* check the request size against SO_MAX_MSG_SIZE using getsockopt */ 561 562 if (!DestinationAddress) { 563 SetLastError(ERROR_INVALID_NETNAME); 564 return 0; 565 } 566 567 #ifndef __REACTOS__ 568 if (icp->sid < 0) { 569 WARN("using system ping command since SOCK_RAW was not supported.\n"); 570 return system_icmp(DestinationAddress, RequestData, RequestSize, 571 RequestOptions, ReplyBuffer, ReplySize, Timeout); 572 #endif 573 574 /* Prepare the request */ 575 #ifdef __REACTOS__ 576 id = GetCurrentProcessId() & 0xFFFF; 577 #else 578 id=getpid() & 0xFFFF; 579 #endif 580 seq=InterlockedIncrement(&icmp_sequence) & 0xFFFF; 581 582 reqsize=ICMP_MINLEN; 583 if (RequestData && RequestSize > 0) 584 reqsize += RequestSize; 585 reqbuf=HeapAlloc(GetProcessHeap(), 0, reqsize); 586 if (reqbuf==NULL) { 587 SetLastError(ERROR_OUTOFMEMORY); 588 return 0; 589 } 590 591 icmp_header=(struct icmp*)reqbuf; 592 icmp_header->icmp_type=ICMP_ECHO; 593 icmp_header->icmp_code=0; 594 icmp_header->icmp_cksum=0; 595 icmp_header->icmp_id=id; 596 icmp_header->icmp_seq=seq; 597 if (RequestData && RequestSize > 0) 598 memcpy(reqbuf+ICMP_MINLEN, RequestData, RequestSize); 599 icmp_header->icmp_cksum=cksum=in_cksum((u_short*)reqbuf,reqsize); 600 601 addr.sin_family=AF_INET; 602 addr.sin_addr.s_addr=DestinationAddress; 603 addr.sin_port=0; 604 605 if (RequestOptions!=NULL) { 606 int val; 607 if (icp->default_opts.OptionsSize==IP_OPTS_UNKNOWN) { 608 socklen_t len; 609 /* Before we mess with the options, get the default values */ 610 len=sizeof(val); 611 getsockopt(icp->sid,IPPROTO_IP,IP_TTL,(char *)&val,&len); 612 icp->default_opts.Ttl=val; 613 614 len=sizeof(val); 615 getsockopt(icp->sid,IPPROTO_IP,IP_TOS,(char *)&val,&len); 616 icp->default_opts.Tos=val; 617 /* FIXME: missing: handling of IP 'flags', and all the other options */ 618 } 619 620 val=RequestOptions->Ttl; 621 setsockopt(icp->sid,IPPROTO_IP,IP_TTL,(char *)&val,sizeof(val)); 622 val=RequestOptions->Tos; 623 setsockopt(icp->sid,IPPROTO_IP,IP_TOS,(char *)&val,sizeof(val)); 624 /* FIXME: missing: handling of IP 'flags', and all the other options */ 625 626 icp->default_opts.OptionsSize=IP_OPTS_CUSTOM; 627 } else if (icp->default_opts.OptionsSize==IP_OPTS_CUSTOM) { 628 int val; 629 630 /* Restore the default options */ 631 val=icp->default_opts.Ttl; 632 setsockopt(icp->sid,IPPROTO_IP,IP_TTL,(char *)&val,sizeof(val)); 633 val=icp->default_opts.Tos; 634 setsockopt(icp->sid,IPPROTO_IP,IP_TOS,(char *)&val,sizeof(val)); 635 /* FIXME: missing: handling of IP 'flags', and all the other options */ 636 637 icp->default_opts.OptionsSize=IP_OPTS_DEFAULT; 638 } 639 640 /* Get ready for receiving the reply 641 * Do it before we send the request to minimize the risk of introducing delays 642 */ 643 #ifdef __REACTOS__ 644 FD_ZERO(&fdr); 645 FD_SET(icp->sid,&fdr); 646 timeout.tv_sec=Timeout/1000; 647 timeout.tv_usec=(Timeout % 1000)*1000; 648 #else 649 fdr.fd = icp->sid; 650 fdr.events = POLLIN; 651 #endif 652 addrlen=sizeof(addr); 653 ier=ReplyBuffer; 654 endbuf=((char *) ReplyBuffer)+ReplySize; 655 maxlen=sizeof(struct ip)+ICMP_MINLEN+RequestSize; 656 657 /* Send the packet */ 658 TRACE("Sending %d bytes (RequestSize=%d) to %s\n", reqsize, RequestSize, inet_ntoa(addr.sin_addr)); 659 #if 0 660 if (TRACE_ON(icmp)){ 661 unsigned char* buf=(unsigned char*)reqbuf; 662 int i; 663 printf("Output buffer:\n"); 664 for (i=0;i<reqsize;i++) 665 printf("%2x,", buf[i]); 666 printf("\n"); 667 } 668 #endif 669 670 send_time = GetTickCount(); 671 res=sendto(icp->sid, (const char*)reqbuf, reqsize, 0, (struct sockaddr*)&addr, sizeof(addr)); 672 HeapFree(GetProcessHeap (), 0, reqbuf); 673 if (res<0) { 674 if (WSAGetLastError()==WSAEMSGSIZE) 675 SetLastError(IP_PACKET_TOO_BIG); 676 else { 677 switch (WSAGetLastError()) { 678 case WSAENETUNREACH: 679 SetLastError(IP_DEST_NET_UNREACHABLE); 680 break; 681 case WSAEHOSTUNREACH: 682 SetLastError(IP_DEST_HOST_UNREACHABLE); 683 break; 684 default: 685 TRACE("unknown error: errno=%d\n",WSAGetLastError()); 686 SetLastError(IP_GENERAL_FAILURE); 687 } 688 } 689 return 0; 690 } 691 692 /* Get the reply */ 693 ip_header=HeapAlloc(GetProcessHeap(), 0, maxlen); 694 ip_header_len=0; /* because gcc was complaining */ 695 #ifdef __REACTOS__ 696 while ((res=select(icp->sid+1,&fdr,NULL,NULL,&timeout))>0) { 697 #else 698 while (poll(&fdr,1,Timeout)>0) { 699 #endif 700 recv_time = GetTickCount(); 701 res=recvfrom(icp->sid, (char*)ip_header, maxlen, 0, (struct sockaddr*)&addr,(int*)&addrlen); 702 TRACE("received %d bytes from %s\n",res, inet_ntoa(addr.sin_addr)); 703 ier->Status=IP_REQ_TIMED_OUT; 704 if (res < 0) 705 break; 706 707 /* Check whether we should ignore this packet */ 708 if ((ip_header->ip_p==IPPROTO_ICMP) && (res>=sizeof(struct ip)+ICMP_MINLEN)) { 709 ip_header_len=ip_header->ip_hl << 2; 710 icmp_header=(struct icmp*)(((char*)ip_header)+ip_header_len); 711 TRACE("received an ICMP packet of type,code=%d,%d\n",icmp_header->icmp_type,icmp_header->icmp_code); 712 if (icmp_header->icmp_type==ICMP_ECHOREPLY) { 713 if ((icmp_header->icmp_id==id) && (icmp_header->icmp_seq==seq)) 714 ier->Status=IP_SUCCESS; 715 } else { 716 switch (icmp_header->icmp_type) { 717 case ICMP_UNREACH: 718 switch (icmp_header->icmp_code) { 719 case ICMP_UNREACH_HOST: 720 #ifdef ICMP_UNREACH_HOST_UNKNOWN 721 case ICMP_UNREACH_HOST_UNKNOWN: 722 #endif 723 #ifdef ICMP_UNREACH_ISOLATED 724 case ICMP_UNREACH_ISOLATED: 725 #endif 726 #ifdef ICMP_UNREACH_HOST_PROHIB 727 case ICMP_UNREACH_HOST_PROHIB: 728 #endif 729 #ifdef ICMP_UNREACH_TOSHOST 730 case ICMP_UNREACH_TOSHOST: 731 #endif 732 ier->Status=IP_DEST_HOST_UNREACHABLE; 733 break; 734 case ICMP_UNREACH_PORT: 735 ier->Status=IP_DEST_PORT_UNREACHABLE; 736 break; 737 case ICMP_UNREACH_PROTOCOL: 738 ier->Status=IP_DEST_PROT_UNREACHABLE; 739 break; 740 case ICMP_UNREACH_SRCFAIL: 741 ier->Status=IP_BAD_ROUTE; 742 break; 743 default: 744 ier->Status=IP_DEST_NET_UNREACHABLE; 745 } 746 break; 747 case ICMP_TIMXCEED: 748 if (icmp_header->icmp_code==ICMP_TIMXCEED_REASS) 749 ier->Status=IP_TTL_EXPIRED_REASSEM; 750 else 751 ier->Status=IP_TTL_EXPIRED_TRANSIT; 752 break; 753 case ICMP_PARAMPROB: 754 ier->Status=IP_PARAM_PROBLEM; 755 break; 756 case ICMP_SOURCEQUENCH: 757 ier->Status=IP_SOURCE_QUENCH; 758 break; 759 } 760 if (ier->Status!=IP_REQ_TIMED_OUT) { 761 struct ip* rep_ip_header; 762 struct icmp* rep_icmp_header; 763 /* The ICMP header size of all the packets we accept is the same */ 764 rep_ip_header=(struct ip*)(((char*)icmp_header)+ICMP_MINLEN); 765 rep_icmp_header=(struct icmp*)(((char*)rep_ip_header)+(rep_ip_header->ip_hl << 2)); 766 767 /* Make sure that this is really a reply to our packet */ 768 if (ip_header_len+ICMP_MINLEN+(rep_ip_header->ip_hl << 2)+ICMP_MINLEN>ip_header->ip_len) { 769 ier->Status=IP_REQ_TIMED_OUT; 770 } else if ((rep_icmp_header->icmp_type!=ICMP_ECHO) || 771 (rep_icmp_header->icmp_code!=0) || 772 (rep_icmp_header->icmp_id!=id) || 773 /* windows doesn't check this checksum, else tracert */ 774 /* behind a Linux 2.2 masquerading firewall would fail*/ 775 /* (rep_icmp_header->icmp_cksum!=cksum) || */ 776 (rep_icmp_header->icmp_seq!=seq)) { 777 /* This was not a reply to one of our packets after all */ 778 TRACE("skipping type,code=%d,%d id,seq=%d,%d cksum=%d\n", 779 rep_icmp_header->icmp_type,rep_icmp_header->icmp_code, 780 rep_icmp_header->icmp_id,rep_icmp_header->icmp_seq, 781 rep_icmp_header->icmp_cksum); 782 TRACE("expected type,code=8,0 id,seq=%d,%d cksum=%d\n", 783 id,seq, 784 cksum); 785 ier->Status=IP_REQ_TIMED_OUT; 786 } 787 } 788 } 789 } 790 791 if (ier->Status==IP_REQ_TIMED_OUT) { 792 /* This packet was not for us. 793 * Decrease the timeout so that we don't enter an endless loop even 794 * if we get flooded with ICMP packets that are not for us. 795 */ 796 #ifdef __REACTOS__ 797 int t = Timeout - (recv_time - send_time); 798 if (t < 0) t = 0; 799 timeout.tv_sec = t / 1000; 800 timeout.tv_usec = (t % 1000) * 1000; 801 FD_ZERO(&fdr); 802 FD_SET(icp->sid, &fdr); 803 #else 804 DWORD t = (recv_time - send_time); 805 if (Timeout > t) Timeout -= t; 806 else Timeout = 0; 807 #endif 808 continue; 809 } else { 810 /* This is a reply to our packet */ 811 memcpy(&ier->Address,&ip_header->ip_src,sizeof(IPAddr)); 812 /* Status is already set */ 813 ier->RoundTripTime= recv_time - send_time; 814 ier->DataSize=res-ip_header_len-ICMP_MINLEN; 815 ier->Reserved=0; 816 ier->Data=endbuf-ier->DataSize; 817 memmove(ier->Data,((char*)ip_header)+ip_header_len+ICMP_MINLEN,ier->DataSize); 818 ier->Options.Ttl=ip_header->ip_ttl; 819 ier->Options.Tos=ip_header->ip_tos; 820 ier->Options.Flags=ip_header->ip_off >> 13; 821 ier->Options.OptionsSize=ip_header_len-sizeof(struct ip); 822 if (ier->Options.OptionsSize!=0) { 823 ier->Options.OptionsData=(unsigned char *) ier->Data-ier->Options.OptionsSize; 824 /* FIXME: We are supposed to rearrange the option's 'source route' data */ 825 memmove(ier->Options.OptionsData,((char*)ip_header)+ip_header_len,ier->Options.OptionsSize); 826 endbuf=(char*)ier->Options.OptionsData; 827 } else { 828 ier->Options.OptionsData=NULL; 829 endbuf=ier->Data; 830 } 831 832 /* Prepare for the next packet */ 833 ier++; 834 835 /* Check out whether there is more but don't wait this time */ 836 #ifdef __REACTOS__ 837 timeout.tv_sec=0; 838 timeout.tv_usec=0; 839 #else 840 Timeout=0; 841 #endif 842 } 843 #ifdef __REACTOS__ 844 FD_ZERO(&fdr); 845 FD_SET(icp->sid,&fdr); 846 #endif 847 } 848 HeapFree(GetProcessHeap(), 0, ip_header); 849 res=ier-(ICMP_ECHO_REPLY*)ReplyBuffer; 850 if (res==0) 851 SetLastError(IP_REQ_TIMED_OUT); 852 TRACE("received %d replies\n",res); 853 return res; 854 } 855 856 /*********************************************************************** 857 * IcmpSendEcho2 (IPHLPAPI.@) 858 */ 859 DWORD WINAPI IcmpSendEcho2( 860 HANDLE IcmpHandle, 861 HANDLE Event, 862 PIO_APC_ROUTINE ApcRoutine, 863 PVOID ApcContext, 864 IPAddr DestinationAddress, 865 LPVOID RequestData, 866 WORD RequestSize, 867 PIP_OPTION_INFORMATION RequestOptions, 868 LPVOID ReplyBuffer, 869 DWORD ReplySize, 870 DWORD Timeout 871 ) 872 { 873 TRACE("(%p, %p, %p, %p, %08x, %p, %d, %p, %p, %d, %d): stub\n", IcmpHandle, 874 Event, ApcRoutine, ApcContext, DestinationAddress, RequestData, 875 RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout); 876 877 if (Event) 878 { 879 FIXME("unsupported for events\n"); 880 return 0; 881 } 882 if (ApcRoutine) 883 { 884 FIXME("unsupported for APCs\n"); 885 return 0; 886 } 887 return IcmpSendEcho(IcmpHandle, DestinationAddress, RequestData, 888 RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout); 889 } 890 891 /*********************************************************************** 892 * IcmpSendEcho2Ex (IPHLPAPI.@) 893 */ 894 DWORD WINAPI IcmpSendEcho2Ex( 895 HANDLE IcmpHandle, 896 HANDLE Event, 897 PIO_APC_ROUTINE ApcRoutine, 898 PVOID ApcContext, 899 IPAddr SourceAddress, 900 IPAddr DestinationAddress, 901 LPVOID RequestData, 902 WORD RequestSize, 903 PIP_OPTION_INFORMATION RequestOptions, 904 LPVOID ReplyBuffer, 905 DWORD ReplySize, 906 DWORD Timeout 907 ) 908 { 909 TRACE("(%p, %p, %p, %p, %08x, %08x, %p, %d, %p, %p, %d, %d): stub\n", IcmpHandle, 910 Event, ApcRoutine, ApcContext, SourceAddress, DestinationAddress, RequestData, 911 RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout); 912 913 if (Event) 914 { 915 FIXME("unsupported for events\n"); 916 return 0; 917 } 918 if (ApcRoutine) 919 { 920 FIXME("unsupported for APCs\n"); 921 return 0; 922 } 923 if (SourceAddress) 924 { 925 FIXME("unsupported for source addresses\n"); 926 return 0; 927 } 928 929 return IcmpSendEcho(IcmpHandle, DestinationAddress, RequestData, 930 RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout); 931 } 932 933 /* 934 * Copyright (c) 1989 The Regents of the University of California. 935 * All rights reserved. 936 * 937 * This code is derived from software contributed to Berkeley by 938 * Mike Muuss. 939 * 940 * Redistribution and use in source and binary forms, with or without 941 * modification, are permitted provided that the following conditions 942 * are met: 943 * 1. Redistributions of source code must retain the above copyright 944 * notice, this list of conditions and the following disclaimer. 945 * 2. Redistributions in binary form must reproduce the above copyright 946 * notice, this list of conditions and the following disclaimer in the 947 * documentation and/or other materials provided with the distribution. 948 * 3. Neither the name of the University nor the names of its contributors 949 * may be used to endorse or promote products derived from this software 950 * without specific prior written permission. 951 * 952 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 953 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 954 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 955 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 956 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 957 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 958 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 959 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 960 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 961 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 962 * SUCH DAMAGE. 963 * 964 */ 965