c6a0096d7ae0423016dedceb6874b885b6fd1f5b
[collectd.git] / src / libping / ping.c
1 /**
2  * PING module
3  *
4  * Copyright (C) 2001 Jeffrey Fulmer <jdfulmer@armstrong.com>
5  * This file is part of LIBPING
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  */
22 #include <ping.h>
23 #include <util.h>
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <netdb.h>
27 #include <pthread.h>
28 #include <stdlib.h>
29
30 #define  MAXPACKET   65535
31 #define  PKTSIZE     64 
32 #define  HDRLEN      ICMP_MINLEN
33 #define  DATALEN     (PKTSIZE-HDRLEN)
34 #define  MAXDATA     (MAXPKT-HDRLEN-TIMLEN)
35 #define  DEF_TIMEOUT 5
36
37 #include "private.h"
38
39 void
40 JOEfreeprotoent( struct protoent *p )
41 {
42   char **a;
43   free( p->p_name );
44   if( p->p_aliases != NULL ){
45     for( a = p->p_aliases; *a != NULL; a++ ){
46       free( *a );
47     }
48   }
49   free( p );
50 }
51
52 void
53 JOEfreehostent( struct hostent *h )
54 {
55   char **p;
56
57   free( h->h_name );
58   if( h->h_aliases != NULL ){
59     for( p = h->h_aliases; *p != NULL; ++p )
60       free( *p );
61     free( h->h_aliases );
62   }
63   if( h->h_addr_list != NULL ){
64     for( p = h->h_addr_list; *p != NULL; ++p )
65       free( *p );
66     free (h->h_addr_list);
67   }
68   free( h );
69 }
70
71 static int 
72 in_checksum( u_short *buf, int len )
73 {
74   register long sum = 0;
75   u_short  answer = 0;
76
77   while( len > 1 ){
78     sum += *buf++;
79     len -= 2;
80   }
81
82   if( len == 1 ){
83     *( u_char* )( &answer ) = *( u_char* )buf;
84     sum += answer;
85   }
86   sum = ( sum >> 16 ) + ( sum & 0xffff );
87   sum += ( sum >> 16 );     
88   answer = ~sum;     
89
90   return ( answer );
91
92
93
94 static int
95 send_ping( const char *host, struct sockaddr_in *taddr, struct ping_priv * datum )
96 {
97   int len;
98   int ss;
99   unsigned char buf[ HDRLEN + DATALEN ];
100
101   const int proto_buf_len = 1024;
102   char   proto_buf[proto_buf_len];
103   struct protoent *proto = NULL;
104   struct protoent proto_datum;
105
106   struct hostent  *hp = NULL;
107   struct hostent  hent;
108   int herrno;
109   char hbf[9000];
110 #if defined(_AIX)
111   char *aixbuf;
112   char *probuf;
113   int  rc;
114 #endif/*_AIX*/
115
116   struct icmp     *icp;
117   unsigned short  last;
118
119   len = HDRLEN + DATALEN;
120
121 #if defined(__GLIBC__)
122   /* for systems using GNU libc */
123   getprotobyname_r("icmp", &proto_datum, proto_buf, proto_buf_len, &proto);
124   if(( gethostbyname_r( host, &hent, hbf, sizeof(hbf), &hp, &herrno ) < 0 )){
125     hp = NULL;
126   }
127 #elif defined(sun)
128   /* Solaris 5++ */
129   proto = getprotobyname_r("icmp", &proto_datum, proto_buf, proto_buf_len);
130   hp    = gethostbyname_r( host, &hent, hbf, sizeof(hbf), &herrno );
131 #elif defined(_AIX)
132   aixbuf = (char*)xmalloc( 9000 );
133   probuf = (char*)xmalloc( 9000 );
134   rc  = getprotobyname_r( "icmp", &proto,
135                         ( struct protoent_data *)(probuf + sizeof( struct protoent)));
136   rc  = gethostbyname_r ( host, (struct hostent *)aixbuf,
137                         (struct hostent_data *)(aixbuf + sizeof(struct hostent)));
138   hp = (struct hostent*)aixbuf;
139 #elif ( defined(hpux) || defined(__osf__) )
140   proto  = getprotobyname( "icmp" ); 
141   hp     = gethostbyname( host );
142   herrno = h_errno;
143 #else
144   /* simply hoping that get*byname is thread-safe */
145   proto  = getprotobyname( "icmp" ); 
146   hp     = gethostbyname( host );
147   herrno = h_errno;
148 #endif/*OS SPECIFICS*/
149   
150   if( proto == NULL ) {
151     return -1;
152   }
153
154   if(hp != NULL ){
155     memcpy( &taddr->sin_addr, hp->h_addr_list[0], sizeof( taddr->sin_addr ));
156     taddr->sin_port = 0;
157     taddr->sin_family = AF_INET;
158   }
159   else if( inet_aton( host, &taddr->sin_addr ) == 0 ){
160     return -1;
161   }
162
163   last = ntohl( taddr->sin_addr.s_addr ) & 0xFF;
164   if(( last == 0x00 ) || ( last == 0xFF )){
165     return -1;
166   }
167
168   if(( datum->sock = socket( AF_INET, SOCK_RAW, proto->p_proto )) < 0 ){
169 #ifdef  DEBUG
170   perror( "sock" );
171 #endif/*DEBUG*/
172     return -2;
173   }
174
175   memset(buf, 0, sizeof(buf));
176   icp = (struct icmp *)buf;
177   icp->icmp_type  = ICMP_ECHO;
178   icp->icmp_code  = 0;
179   icp->icmp_cksum = 0;
180   icp->icmp_id    = getpid() & 0xFFFF;
181   icp->icmp_seq   = icp->icmp_id;
182   icp->icmp_cksum = in_checksum((u_short *)icp, len );
183
184   if(( ss = sendto( datum->sock, buf, sizeof( buf ), 0, 
185      (struct sockaddr*)taddr, sizeof( *taddr ))) < 0 ){
186 #ifdef  DEBUG
187   perror( "sock" );
188 #endif/*DEBUG*/
189     return -2;
190   }
191   if( ss != len ){
192 #ifdef  DEBUG
193   perror( "malformed packet" );
194 #endif/*DEBUG*/
195     return -2;
196   }
197
198 #if defined(_AIX)
199   free( aixbuf );
200   free( probuf );
201 #endif 
202   /* JOEfreeprotoent( proto ); */
203   /* JOEfreeprotoent( &proto_datum ); */
204   /* JOEfreehostent( hp ); */
205   /* JOEfreehostent( &hent ); */
206   return 0;
207 }
208
209 static int 
210 recv_ping( struct sockaddr_in *taddr, struct ping_priv * datum )
211 {
212   int len;
213   socklen_t from;
214   int nf, cc;
215   unsigned char buf[ HDRLEN + DATALEN ];
216   struct icmp        *icp;
217   struct sockaddr_in faddr;
218   struct timeval to;
219   fd_set readset;
220
221   to.tv_sec = datum->timo / 100000;
222   to.tv_usec = ( datum->timo - ( to.tv_sec * 100000 ) ) * 10;
223
224   FD_ZERO( &readset );
225   FD_SET( datum->sock, &readset );
226   /* we use select to see if there is any activity
227      on the socket.  If not, then we've requested an
228      unreachable network and we'll time out here. */
229   if(( nf = select( datum->sock + 1, &readset, NULL, NULL, &to )) < 0 ){
230     datum->rrt = -4;
231 #ifdef  DEBUG
232     perror( "select" );
233 #endif/*DEBUG*/    
234     return 0;
235   }
236   if( nf == 0 ){ 
237     return -1; 
238   }
239
240   len = HDRLEN + DATALEN;
241   from = sizeof( faddr ); 
242
243   cc = recvfrom( datum->sock, buf, len, 0, (struct sockaddr*)&faddr, &from );
244   if( cc < 0 ){
245     datum->rrt = -4;
246 #ifdef  DEBUG
247     perror( "recvfrom" );
248 #endif/*DEBUG*/    
249     return 0;
250   }
251
252   icp = (struct icmp *)(buf + HDRLEN + DATALEN );
253   if( faddr.sin_addr.s_addr != taddr->sin_addr.s_addr ){
254     return 1;
255   }
256   /*****
257   if( icp->icmp_id   != ( getpid() & 0xFFFF )){
258     printf( "id: %d\n",  icp->icmp_id );
259     return 1; 
260   }
261   *****/
262   return 0;
263 }
264
265 int 
266 myping( const char *hostname, int t , struct ping_priv * datum)
267 {
268   int err;
269   int rrt;
270   struct sockaddr_in sa;
271   struct timeval mytime;
272  
273   datum->ident = getpid() & 0xFFFF;
274
275   if( t == 0 ) datum->timo = 2; 
276   else         datum->timo = t;
277
278   datum->rrt = 0;
279   
280   (void) gettimeofday( &mytime, (struct timezone *)NULL); 
281   if(( err = send_ping( hostname, &sa, datum)) < 0 ){
282     close( datum->sock );
283     return err;
284   }
285   do {
286     rrt = elapsed_time( &mytime );
287     if (datum->rrt < 0)
288       return 0;
289     datum->rrt = rrt;
290     if (datum->rrt > datum->timo * 1000 ) {
291       close( datum->sock );
292       return 0;
293     }
294   } while( recv_ping( &sa, datum ));
295   close( datum->sock ); 
296  
297   return 1;
298 }
299
300 int
301 pinghost( const char *hostname )
302 {
303   struct ping_priv datum = ping_priv_default();
304   return myping( hostname, 0, &datum );
305 }
306
307 int
308 pingthost( const char *hostname, int t )
309 {
310   struct ping_priv datum = ping_priv_default();
311   return myping( hostname, t, &datum );
312 }
313
314 int
315 tpinghost( const char *hostname )
316 {
317   int ret;
318   struct ping_priv datum = ping_priv_default();
319
320   ret = myping( hostname, 0, &datum );
321   if(ret > 0 )
322     ret = datum.rrt;
323   return ret;
324
325
326 int
327 tpingthost( const char *hostname, int t )
328 {
329   int ret;
330   struct ping_priv datum = ping_priv_default();
331
332   ret = myping( hostname, t, &datum );
333   if(ret > 0 )
334     ret = datum.rrt;
335   return ret;
336 }
337