[IPHLPAPI] Fix IcmpSendEcho output when host is not reachable
[reactos.git] / dll / win32 / iphlpapi / icmp.c
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 */