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>
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
37 #include "private.h"
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 }
52 void
53 JOEfreehostent( struct hostent *h )
54 {
55 char **p;
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 }
71 static int
72 in_checksum( u_short *buf, int len )
73 {
74 register long sum = 0;
75 u_short answer = 0;
77 while( len > 1 ){
78 sum += *buf++;
79 len -= 2;
80 }
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;
90 return ( answer );
92 }
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 ];
101 const int proto_buf_len = 1024;
102 char proto_buf[proto_buf_len];
103 struct protoent *proto = NULL;
104 struct protoent proto_datum;
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*/
116 struct icmp *icp;
117 unsigned short last;
119 len = HDRLEN + DATALEN;
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*/
150 if( proto == NULL ) {
151 return -1;
152 }
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 }
163 last = ntohl( taddr->sin_addr.s_addr ) & 0xFF;
164 if(( last == 0x00 ) || ( last == 0xFF )){
165 return -1;
166 }
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 }
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; /* FIXME this is not nice.. */
182 icp->icmp_cksum = in_checksum((u_short *)icp, len );
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 }
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 }
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;
221 to.tv_sec = datum->timo / 100000;
222 to.tv_usec = ( datum->timo - ( to.tv_sec * 100000 ) ) * 10;
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 }
240 len = HDRLEN + DATALEN;
241 from = sizeof( faddr );
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 }
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 }
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;
273 datum->ident = getpid() & 0xFFFF;
275 if( t == 0 ) datum->timo = 2;
276 else datum->timo = t;
278 datum->rrt = 0;
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 );
297 return 1;
298 }
300 int
301 pinghost( const char *hostname )
302 {
303 struct ping_priv datum = ping_priv_default();
304 return myping( hostname, 0, &datum );
305 }
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 }
314 int
315 tpinghost( const char *hostname )
316 {
317 int ret;
318 struct ping_priv datum = ping_priv_default();
320 ret = myping( hostname, 0, &datum );
321 if(ret > 0 )
322 ret = datum.rrt;
323 return ret;
324 }
326 int
327 tpingthost( const char *hostname, int t )
328 {
329 int ret;
330 struct ping_priv datum = ping_priv_default();
332 ret = myping( hostname, t, &datum );
333 if(ret > 0 )
334 ret = datum.rrt;
335 return ret;
336 }