source: trunk/src/icmp/icmp.cpp@ 3926

Last change on this file since 3926 was 3926, checked in by bird, 25 years ago

Added the CVS keyword Id.

File size: 16.7 KB
Line 
1/* $Id: icmp.cpp,v 1.2 2000-08-02 16:14:19 bird Exp $ */
2/*
3 * ICMP
4 *
5 * Francois Gouget, 1999, based on the work of
6 * RW Hall, 1999, based on public domain code PING.C by Mike Muus (1983)
7 * and later works (c) 1989 Regents of Univ. of California - see copyright
8 * notice at end of source-code.
9 *
10 *
11 * Future work:
12 * - Systems like FreeBSD don't seem to support the IP_TTL option and maybe others.
13 * But using IP_HDRINCL and building the IP header by hand might work.
14 * - Not all IP options are supported.
15 * - Are ICMP handles real handles, i.e. inheritable and all? There might be some
16 * more work to do here, including server side stuff with synchronization.
17 * - Is it correct to use malloc for the internal buffer, for allocating the
18 * handle's structure?
19 * - This API should probably be thread safe. Is it really?
20 * - Using the winsock functions has not been tested.
21 */
22
23#include <sys/types.h>
24#include <time.h>
25#include <malloc.h>
26#include <string.h>
27#include <errno.h>
28
29#include "windef.h"
30#include "winbase.h"
31#include "winsock.h"
32#include "winerror.h"
33
34#include <netinet/in_systm.h>
35#include <netinet/ip.h>
36
37#include <process.h>
38
39#include <ipexport.h>
40#include <icmpapi.h>
41
42#include "icmp.h"
43
44#include <misc.h>
45
46#define ISOCK_SOCKET SOCKET
47#define ISOCK_ISVALID(a) ((a)!=INVALID_SOCKET)
48#define ISOCK_getsockopt(a,b,c,d,e) getsockopt(a,b,c,d,e)
49#define ISOCK_recvfrom(a,b,c,d,e,f) recvfrom(a,b,c,d,e,f)
50#define ISOCK_select(a,b,c,d,e) select(a,b,c,d,e)
51#define ISOCK_sendto(a,b,c,d,e,f) sendto(a,b,c,d,e,f)
52#define ISOCK_setsockopt(a,b,c,d,e) setsockopt(a,b,c,d,e)
53#define ISOCK_shutdown(a,b) shutdown(a,b)
54#define ISOCK_socket(a,b,c) socket(a,b,c)
55
56typedef struct {
57 ISOCK_SOCKET sid;
58 IP_OPTION_INFORMATION default_opts;
59} icmp_t;
60
61#define IP_OPTS_UNKNOWN 0
62#define IP_OPTS_DEFAULT 1
63#define IP_OPTS_CUSTOM 2
64
65/* The sequence number is unique process wide, so that all threads
66 * have a distinct sequence number.
67 */
68static LONG icmp_sequence=0;
69
70/* Odin specific - private def of gettimeofday func */
71void gettimeofday(struct timeval *t,struct timezone *z)
72{
73 t->tv_sec=clock();
74 t->tv_usec=0;
75}
76
77static int in_cksum(u_short *addr, int len)
78{
79 int nleft=len;
80 u_short *w = addr;
81 int sum = 0;
82 u_short answer = 0;
83
84 while (nleft > 1) {
85 sum += *w++;
86 nleft -= 2;
87 }
88
89 if (nleft == 1) {
90 *(u_char *)(&answer) = *(u_char *)w;
91 sum += answer;
92 }
93
94 sum = (sum >> 16) + (sum & 0xffff);
95 sum += (sum >> 16);
96 answer = ~sum;
97 return(answer);
98}
99
100
101
102/*
103 * Exported Routines.
104 */
105
106/***********************************************************************
107 * IcmpCreateFile
108 */
109HANDLE WINAPI IcmpCreateFile(VOID)
110{
111 icmp_t* icp;
112
113 ISOCK_SOCKET sid=ISOCK_socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
114 if (!ISOCK_ISVALID(sid)) {
115 SetLastError(ERROR_ACCESS_DENIED);
116 return INVALID_HANDLE_VALUE;
117 }
118
119 icp=(icmp_t *)malloc(sizeof(*icp));
120 if (icp==NULL) {
121 SetLastError(IP_NO_RESOURCES);
122 return INVALID_HANDLE_VALUE;
123 }
124 icp->sid=sid;
125 icp->default_opts.OptionsSize=IP_OPTS_UNKNOWN;
126 return (HANDLE)icp;
127}
128
129
130/***********************************************************************
131 * IcmpCloseHandle
132 */
133BOOL WINAPI IcmpCloseHandle(HANDLE IcmpHandle)
134{
135 icmp_t* icp=(icmp_t*)IcmpHandle;
136 if (IcmpHandle==INVALID_HANDLE_VALUE) {
137 /* FIXME: in fact win98 seems to ignore the handle value !!! */
138 SetLastError(ERROR_INVALID_HANDLE);
139 return FALSE;
140 }
141
142 ISOCK_shutdown(icp->sid,2);
143 free(icp);
144 return TRUE;
145}
146
147
148/***********************************************************************
149 * IcmpSendEcho
150 */
151DWORD WINAPI IcmpSendEcho(
152 HANDLE IcmpHandle,
153 IPAddr DestinationAddress,
154 LPVOID RequestData,
155 WORD RequestSize,
156 PIP_OPTION_INFORMATION RequestOptions,
157 LPVOID ReplyBuffer,
158 DWORD ReplySize,
159 DWORD Timeout
160 )
161{
162 icmp_t* icp=(icmp_t*)IcmpHandle;
163 unsigned char* reqbuf;
164 int reqsize;
165
166 struct icmp_echo_reply* ier;
167 struct ip* ip_header;
168 struct icmp* icmp_header;
169 char* endbuf;
170 int ip_header_len;
171 int maxlen;
172 fd_set fdr;
173 struct timeval timeout,send_time,recv_time;
174 struct sockaddr_in addr;
175 int addrlen;
176 unsigned short id,seq,cksum;
177 int res;
178
179 if (IcmpHandle==INVALID_HANDLE_VALUE) {
180 /* FIXME: in fact win98 seems to ignore the handle value !!! */
181 SetLastError(ERROR_INVALID_HANDLE);
182 return 0;
183 }
184
185 if (ReplySize<sizeof(ICMP_ECHO_REPLY)+ICMP_MINLEN) {
186 SetLastError(IP_BUF_TOO_SMALL);
187 return 0;
188 }
189 /* check the request size against SO_MAX_MSG_SIZE using getsockopt */
190
191 /* Prepare the request */
192 id=getpid() & 0xFFFF;
193 seq=InterlockedIncrement(&icmp_sequence) & 0xFFFF;
194
195 reqsize=ICMP_MINLEN+RequestSize;
196 reqbuf=(unsigned char *)malloc(reqsize);
197 if (reqbuf==NULL) {
198 SetLastError(ERROR_OUTOFMEMORY);
199 return 0;
200 }
201
202 icmp_header=(struct icmp*)reqbuf;
203 icmp_header->icmp_type=ICMP_ECHO;
204 icmp_header->icmp_code=0;
205 icmp_header->icmp_cksum=0;
206 icmp_header->icmp_id=id;
207 icmp_header->icmp_seq=seq;
208 memcpy(reqbuf+ICMP_MINLEN, RequestData, RequestSize);
209 icmp_header->icmp_cksum=cksum=in_cksum((u_short*)reqbuf,reqsize);
210
211 addr.sin_family=AF_INET;
212 addr.sin_addr.s_addr=DestinationAddress;
213 addr.sin_port=0;
214
215 if (RequestOptions!=NULL) {
216 int val;
217 if (icp->default_opts.OptionsSize==IP_OPTS_UNKNOWN) {
218 int len;
219 /* Before we mess with the options, get the default values */
220 len=sizeof(val);
221 ISOCK_getsockopt(icp->sid,IPPROTO_IP,IP_TTL,(char *)&val,&len);
222 icp->default_opts.Ttl=val;
223
224 len=sizeof(val);
225 ISOCK_getsockopt(icp->sid,IPPROTO_IP,IP_TOS,(char *)&val,&len);
226 icp->default_opts.Tos=val;
227 /* FIXME: missing: handling of IP 'flags', and all the other options */
228 }
229
230 val=RequestOptions->Ttl;
231 ISOCK_setsockopt(icp->sid,IPPROTO_IP,IP_TTL,(char *)&val,sizeof(val));
232 val=RequestOptions->Tos;
233 ISOCK_setsockopt(icp->sid,IPPROTO_IP,IP_TOS,(char *)&val,sizeof(val));
234 /* FIXME: missing: handling of IP 'flags', and all the other options */
235
236 icp->default_opts.OptionsSize=IP_OPTS_CUSTOM;
237 } else if (icp->default_opts.OptionsSize==IP_OPTS_CUSTOM) {
238 int val;
239
240 /* Restore the default options */
241 val=icp->default_opts.Ttl;
242 ISOCK_setsockopt(icp->sid,IPPROTO_IP,IP_TTL,(char *)&val,sizeof(val));
243 val=icp->default_opts.Tos;
244 ISOCK_setsockopt(icp->sid,IPPROTO_IP,IP_TOS,(char *)&val,sizeof(val));
245 /* FIXME: missing: handling of IP 'flags', and all the other options */
246
247 icp->default_opts.OptionsSize=IP_OPTS_DEFAULT;
248 }
249
250 /* Get ready for receiving the reply
251 * Do it before we send the request to minimize the risk of introducing delays
252 */
253 FD_ZERO(&fdr);
254 FD_SET(icp->sid,&fdr);
255 timeout.tv_sec=Timeout/1000;
256 timeout.tv_usec=(Timeout % 1000)*1000;
257 addrlen=sizeof(addr);
258 ier=(struct icmp_echo_reply *)ReplyBuffer;
259 ip_header=(struct ip *) ((char *) ReplyBuffer+sizeof(ICMP_ECHO_REPLY));
260 endbuf=(char *) ReplyBuffer+ReplySize;
261 maxlen=ReplySize-sizeof(ICMP_ECHO_REPLY);
262
263 /* Send the packet */
264 dprintf(("ICMP: Sending %d bytes (RequestSize=%d) to %s\n", reqsize, RequestSize, inet_ntoa(addr.sin_addr)));
265
266 gettimeofday(&send_time,NULL);
267 res=ISOCK_sendto(icp->sid, (const char *)reqbuf, reqsize, 0, (struct sockaddr*)&addr, sizeof(addr));
268 free(reqbuf);
269 if (res<0) {
270 if (errno==WSAEMSGSIZE)
271 SetLastError(IP_PACKET_TOO_BIG);
272 else {
273 switch (errno) {
274 case WSAENETUNREACH:
275 SetLastError(IP_DEST_NET_UNREACHABLE);
276 break;
277 case WSAEHOSTUNREACH:
278 SetLastError(IP_DEST_NET_UNREACHABLE);
279 break;
280 default:
281 dprintf(("ICMP: unknown error: errno=%d\n",errno));
282 SetLastError(ERROR_UNKNOWN);
283 }
284 }
285 return 0;
286 }
287
288 /* Get the reply */
289 ip_header_len=0; /* because gcc was complaining */
290 while ((res=ISOCK_select(icp->sid+1,&fdr,NULL,NULL,&timeout))>0) {
291 gettimeofday(&recv_time,NULL);
292 res=ISOCK_recvfrom(icp->sid, (char*)ip_header, maxlen, 0, (struct sockaddr*)&addr,&addrlen);
293 dprintf(("ICMP: received %d bytes from %s\n",res, inet_ntoa(addr.sin_addr)));
294 ier->Status=IP_REQ_TIMED_OUT;
295
296 /* Check whether we should ignore this packet */
297 if ((ip_header->ip_p==IPPROTO_ICMP) && (res>=sizeof(struct ip)+ICMP_MINLEN)) {
298 ip_header_len=ip_header->ip_hl << 2;
299 icmp_header=(struct icmp*)(((char*)ip_header)+ip_header_len);
300 dprintf(("ICMP: received an ICMP packet of type,code=%d,%d\n",icmp_header->icmp_type,icmp_header->icmp_code));
301 if (icmp_header->icmp_type==ICMP_ECHOREPLY) {
302 if ((icmp_header->icmp_id==id) && (icmp_header->icmp_seq==seq))
303 ier->Status=IP_SUCCESS;
304 } else {
305 switch (icmp_header->icmp_type) {
306 case ICMP_UNREACH:
307 switch (icmp_header->icmp_code) {
308 case ICMP_UNREACH_HOST:
309#ifdef ICMP_UNREACH_HOST_UNKNOWN
310 case ICMP_UNREACH_HOST_UNKNOWN:
311#endif
312#ifdef ICMP_UNREACH_ISOLATED
313 case ICMP_UNREACH_ISOLATED:
314#endif
315#ifdef ICMP_UNREACH_HOST_PROHIB
316 case ICMP_UNREACH_HOST_PROHIB:
317#endif
318#ifdef ICMP_UNREACH_TOSHOST
319 case ICMP_UNREACH_TOSHOST:
320#endif
321 ier->Status=IP_DEST_HOST_UNREACHABLE;
322 break;
323 case ICMP_UNREACH_PORT:
324 ier->Status=IP_DEST_PORT_UNREACHABLE;
325 break;
326 case ICMP_UNREACH_PROTOCOL:
327 ier->Status=IP_DEST_PROT_UNREACHABLE;
328 break;
329 case ICMP_UNREACH_SRCFAIL:
330 ier->Status=IP_BAD_ROUTE;
331 break;
332 default:
333 ier->Status=IP_DEST_NET_UNREACHABLE;
334 }
335 break;
336 case ICMP_TIMXCEED:
337 if (icmp_header->icmp_code==ICMP_TIMXCEED_REASS)
338 ier->Status=IP_TTL_EXPIRED_REASSEM;
339 else
340 ier->Status=IP_TTL_EXPIRED_TRANSIT;
341 break;
342 case ICMP_PARAMPROB:
343 ier->Status=IP_PARAM_PROBLEM;
344 break;
345 case ICMP_SOURCEQUENCH:
346 ier->Status=IP_SOURCE_QUENCH;
347 break;
348 }
349 if (ier->Status!=IP_REQ_TIMED_OUT) {
350 struct ip* rep_ip_header;
351 struct icmp* rep_icmp_header;
352 /* The ICMP header size of all the packets we accept is the same */
353 rep_ip_header=(struct ip*)(((char*)icmp_header)+ICMP_MINLEN);
354 rep_icmp_header=(struct icmp*)(((char*)rep_ip_header)+(rep_ip_header->ip_hl << 2));
355
356 /* Make sure that this is really a reply to our packet */
357 if (ip_header_len+ICMP_MINLEN+(rep_ip_header->ip_hl << 2)+ICMP_MINLEN>ip_header->ip_len) {
358 ier->Status=IP_REQ_TIMED_OUT;
359 } else if ((rep_icmp_header->icmp_type!=ICMP_ECHO) ||
360 (rep_icmp_header->icmp_code!=0) ||
361 (rep_icmp_header->icmp_id!=id) ||
362 (rep_icmp_header->icmp_seq!=seq) ||
363 (rep_icmp_header->icmp_cksum!=cksum)) {
364 /* This was not a reply to one of our packets after all */
365 dprintf(("ICMP: skipping type,code=%d,%d id,seq=%d,%d cksum=%d\n",
366 rep_icmp_header->icmp_type,rep_icmp_header->icmp_code,
367 rep_icmp_header->icmp_id,rep_icmp_header->icmp_seq,
368 rep_icmp_header->icmp_cksum));
369 dprintf(("ICMP: expected type,code=8,0 id,seq=%d,%d cksum=%d\n",
370 id,seq,
371 cksum));
372 ier->Status=IP_REQ_TIMED_OUT;
373 }
374 }
375 }
376 }
377
378 if (ier->Status==IP_REQ_TIMED_OUT) {
379 /* This packet was not for us.
380 * Decrease the timeout so that we don't enter an endless loop even
381 * if we get flooded with ICMP packets that are not for us.
382 */
383 timeout.tv_sec=Timeout/1000-(recv_time.tv_sec-send_time.tv_sec);
384 timeout.tv_usec=(Timeout % 1000)*1000+send_time.tv_usec-(recv_time.tv_usec-send_time.tv_usec);
385 if (timeout.tv_usec<0) {
386 timeout.tv_usec+=1000000;
387 timeout.tv_sec--;
388 }
389 continue;
390 } else {
391 /* This is a reply to our packet */
392 memcpy(&ier->Address,&ip_header->ip_src,sizeof(IPAddr));
393 /* Status is already set */
394 ier->RoundTripTime=(recv_time.tv_sec-send_time.tv_sec)*1000+(recv_time.tv_usec-send_time.tv_usec)/1000;
395 ier->DataSize=res-ip_header_len-ICMP_MINLEN;
396 ier->Reserved=0;
397 ier->Data=endbuf-ier->DataSize;
398 memmove(ier->Data,((char*)ip_header)+ip_header_len+ICMP_MINLEN,ier->DataSize);
399 ier->Options.Ttl=ip_header->ip_ttl;
400 ier->Options.Tos=ip_header->ip_tos;
401 ier->Options.Flags=ip_header->ip_off >> 13;
402 ier->Options.OptionsSize=ip_header_len-sizeof(struct ip);
403 if (ier->Options.OptionsSize!=0) {
404 ier->Options.OptionsData=(unsigned char *) ier->Data-ier->Options.OptionsSize;
405 /* FIXME: We are supposed to rearrange the option's 'source route' data */
406 memmove(ier->Options.OptionsData,((char*)ip_header)+ip_header_len,ier->Options.OptionsSize);
407 endbuf=(char *)ier->Options.OptionsData;
408 } else {
409 ier->Options.OptionsData=NULL;
410 endbuf=(char *)ier->Data;
411 }
412
413 /* Prepare for the next packet */
414 ier++;
415 ip_header=(struct ip*)(((char*)ip_header)+sizeof(ICMP_ECHO_REPLY));
416 maxlen=endbuf-(char*)ip_header;
417
418 /* Check out whether there is more but don't wait this time */
419 timeout.tv_sec=0;
420 timeout.tv_usec=0;
421 }
422 FD_ZERO(&fdr);
423 FD_SET(icp->sid,&fdr);
424 }
425 res=ier-(ICMP_ECHO_REPLY*)ReplyBuffer;
426 if (res==0)
427 SetLastError(IP_REQ_TIMED_OUT);
428 dprintf(("ICMP: received %d replies\n",res));
429 return res;
430}
431
432/*
433 * Copyright (c) 1989 The Regents of the University of California.
434 * All rights reserved.
435 *
436 * This code is derived from software contributed to Berkeley by
437 * Mike Muuss.
438 *
439 * Redistribution and use in source and binary forms, with or without
440 * modification, are permitted provided that the following conditions
441 * are met:
442 * 1. Redistributions of source code must retain the above copyright
443 * notice, this list of conditions and the following disclaimer.
444 * 2. Redistributions in binary form must reproduce the above copyright
445 * notice, this list of conditions and the following disclaimer in the
446 * documentation and/or other materials provided with the distribution.
447 * 3. All advertising materials mentioning features or use of this software
448 * must display the following acknowledgement:
449 * This product includes software developed by the University of
450 * California, Berkeley and its contributors.
451 * 4. Neither the name of the University nor the names of its contributors
452 * may be used to endorse or promote products derived from this software
453 * without specific prior written permission.
454 *
455 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
456 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
457 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
458 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
459 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
460 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
461 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
462 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
463 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
464 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
465 * SUCH DAMAGE.
466 *
467 */
Note: See TracBrowser for help on using the repository browser.