xref: /reactos/dll/win32/iphlpapi/icmp.c (revision 3c774903)
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