netsukuku/src/libping.c
Kirill Sotnikov f1761cad9a git repo init
2013-09-16 13:53:25 +04:00

378 lines
8.6 KiB
C

/**
* PING module
*
* Copyright (C) 2001 Jeffrey Fulmer <jdfulmer@armstrong.com>
* 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 <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <pthread.h>
#include <stdlib.h>
#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;
}