/* Copyright (C) 1998, 2001, 2002, 2004 Free Software Foundation, Inc. This file is part of GNU Inetutils. GNU Inetutils is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU Inetutils is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Inetutils; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include /*#include -- deliberately not including this */ #ifdef HAVE_NETINET_IP_VAR_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "ping_common.h" #include "ping_impl.h" static char short_options[] = "VLhc:dfi:l:np:qRrs:t:v"; static struct option long_options[] = { /* Help options */ {"version", no_argument, NULL, 'V'}, {"license", no_argument, NULL, 'L'}, {"help", no_argument, NULL, 'h'}, /* Common options */ {"count", required_argument, NULL, 'c'}, {"debug", no_argument, NULL, 'd'}, {"ignore-routing", no_argument, NULL, 'r'}, {"size", required_argument, NULL, 's'}, {"interval",required_argument, NULL, 'i'}, {"numeric", no_argument, NULL, 'n'}, {"verbose", no_argument, NULL, 'v'}, /* Packet types */ {"type", required_argument, NULL, 't'}, {"echo", no_argument, NULL, ICMP_ECHO}, {"timestamp",no_argument, NULL, ICMP_TIMESTAMP}, {"address", no_argument, NULL, ICMP_ADDRESS}, {"router", no_argument, NULL, ICMP_ROUTERDISCOVERY}, /* echo-specific options */ {"flood", no_argument, NULL, 'f'}, {"preload", required_argument, NULL, 'l'}, {"pattern", required_argument, NULL, 'p'}, {"quiet", no_argument, NULL, 'q'}, {"route", no_argument, NULL, 'R'}, {NULL, no_argument, NULL, 0} }; extern int ping_echo (int argc, char **argv); extern int ping_timestamp (int argc, char **argv); extern int ping_address (int argc, char **argv); extern int ping_router (int argc, char **argv); PING *ping; u_char *data_buffer; size_t data_length = PING_DATALEN; unsigned options; unsigned long preload = 0; int (*ping_type) (int argc, char **argv) = ping_echo; static void show_usage (void); static void decode_type (const char *optarg); static int send_echo (PING *ping); int main (int argc, char **argv) { int c; char *p; int one = 1; u_char pattern[16]; int pattern_len = 16; u_char *patptr = NULL; int is_root = getuid () == 0; if ((ping = ping_init (ICMP_ECHO, getpid ())) == NULL) { fprintf (stderr, "can't init ping: %s\n", strerror (errno)); exit (1); } ping_set_sockopt (ping, SO_BROADCAST, (char *)&one, sizeof (one)); /* Reset root privileges */ setuid (getuid ()); /* Parse command line */ while ((c = getopt_long (argc, argv, short_options, long_options, NULL)) != EOF) { switch (c) { case 'V': printf ("ping - %s %s\n", PACKAGE_NAME, PACKAGE_VERSION); printf ("Copyright (C) 2004 Free Software Foundation, Inc.\n"); printf ("%s comes with ABSOLUTELY NO WARRANTY.\n", PACKAGE_NAME); printf ("You may redistribute copies of %s\n", PACKAGE_NAME); printf ("under the terms of the GNU General Public License.\n"); printf ("For more information about these matters, "); printf ("see the files named COPYING.\n"); exit (0); break; case 'L': show_license (); exit (0); case 'h': show_usage (); exit (0); break; case 'c': ping_set_count (ping, ping_cvt_number (optarg, 0, 0)); break; case 'd': ping_set_sockopt (ping, SO_DEBUG, &one, sizeof (one)); break; case 'r': ping_set_sockopt (ping, SO_DONTROUTE, &one, sizeof (one)); break; case 'i': options |= OPT_INTERVAL; ping_set_interval (ping, ping_cvt_number (optarg, 0, 0)); break; case 'p': decode_pattern (optarg, &pattern_len, pattern); patptr = pattern; break; case 's': data_length = ping_cvt_number (optarg, PING_MAX_DATALEN, 1); break; case 'n': options |= OPT_NUMERIC; break; case 'q': options |= OPT_QUIET; break; case 'R': options |= OPT_RROUTE; break; case 'v': options |= OPT_VERBOSE; break; case 'l': if (!is_root) { fprintf (stderr, "ping: option not allowed: --preload\n"); exit (1); } preload = strtoul (optarg, &p, 0); if (*p || preload > INT_MAX) { fprintf (stderr, "ping: invalid preload value (%s)\n", optarg); exit (1); } break; case 'f': if (!is_root) { fprintf (stderr, "ping: option not allowed: --flood\n"); exit (1); } options |= OPT_FLOOD; setbuf (stdout, (char *)NULL); break; case 't': decode_type (optarg); break; case ICMP_ECHO: decode_type ("echo"); break; case ICMP_TIMESTAMP: decode_type ("timestamp"); break; case ICMP_ADDRESS: if (!is_root) { fprintf (stderr, "ping: option not allowed: --address\n"); exit (1); } decode_type ("address"); break; case ICMP_ROUTERDISCOVERY: if (!is_root) { fprintf (stderr, "ping: option not allowed: --router\n"); exit (1); } decode_type ("router"); break; default: fprintf (stderr, "%c: not implemented\n", c); exit (1); } } argc -= optind; argv += optind; if (argc == 0) { show_usage (); exit (0); } init_data_buffer (patptr, pattern_len); return (*ping_type)(argc, argv); } void decode_type (const char *optarg) { if (strcasecmp (optarg, "echo") == 0) ping_type = ping_echo; else if (strcasecmp (optarg, "timestamp") == 0) ping_type = ping_timestamp; else if (strcasecmp (optarg, "address") == 0) ping_type = ping_address; #if 0 else if (strcasecmp (optarg, "router") == 0) ping_type = ping_router; #endif else { fprintf (stderr, "unsupported packet type: %s\n", optarg); exit (1); } } int volatile stop = 0; RETSIGTYPE sig_int (int signal) { stop = 1; } int ping_run (PING *ping, int (*finish)()) { fd_set fdset; int fdmax; struct timeval timeout; struct timeval last, intvl, now; struct timeval *t = NULL; int finishing = 0; int nresp = 0; signal (SIGINT, sig_int); fdmax = ping->ping_fd+1; while (preload--) send_echo (ping); if (options & OPT_FLOOD) { intvl.tv_sec = 0; intvl.tv_usec = 10000; } else { intvl.tv_sec = ping->ping_interval; intvl.tv_usec = 0; } gettimeofday (&last, NULL); send_echo (ping); while (!stop) { int n; FD_ZERO (&fdset); FD_SET (ping->ping_fd, &fdset); gettimeofday (&now, NULL); timeout.tv_sec = last.tv_sec + intvl.tv_sec - now.tv_sec; timeout.tv_usec = last.tv_usec + intvl.tv_usec - now.tv_usec; while (timeout.tv_usec < 0) { timeout.tv_usec += 1000000; timeout.tv_sec--; } while (timeout.tv_usec >= 1000000) { timeout.tv_usec -= 1000000; timeout.tv_sec++; } if (timeout.tv_sec < 0) timeout.tv_sec = timeout.tv_usec = 0; if ((n = select (fdmax, &fdset, NULL, NULL, &timeout)) < 0) { if (errno != EINTR) perror ("select"); continue; } else if (n == 1) { if (ping_recv (ping) == 0) nresp++; if (t == 0) { gettimeofday (&now, NULL); t = &now; } if (ping->ping_count && nresp >= ping->ping_count) break; } else { if (!ping->ping_count || ping->ping_num_xmit < ping->ping_count) { send_echo (ping); if (!(options & OPT_QUIET) && options & OPT_FLOOD) { putchar ('.'); } } else if (finishing) break; else { finishing = 1; intvl.tv_sec = MAXWAIT; } gettimeofday (&last, NULL); } } if (finish) return (*finish)(); return 0; } int send_echo (PING *ping) { int off = 0; if (PING_TIMING (data_length)) { struct timeval tv; gettimeofday (&tv, NULL); ping_set_data (ping, &tv, 0, sizeof (tv)); off += sizeof (tv); } if (data_buffer) ping_set_data (ping, data_buffer, off, data_length > PING_HEADER_LEN ? data_length - PING_HEADER_LEN : data_length); return ping_xmit (ping); } int ping_finish () { fflush (stdout); printf ("--- %s ping statistics ---\n", ping->ping_hostname); printf ("%ld packets transmitted, ", ping->ping_num_xmit); printf ("%ld packets received, ", ping->ping_num_recv); if (ping->ping_num_rept) printf ("+%ld duplicates, ", ping->ping_num_rept); if (ping->ping_num_xmit) { if (ping->ping_num_recv > ping->ping_num_xmit) printf ("-- somebody's printing up packets!"); else printf ("%d%% packet loss", (int) (((ping->ping_num_xmit - ping->ping_num_recv) * 100) / ping->ping_num_xmit)); } printf ("\n"); return 0; } void show_usage (void) { printf ("\ Usage: ping [OPTION]... [ADDRESS]...\n\ \n\ Informational options:\n\ -h, --help display this help and exit\n\ -L, --license display license and exit\n\ -V, --version output version information and exit\n\ Options controlling ICMP request types:\n\ --echo Send ICMP_ECHO requests (default)\n\ * --address Send ICMP_ADDRESS packets\n\ --timestamp Send ICMP_TIMESTAMP packets\n\ * --router Send ICMP_ROUTERDISCOVERY packets\n\ Options valid for all request types:\n\ -c, --count N stop after sending N packets\n\ -d, --debug set the SO_DEBUG option\n\ -i, --interval N wait N seconds between sending each packet\n\ -n, --numeric do not resolve host addresses\n\ -r, --ignore-routing send directly to a host on an attached network\n\ -v, --verbose verbose output\n\ Options valid for --echo requests:\n\ * -f, --flood flood ping \n\ * -l, --preload N send N packets as fast as possible before falling into\n\ normal mode of behavior\n\ -p, --pattern PAT fill ICMP packet with given pattern (hex)\n\ -q, --quiet quiet output\n\ -R, --route record route\n\ -s, --size N set number of data octets to send\n\ \n\ Options marked with an * are available only to super-user\n\ \n\ report bugs to " PACKAGE_BUGREPORT ".\n\ "); }