/** * PING module * * Copyright (C) 2001 Jeffrey Fulmer * This file is part of LIBPING * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * -- * * This code has been lightly modified to adapt it in the Netsukuku source * code. */ #include #include #include #include #include #include "libping.h" #include "xmalloc.h" #include "log.h" #define MAXPACKET 65535 #define PKTSIZE 64 #define HDRLEN ICMP_MINLEN #define DATALEN (PKTSIZE-HDRLEN) #define MAXDATA (MAXPKT-HDRLEN-TIMLEN) #define DEF_TIMEOUT 5 struct ping_priv ping_priv_default (void) { struct ping_priv datum; datum.ident = IDENT_DEFAULT; datum.timo = TIMO_DEFAULT; return datum; } /** * elapsed_time * returns an int value for the difference * between now and starttime in milliseconds. */ int elapsed_time( struct timeval *starttime ){ struct timeval *newtime; int elapsed; newtime = (struct timeval*)xmalloc( sizeof(struct timeval)); gettimeofday(newtime,NULL); elapsed = 0; if(( newtime->tv_usec - starttime->tv_usec) > 0 ){ elapsed += (newtime->tv_usec - starttime->tv_usec)/1000 ; } else{ elapsed += ( 1000000 + newtime->tv_usec - starttime->tv_usec ) /1000; newtime->tv_sec--; } if(( newtime->tv_sec - starttime->tv_sec ) > 0 ){ elapsed += 1000 * ( newtime->tv_sec - starttime->tv_sec ); } if( elapsed < 1 ) elapsed = 1; xfree( newtime ); return( elapsed ); } void JOEfreeprotoent( struct protoent *p ) { char **a; xfree( p->p_name ); if( p->p_aliases != NULL ){ for( a = p->p_aliases; *a != NULL; a++ ){ xfree( *a ); } } xfree( p ); } void JOEfreehostent( struct hostent *h ) { char **p; xfree( h->h_name ); if( h->h_aliases != NULL ){ for( p = h->h_aliases; *p != NULL; ++p ) xfree( *p ); xfree( h->h_aliases ); } if( h->h_addr_list != NULL ){ for( p = h->h_addr_list; *p != NULL; ++p ) xfree( *p ); xfree (h->h_addr_list); } xfree( h ); } static int in_checksum( u_short *buf, int len ) { register long sum = 0; u_short answer = 0; while( len > 1 ){ sum += *buf++; len -= 2; } if( len == 1 ){ *( u_char* )( &answer ) = *( u_char* )buf; sum += answer; } sum = ( sum >> 16 ) + ( sum & 0xffff ); sum += ( sum >> 16 ); answer = ~sum; return ( answer ); } static int send_ping( const char *host, struct sockaddr_in *taddr, struct ping_priv * datum ) { int len; int ss; unsigned char buf[ HDRLEN + DATALEN ]; #define PROTO_BUF_LEN 1024 char proto_buf[PROTO_BUF_LEN]; struct protoent *proto = NULL; struct protoent proto_datum; struct hostent *hp = NULL; struct hostent hent; int herrno; char hbf[9000]; #if defined(_AIX) char *aixbuf; char *probuf; int rc; #endif/*_AIX*/ struct icmp *icp; unsigned short last; len = HDRLEN + DATALEN; #if defined(__GLIBC__) /* for systems using GNU libc */ getprotobyname_r("icmp", &proto_datum, proto_buf, PROTO_BUF_LEN, &proto); if(( gethostbyname_r( host, &hent, hbf, sizeof(hbf), &hp, &herrno ) < 0 )){ hp = NULL; } #elif defined(sun) /* Solaris 5++ */ proto = getprotobyname_r("icmp", &proto_datum, proto_buf, PROTO_BUF_LEN); hp = gethostbyname_r( host, &hent, hbf, sizeof(hbf), &herrno ); #elif defined(_AIX) aixbuf = (char*)xmalloc( 9000 ); probuf = (char*)xmalloc( 9000 ); rc = getprotobyname_r( "icmp", &proto, ( struct protoent_data *)(probuf + sizeof( struct protoent))); rc = gethostbyname_r ( host, (struct hostent *)aixbuf, (struct hostent_data *)(aixbuf + sizeof(struct hostent))); hp = (struct hostent*)aixbuf; #elif ( defined(hpux) || defined(__osf__) ) proto = getprotobyname( "icmp" ); hp = gethostbyname( host ); herrno = h_errno; #else /* simply hoping that get*byname is thread-safe */ proto = getprotobyname( "icmp" ); hp = gethostbyname( host ); herrno = h_errno; #endif/*OS SPECIFICS*/ if( proto == NULL ) { return -1; } if(hp != NULL ){ memcpy( &taddr->sin_addr, hp->h_addr_list[0], sizeof( taddr->sin_addr )); taddr->sin_port = 0; taddr->sin_family = AF_INET; } else if( inet_aton( host, &taddr->sin_addr ) == 0 ){ return -1; } last = ntohl( taddr->sin_addr.s_addr ) & 0xFF; if(( last == 0x00 ) || ( last == 0xFF )){ return -1; } if(( datum->sock = socket( AF_INET, SOCK_RAW, proto->p_proto )) < 0 ){ #ifdef DEBUG debug(DBG_NORMAL, ERROR_MSG "sock: %s" ERROR_POS, strerror(errno)); #endif/*DEBUG*/ return -2; } icp = (struct icmp *)buf; icp->icmp_type = ICMP_ECHO; icp->icmp_code = 0; icp->icmp_cksum = 0; icp->icmp_id = getpid() & 0xFFFF; icp->icmp_cksum = in_checksum((u_short *)icp, len ); if(( ss = sendto( datum->sock, buf, sizeof( buf ), 0, (struct sockaddr*)taddr, sizeof( *taddr ))) < 0 ){ #ifdef DEBUG debug(DBG_NORMAL, ERROR_MSG "sock: %s" ERROR_POS, strerror(errno)); #endif/*DEBUG*/ return -2; } if( ss != len ){ #ifdef DEBUG debug(DBG_NORMAL, ERROR_MSG "sock: %s" ERROR_POS, strerror(errno)); #endif/*DEBUG*/ return -2; } #if defined(_AIX) xfree( aixbuf ); xfree( probuf ); #endif /* JOEfreeprotoent( proto ); */ /* JOEfreeprotoent( &proto_datum ); */ /* JOEfreehostent( hp ); */ /* JOEfreehostent( &hent ); */ return 0; } static int recv_ping( struct sockaddr_in *taddr, struct ping_priv * datum ) { int len; int from; int nf, cc; unsigned char buf[ HDRLEN + DATALEN ]; struct icmp *icp; struct sockaddr_in faddr; struct timeval to; fd_set readset; to.tv_sec = datum->timo / 100000; to.tv_usec = ( datum->timo - ( to.tv_sec * 100000 ) ) * 10; FD_ZERO( &readset ); FD_SET( datum->sock, &readset ); /* we use select to see if there is any activity on the socket. If not, then we've requested an unreachable network and we'll time out here. */ if(( nf = select( datum->sock + 1, &readset, NULL, NULL, &to )) < 0 ){ datum->rrt = -4; #ifdef DEBUG debug(DBG_NORMAL, ERROR_MSG "sock: %s" ERROR_POS, strerror(errno)); #endif/*DEBUG*/ return 0; } if( nf == 0 ){ return -1; } len = HDRLEN + DATALEN; from = sizeof( faddr ); cc = recvfrom( datum->sock, buf, len, 0, (struct sockaddr*)&faddr,(socklen_t*)&from ); if( cc < 0 ){ datum->rrt = -4; #ifdef DEBUG debug(DBG_NORMAL, ERROR_MSG "sock: %s" ERROR_POS, strerror(errno)); #endif/*DEBUG*/ return 0; } icp = (struct icmp *)(buf + HDRLEN + DATALEN ); if( faddr.sin_addr.s_addr != taddr->sin_addr.s_addr ){ return 1; } /***** if( icp->icmp_id != ( getpid() & 0xFFFF )){ printf( "id: %d\n", icp->icmp_id ); return 1; } *****/ return 0; } int myping( const char *hostname, int t , struct ping_priv * datum) { int err; int rrt; struct sockaddr_in sa; struct timeval mytime; datum->ident = getpid() & 0xFFFF; if( t == 0 ) datum->timo = 2; else datum->timo = t; datum->rrt = 0; (void) gettimeofday( &mytime, (struct timezone *)NULL); if(( err = send_ping( hostname, &sa, datum)) < 0 ){ close( datum->sock ); return err; } do { rrt = elapsed_time( &mytime ); if (datum->rrt < 0) return 0; datum->rrt = rrt; if (datum->rrt > datum->timo * 1000 ) { close( datum->sock ); return 0; } } while( recv_ping( &sa, datum )); close( datum->sock ); return 1; } int pinghost( const char *hostname ) { struct ping_priv datum = ping_priv_default(); return myping( hostname, 0, &datum ); } int pingthost( const char *hostname, int t ) { struct ping_priv datum = ping_priv_default(); return myping( hostname, t, &datum ); } int tpinghost( const char *hostname ) { int ret; struct ping_priv datum = ping_priv_default(); ret = myping( hostname, 0, &datum ); if(ret > 0 ) ret = datum.rrt; return ret; } int tpingthost( const char *hostname, int t ) { int ret; struct ping_priv datum = ping_priv_default(); ret = myping( hostname, t, &datum ); if(ret > 0 ) ret = datum.rrt; return ret; }