#include <stdio.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <string.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <stdlib.h>
#ifndef __FreeBSD__
#include <malloc.h>
#endif
#include <unistd.h>
#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;
}
syntax highlighted by Code2HTML, v. 0.9.1