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