#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __FreeBSD__ #include #endif #include #include "list.h" #include "config.h" #include "pinger.h" /* Define this on unsafe systems to prevent non-privileged users to somewhat access raw sockets */ #undef DONT_DROP_ROOT int debug=0; ListItem pinger_iterator; #include "pinger.h" unsigned long timeval_diff(struct timeval *a, struct timeval *b) { return ( (long)(b->tv_usec - a->tv_usec) + ( (long)(b->tv_sec - a->tv_sec) * 1000000L) ); } int pinger_init(pinger *p) { if (!(p->proto=getprotobyname("icmp"))) { printf("Error: unknown protocol icmp\n"); return -1; } if ((p->socket=socket(AF_INET,SOCK_RAW,p->proto->p_proto))<1) { if (errno==EPERM) fprintf(stderr,"Error: this program must be run as root\n"); else perror("Socket"); return -2; } /* Drop Root */ #ifndef DONT_DROP_ROOT setreuid(getuid(),getuid()); setregid(getgid(),getgid()); #endif ListInit(&p->hosts); p->ident = getpid() & 0xFFFF; return 0; } pinger_host *pinger_addhost(pinger *pg, char *hostname) { struct hostent *h; pinger_host *p; int i; struct sockaddr_in *sock; sock = (struct sockaddr_in*)(malloc(sizeof(struct sockaddr_in))); if (!sock) { perror("malloc\n"); return 0; } memset(sock, 0, sizeof(struct sockaddr_in)); sock->sin_family = AF_INET; #ifdef HAVE_INET_ATON if (!(inet_aton(hostname,&sock->sin_addr))) { #else /* inet_addr returns -1 when errors occur */ if ((sock->sin_addr.s_addr=inet_addr(hostname)) == -1) { #endif if (!(h = gethostbyname(hostname))) { printf("Unknown host: %s",hostname); free(sock); return 0; } sock->sin_family = h->h_addrtype; memcpy(&sock->sin_addr, h->h_addr, h->h_length); } p=(pinger_host*)(malloc(sizeof(pinger_host))); if (!p) { perror("malloc"); free(sock); return 0; } memset(p,0,sizeof(pinger_host)); p->sock = sock; p->lastping = time(0); p->lastreply = time(0); p->sequence = 0; p->timeoutptr = 0; ListInsert(&pg->hosts, (void*)p); /* This shouldn't be here though */ pinger_iterator=0; memset(p->timeout, 0, sizeof(p->timeout)); return p; } /* taken from GNU ping.c */ static int in_cksum(u_short *addr, int len) { register int nleft = len; register u_short *w = addr; register int sum = 0; u_short answer = 0; /* * Our algorithm is simple, using a 32 bit accumulator (sum), we add * sequential 16 bit words to it, and at the end, fold back all the * carry bits from the top 16 bits into the lower 16 bits. */ while (nleft > 1) { sum += *w++; nleft -= 2; } /* mop up an odd byte, if necessary */ if (nleft == 1) { *(u_char *)(&answer) = *(u_char *)w ; sum += answer; } /* add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* truncate to 16 bits */ return(answer); } int pinger_poll(pinger *p, pinger_host *h) { STRUCT_ICMP *icp; if (h->flags& PINGER_DONTPING) return 0; if (h->tcpport) { return tcp_isalive(h); } else if (h->udpport) { return udp_isalive(h); } if (debug) fprintf(stderr,"Pinging %s...\n", inet_ntoa(*(struct in_addr *)&h->sock->sin_addr.s_addr)); icp = (STRUCT_ICMP *)p->outbuf; h->lastping = time(0); icp->ICMP_TYPE = ICMP_ECHO; icp->ICMP_CODE = 0; icp->ICMP_CKSUM = 0; icp->ICMP_SEQ = h->sequence++; icp->ICMP_ID = p->ident; /* ID */ icp->ICMP_CKSUM = in_cksum((u_short *)icp, DATALEN + 8); gettimeofday(&h->lp,0); return sendto(p->socket, (char *)p->outbuf, DATALEN+8 , 0, (struct sockaddr *) h->sock, sizeof(struct sockaddr)); } int pinger_pollall(pinger *p) { ListItem iterator; for (iterator=p->hosts;iterator;iterator=iterator->next) { pinger_poll(p, (pinger_host*)ListData(iterator) ); } return 0; } pinger_host *pinger_pollinit(pinger *p) { pinger_iterator=p->hosts; return (pinger_host*)ListData(pinger_iterator); } pinger_host *pinger_pollnext(pinger *p) { pinger_host *phost; if (!pinger_iterator) { pinger_iterator=p->hosts; return 0; } if (pinger_iterator) { phost=(pinger_host*)ListData(pinger_iterator); pinger_poll(p, phost ); pinger_iterator=pinger_iterator->next; return phost; } else return 0; } unsigned long pinger_checkoneandsleep(pinger *p,unsigned long usecs) { struct timeval tv; #ifndef linux struct timeval delay,enddelay; long diff; #endif struct sockaddr_in host; struct ip *iph; STRUCT_ICMP *icp; int hlen,i,fdmax=0; ListItem iterator; pinger_host *h; int s,count,size; tv.tv_sec=0; tv.tv_usec=usecs; FD_ZERO(&p->fdmask_read); FD_ZERO(&p->fdmask_write); FD_SET(p->socket,&p->fdmask_read); fdmax = p->socket; for(i=1, iterator=p->hosts; iterator; iterator=iterator->next) { h=(pinger_host*)ListData(iterator); if (h->tcp_socket) { FD_SET(h->tcp_socket, &p->fdmask_write); } if (h->tcp_socket>fdmax) fdmax=h->tcp_socket; } #ifndef linux gettimeofday(&delay,0); #endif if ((s=select(fdmax+1, &p->fdmask_read, &p->fdmask_write, 0, &tv))>0) { if (FD_ISSET(p->socket, &p->fdmask_read)) { size=sizeof(struct sockaddr_in); if ((count=recvfrom(p->socket,(char*) p->inbuf, MAXPACKET, 0, (struct sockaddr*)&host, &size))>=0) { /* Handle Packet */ iph=(struct ip*)p->inbuf; hlen = iph->ip_hl << 2; icp = (STRUCT_ICMP*)(p->inbuf + hlen); if (icp->ICMP_TYPE == ICMP_ECHOREPLY || (icp->ICMP_TYPE == ICMP_UNREACH && icp->ICMP_CODE==ICMP_UNREACH_PORT) ) { if (icp->ICMP_TYPE == ICMP_UNREACH || icp->ICMP_ID == p->ident) for (iterator=p->hosts; iterator; iterator=iterator->next) { h=(pinger_host *)ListData(iterator); if (h->sock->sin_addr.s_addr == host.sin_addr.s_addr) { int incoming_udp_port; struct ip *iph; struct udphdr *udph; /* Calculate port */ if (icp->ICMP_TYPE == ICMP_UNREACH){ iph = (struct ip*)((char*)icp + sizeof(STRUCT_ICMP)); if (iph->ip_p == IPPROTO_UDP) { udph = (struct udphdr *) ((char *)iph + sizeof(struct ip)); incoming_udp_port = ntohs(udph->UDP_DPORT); if (incoming_udp_port != h->udpport) continue; if (debug) fprintf(stderr,"ICMP port unreachable UDP %d from %s\n", incoming_udp_port, inet_ntoa(*(struct in_addr *)&host.sin_addr.s_addr)); } } if (debug && !h->udpport) printf("Incoming from host %s\n", inet_ntoa(*(struct in_addr *)&host.sin_addr.s_addr)); if (h->udpport) { h->udpstatus=1; } else { h->lastreply = time(0); gettimeofday(&h->lr,0); if (debug) printf("Got reply from %s, timeout %lu ms (stored at position %d)\n", inet_ntoa(*(struct in_addr *)&host.sin_addr.s_addr), timeval_diff(&h->lp, &h->lr)/1000, h->timeoutptr); h->timeout[h->timeoutptr++] = timeval_diff (&h->lp, &h->lr) / 1000; if (h->timeoutptr > TIMEOUT_SIZE) h->timeoutptr=0; } #ifndef linux gettimeofday(&enddelay,0); diff = usecs - timeval_diff(&delay, &enddelay); if (diff<0) diff=0; tv.tv_usec=diff; #endif return tv.tv_usec; } } } else if (debug) printf("Unknown ICMP type %d\n",icp->ICMP_TYPE); } else perror("recvfrom"); } /* Check the rest of them */ for (iterator=p->hosts;iterator;iterator=iterator->next) { h = (pinger_host*)ListData(iterator); if (h->tcp_socket && FD_ISSET(h->tcp_socket, &p->fdmask_write)) { int dummy,dummysize=sizeof(int); /* dummy= connect(h->tcp_socket, (struct sockaddr*) h->tcpsock, sizeof(struct sockaddr_in)); if (dummy == 0) { h->lastreply = time(0); if (debug) fprintf(stderr,"Success connecting to %s port %d\n", inet_ntoa(*(struct in_addr *)&h->sock->sin_addr.s_addr), h->tcpport); } else if (debug) { fprintf(stderr,"Error connecting to %s port %d (socket %d) - ", inet_ntoa(*(struct in_addr *)&h->sock->sin_addr.s_addr), h->tcpport, h->tcp_socket); perror("connect"); } */ /* Check the SO_ERROR */ if (getsockopt(h->tcp_socket, SOL_SOCKET, SO_ERROR, &dummy, &dummysize)<0) { perror("pinger.c: pinger_checkoneandsleep() - getsockopt"); } else { if (!dummy) /* Ok, connnect succeeded */ { h->lastreply = time(0); if (debug) fprintf(stderr,"Success connecting to %s port %d\n", inet_ntoa(*(struct in_addr *)&h->sock->sin_addr.s_addr), h->tcpport); } else if (debug) { fprintf(stderr,"Error connecting to %s port %d (socket %d) - ", inet_ntoa(*(struct in_addr *)&h->sock->sin_addr.s_addr), h->tcpport, h->tcp_socket); fprintf(stderr,"SOERROR %d\n",dummy); } } close(h->tcp_socket); h->tcp_socket = 0; free(h->tcpsock); } } } #ifndef linux gettimeofday(&enddelay,0); diff = usecs - timeval_diff(&delay, &enddelay); if (diff<0) diff=0; tv.tv_usec=diff; #endif return tv.tv_usec; }