ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

icmp_main.c
Go to the documentation of this file.
00001 /*
00002  * ICMP
00003  *
00004  * Francois Gouget, 1999, based on the work of
00005  *   RW Hall, 1999, based on public domain code PING.C by Mike Muus (1983)
00006  *   and later works (c) 1989 Regents of Univ. of California - see copyright
00007  *   notice at end of source-code.
00008  *
00009  * This library is free software; you can redistribute it and/or
00010  * modify it under the terms of the GNU Lesser General Public
00011  * License as published by the Free Software Foundation; either
00012  * version 2.1 of the License, or (at your option) any later version.
00013  *
00014  * This library is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017  * Lesser General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU Lesser General Public
00020  * License along with this library; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022  */
00023 
00024 /* Future work:
00025  * - Systems like FreeBSD don't seem to support the IP_TTL option and maybe others.
00026  *   But using IP_HDRINCL and building the IP header by hand might work.
00027  * - Not all IP options are supported.
00028  * - Are ICMP handles real handles, i.e. inheritable and all? There might be some
00029  *   more work to do here, including server side stuff with synchronization.
00030  * - Is it correct to use malloc for the internal buffer, for allocating the
00031  *   handle's structure?
00032  * - This API should probably be thread safe. Is it really?
00033  * - Using the winsock functions has not been tested.
00034  */
00035 
00036 #include "config.h"
00037 
00038 #include <sys/types.h>
00039 #ifdef HAVE_SYS_SOCKET_H
00040 # include <sys/socket.h>
00041 #endif
00042 #ifdef HAVE_NETDB_H
00043 # include <netdb.h>
00044 #endif
00045 #ifdef HAVE_NETINET_IN_SYSTM_H
00046 # include <netinet/in_systm.h>
00047 #endif
00048 #ifdef HAVE_NETINET_IN_H
00049 # include <netinet/in.h>
00050 #endif
00051 
00052 #ifdef HAVE_SYS_TIME_H
00053 # include <sys/time.h>
00054 #endif
00055 #include <stdarg.h>
00056 #include <string.h>
00057 #include <errno.h>
00058 #ifdef HAVE_UNISTD_H
00059 # include <unistd.h>
00060 #endif
00061 #ifdef HAVE_ARPA_INET_H
00062 # include <arpa/inet.h>
00063 #endif
00064 
00065 #include "windef.h"
00066 #include "winbase.h"
00067 #include "winerror.h"
00068 #include "ipexport.h"
00069 #include <ws2tcpip.h>
00070 #include "icmpapi.h"
00071 #include "wine/debug.h"
00072 
00073 /* Set up endiannes macros for the ip and ip_icmp BSD headers */
00074 #ifndef BIG_ENDIAN
00075 #define BIG_ENDIAN       4321
00076 #endif
00077 #ifndef LITTLE_ENDIAN
00078 #define LITTLE_ENDIAN    1234
00079 #endif
00080 #ifndef BYTE_ORDER
00081 #ifdef WORDS_BIGENDIAN
00082 #define BYTE_ORDER       BIG_ENDIAN
00083 #else
00084 #define BYTE_ORDER       LITTLE_ENDIAN
00085 #endif
00086 #endif /* BYTE_ORDER */
00087 
00088 #define u_int16_t  WORD
00089 #define u_int32_t  DWORD
00090 
00091 /* These are BSD headers. We use these here because they are needed on
00092  * libc5 Linux systems. On other platforms they are usually simply more
00093  * complete than the native stuff, and cause less portability problems
00094  * so we use them anyway.
00095  */
00096 #include "ip.h"
00097 #include "ip_icmp.h"
00098 
00099 
00100 WINE_DEFAULT_DEBUG_CHANNEL(icmp);
00101 
00102 
00103 typedef struct {
00104     int sid;
00105     IP_OPTION_INFORMATION default_opts;
00106 } icmp_t;
00107 
00108 #define IP_OPTS_UNKNOWN     0
00109 #define IP_OPTS_DEFAULT     1
00110 #define IP_OPTS_CUSTOM      2
00111 
00112 /* The sequence number is unique process wide, so that all threads
00113  * have a distinct sequence number.
00114  */
00115 static LONG icmp_sequence=0;
00116 
00117 static int in_cksum(u_short *addr, int len)
00118 {
00119     int nleft=len;
00120     u_short *w = addr;
00121     int sum = 0;
00122     u_short answer = 0;
00123 
00124     while (nleft > 1) {
00125         sum += *w++;
00126         nleft -= 2;
00127     }
00128 
00129     if (nleft == 1) {
00130         *(u_char *)(&answer) = *(u_char *)w;
00131         sum += answer;
00132     }
00133 
00134     sum = (sum >> 16) + (sum & 0xffff);
00135     sum  += (sum >> 16);
00136     answer = ~sum;
00137     return(answer);
00138 }
00139 
00140 
00141 
00142 /*
00143  * Exported Routines.
00144  */
00145 
00146 BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
00147 {
00148   WSADATA wsaData;
00149 
00150   switch (fdwReason) {
00151     case DLL_PROCESS_ATTACH:
00152       WSAStartup(MAKEWORD(2, 2), &wsaData);
00153       break;
00154 
00155     case DLL_PROCESS_DETACH:
00156       WSACleanup();
00157       break;
00158   }
00159   return TRUE;
00160 }
00161 
00162 /***********************************************************************
00163  *      IcmpCreateFile (ICMP.@)
00164  */
00165 HANDLE WINAPI IcmpCreateFile(VOID)
00166 {
00167     icmp_t* icp;
00168 
00169     int sid=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
00170     if (sid < 0) {
00171         MESSAGE("WARNING: Trying to use ICMP (network ping) will fail unless running as root\n");
00172         SetLastError(ERROR_ACCESS_DENIED);
00173         return INVALID_HANDLE_VALUE;
00174     }
00175 
00176     icp=HeapAlloc(GetProcessHeap(), 0, sizeof(*icp));
00177     if (icp==NULL) {
00178         SetLastError(IP_NO_RESOURCES);
00179         return INVALID_HANDLE_VALUE;
00180     }
00181     icp->sid=sid;
00182     icp->default_opts.OptionsSize=IP_OPTS_UNKNOWN;
00183     return (HANDLE)icp;
00184 }
00185 
00186 
00187 /***********************************************************************
00188  *      IcmpCloseHandle (ICMP.@)
00189  */
00190 BOOL WINAPI IcmpCloseHandle(HANDLE  IcmpHandle)
00191 {
00192     icmp_t* icp=(icmp_t*)IcmpHandle;
00193     if (IcmpHandle==INVALID_HANDLE_VALUE) {
00194         /* FIXME: in fact win98 seems to ignore the handle value !!! */
00195         SetLastError(ERROR_INVALID_HANDLE);
00196         return FALSE;
00197     }
00198 
00199     shutdown(icp->sid,2);
00200     HeapFree(GetProcessHeap (), 0, icp);
00201     return TRUE;
00202 }
00203 
00204 
00205 /***********************************************************************
00206  *      IcmpSendEcho (ICMP.@)
00207  */
00208 DWORD WINAPI IcmpSendEcho(
00209     HANDLE                   IcmpHandle,
00210     IPAddr                   DestinationAddress,
00211     LPVOID                   RequestData,
00212     WORD                     RequestSize,
00213     PIP_OPTION_INFORMATION   RequestOptions,
00214     LPVOID                   ReplyBuffer,
00215     DWORD                    ReplySize,
00216     DWORD                    Timeout
00217     )
00218 {
00219     icmp_t* icp=(icmp_t*)IcmpHandle;
00220     unsigned char* reqbuf;
00221     int reqsize;
00222 
00223     struct icmp_echo_reply* ier;
00224     struct ip* ip_header;
00225     struct icmp* icmp_header;
00226     char* endbuf;
00227     int ip_header_len;
00228     int maxlen;
00229     fd_set fdr;
00230     struct timeval timeout;
00231     DWORD send_time,recv_time;
00232     struct sockaddr_in addr;
00233     unsigned int addrlen;
00234     unsigned short id,seq,cksum;
00235     int res;
00236 
00237     if (IcmpHandle==INVALID_HANDLE_VALUE) {
00238         /* FIXME: in fact win98 seems to ignore the handle value !!! */
00239         SetLastError(ERROR_INVALID_HANDLE);
00240         return 0;
00241     }
00242 
00243     if (ReplySize<sizeof(ICMP_ECHO_REPLY)+ICMP_MINLEN) {
00244         SetLastError(IP_BUF_TOO_SMALL);
00245         return 0;
00246     }
00247     /* check the request size against SO_MAX_MSG_SIZE using getsockopt */
00248 
00249     /* Prepare the request */
00250     id=GetCurrentProcessId() & 0xFFFF;
00251     seq=InterlockedIncrement(&icmp_sequence) & 0xFFFF;
00252 
00253     reqsize=ICMP_MINLEN+RequestSize;
00254     reqbuf=HeapAlloc(GetProcessHeap(), 0, reqsize);
00255     if (reqbuf==NULL) {
00256         SetLastError(ERROR_OUTOFMEMORY);
00257         return 0;
00258     }
00259 
00260     icmp_header=(struct icmp*)reqbuf;
00261     icmp_header->icmp_type=ICMP_ECHO;
00262     icmp_header->icmp_code=0;
00263     icmp_header->icmp_cksum=0;
00264     icmp_header->icmp_id=id;
00265     icmp_header->icmp_seq=seq;
00266     memcpy(reqbuf+ICMP_MINLEN, RequestData, RequestSize);
00267     icmp_header->icmp_cksum=cksum=in_cksum((u_short*)reqbuf,reqsize);
00268 
00269     addr.sin_family=AF_INET;
00270     addr.sin_addr.s_addr=DestinationAddress;
00271     addr.sin_port=0;
00272 
00273     if (RequestOptions!=NULL) {
00274         int val;
00275         if (icp->default_opts.OptionsSize==IP_OPTS_UNKNOWN) {
00276             int len;
00277             /* Before we mess with the options, get the default values */
00278             len=sizeof(val);
00279             getsockopt(icp->sid,IPPROTO_IP,IP_TTL,(char *)&val,&len);
00280             icp->default_opts.Ttl=val;
00281 
00282             len=sizeof(val);
00283             getsockopt(icp->sid,IPPROTO_IP,IP_TOS,(char *)&val,&len);
00284             icp->default_opts.Tos=val;
00285             /* FIXME: missing: handling of IP 'flags', and all the other options */
00286         }
00287 
00288         val=RequestOptions->Ttl;
00289         setsockopt(icp->sid,IPPROTO_IP,IP_TTL,(char *)&val,sizeof(val));
00290         val=RequestOptions->Tos;
00291         setsockopt(icp->sid,IPPROTO_IP,IP_TOS,(char *)&val,sizeof(val));
00292         /* FIXME:  missing: handling of IP 'flags', and all the other options */
00293 
00294         icp->default_opts.OptionsSize=IP_OPTS_CUSTOM;
00295     } else if (icp->default_opts.OptionsSize==IP_OPTS_CUSTOM) {
00296         int val;
00297 
00298         /* Restore the default options */
00299         val=icp->default_opts.Ttl;
00300         setsockopt(icp->sid,IPPROTO_IP,IP_TTL,(char *)&val,sizeof(val));
00301         val=icp->default_opts.Tos;
00302         setsockopt(icp->sid,IPPROTO_IP,IP_TOS,(char *)&val,sizeof(val));
00303         /* FIXME: missing: handling of IP 'flags', and all the other options */
00304 
00305         icp->default_opts.OptionsSize=IP_OPTS_DEFAULT;
00306     }
00307 
00308     /* Get ready for receiving the reply
00309      * Do it before we send the request to minimize the risk of introducing delays
00310      */
00311     FD_ZERO(&fdr);
00312     FD_SET(icp->sid,&fdr);
00313     timeout.tv_sec=Timeout/1000;
00314     timeout.tv_usec=(Timeout % 1000)*1000;
00315     addrlen=sizeof(addr);
00316     ier=ReplyBuffer;
00317     ip_header=(struct ip *) ((char *) ReplyBuffer+sizeof(ICMP_ECHO_REPLY));
00318     endbuf=(char *) ReplyBuffer+ReplySize;
00319     maxlen=ReplySize-sizeof(ICMP_ECHO_REPLY);
00320 
00321     /* Send the packet */
00322     TRACE("Sending %d bytes (RequestSize=%d) to %s\n", reqsize, RequestSize, inet_ntoa(addr.sin_addr));
00323 #if 0
00324     if (TRACE_ON(icmp)){
00325         unsigned char* buf=(unsigned char*)reqbuf;
00326         int i;
00327         printf("Output buffer:\n");
00328         for (i=0;i<reqsize;i++)
00329             printf("%2x,", buf[i]);
00330         printf("\n");
00331     }
00332 #endif
00333 
00334     send_time = GetTickCount();
00335     res=sendto(icp->sid, (const char*)reqbuf, reqsize, 0, (struct sockaddr*)&addr, sizeof(addr));
00336     HeapFree(GetProcessHeap (), 0, reqbuf);
00337     if (res<0) {
00338         if (WSAGetLastError()==WSAEMSGSIZE)
00339             SetLastError(IP_PACKET_TOO_BIG);
00340         else {
00341             switch (WSAGetLastError()) {
00342             case WSAENETUNREACH:
00343                 SetLastError(IP_DEST_NET_UNREACHABLE);
00344                 break;
00345             case WSAEHOSTUNREACH:
00346                 SetLastError(IP_DEST_HOST_UNREACHABLE);
00347                 break;
00348             default:
00349                 TRACE("unknown error: errno=%d\n",WSAGetLastError());
00350                 SetLastError(IP_GENERAL_FAILURE);
00351             }
00352         }
00353         return 0;
00354     }
00355 
00356     /* Get the reply */
00357     ip_header_len=0; /* because gcc was complaining */
00358     while ((res=select(icp->sid+1,&fdr,NULL,NULL,&timeout))>0) {
00359         recv_time = GetTickCount();
00360         res=recvfrom(icp->sid, (char*)ip_header, maxlen, 0, (struct sockaddr*)&addr,(int*)&addrlen);
00361         TRACE("received %d bytes from %s\n",res, inet_ntoa(addr.sin_addr));
00362         ier->Status=IP_REQ_TIMED_OUT;
00363 
00364         /* Check whether we should ignore this packet */
00365         if ((ip_header->ip_p==IPPROTO_ICMP) && (res>=sizeof(struct ip)+ICMP_MINLEN)) {
00366             ip_header_len=ip_header->ip_hl << 2;
00367             icmp_header=(struct icmp*)(((char*)ip_header)+ip_header_len);
00368             TRACE("received an ICMP packet of type,code=%d,%d\n",icmp_header->icmp_type,icmp_header->icmp_code);
00369             if (icmp_header->icmp_type==ICMP_ECHOREPLY) {
00370                 if ((icmp_header->icmp_id==id) && (icmp_header->icmp_seq==seq))
00371                     ier->Status=IP_SUCCESS;
00372             } else {
00373                 switch (icmp_header->icmp_type) {
00374                 case ICMP_UNREACH:
00375                     switch (icmp_header->icmp_code) {
00376                     case ICMP_UNREACH_HOST:
00377 #ifdef ICMP_UNREACH_HOST_UNKNOWN
00378                     case ICMP_UNREACH_HOST_UNKNOWN:
00379 #endif
00380 #ifdef ICMP_UNREACH_ISOLATED
00381                     case ICMP_UNREACH_ISOLATED:
00382 #endif
00383 #ifdef ICMP_UNREACH_HOST_PROHIB
00384                     case ICMP_UNREACH_HOST_PROHIB:
00385 #endif
00386 #ifdef ICMP_UNREACH_TOSHOST
00387                     case ICMP_UNREACH_TOSHOST:
00388 #endif
00389                         ier->Status=IP_DEST_HOST_UNREACHABLE;
00390                         break;
00391                     case ICMP_UNREACH_PORT:
00392                         ier->Status=IP_DEST_PORT_UNREACHABLE;
00393                         break;
00394                     case ICMP_UNREACH_PROTOCOL:
00395                         ier->Status=IP_DEST_PROT_UNREACHABLE;
00396                         break;
00397                     case ICMP_UNREACH_SRCFAIL:
00398                         ier->Status=IP_BAD_ROUTE;
00399                         break;
00400                     default:
00401                         ier->Status=IP_DEST_NET_UNREACHABLE;
00402                     }
00403                     break;
00404                 case ICMP_TIMXCEED:
00405                     if (icmp_header->icmp_code==ICMP_TIMXCEED_REASS)
00406                         ier->Status=IP_TTL_EXPIRED_REASSEM;
00407                     else
00408                         ier->Status=IP_TTL_EXPIRED_TRANSIT;
00409                     break;
00410                 case ICMP_PARAMPROB:
00411                     ier->Status=IP_PARAM_PROBLEM;
00412                     break;
00413                 case ICMP_SOURCEQUENCH:
00414                     ier->Status=IP_SOURCE_QUENCH;
00415                     break;
00416                 }
00417                 if (ier->Status!=IP_REQ_TIMED_OUT) {
00418                     struct ip* rep_ip_header;
00419                     struct icmp* rep_icmp_header;
00420                     /* The ICMP header size of all the packets we accept is the same */
00421                     rep_ip_header=(struct ip*)(((char*)icmp_header)+ICMP_MINLEN);
00422                     rep_icmp_header=(struct icmp*)(((char*)rep_ip_header)+(rep_ip_header->ip_hl << 2));
00423 
00424             /* Make sure that this is really a reply to our packet */
00425                     if (ip_header_len+ICMP_MINLEN+(rep_ip_header->ip_hl << 2)+ICMP_MINLEN>ip_header->ip_len) {
00426             ier->Status=IP_REQ_TIMED_OUT;
00427                     } else if ((rep_icmp_header->icmp_type!=ICMP_ECHO) ||
00428                         (rep_icmp_header->icmp_code!=0) ||
00429                         (rep_icmp_header->icmp_id!=id) ||
00430                         /* windows doesn't check this checksum, else tracert */
00431                         /* behind a Linux 2.2 masquerading firewall would fail*/
00432                         /* (rep_icmp_header->icmp_cksum!=cksum) || */
00433                         (rep_icmp_header->icmp_seq!=seq)) {
00434                         /* This was not a reply to one of our packets after all */
00435                         TRACE("skipping type,code=%d,%d id,seq=%d,%d cksum=%d\n",
00436                             rep_icmp_header->icmp_type,rep_icmp_header->icmp_code,
00437                             rep_icmp_header->icmp_id,rep_icmp_header->icmp_seq,
00438                             rep_icmp_header->icmp_cksum);
00439                         TRACE("expected type,code=8,0 id,seq=%d,%d cksum=%d\n",
00440                             id,seq,
00441                             cksum);
00442             ier->Status=IP_REQ_TIMED_OUT;
00443             }
00444                 }
00445         }
00446     }
00447 
00448         if (ier->Status==IP_REQ_TIMED_OUT) {
00449             /* This packet was not for us.
00450              * Decrease the timeout so that we don't enter an endless loop even
00451              * if we get flooded with ICMP packets that are not for us.
00452              */
00453             int t = Timeout - (recv_time - send_time);
00454             if (t < 0) t = 0;
00455             timeout.tv_sec = t / 1000;
00456             timeout.tv_usec = (t % 1000) * 1000;
00457             continue;
00458         } else {
00459             /* This is a reply to our packet */
00460             memcpy(&ier->Address,&ip_header->ip_src,sizeof(IPAddr));
00461             /* Status is already set */
00462             ier->RoundTripTime= recv_time - send_time;
00463             ier->DataSize=res-ip_header_len-ICMP_MINLEN;
00464             ier->Reserved=0;
00465             ier->Data=endbuf-ier->DataSize;
00466             memmove(ier->Data,((char*)ip_header)+ip_header_len+ICMP_MINLEN,ier->DataSize);
00467             ier->Options.Ttl=ip_header->ip_ttl;
00468             ier->Options.Tos=ip_header->ip_tos;
00469             ier->Options.Flags=ip_header->ip_off >> 13;
00470             ier->Options.OptionsSize=ip_header_len-sizeof(struct ip);
00471             if (ier->Options.OptionsSize!=0) {
00472                 ier->Options.OptionsData=(unsigned char *) ier->Data-ier->Options.OptionsSize;
00473                 /* FIXME: We are supposed to rearrange the option's 'source route' data */
00474                 memmove(ier->Options.OptionsData,((char*)ip_header)+ip_header_len,ier->Options.OptionsSize);
00475                 endbuf=(char*)ier->Options.OptionsData;
00476             } else {
00477                 ier->Options.OptionsData=NULL;
00478                 endbuf=ier->Data;
00479             }
00480 
00481             /* Prepare for the next packet */
00482             ier++;
00483             ip_header=(struct ip*)(((char*)ip_header)+sizeof(ICMP_ECHO_REPLY));
00484             maxlen=endbuf-(char*)ip_header;
00485 
00486             /* Check out whether there is more but don't wait this time */
00487             timeout.tv_sec=0;
00488             timeout.tv_usec=0;
00489         }
00490         FD_ZERO(&fdr);
00491         FD_SET(icp->sid,&fdr);
00492     }
00493     res=ier-(ICMP_ECHO_REPLY*)ReplyBuffer;
00494     if (res==0)
00495         SetLastError(IP_REQ_TIMED_OUT);
00496     TRACE("received %d replies\n",res);
00497     return res;
00498 }
00499 
00500 /*
00501  * Copyright (c) 1989 The Regents of the University of California.
00502  * All rights reserved.
00503  *
00504  * This code is derived from software contributed to Berkeley by
00505  * Mike Muuss.
00506  *
00507  * Redistribution and use in source and binary forms, with or without
00508  * modification, are permitted provided that the following conditions
00509  * are met:
00510  * 1. Redistributions of source code must retain the above copyright
00511  *    notice, this list of conditions and the following disclaimer.
00512  * 2. Redistributions in binary form must reproduce the above copyright
00513  *    notice, this list of conditions and the following disclaimer in the
00514  *    documentation and/or other materials provided with the distribution.
00515  * 3. Neither the name of the University nor the names of its contributors
00516  *    may be used to endorse or promote products derived from this software
00517  *    without specific prior written permission.
00518  *
00519  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00520  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00521  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00522  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00523  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00524  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00525  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00526  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00527  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00528  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00529  * SUCH DAMAGE.
00530  *
00531  */

Generated on Fri May 25 2012 04:22:05 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.