diff --git a/sbin/ping/Makefile b/sbin/ping/Makefile index a7ed7666b118..a4a3685abab0 100644 --- a/sbin/ping/Makefile +++ b/sbin/ping/Makefile @@ -4,5 +4,6 @@ PROG= ping MAN8= ping.8 BINOWN= root BINMODE=4555 +COPTS+= -Wall -Wmissing-prototypes .include diff --git a/sbin/ping/ping.8 b/sbin/ping/ping.8 index 1bb00a996ecc..c42272c1602e 100644 --- a/sbin/ping/ping.8 +++ b/sbin/ping/ping.8 @@ -30,9 +30,9 @@ .\" SUCH DAMAGE. .\" .\" @(#)ping.8 8.2 (Berkeley) 12/11/93 -.\" $Id$ +.\" $Id: ping.8,v 1.7 1997/02/22 14:33:06 peter Exp $ .\" -.Dd December 11, 1993 +.Dd March 1, 1997 .Dt PING 8 .Os BSD 4.3 .Sh NAME @@ -154,8 +154,14 @@ option in the .Tn ECHO_REQUEST packet and displays the route buffer on returned packets. -Note that the IP header is only large enough for nine such routes. -Many hosts ignore or discard this option. +Note that the IP header is only large enough for nine such routes; +the +.Xr traceroute 8 +command is usually better at determining the route packets take to a +particular destination. +Many hosts ignore or discard the +.Tn RECORD_ROUTE +option. .It Fl r Bypass the normal routing tables and send directly to a host on an attached network. @@ -337,6 +343,31 @@ packets that they use for packets, for example either 30 or 60. Others may use completely wild values. .El +.Sh RETURN VALUES +The +.Nm ping +command returns an exit status of zero if at least one response was +heard from the specified +.Ar host ; +a status of two if the transmission was successful but no responses +were received; or another value +.Pq from Aq Pa sysexits.h +if an error occurred. +.Sh SEE ALSO +.Xr netstat 1 , +.Xr ifconfig 8 , +.Xr routed 8 , +.Xr traceroute 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . +.Sh AUTHORS +The original +.Nm +command was written by Mike Muuss while at the US Army Ballistics +Research Laboratory. .Sh BUGS Many Hosts and Gateways ignore the .Tn RECORD_ROUTE @@ -354,12 +385,3 @@ broadcast address should only be done under very controlled conditions. The .Fl v option is not worth much on busy hosts. -.Sh SEE ALSO -.Xr netstat 1 , -.Xr ifconfig 8 , -.Xr routed 8 -.Sh HISTORY -The -.Nm -command appeared in -.Bx 4.3 . diff --git a/sbin/ping/ping.c b/sbin/ping/ping.c index 589ba4b98e94..bb9be21f7032 100644 --- a/sbin/ping/ping.c +++ b/sbin/ping/ping.c @@ -35,13 +35,17 @@ */ #ifndef lint -static char copyright[] = +static const char copyright[] = "@(#) Copyright (c) 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint +/* static char sccsid[] = "@(#)ping.c 8.1 (Berkeley) 6/5/93"; +*/ +static const char rcsid[] = + "$Id$"; #endif /* not lint */ /* @@ -62,24 +66,30 @@ static char sccsid[] = "@(#)ping.c 8.1 (Berkeley) 6/5/93"; * This program has to run SUID to ROOT to access the ICMP socket. */ -#include +#include /* NB: we rely on this for */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include #include #include -#include -#include -#include #include +#include #include #include #include -#include -#include -#include -#include -#include -#include +#include #define DEFDATALEN (64 - 8) /* default data length */ #define MAXIPLEN 60 @@ -145,26 +155,38 @@ double tsum = 0.0; /* sum of all times, for doing average */ int reset_kerninfo; sig_atomic_t siginfo_p; -char *pr_addr(); -void catcher(), finish(), status(), check_status(); +static void fill(char *, char *); +static u_short in_cksum(u_short *, int); +static void catcher(int sig); +static void check_status(void); +static void finish(int) __dead2; +static void pinger(void); +static char *pr_addr(struct in_addr); +static void pr_icmph(struct icmp *); +static void pr_iph(struct ip *); +static void pr_pack(char *, int, struct sockaddr_in *); +static void pr_retip(struct ip *); +static void status(int); +static void tvsub(struct timeval *, struct timeval *); +static void usage(const char *) __dead2; +int main(argc, argv) int argc; - char **argv; + char *const *argv; { - extern int errno, optind; - extern char *optarg; struct timeval timeout; struct hostent *hp; struct sockaddr_in *to; - struct protoent *proto; struct termios ts; register int i; int ch, fdmask, hold, packlen, preload, sockerrno; struct in_addr ifaddr; unsigned char ttl, loop; u_char *datap, *packet; - char *target, hnamebuf[MAXHOSTNAMELEN], *malloc(); + char *target, hnamebuf[MAXHOSTNAMELEN]; + char *ep; + u_long ultmp; #ifdef IP_OPTIONS char rspace[3 + 4 * NROUTES + 1]; /* record route space */ #endif @@ -175,66 +197,59 @@ main(argc, argv) * then drop our setuid bit. Save error reporting for * after arg parsing. */ - proto = getprotobyname("icmp"); - if (proto) { - s = socket(AF_INET, SOCK_RAW, proto->p_proto); - sockerrno = errno; - } + s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + sockerrno = errno; setuid(getuid()); preload = 0; datap = &outpack[8 + sizeof(struct timeval)]; - while ((ch = getopt(argc, argv, "I:LQRT:c:adfh:i:l:np:qrs:v")) != EOF) + while ((ch = getopt(argc, argv, "I:LQRT:c:adfh:i:l:np:qrs:v")) != -1) { switch(ch) { case 'a': options |= F_AUDIBLE; break; case 'c': - npackets = atoi(optarg); - if (npackets <= 0) { - (void)fprintf(stderr, - "ping: bad number of packets to transmit.\n"); - exit(1); - } + ultmp = strtoul(optarg, &ep, 0); + if (*ep || ep == optarg || ultmp > LONG_MAX || !ultmp) + errx(EX_USAGE, + "invalid count of packets to transmit: `%s'", + optarg); + npackets = ultmp; break; case 'd': options |= F_SO_DEBUG; break; case 'f': if (getuid()) { - (void)fprintf(stderr, - "ping: %s\n", strerror(EPERM)); - exit(1); + errno = EPERM; + err(EX_NOPERM, "-f flag"); } options |= F_FLOOD; setbuf(stdout, (char *)NULL); break; case 'i': /* wait between sending packets */ - interval = atoi(optarg); - if (interval <= 0) { - (void)fprintf(stderr, - "ping: bad timing interval.\n"); - exit(1); - } + ultmp = strtoul(optarg, &ep, 0); + if (*ep || ep == optarg || ultmp > INT_MAX) + errx(EX_USAGE, + "invalid timing interval: `%s'", optarg); options |= F_INTERVAL; + interval = ultmp; break; case 'I': /* multicast interface */ - if (inet_aton(optarg, &ifaddr) == 0) { - (void)fprintf(stderr, - "ping: bad multicast interface.\n"); - exit(1); - } + if (inet_aton(optarg, &ifaddr) == 0) + errx(EX_USAGE, + "invalid multicast interface: `%s'", + optarg); options |= F_MIF; break; case 'l': - preload = atoi(optarg); - if (preload < 0) { - (void)fprintf(stderr, - "ping: bad preload value.\n"); - exit(1); - } + ultmp = strtoul(optarg, &ep, 0); + if (*ep || ep == optarg || ultmp > INT_MAX) + errx(EX_USAGE, + "invalid preload value: `%s'", optarg); + preload = ultmp; break; case 'L': options |= F_NOLOOP; @@ -260,87 +275,80 @@ main(argc, argv) options |= F_SO_DONTROUTE; break; case 's': /* size of packet to send */ - datalen = atoi(optarg); - if (datalen > MAXPACKET) { - (void)fprintf(stderr, - "ping: packet size too large.\n"); - exit(1); - } - if (datalen <= 0) { - (void)fprintf(stderr, - "ping: illegal packet size.\n"); - exit(1); - } + ultmp = strtoul(optarg, &ep, 0); + if (ultmp > MAXPACKET) + errx(EX_USAGE, "packet size too large: %lu", + ultmp); + if (*ep || ep == optarg || !ultmp) + errx(EX_USAGE, "invalid packet size: `%s'", + optarg); + datalen = ultmp; break; case 'T': /* multicast TTL */ - i = atoi(optarg); - if (i < 0 || i > 255) { - (void)fprintf(stderr, - "ping: illegal multicast TTL.\n"); - exit(1); - } - ttl = i; + ultmp = strtoul(optarg, &ep, 0); + if (*ep || ep == optarg || ultmp > 255) + errx(EX_USAGE, "invalid multicast TTL: `%s'", + optarg); + ttl = ultmp; options |= F_MTTL; break; case 'v': options |= F_VERBOSE; break; default: - usage(); - } - argc -= optind; - argv += optind; - if (argc != 1) - usage(); - target = *argv; + usage(argv[0]); + } + } + + if (argc - optind != 1) + usage(argv[0]); + target = argv[optind]; bzero((char *)&whereto, sizeof(struct sockaddr)); to = (struct sockaddr_in *)&whereto; to->sin_family = AF_INET; - to->sin_addr.s_addr = inet_addr(target); - if (to->sin_addr.s_addr != (u_int)-1) + if (inet_aton(target, &to->sin_addr) != 0) { hostname = target; - else { - hp = gethostbyname(target); - if (!hp) { - (void)fprintf(stderr, - "ping: unknown host %s\n", target); - exit(1); - } - to->sin_family = hp->h_addrtype; - bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length); + } else { + hp = gethostbyname2(target, AF_INET); + if (!hp) + errx(EX_NOHOST, "cannot resolve %s: %s", + target, hstrerror(h_errno)); + + to->sin_len = sizeof *to; + memcpy(&to->sin_addr, hp->h_addr_list[0], sizeof to->sin_addr); (void)strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1); + hnamebuf[(sizeof hnamebuf) - 1] = '\0'; hostname = hnamebuf; } - if (options & F_FLOOD && options & F_INTERVAL) { - (void)fprintf(stderr, - "ping: -f and -i incompatible options.\n"); - exit(1); - } + if (options & F_FLOOD && options & F_INTERVAL) + errx(EX_USAGE, "-f and -i: incompatible options"); + + if (options & F_FLOOD && IN_MULTICAST(ntohl(to->sin_addr.s_addr))) + errx(EX_USAGE, + "-f flag cannot be used with multicast destination"); + if (options & (F_MIF | F_NOLOOP | F_MTTL) + && !IN_MULTICAST(ntohl(to->sin_addr.s_addr))) + errx(EX_USAGE, + "-I, -L, -T flags cannot be used with unicast destination"); if (datalen >= sizeof(struct timeval)) /* can we time transfer */ timing = 1; packlen = datalen + MAXIPLEN + MAXICMPLEN; - if (!(packet = (u_char *)malloc((u_int)packlen))) { - (void)fprintf(stderr, "ping: out of memory.\n"); - exit(1); - } + if (!(packet = (u_char *)malloc((size_t)packlen))) + err(EX_UNAVAILABLE, "malloc"); + if (!(options & F_PINGFILLED)) for (i = 8; i < datalen; ++i) *datap++ = i; ident = getpid() & 0xFFFF; - if (!proto) { - (void)fprintf(stderr, "ping: unknown protocol icmp.\n"); - exit(1); - } if (s < 0) { errno = sockerrno; - perror("ping: socket"); - exit(1); + err(EX_OSERR, "socket"); } hold = 1; if (options & F_SO_DEBUG) @@ -357,36 +365,30 @@ main(argc, argv) rspace[IPOPT_OLEN] = sizeof(rspace)-1; rspace[IPOPT_OFFSET] = IPOPT_MINOFF; if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace, - sizeof(rspace)) < 0) { - perror("ping: record route"); - exit(1); - } + sizeof(rspace)) < 0) + err(EX_OSERR, "setsockopt IP_OPTIONS"); #else - (void)fprintf(stderr, - "ping: record route not available in this implementation.\n"); - exit(1); + errx(EX_UNAVAILABLE, + "record route not available in this implementation"); #endif /* IP_OPTIONS */ } if (options & F_NOLOOP) { if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) < 0) { - perror("ping: IP_MULTICAST_LOOP"); - exit(1); + err(EX_OSERR, "setsockopt IP_MULTICAST_LOOP"); } } if (options & F_MTTL) { if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) { - perror("ping: IP_MULTICAST_TTL"); - exit(1); + err(EX_OSERR, "setsockopt IP_MULTICAST_TTL"); } } if (options & F_MIF) { if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &ifaddr, sizeof(ifaddr)) < 0) { - perror("ping: IP_MULTICAST_IF"); - exit(1); + err(EX_OSERR, "setsockopt IP_MULTICAST_IF"); } } @@ -394,7 +396,8 @@ main(argc, argv) * When pinging the broadcast address, you can get a lot of answers. * Doing something so evil is useful if you are trying to stress the * ethernet, or just want to fill the arp cache to get some stuff for - * /etc/ethers. + * /etc/ethers. But beware: RFC 1122 allows hosts to ignore broadcast + * or multicast pings if they wish. */ hold = 48 * 1024; (void)setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold, @@ -402,7 +405,7 @@ main(argc, argv) if (to->sin_family == AF_INET) (void)printf("PING %s (%s): %d data bytes\n", hostname, - inet_ntoa(*(struct in_addr *)&to->sin_addr.s_addr), + inet_ntoa(to->sin_addr), datalen); else (void)printf("PING %s: %d data bytes\n", hostname, datalen); @@ -418,8 +421,7 @@ main(argc, argv) sigemptyset(&si_sa.sa_mask); si_sa.sa_flags = 0; if (sigaction(SIGINFO, &si_sa, 0) == -1) { - perror("sigaction"); - exit(1); + err(EX_OSERR, "sigsction"); } if (tcgetattr(STDOUT_FILENO, &ts) != -1) { @@ -432,7 +434,7 @@ main(argc, argv) pinger(); if ((options & F_FLOOD) == 0) - catcher(); /* start things going */ + catcher(0); /* start things going */ for (;;) { struct sockaddr_in from; @@ -461,7 +463,7 @@ main(argc, argv) if (npackets && nreceived >= npackets) break; } - finish(); + finish(0); /* NOTREACHED */ } @@ -475,8 +477,8 @@ main(argc, argv) * launched exactly at 1-second intervals). This does not affect the * quality of the delay and loss statistics. */ -void -catcher() +static void +catcher(int sig) { int waittime; @@ -504,7 +506,8 @@ catcher() * of the data portion are used to hold a UNIX "timeval" struct in host * byte-order, to compute the round-trip time. */ -pinger() +static void +pinger(void) { register struct icmp *icp; register int cc; @@ -532,10 +535,12 @@ pinger() sizeof(struct sockaddr)); if (i < 0 || i != cc) { - if (i < 0) - perror("ping: sendto"); - (void)printf("ping: wrote %s %d chars, ret=%d\n", - hostname, cc, i); + if (i < 0) { + warn("sendto"); + } else { + warn("%s: partial write: %d of %d bytes", + hostname, cc, i); + } } if (!(options & F_QUIET) && options & F_FLOOD) (void)write(STDOUT_FILENO, &DOT, 1); @@ -548,6 +553,7 @@ pinger() * which arrive ('tis only fair). This permits multiple copies of this * program to be run without having intermingled output (or statistics!). */ +static void pr_pack(buf, cc, from) char *buf; int cc; @@ -571,9 +577,8 @@ pr_pack(buf, cc, from) hlen = ip->ip_hl << 2; if (cc < hlen + ICMP_MINLEN) { if (options & F_VERBOSE) - (void)fprintf(stderr, - "ping: packet too short (%d bytes) from %s\n", cc, - inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr)); + warn("packet too short (%d bytes) from %s", cc, + inet_ntoa(from->sin_addr)); return; } @@ -664,7 +669,7 @@ pr_pack(buf, cc, from) (oicmp->icmp_type == ICMP_ECHO) && (oicmp->icmp_id == ident))) { (void)printf("%d bytes from %s: ", cc, - pr_addr(from->sin_addr.s_addr)); + pr_addr(from->sin_addr)); pr_icmph(icp); } else return; @@ -689,10 +694,13 @@ pr_pack(buf, cc, from) l = (l<<8) + *++cp; l = (l<<8) + *++cp; l = (l<<8) + *++cp; - if (l == 0) - (void)printf("\t0.0.0.0"); - else - (void)printf("\t%s", pr_addr(ntohl(l))); + if (l == 0) { + printf("\t0.0.0.0"); + } else { + struct in_addr ina; + ina.s_addr = ntohl(l); + printf("\t%s", pr_addr(ina)); + } hlen -= 4; j -= 4; if (j <= IPOPT_MINOFF) @@ -727,10 +735,13 @@ pr_pack(buf, cc, from) l = (l<<8) + *++cp; l = (l<<8) + *++cp; l = (l<<8) + *++cp; - if (l == 0) - (void)printf("\t0.0.0.0"); - else - (void)printf("\t%s", pr_addr(ntohl(l))); + if (l == 0) { + printf("\t0.0.0.0"); + } else { + struct in_addr ina; + ina.s_addr = ntohl(l); + printf("\t%s", pr_addr(ina)); + } hlen -= 4; i -= 4; if (i <= 0) @@ -755,6 +766,7 @@ pr_pack(buf, cc, from) * in_cksum -- * Checksum routine for Internet Protocol family headers (C Version) */ +u_short in_cksum(addr, len) u_short *addr; int len; @@ -792,6 +804,7 @@ in_cksum(addr, len) * Subtract 2 timeval structs: out = out - in. Out is assumed to * be >= in. */ +static void tvsub(out, in) register struct timeval *out, *in; { @@ -807,14 +820,14 @@ tvsub(out, in) * Print out statistics when SIGINFO is received. */ -void +static void status(sig) int sig; { siginfo_p = 1; } -void +static void check_status() { if (siginfo_p) { @@ -833,8 +846,8 @@ check_status() * finish -- * Print out statistics, and give up. */ -void -finish() +static void +finish(int sig) { struct termios ts; @@ -888,6 +901,7 @@ static char *ttab[] = { * pr_icmph -- * Print a descriptive string about an ICMP header. */ +static void pr_icmph(icp) struct icmp *icp; { @@ -1034,6 +1048,7 @@ pr_icmph(icp) * pr_iph -- * Print an IP header with options. */ +static void pr_iph(ip) struct ip *ip; { @@ -1047,7 +1062,7 @@ pr_iph(ip) (void)printf(" %1x %1x %02x %04x %04x", ip->ip_v, ip->ip_hl, ip->ip_tos, ntohs(ip->ip_len), ntohs(ip->ip_id)); - (void)printf(" %1x %04x", (ntohl(ip->ip_off) & 0xe000) >> 13, + (void)printf(" %1lx %04lx", (ntohl(ip->ip_off) & 0xe000) >> 13, ntohl(ip->ip_off) & 0x1fff); (void)printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ntohs(ip->ip_sum)); @@ -1065,20 +1080,19 @@ pr_iph(ip) * Return an ascii host address as a dotted quad and optionally with * a hostname. */ -char * -pr_addr(l) - u_long l; +static char * +pr_addr(ina) + struct in_addr ina; { struct hostent *hp; static char buf[80]; if ((options & F_NUMERIC) || - !(hp = gethostbyaddr((char *)&l, 4, AF_INET))) - (void)snprintf(buf, sizeof(buf), "%s", - inet_ntoa(*(struct in_addr *)&l)); + !(hp = gethostbyaddr((char *)&ina, 4, AF_INET))) + return inet_ntoa(ina); else (void)snprintf(buf, sizeof(buf), "%s (%s)", hp->h_name, - inet_ntoa(*(struct in_addr *)&l)); + inet_ntoa(ina)); return(buf); } @@ -1086,6 +1100,7 @@ pr_addr(l) * pr_retip -- * Dump some info on a returned (via ICMP) IP packet. */ +static void pr_retip(ip) struct ip *ip; { @@ -1104,6 +1119,7 @@ pr_retip(ip) (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); } +static void fill(bp, patp) char *bp, *patp; { @@ -1111,12 +1127,12 @@ fill(bp, patp) int pat[16]; char *cp; - for (cp = patp; *cp; cp++) - if (!isxdigit(*cp)) { - (void)fprintf(stderr, - "ping: patterns must be specified as hex digits.\n"); - exit(1); - } + for (cp = patp; *cp; cp++) { + if (!isxdigit(*cp)) + errx(EX_USAGE, + "patterns must be specified as hex digits"); + + } ii = sscanf(patp, "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], @@ -1137,9 +1153,15 @@ fill(bp, patp) } } -usage() +static void +usage(argv0) + const char *argv0; { - (void)fprintf(stderr, - "usage: ping [-LQRadfnqrv] [-c count] [-i wait] [-I interface]\n\t[-l preload] [-p pattern] [-s packetsize] [-T ttl] host\n"); - exit(1); + warnx("usage:"); + fprintf(stderr, + "\t%s [-QRadfnqrv] [-c count] [-i wait] [-l preload] " + "[-p pattern]\n\t\t[-s packetsize] " + "[host | [-L] [-I iface] [-T ttl] mcast-group]\n", + argv0); + exit(EX_USAGE); }