implemented numeric sort (sort -g)
[oweals/busybox.git] / ping.c
1 /*
2  * $Id: ping.c,v 1.6 1999/12/11 08:41:28 andersen Exp $
3  * Mini ping implementation for busybox
4  *
5  * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
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 GNU
15  * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  *
21  * This version of ping is adapted from the ping in netkit-base 0.10,
22  * which is:
23  *
24  * Copyright (c) 1989 The Regents of the University of California.
25  * All rights reserved.
26  *
27  * This code is derived from software contributed to Berkeley by
28  * Mike Muuss.
29  * 
30  * Original copyright notice is retained at the end of this file.
31  */
32
33 #include "internal.h"
34 #include <sys/param.h>
35 #include <sys/socket.h>
36 #include <sys/file.h>
37 #include <sys/time.h>
38 #include <sys/times.h>
39 #include <sys/signal.h>
40
41 #include <netinet/in.h>
42 #include <netinet/ip.h>
43 #include <netinet/ip_icmp.h>
44 #include <arpa/inet.h>
45 #include <netdb.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <errno.h>
49
50 #define DEFDATALEN      56
51 #define MAXIPLEN        60
52 #define MAXICMPLEN      76
53 #define MAXPACKET       65468
54 #define MAX_DUP_CHK     (8 * 128)
55 #define MAXWAIT         10
56 #define PINGINTERVAL    1 /* second */
57
58 #define O_QUIET         (1 << 0)
59
60 #define A(bit)          rcvd_tbl[(bit)>>3]      /* identify byte in array */
61 #define B(bit)          (1 << ((bit) & 0x07))   /* identify bit in byte */
62 #define SET(bit)        (A(bit) |= B(bit))
63 #define CLR(bit)        (A(bit) &= (~B(bit)))
64 #define TST(bit)        (A(bit) & B(bit))
65
66 /* common routines */
67 static int in_cksum(unsigned short *buf, int sz)
68 {
69     int nleft = sz;
70     int sum = 0;
71     unsigned short *w = buf;
72     unsigned short ans = 0;
73
74     while (nleft > 1) {
75         sum += *w++;
76         nleft -= 2;
77     }
78
79     if (nleft == 1) { 
80         *(unsigned char *)(&ans) = *(unsigned char *)w;
81         sum += ans;
82     }
83
84     sum = (sum >> 16) + (sum & 0xFFFF);
85     sum += (sum >> 16);
86     ans = ~sum;
87     return(ans);
88 }   
89
90 /* simple version */
91 #ifdef BB_SIMPLE_PING
92 static const char* ping_usage = "ping host\n\n";
93
94 static char* hostname = NULL;
95
96 static void noresp(int ign)
97 {
98     printf("No response from %s\n", hostname);
99     exit(0);
100 }
101
102 static int ping(const char *host)
103 {
104     struct hostent *h;
105     struct sockaddr_in pingaddr;
106     struct icmp *pkt;
107     int pingsock, c;
108     char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
109     
110     if ((pingsock = socket(AF_INET, SOCK_RAW, 1)) < 0) { /* 1 == ICMP */
111         perror("ping");
112         exit(1);
113     }
114
115     /* drop root privs if running setuid */
116     setuid(getuid());
117     
118     memset(&pingaddr, 0, sizeof(struct sockaddr_in));
119     pingaddr.sin_family = AF_INET;
120     if (!(h = gethostbyname(host))) {
121         fprintf(stderr, "ping: unknown host %s\n", host);
122         exit(1);
123     }   
124     memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr));
125     hostname = h->h_name;
126
127     pkt = (struct icmp *)packet;
128     memset(pkt, 0, sizeof(packet));    
129     pkt->icmp_type = ICMP_ECHO;    
130     pkt->icmp_cksum = in_cksum((unsigned short *)pkt, sizeof(packet));
131    
132     c = sendto(pingsock, packet, sizeof(packet), 0, 
133                (struct sockaddr *)&pingaddr, sizeof(struct sockaddr_in));
134
135     if (c < 0 || c != sizeof(packet)) {
136         if (c < 0) perror("ping");
137         fprintf(stderr, "ping: write incomplete\n");
138         exit(1);
139     }
140
141     signal(SIGALRM, noresp);
142     alarm(5); /* give the host 5000ms to respond */
143     /* listen for replies */
144     while (1) {
145         struct sockaddr_in from;
146         size_t fromlen = sizeof(from);
147
148         if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
149                           (struct sockaddr *)&from, &fromlen)) < 0) {
150             if (errno == EINTR) continue;
151             perror("ping");
152             continue;
153         }
154         if (c >= 76) { /* ip + icmp */
155             struct iphdr *iphdr = (struct iphdr *)packet;
156             pkt = (struct icmp *)(packet + (iphdr->ihl << 2)); /* skip ip hdr */
157             if (pkt->icmp_type == ICMP_ECHOREPLY) break;
158         }          
159     }
160     printf("%s is alive!\n", hostname);
161     return(TRUE);
162 }
163
164 extern int ping_main(int argc, char **argv)
165 {
166     argc--;
167     argv++;
168     if (argc < 1) usage(ping_usage);
169     ping(*argv);
170     exit(TRUE);
171 }
172
173 #else 
174 /* full(er) version */
175 static const char* ping_usage = "ping [OPTION]... host\n\n"
176 "Send ICMP ECHO_REQUEST packets to network hosts.\n\n"
177 "Options:\n"
178 "\t-q\t\tQuiet mode, only displays output at start and when finished.\n"
179 "\t-c COUNT\tSend only COUNT pings.\n";
180
181 static char *hostname = NULL;
182 static struct sockaddr_in pingaddr;
183 static int pingsock = -1;
184
185 static long ntransmitted = 0, nreceived = 0, nrepeats = 0, pingcount = 0;
186 static int myid = 0, options = 0;
187 static unsigned long tmin = ULONG_MAX, tmax = 0, tsum = 0;
188 static char rcvd_tbl[MAX_DUP_CHK / 8];
189
190 static void sendping(int);
191 static void pingstats(int);
192 static void unpack(char *, int, struct sockaddr_in *);
193
194 static void ping(char *);
195
196 /**************************************************************************/
197
198 static void pingstats(int ign) {
199     signal(SIGINT, SIG_IGN);
200     
201     printf("\n--- %s ping statistics ---\n", hostname);
202     printf("%ld packets transmitted, ", ntransmitted);
203     printf("%ld packets received, ", nreceived);
204     if (nrepeats)
205         printf("%ld duplicates, ", nrepeats);
206     if (ntransmitted)
207         printf("%ld%% packet loss\n", 
208                (ntransmitted - nreceived)*100/ntransmitted);
209     if (nreceived)
210         printf("round-trip min/avg/max = %lu.%lu/%lu.%lu/%lu.%lu ms\n",
211                tmin/10, tmin%10, 
212                (tsum/(nreceived+nrepeats))/10,
213                (tsum/(nreceived+nrepeats))%10,
214                tmax/10, tmax%10);
215     exit(0);
216 }
217
218 static void sendping(int ign)
219 {
220     struct icmp *pkt;
221     int i;
222     char packet[DEFDATALEN + 8];
223
224     pkt = (struct icmp *)packet;
225     
226     pkt->icmp_type = ICMP_ECHO;
227     pkt->icmp_code = 0;
228     pkt->icmp_cksum = 0;
229     pkt->icmp_seq = ntransmitted++;
230     pkt->icmp_id = myid; 
231     CLR(pkt->icmp_seq % MAX_DUP_CHK);
232
233     gettimeofday((struct timeval *)&packet[8], NULL);
234     pkt->icmp_cksum = in_cksum((unsigned short *)pkt, sizeof(packet));
235     
236     i = sendto(pingsock, packet, sizeof(packet), 0, 
237                (struct sockaddr *)&pingaddr, sizeof(struct sockaddr_in));
238
239     if (i < 0 || i != sizeof(packet)) {
240         if (i < 0) perror("ping");
241         fprintf(stderr, "ping wrote %d chars; %d expected\n", i, sizeof(packet));
242         exit(1);
243     }
244
245     signal(SIGALRM, sendping);
246     if (pingcount == 0 || ntransmitted < pingcount) { /* schedule next in 1s */
247         alarm(PINGINTERVAL);
248     } else { /* done, wait for the last ping to come back */
249         /* todo, don't necessarily need to wait so long... */
250         signal(SIGALRM, pingstats);
251         alarm(MAXWAIT);
252     }
253 }
254     
255 static void unpack(char *buf, int sz, struct sockaddr_in *from)
256 {
257     struct icmp *icmppkt;
258     struct iphdr *iphdr;
259     struct timeval tv, *tp;
260     int hlen, dupflag;
261     unsigned long triptime;
262     
263     gettimeofday(&tv, NULL);
264     
265     /* check IP header */
266     iphdr = (struct iphdr *)buf;
267     hlen = iphdr->ihl << 2;
268     /* discard if too short */
269     if (sz < (DEFDATALEN + ICMP_MINLEN)) return;
270
271     sz -= hlen;
272     icmppkt = (struct icmp *)(buf + hlen);
273     
274     if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
275         if (icmppkt->icmp_id != myid) return; /* not our ping */
276         ++nreceived;
277         tp = (struct timeval *)icmppkt->icmp_data;
278
279         if ((tv.tv_usec -= tp->tv_usec) < 0) {
280             --tv.tv_sec;
281             tv.tv_usec += 1000000;
282         }
283         tv.tv_sec -= tp->tv_sec;
284         
285         triptime = tv.tv_sec * 10000 + (tv.tv_usec / 100);
286         tsum += triptime;
287         if (triptime < tmin) tmin = triptime;
288         if (triptime > tmax) tmax = triptime;
289
290         if (TST(icmppkt->icmp_seq % MAX_DUP_CHK)) {
291             ++nrepeats;
292             --nreceived;
293             dupflag = 1;
294         } else {
295             SET(icmppkt->icmp_seq % MAX_DUP_CHK);
296             dupflag = 0;
297         }
298
299         if (options & O_QUIET) return;
300
301         printf("%d bytes from %s: icmp_seq=%u", sz, 
302                inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr),
303                icmppkt->icmp_seq);
304         printf(" ttl=%d", iphdr->ttl);
305         printf(" time=%lu.%lu ms", triptime/10, triptime%10);
306         if (dupflag) printf(" (DUP!)");
307         printf("\n");
308     } else {
309         fprintf(stderr, "Warning: unknown ICMP packet received (not echo-reply)\n");
310     }   
311 }
312
313 static void ping(char *host)
314 {
315     struct protoent *proto;
316     struct hostent *h;
317     char buf[MAXHOSTNAMELEN];
318     char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
319     int sockopt;
320     
321     if (!(proto = getprotobyname("icmp"))) {
322         fprintf(stderr, "ping: unknown protocol icmp\n");
323         exit(1);
324     }
325     if ((pingsock = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
326         if (errno == EPERM) {
327             fprintf(stderr, "ping: permission denied. (are you root?)\n");
328         } else {
329             perror("ping");
330         }
331         exit(1);
332     }
333
334     /* drop root privs if running setuid */
335     setuid(getuid());
336     
337     memset(&pingaddr, 0, sizeof(struct sockaddr_in));
338     pingaddr.sin_family = AF_INET;
339     if (!(h = gethostbyname(host))) {
340         fprintf(stderr, "ping: unknown host %s\n", host);
341         exit(1);
342     }
343
344     if (h->h_addrtype != AF_INET) {
345         fprintf(stderr, "ping: unknown address type; only AF_INET is currently supported.\n");
346         exit(1);
347     }
348         
349     pingaddr.sin_family = AF_INET; /* h->h_addrtype */
350     memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr)); 
351     strncpy(buf, h->h_name, sizeof(buf)-1);
352     hostname = buf;
353
354     /* enable broadcast pings */
355     sockopt = 1;
356     setsockopt(pingsock, SOL_SOCKET, SO_BROADCAST, (char *)&sockopt, sizeof(sockopt));
357     
358     /* set recv buf for broadcast pings */
359     sockopt = 48 * 1024;
360     setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, (char *)&sockopt, sizeof(sockopt));
361
362     printf("PING %s (%s): %d data bytes\n", 
363            hostname, inet_ntoa(*(struct in_addr *)&pingaddr.sin_addr.s_addr),
364            DEFDATALEN);
365
366     signal(SIGINT, pingstats);
367     
368     /* start the ping's going ... */
369     sendping(0);
370         
371     /* listen for replies */
372     while (1) {
373         struct sockaddr_in from;
374         size_t fromlen = sizeof(from);
375         int c;
376
377         if ((c = recvfrom(pingsock, packet, sizeof(packet), 0,
378                           (struct sockaddr *)&from, &fromlen)) < 0) {
379             if (errno == EINTR) continue;
380             perror("ping");
381             continue;
382         }
383         unpack(packet, c, &from);
384         if (pingcount > 0 && nreceived >= pingcount) break;     
385     }
386     pingstats(0);
387 }
388
389 extern int ping_main(int argc, char **argv)
390 {
391     char *thisarg;
392     
393     argc--;
394     argv++;
395     options = 0;
396     /* Parse any options */
397     while (argc > 1) {
398         if (**argv != '-') usage(ping_usage);
399         thisarg = *argv; thisarg++;
400         switch (*thisarg) {
401             case 'q': options |= O_QUIET; break;
402             case 'c':
403                 argc--; argv++;
404                 pingcount = atoi(*argv);                
405                 break;
406             default:
407                 usage(ping_usage);
408         }
409         argc--; argv++;
410     }
411     if (argc < 1) usage(ping_usage);       
412
413     myid = getpid() & 0xFFFF;
414     ping(*argv);
415     exit(TRUE);
416 }
417 #endif
418
419 /*
420  * Copyright (c) 1989 The Regents of the University of California.
421  * All rights reserved.
422  *
423  * This code is derived from software contributed to Berkeley by
424  * Mike Muuss.
425  *
426  * Redistribution and use in source and binary forms, with or without
427  * modification, are permitted provided that the following conditions
428  * are met:
429  * 1. Redistributions of source code must retain the above copyright
430  *    notice, this list of conditions and the following disclaimer.
431  * 2. Redistributions in binary form must reproduce the above copyright
432  *    notice, this list of conditions and the following disclaimer in the
433  *    documentation and/or other materials provided with the distribution.
434  * 3. All advertising materials mentioning features or use of this software
435  *    must display the following acknowledgement:
436  *      This product includes software developed by the University of
437  *      California, Berkeley and its contributors.
438  * 4. Neither the name of the University nor the names of its contributors
439  *    may be used to endorse or promote products derived from this software
440  *    without specific prior written permission.
441  *
442  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
443  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
444  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
445  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
446  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
447  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
448  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
449  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
450  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
451  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
452  * SUCH DAMAGE.
453  */