/** * 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; }