[IPHLPAPI_APITEST] Move icmp tests from winetests to apitests where they belong....
[reactos.git] / reactos / dll / win32 / icmp / icmp_main.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 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 /* Future work:
25 * - Systems like FreeBSD don't seem to support the IP_TTL option and maybe others.
26 * But using IP_HDRINCL and building the IP header by hand might work.
27 * - Not all IP options are supported.
28 * - Are ICMP handles real handles, i.e. inheritable and all? There might be some
29 * more work to do here, including server side stuff with synchronization.
30 * - Is it correct to use malloc for the internal buffer, for allocating the
31 * handle's structure?
32 * - This API should probably be thread safe. Is it really?
33 * - Using the winsock functions has not been tested.
34 */
35
36 #define WIN32_NO_STATUS
37 #define _INC_WINDOWS
38 #define COM_NO_WINDOWS_H
39
40 #include <config.h>
41
42 #include <sys/types.h>
43 #ifdef HAVE_SYS_SOCKET_H
44 # include <sys/socket.h>
45 #endif
46 #ifdef HAVE_NETDB_H
47 # include <netdb.h>
48 #endif
49 #ifdef HAVE_NETINET_IN_SYSTM_H
50 # include <netinet/in_systm.h>
51 #endif
52 #ifdef HAVE_NETINET_IN_H
53 # include <netinet/in.h>
54 #endif
55
56 #ifdef HAVE_SYS_TIME_H
57 # include <sys/time.h>
58 #endif
59 #include <stdarg.h>
60 //#include <string.h>
61 //#include <errno.h>
62 #ifdef HAVE_UNISTD_H
63 # include <unistd.h>
64 #endif
65 #ifdef HAVE_ARPA_INET_H
66 # include <arpa/inet.h>
67 #endif
68
69 #include <windef.h>
70 #include <winbase.h>
71 //#include "winerror.h"
72 #include <ipexport.h>
73 //#include <ws2tcpip.h>
74 //#include "icmpapi.h"
75 #include <wine/debug.h>
76
77 /* Set up endiannes macros for the ip and ip_icmp BSD headers */
78 #ifndef BIG_ENDIAN
79 #define BIG_ENDIAN 4321
80 #endif
81 #ifndef LITTLE_ENDIAN
82 #define LITTLE_ENDIAN 1234
83 #endif
84 #ifndef BYTE_ORDER
85 #ifdef WORDS_BIGENDIAN
86 #define BYTE_ORDER BIG_ENDIAN
87 #else
88 #define BYTE_ORDER LITTLE_ENDIAN
89 #endif
90 #endif /* BYTE_ORDER */
91
92 #define u_int16_t WORD
93 #define u_int32_t DWORD
94
95 /* These are BSD headers. We use these here because they are needed on
96 * libc5 Linux systems. On other platforms they are usually simply more
97 * complete than the native stuff, and cause less portability problems
98 * so we use them anyway.
99 */
100 #include "ip.h"
101 #include "ip_icmp.h"
102
103
104 WINE_DEFAULT_DEBUG_CHANNEL(icmp);
105
106
107 typedef struct {
108 int sid;
109 IP_OPTION_INFORMATION default_opts;
110 } icmp_t;
111
112 #define IP_OPTS_UNKNOWN 0
113 #define IP_OPTS_DEFAULT 1
114 #define IP_OPTS_CUSTOM 2
115
116 /* The sequence number is unique process wide, so that all threads
117 * have a distinct sequence number.
118 */
119 static LONG icmp_sequence=0;
120
121 static int in_cksum(u_short *addr, int len)
122 {
123 int nleft=len;
124 u_short *w = addr;
125 int sum = 0;
126 u_short answer = 0;
127
128 while (nleft > 1) {
129 sum += *w++;
130 nleft -= 2;
131 }
132
133 if (nleft == 1) {
134 *(u_char *)(&answer) = *(u_char *)w;
135 sum += answer;
136 }
137
138 sum = (sum >> 16) + (sum & 0xffff);
139 sum += (sum >> 16);
140 answer = ~sum;
141 return(answer);
142 }
143
144
145
146 /*
147 * Exported Routines.
148 */
149
150 BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
151 {
152 WSADATA wsaData;
153
154 switch (fdwReason) {
155 case DLL_PROCESS_ATTACH:
156 WSAStartup(MAKEWORD(2, 2), &wsaData);
157 break;
158
159 case DLL_PROCESS_DETACH:
160 WSACleanup();
161 break;
162 }
163 return TRUE;
164 }
165
166 /***********************************************************************
167 * IcmpCreateFile (ICMP.@)
168 */
169 HANDLE WINAPI IcmpCreateFile(VOID)
170 {
171 icmp_t* icp;
172
173 int sid=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
174 if (sid < 0) {
175 MESSAGE("WARNING: Trying to use ICMP (network ping) will fail unless running as root\n");
176 SetLastError(ERROR_ACCESS_DENIED);
177 return INVALID_HANDLE_VALUE;
178 }
179
180 icp=HeapAlloc(GetProcessHeap(), 0, sizeof(*icp));
181 if (icp==NULL) {
182 closesocket(sid);
183 SetLastError(IP_NO_RESOURCES);
184 return INVALID_HANDLE_VALUE;
185 }
186 icp->sid=sid;
187 icp->default_opts.OptionsSize=IP_OPTS_UNKNOWN;
188 return (HANDLE)icp;
189 }
190
191
192 /***********************************************************************
193 * IcmpCloseHandle (ICMP.@)
194 */
195 BOOL WINAPI IcmpCloseHandle(HANDLE IcmpHandle)
196 {
197 icmp_t* icp=(icmp_t*)IcmpHandle;
198 if (IcmpHandle==INVALID_HANDLE_VALUE) {
199 /* FIXME: in fact win98 seems to ignore the handle value !!! */
200 SetLastError(ERROR_INVALID_HANDLE);
201 return FALSE;
202 }
203
204 shutdown(icp->sid,2);
205 HeapFree(GetProcessHeap (), 0, icp);
206 return TRUE;
207 }
208
209
210 /***********************************************************************
211 * IcmpSendEcho (ICMP.@)
212 */
213 DWORD WINAPI IcmpSendEcho(
214 HANDLE IcmpHandle,
215 IPAddr DestinationAddress,
216 LPVOID RequestData,
217 WORD RequestSize,
218 PIP_OPTION_INFORMATION RequestOptions,
219 LPVOID ReplyBuffer,
220 DWORD ReplySize,
221 DWORD Timeout
222 )
223 {
224 icmp_t* icp=(icmp_t*)IcmpHandle;
225 unsigned char* reqbuf;
226 int reqsize;
227
228 struct icmp_echo_reply* ier;
229 struct ip* ip_header;
230 struct icmp* icmp_header;
231 char* endbuf;
232 int ip_header_len;
233 int maxlen;
234 fd_set fdr;
235 struct timeval timeout;
236 DWORD send_time,recv_time;
237 struct sockaddr_in addr;
238 unsigned int addrlen;
239 unsigned short id,seq,cksum;
240 int res;
241
242 if (IcmpHandle==INVALID_HANDLE_VALUE) {
243 /* FIXME: in fact win98 seems to ignore the handle value !!! */
244 SetLastError(ERROR_INVALID_HANDLE);
245 return 0;
246 }
247
248 if (ReplySize<sizeof(ICMP_ECHO_REPLY)+ICMP_MINLEN) {
249 SetLastError(IP_BUF_TOO_SMALL);
250 return 0;
251 }
252 /* check the request size against SO_MAX_MSG_SIZE using getsockopt */
253
254 /* Prepare the request */
255 id=GetCurrentProcessId() & 0xFFFF;
256 seq=InterlockedIncrement(&icmp_sequence) & 0xFFFF;
257
258 reqsize=ICMP_MINLEN+RequestSize;
259 reqbuf=HeapAlloc(GetProcessHeap(), 0, reqsize);
260 if (reqbuf==NULL) {
261 SetLastError(ERROR_OUTOFMEMORY);
262 return 0;
263 }
264
265 icmp_header=(struct icmp*)reqbuf;
266 icmp_header->icmp_type=ICMP_ECHO;
267 icmp_header->icmp_code=0;
268 icmp_header->icmp_cksum=0;
269 icmp_header->icmp_id=id;
270 icmp_header->icmp_seq=seq;
271 memcpy(reqbuf+ICMP_MINLEN, RequestData, RequestSize);
272 icmp_header->icmp_cksum=cksum=in_cksum((u_short*)reqbuf,reqsize);
273
274 addr.sin_family=AF_INET;
275 addr.sin_addr.s_addr=DestinationAddress;
276 addr.sin_port=0;
277
278 if (RequestOptions!=NULL) {
279 int val;
280 if (icp->default_opts.OptionsSize==IP_OPTS_UNKNOWN) {
281 int len;
282 /* Before we mess with the options, get the default values */
283 len=sizeof(val);
284 getsockopt(icp->sid,IPPROTO_IP,IP_TTL,(char *)&val,&len);
285 icp->default_opts.Ttl=val;
286
287 len=sizeof(val);
288 getsockopt(icp->sid,IPPROTO_IP,IP_TOS,(char *)&val,&len);
289 icp->default_opts.Tos=val;
290 /* FIXME: missing: handling of IP 'flags', and all the other options */
291 }
292
293 val=RequestOptions->Ttl;
294 setsockopt(icp->sid,IPPROTO_IP,IP_TTL,(char *)&val,sizeof(val));
295 val=RequestOptions->Tos;
296 setsockopt(icp->sid,IPPROTO_IP,IP_TOS,(char *)&val,sizeof(val));
297 /* FIXME: missing: handling of IP 'flags', and all the other options */
298
299 icp->default_opts.OptionsSize=IP_OPTS_CUSTOM;
300 } else if (icp->default_opts.OptionsSize==IP_OPTS_CUSTOM) {
301 int val;
302
303 /* Restore the default options */
304 val=icp->default_opts.Ttl;
305 setsockopt(icp->sid,IPPROTO_IP,IP_TTL,(char *)&val,sizeof(val));
306 val=icp->default_opts.Tos;
307 setsockopt(icp->sid,IPPROTO_IP,IP_TOS,(char *)&val,sizeof(val));
308 /* FIXME: missing: handling of IP 'flags', and all the other options */
309
310 icp->default_opts.OptionsSize=IP_OPTS_DEFAULT;
311 }
312
313 /* Get ready for receiving the reply
314 * Do it before we send the request to minimize the risk of introducing delays
315 */
316 FD_ZERO(&fdr);
317 FD_SET(icp->sid,&fdr);
318 timeout.tv_sec=Timeout/1000;
319 timeout.tv_usec=(Timeout % 1000)*1000;
320 addrlen=sizeof(addr);
321 ier=ReplyBuffer;
322 ip_header=(struct ip *) ((char *) ReplyBuffer+sizeof(ICMP_ECHO_REPLY));
323 endbuf=(char *) ReplyBuffer+ReplySize;
324 maxlen=ReplySize-sizeof(ICMP_ECHO_REPLY);
325
326 /* Send the packet */
327 TRACE("Sending %d bytes (RequestSize=%d) to %s\n", reqsize, RequestSize, inet_ntoa(addr.sin_addr));
328 #if 0
329 if (TRACE_ON(icmp)){
330 unsigned char* buf=(unsigned char*)reqbuf;
331 int i;
332 printf("Output buffer:\n");
333 for (i=0;i<reqsize;i++)
334 printf("%2x,", buf[i]);
335 printf("\n");
336 }
337 #endif
338
339 send_time = GetTickCount();
340 res=sendto(icp->sid, (const char*)reqbuf, reqsize, 0, (struct sockaddr*)&addr, sizeof(addr));
341 HeapFree(GetProcessHeap (), 0, reqbuf);
342 if (res<0) {
343 if (WSAGetLastError()==WSAEMSGSIZE)
344 SetLastError(IP_PACKET_TOO_BIG);
345 else {
346 switch (WSAGetLastError()) {
347 case WSAENETUNREACH:
348 SetLastError(IP_DEST_NET_UNREACHABLE);
349 break;
350 case WSAEHOSTUNREACH:
351 SetLastError(IP_DEST_HOST_UNREACHABLE);
352 break;
353 default:
354 TRACE("unknown error: errno=%d\n",WSAGetLastError());
355 SetLastError(IP_GENERAL_FAILURE);
356 }
357 }
358 return 0;
359 }
360
361 /* Get the reply */
362 ip_header_len=0; /* because gcc was complaining */
363 while ((res=select(icp->sid+1,&fdr,NULL,NULL,&timeout))>0) {
364 recv_time = GetTickCount();
365 res=recvfrom(icp->sid, (char*)ip_header, maxlen, 0, (struct sockaddr*)&addr,(int*)&addrlen);
366 TRACE("received %d bytes from %s\n",res, inet_ntoa(addr.sin_addr));
367 ier->Status=IP_REQ_TIMED_OUT;
368
369 /* Check whether we should ignore this packet */
370 if ((ip_header->ip_p==IPPROTO_ICMP) && (res>=sizeof(struct ip)+ICMP_MINLEN)) {
371 ip_header_len=ip_header->ip_hl << 2;
372 icmp_header=(struct icmp*)(((char*)ip_header)+ip_header_len);
373 TRACE("received an ICMP packet of type,code=%d,%d\n",icmp_header->icmp_type,icmp_header->icmp_code);
374 if (icmp_header->icmp_type==ICMP_ECHOREPLY) {
375 if ((icmp_header->icmp_id==id) && (icmp_header->icmp_seq==seq))
376 ier->Status=IP_SUCCESS;
377 } else {
378 switch (icmp_header->icmp_type) {
379 case ICMP_UNREACH:
380 switch (icmp_header->icmp_code) {
381 case ICMP_UNREACH_HOST:
382 #ifdef ICMP_UNREACH_HOST_UNKNOWN
383 case ICMP_UNREACH_HOST_UNKNOWN:
384 #endif
385 #ifdef ICMP_UNREACH_ISOLATED
386 case ICMP_UNREACH_ISOLATED:
387 #endif
388 #ifdef ICMP_UNREACH_HOST_PROHIB
389 case ICMP_UNREACH_HOST_PROHIB:
390 #endif
391 #ifdef ICMP_UNREACH_TOSHOST
392 case ICMP_UNREACH_TOSHOST:
393 #endif
394 ier->Status=IP_DEST_HOST_UNREACHABLE;
395 break;
396 case ICMP_UNREACH_PORT:
397 ier->Status=IP_DEST_PORT_UNREACHABLE;
398 break;
399 case ICMP_UNREACH_PROTOCOL:
400 ier->Status=IP_DEST_PROT_UNREACHABLE;
401 break;
402 case ICMP_UNREACH_SRCFAIL:
403 ier->Status=IP_BAD_ROUTE;
404 break;
405 default:
406 ier->Status=IP_DEST_NET_UNREACHABLE;
407 }
408 break;
409 case ICMP_TIMXCEED:
410 if (icmp_header->icmp_code==ICMP_TIMXCEED_REASS)
411 ier->Status=IP_TTL_EXPIRED_REASSEM;
412 else
413 ier->Status=IP_TTL_EXPIRED_TRANSIT;
414 break;
415 case ICMP_PARAMPROB:
416 ier->Status=IP_PARAM_PROBLEM;
417 break;
418 case ICMP_SOURCEQUENCH:
419 ier->Status=IP_SOURCE_QUENCH;
420 break;
421 }
422 if (ier->Status!=IP_REQ_TIMED_OUT) {
423 struct ip* rep_ip_header;
424 struct icmp* rep_icmp_header;
425 /* The ICMP header size of all the packets we accept is the same */
426 rep_ip_header=(struct ip*)(((char*)icmp_header)+ICMP_MINLEN);
427 rep_icmp_header=(struct icmp*)(((char*)rep_ip_header)+(rep_ip_header->ip_hl << 2));
428
429 /* Make sure that this is really a reply to our packet */
430 if (ip_header_len+ICMP_MINLEN+(rep_ip_header->ip_hl << 2)+ICMP_MINLEN>ip_header->ip_len) {
431 ier->Status=IP_REQ_TIMED_OUT;
432 } else if ((rep_icmp_header->icmp_type!=ICMP_ECHO) ||
433 (rep_icmp_header->icmp_code!=0) ||
434 (rep_icmp_header->icmp_id!=id) ||
435 /* windows doesn't check this checksum, else tracert */
436 /* behind a Linux 2.2 masquerading firewall would fail*/
437 /* (rep_icmp_header->icmp_cksum!=cksum) || */
438 (rep_icmp_header->icmp_seq!=seq)) {
439 /* This was not a reply to one of our packets after all */
440 TRACE("skipping type,code=%d,%d id,seq=%d,%d cksum=%d\n",
441 rep_icmp_header->icmp_type,rep_icmp_header->icmp_code,
442 rep_icmp_header->icmp_id,rep_icmp_header->icmp_seq,
443 rep_icmp_header->icmp_cksum);
444 TRACE("expected type,code=8,0 id,seq=%d,%d cksum=%d\n",
445 id,seq,
446 cksum);
447 ier->Status=IP_REQ_TIMED_OUT;
448 }
449 }
450 }
451 }
452
453 if (ier->Status==IP_REQ_TIMED_OUT) {
454 /* This packet was not for us.
455 * Decrease the timeout so that we don't enter an endless loop even
456 * if we get flooded with ICMP packets that are not for us.
457 */
458 int t = Timeout - (recv_time - send_time);
459 if (t < 0) t = 0;
460 timeout.tv_sec = t / 1000;
461 timeout.tv_usec = (t % 1000) * 1000;
462 continue;
463 } else {
464 /* This is a reply to our packet */
465 memcpy(&ier->Address,&ip_header->ip_src,sizeof(IPAddr));
466 /* Status is already set */
467 ier->RoundTripTime= recv_time - send_time;
468 ier->DataSize=res-ip_header_len-ICMP_MINLEN;
469 ier->Reserved=0;
470 ier->Data=endbuf-ier->DataSize;
471 memmove(ier->Data,((char*)ip_header)+ip_header_len+ICMP_MINLEN,ier->DataSize);
472 ier->Options.Ttl=ip_header->ip_ttl;
473 ier->Options.Tos=ip_header->ip_tos;
474 ier->Options.Flags=ip_header->ip_off >> 13;
475 ier->Options.OptionsSize=ip_header_len-sizeof(struct ip);
476 if (ier->Options.OptionsSize!=0) {
477 ier->Options.OptionsData=(unsigned char *) ier->Data-ier->Options.OptionsSize;
478 /* FIXME: We are supposed to rearrange the option's 'source route' data */
479 memmove(ier->Options.OptionsData,((char*)ip_header)+ip_header_len,ier->Options.OptionsSize);
480 endbuf=(char*)ier->Options.OptionsData;
481 } else {
482 ier->Options.OptionsData=NULL;
483 endbuf=ier->Data;
484 }
485
486 /* Prepare for the next packet */
487 ier++;
488 ip_header=(struct ip*)(((char*)ip_header)+sizeof(ICMP_ECHO_REPLY));
489 maxlen=endbuf-(char*)ip_header;
490
491 /* Check out whether there is more but don't wait this time */
492 timeout.tv_sec=0;
493 timeout.tv_usec=0;
494 }
495 FD_ZERO(&fdr);
496 FD_SET(icp->sid,&fdr);
497 }
498 res=ier-(ICMP_ECHO_REPLY*)ReplyBuffer;
499 if (res==0)
500 SetLastError(IP_REQ_TIMED_OUT);
501 TRACE("received %d replies\n",res);
502 return res;
503 }
504
505 /*
506 * Copyright (c) 1989 The Regents of the University of California.
507 * All rights reserved.
508 *
509 * This code is derived from software contributed to Berkeley by
510 * Mike Muuss.
511 *
512 * Redistribution and use in source and binary forms, with or without
513 * modification, are permitted provided that the following conditions
514 * are met:
515 * 1. Redistributions of source code must retain the above copyright
516 * notice, this list of conditions and the following disclaimer.
517 * 2. Redistributions in binary form must reproduce the above copyright
518 * notice, this list of conditions and the following disclaimer in the
519 * documentation and/or other materials provided with the distribution.
520 * 3. Neither the name of the University nor the names of its contributors
521 * may be used to endorse or promote products derived from this software
522 * without specific prior written permission.
523 *
524 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
525 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
526 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
527 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
528 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
529 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
530 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
531 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
532 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
533 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
534 * SUCH DAMAGE.
535 *
536 */