Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenicmp_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
1.7.6.1
|