home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / UNIX / Networking / fping-2.2b1-MIHS / src / fping.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-01-08  |  49.5 KB  |  1,720 lines

  1. /* 
  2.  * fping: fast-ping, file-ping, favorite-ping, funky-ping
  3.  *
  4.  *   Ping a list of target hosts in a round robin fashion.
  5.  *   A better ping overall.
  6.  *
  7.  */
  8.  
  9. /* 
  10.  ***************************************************
  11.  *
  12.  * Standard RCS Header information (see co(1))
  13.  *
  14.  * $Author: schemers $
  15.  *
  16.  * $Date: 1997/01/08 20:29:33 $
  17.  *
  18.  * $Revision: 2.2 $
  19.  *
  20.  * $Locker:  $
  21.  *
  22.  * $Source: /afs/ir/group/networking/src/fping/fping-2.2/src/RCS/fping.c,v $
  23.  *
  24.  * $State: Exp $
  25.  *
  26.  * $Log: fping.c,v $
  27.  * Revision 2.2  1997/01/08 20:29:33  schemers
  28.  * changes for autoconf/automake
  29.  *
  30.  * Revision 2.1  1997/01/08 19:07:18  schemers
  31.  * checked in RL "Bob"'s changes before configure'ing
  32.  *
  33.  * Revision 2.0  1994/10/31  21:26:23  schemers
  34.  * many changes by RL "Bob" Morgan
  35.  *   add timing data collection, loop mode, per-packet output, etc
  36.  *
  37.  * Revision 1.24  1993/12/10  23:11:39  schemers
  38.  * commented out seteuid(getuid()) since it isn't needed
  39.  *
  40.  * Revision 1.23  1993/12/10  18:33:41  schemers
  41.  * Took out the -f option for non-root users. This can be enabled by
  42.  * defining ENABLE_F_OPTION before compiling. There is a call to
  43.  * access before opening the file, but there is a race condition.
  44.  * Reading from stdin is much safer.
  45.  *
  46.  * Revision 1.22  1993/11/16  19:49:24  schemers
  47.  * Took out setuid(getuid()) and used access() system call to
  48.  * check for access to the file specified with "-f".
  49.  *
  50.  * Revision 1.21  1993/07/20  18:08:19  schemers
  51.  * commented out the test to make sure the ping packet came from the
  52.  * same IP address as the one we sent to. This could cause problems on
  53.  * multi-homed hosts.
  54.  *
  55.  * Revision 1.20  1993/02/23  00:16:38  schemers
  56.  * fixed syntax error (should have compiled before checking in...)
  57.  *
  58.  * Revision 1.19  1993/02/23  00:15:15  schemers
  59.  * turned off printing of "is alive" when -a is specified.
  60.  *
  61.  * Revision 1.18  1992/07/28  15:16:44  schemers
  62.  * added a fflush(stdout) call before the summary is sent to stderr, so
  63.  * everything shows up in the right order.
  64.  *
  65.  * Revision 1.17  1992/07/23  03:29:42  schemers
  66.  * fixed declaration of timeval_diff.
  67.  *
  68.  * Revision 1.16  1992/07/22  19:24:37  schemers
  69.  * Modified file reading so it would skip blanks lines or lines starting
  70.  * with a '#'. Now you can do something like:
  71.  *
  72.  * fping -ad < /etc/hosts
  73.  *
  74.  * Revision 1.15  1992/07/21  17:07:18  schemers
  75.  * Put in sanity checks so only root can specify "dangerous" options.
  76.  * Changed usage to show switchs in alphabetical order.
  77.  *
  78.  * Revision 1.14  1992/07/21  16:40:52  schemers
  79.  * Now when sendto returns an error, the host is considered unreachable and
  80.  * and the error message (from errno) is displayed.
  81.  *
  82.  * Revision 1.13  1992/07/17  21:02:17  schemers
  83.  * changed default timeout to 2500 msec (for WANs), and default try
  84.  * to 3. This gives 10 second overall timeout.
  85.  *
  86.  * Added -e option for showing elapsed (round-trip) time on packets
  87.  *
  88.  * Modified -s option to inlude to round-trip stats
  89.  *
  90.  * Added #ifndef DEFAULT_* stuff its easier to change the defaults
  91.  *
  92.  * Reorganized main loop.
  93.  *
  94.  * cleaned up timeval stuff. removed set_timeval and timeval_expired
  95.  * since they aren't needed anymore. Just use timeval_diff.
  96.  *
  97.  * Revision 1.12  1992/07/17  16:38:54  schemers
  98.  * move socket create call so I could do a setuid(getuid()) before the
  99.  * fopen call is made. Once the socket is created root privs aren't needed
  100.  * to send stuff out on it.
  101.  *
  102.  * Revision 1.11  1992/07/17  16:28:38  schemers
  103.  * moved num_timeout counter. It really was for debug purposes and didn't
  104.  * make sense to the general public :-) Now it is the number of timeouts
  105.  * (pings that didn't get received with the time limit).
  106.  *
  107.  * Revision 1.10  1992/07/16  16:24:38  schemers
  108.  * changed usage() to use fprintf(stderr,"...");
  109.  *
  110.  * Revision 1.9  1992/07/16  16:00:04  schemers
  111.  * Added _NO_PROTO stuff for older compilers, and _POSIX_SOURCE
  112.  * for unistd.h, and _POSIX_SOURCE for stdlib.h. Also added
  113.  * check for __cplusplus.
  114.  *
  115.  * Revision 1.8  1992/07/16  05:44:41  schemers
  116.  * changed -a and -u to only show hostname in results. This is
  117.  * for easier parsing. Also added -v flag
  118.  *
  119.  * Revision 1.7  1992/07/14  18:45:23  schemers
  120.  * initialized last_time in add_host function
  121.  *
  122.  * Revision 1.6  1992/07/14  18:32:40  schemers
  123.  * changed select to use FD_ macros
  124.  *
  125.  * Revision 1.5  1992/07/14  17:21:22  schemers
  126.  * standardized exit status codes
  127.  *
  128.  * Revision 1.4  1992/06/26  15:25:35  schemers
  129.  * changed name from rrping to fping
  130.  *
  131.  * Revision 1.3  1992/06/24  15:39:32  schemers
  132.  * added -d option for unreachable systems
  133.  *
  134.  * Revision 1.2  1992/06/23  03:01:23  schemers
  135.  * misc fixes from R.L. "Bob" Morgan
  136.  *
  137.  * Revision 1.1  1992/06/19  18:23:52  schemers
  138.  * Initial revision
  139.  *
  140.  *--------------------------------------------------
  141.  * Copyright (c) 1992, 1994, 1997 Board of Trustees
  142.  *            Leland Stanford Jr. University
  143.  ***************************************************
  144.  */
  145.  
  146. /*
  147.  * Redistribution and use in source and binary forms are permitted
  148.  * provided that the above copyright notice and this paragraph are
  149.  * duplicated in all such forms and that any documentation,
  150.  * advertising materials, and other materials related to such
  151.  * distribution and use acknowledge that the software was developed
  152.  * by Stanford University.  The name of the University may not be used 
  153.  * to endorse or promote products derived from this software without 
  154.  * specific prior written permission.
  155.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  156.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  157.  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  158.  */
  159.  
  160. #ifndef _NO_PROTO
  161. #if !__STDC__ && !defined(__cplusplus) && !defined(FUNCPROTO) \
  162.                                                  && !defined(_POSIX_SOURCE)
  163. #define _NO_PROTO
  164. #endif /* __STDC__ */
  165. #endif /* _NO_PROTO */
  166.  
  167. #ifdef __cplusplus
  168. extern "C" {
  169. #endif
  170.  
  171. #include <config.h>
  172.  
  173.  
  174. #include <stdio.h>
  175. #include <errno.h>
  176. #include <time.h>
  177. #include <signal.h>
  178.  
  179. #ifdef HAVE_UNISTD_H
  180. #include <unistd.h>
  181. #endif
  182.  
  183. #ifdef HAVE_STDLIB_H
  184. #include <stdlib.h>
  185. #endif
  186.  
  187. #include <string.h>
  188.  
  189. #include <sys/types.h>
  190. #include <sys/time.h>
  191. #include <sys/socket.h>
  192.  
  193. #if HAVE_SYS_FILE_H
  194. #include <sys/file.h>
  195. #endif
  196.  
  197. #include <netinet/in_systm.h>
  198. #include <netinet/in.h>
  199.  
  200. /* Linux has bizarre ip.h and ip_icmp.h */
  201. #if defined(__linux__)
  202. #include "linux.h"
  203. #else
  204. #include <netinet/ip.h>
  205. #include <netinet/ip_icmp.h>
  206. #endif
  207.  
  208. #include <arpa/inet.h>
  209. #include <netdb.h>
  210.  
  211. /* RS6000 has sys/select.h */
  212. #ifdef HAVE_SYS_SELECT_H
  213. #include <sys/select.h>
  214. #endif
  215.  
  216. #include "options.h"
  217.  
  218. /* externals */
  219.  
  220. extern char *optarg;
  221. extern int optind,opterr;
  222. extern char *sys_errlist[];
  223. extern int h_errno;
  224.  
  225. #ifdef __cplusplus
  226. }
  227. #endif
  228.  
  229. /* Ping packet defines */
  230.  
  231. /* data added after ICMP header for our nefarious purposes */
  232.  
  233. typedef struct ping_data {
  234.      int                  ping_count;         /* counts up to -c count or 1 */
  235.      struct timeval       ping_ts;            /* time sent */
  236. } PING_DATA;
  237.  
  238. #define MIN_PING_DATA sizeof(PING_DATA)
  239. #define    MAX_IP_PACKET 65536    /* (theoretical) max IP packet size */
  240. #define SIZE_IP_HDR 20
  241. #define SIZE_ICMP_HDR ICMP_MINLEN   /* from ip_icmp.h */
  242. #define MAX_PING_DATA (MAX_IP_PACKET - SIZE_IP_HDR - SIZE_ICMP_HDR)
  243. /* sized so as to be like traditional ping */
  244. #define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA + 44) 
  245.  
  246. /* maxima and minima */
  247. #define MAX_COUNT 10000
  248. #define MIN_INTERVAL 10                  /* in millisec */
  249. #define MIN_PERHOST_INTERVAL 20          /* in millisec */
  250. #define MIN_TIMEOUT 50                   /* in millisec */
  251. #define MAX_RETRY 20
  252.  
  253. /* response time array flags */
  254. #define RESP_WAITING -1
  255. #define RESP_UNUSED -2
  256.  
  257. /* debugging flags */
  258. #ifdef DEBUG
  259. #define DBG_TRACE 1
  260. #define DBG_SENT_TIMES 2
  261. #define DBG_RANDOM_LOSE_FEW 4
  262. #define DBG_RANDOM_LOSE_MANY 8
  263. #define DBG_PRINT_PER_SYSTEM 16
  264. #define DBG_REPORT_ALL_RTTS 32
  265. #endif
  266.  
  267. /* Long names for ICMP packet types */
  268. char *icmp_type_str[19] = {
  269.   "ICMP Echo Reply",        /* 0 */
  270.   "",
  271.   "",
  272.   "ICMP Unreachable",       /* 3 */
  273.   "ICMP Source Quench",     /* 4 */
  274.   "ICMP Redirect",          /* 5 */
  275.   "",
  276.   "",
  277.   "ICMP Echo",              /* 8 */
  278.   "",
  279.   "",
  280.   "ICMP Time Exceeded",     /* 11 */
  281.   "ICMP Paramter Problem",  /* 12 */
  282.   "ICMP Timestamp Request", /* 13 */
  283.   "ICMP Timestamp Reply",   /* 14 */
  284.   "ICMP Information Request", /* 15 */
  285.   "ICMP Information Reply",   /* 16 */
  286.   "ICMP Mask Request",      /* 17 */
  287.   "ICMP Mask Reply"         /* 18 */
  288. };
  289. char *icmp_unreach_str[16] = {
  290.   "ICMP Network Unreachable",    /* 0 */
  291.   "ICMP Host Unreachable",       /* 1 */
  292.   "ICMP Protocol Unreachable",   /* 2 */
  293.   "ICMP Port Unreachable",       /* 3 */
  294.   "ICMP Unreachable (Fragmentation Needed)",      /* 4 */
  295.   "ICMP Unreachable (Source Route Failed)"        /* 5 */
  296.   "ICMP Unreachable (Destination Network Unknown)",                /* 6 */
  297.   "ICMP Unreachable (Destination Host Unknown)",                   /* 7 */
  298.   "ICMP Unreachable (Source Host Isolated)",                       /* 8 */
  299.   "ICMP Unreachable (Communication with Network Prohibited)",      /* 9 */
  300.   "ICMP Unreachable (Communication with Host Prohibited)",         /* 10 */
  301.   "ICMP Unreachable (Network Unreachable For Type Of Service)",    /* 11 */
  302.   "ICMP Unreachable (Host Unreachable For Type Of Service)",       /* 12 */
  303.   "ICMP Unreachable (Communication Administratively Prohibited)",  /* 13 */
  304.   "ICMP Unreachable (Host Precedence Violation)",                  /* 14 */
  305.   "ICMP Unreachable (Precedence cutoff in effect)"                 /* 15 */
  306. };
  307. #define    ICMP_UNREACH_MAXTYPE        15
  308.  
  309. /* entry used to keep track of each host we are pinging */
  310.  
  311. typedef struct host_entry {
  312.      struct host_entry    *prev,*next;        /* doubly linked list */
  313.      int                  i;                  /* index into array */
  314.      char                 *name;              /* name as given by user */
  315.      char                 *host;              /* text description of host */
  316.      char                 *pad;               /* pad to align print names */
  317.      struct sockaddr_in   saddr;              /* internet address */
  318.      int                  timeout;            /* time to wait for response */
  319.      u_char               running;            /* unset when through sending */
  320.      u_char               waiting;            /* waiting for response */
  321.      struct timeval       last_send_time;     /* time of last packet sent */
  322.      int                  num_sent;           /* number of ping packets sent */
  323.      int                  num_recv;           /* number of pings received */
  324.      int                  max_reply;          /* longest response time */
  325.      int                  min_reply;          /* shortest response time */
  326.      int                  total_time;         /* sum of response times */
  327.      int                  num_sent_i;         /* number of ping packets sent */
  328.      int                  num_recv_i;         /* number of pings received */
  329.      int                  max_reply_i;        /* longest response time */
  330.      int                  min_reply_i;        /* shortest response time */
  331.      int                  total_time_i;       /* sum of response times */
  332.      int                  *resp_times;        /* individual response times */
  333. #ifdef DEBUG
  334.      int                  *sent_times;        /* per-sent-ping timestamp */
  335. #endif
  336. } HOST_ENTRY;
  337.  
  338. /* globals */
  339.  
  340. HOST_ENTRY *rrlist=NULL;    /* linked list of hosts be pinged */
  341. HOST_ENTRY **table=NULL;    /* array of pointers to items in the list */
  342. HOST_ENTRY *cursor;
  343.  
  344. char *prog;
  345. int ident;                  /* our pid */
  346. int s;                      /* socket */
  347. u_int debugging = 0;
  348.  
  349. /* times get *100 because all times are calculated in 10 usec units, not ms */
  350. u_int retry = DEFAULT_RETRY;
  351. u_int timeout = DEFAULT_TIMEOUT * 100; 
  352. u_int interval = DEFAULT_INTERVAL * 100;
  353. u_int perhost_interval = DEFAULT_PERHOST_INTERVAL * 100;
  354. float backoff = DEFAULT_BACKOFF_FACTOR;
  355. u_int select_time = DEFAULT_SELECT_TIME * 100;
  356. u_int ping_data_size = DEFAULT_PING_DATA_SIZE;
  357. u_int ping_pkt_size;
  358. u_int count = 1;
  359. u_int trials;
  360. u_int report_interval = 0;
  361.  
  362. /* global stats */
  363. long max_reply=0;
  364. long min_reply=1000000;
  365. int total_replies=0;
  366. double sum_replies=0;
  367. int max_hostname_len = 0;
  368. int num_jobs=0;                   /* number of hosts still to do */
  369. int num_hosts;                    /* total number of hosts */
  370. int num_alive=0,                  /* total number alive */
  371.     num_unreachable=0,            /* total number unreachable */
  372.     num_noaddress=0;              /* total number of addresses not found */
  373. int num_timeout=0,                /* number of times select timed out */
  374.     num_pingsent=0,               /* total pings sent */
  375.     num_pingreceived=0,           /* total pings received */
  376.     num_othericmprcvd=0;          /* total non-echo-reply ICMP received */
  377.  
  378. struct timeval current_time;      /* current time (pseudo) */
  379. struct timeval start_time; 
  380. struct timeval end_time;
  381. struct timeval last_send_time;         /* time last ping was sent */
  382. struct timeval last_report_time;       /* time last report was printed */
  383. struct timezone tz;
  384.  
  385. /* switches */
  386. int verbose_flag,quiet_flag,stats_flag,unreachable_flag,alive_flag;
  387. int elapsed_flag,version_flag,count_flag,loop_flag;
  388. int per_recv_flag,report_all_rtts_flag,name_flag,addr_flag,backoff_flag;
  389. int multif_flag;
  390. #ifdef DEBUG
  391. int randomly_lose_flag,sent_times_flag,trace_flag,print_per_system_flag;
  392. int lose_factor;
  393. #endif
  394.  
  395. char *filename=NULL;               /* file containing hosts to ping */
  396.  
  397. /* forward declarations */
  398.  
  399. #ifdef _NO_PROTO
  400.  
  401. void add_name();
  402. void add_addr();
  403. char *na_cat();
  404. char *cpystr();
  405. void crash_and_burn();
  406. void errno_crash_and_burn();
  407. char *get_host_by_address();
  408. int in_cksum();
  409. void u_sleep();
  410. int recvfrom_wto ();
  411. void remove_job();
  412. void send_ping();
  413. void usage();
  414. int wait_for_reply();
  415. long timeval_diff();
  416. void print_per_system_stats();
  417. void print_per_system_splits();
  418. void print_global_stats();
  419. void finish();
  420. int handle_random_icmp();
  421. char *sprint_tm();
  422.  
  423. #else
  424.  
  425. void add_name(char *name);
  426. void add_addr(char *name, char *host, struct in_addr ipaddr);
  427. char *na_cat(char *name, struct in_addr ipaddr);
  428. char *cpystr(char *string);
  429. void crash_and_burn(char *message);
  430. void errno_crash_and_burn(char *message);
  431. char *get_host_by_address(struct in_addr in);
  432. int in_cksum(u_short *p, int n);
  433. void u_sleep(int u_sec);
  434. int recvfrom_wto (int s, char *buf, int len, struct sockaddr *saddr, int timo);
  435. void remove_job(HOST_ENTRY *h);
  436. void send_ping(int s,HOST_ENTRY *h);
  437. long timeval_diff(struct timeval *a,struct timeval *b);
  438. void usage();
  439. int wait_for_reply();
  440. void print_per_system_stats();
  441. void print_per_system_splits();
  442. void print_global_stats();
  443. void finish();
  444. int handle_random_icmp(struct icmp *p, int psize, struct sockaddr_in *addr);
  445. char *sprint_tm(int t);
  446.  
  447. #endif
  448.  
  449. #ifdef _NO_PROTO
  450. int main(argc,argv)
  451. int argc; char **argv;
  452. #else
  453. int main(int argc, char **argv)
  454. #endif
  455. {
  456.  
  457.   int c, i, n;
  458.   u_int lt, ht;
  459.   int advance;
  460.   struct protoent *proto;
  461.   char *buf;
  462.  
  463.   /* check if we are root */
  464.  
  465.   if (geteuid()) {
  466.       fprintf(stderr,
  467.         "This program can only be run by root, or it must be setuid root.\n");
  468.       exit(3);
  469.   }
  470.  
  471.   if ((proto = getprotobyname("icmp")) == NULL) 
  472.              crash_and_burn("icmp: unknown protocol");
  473.   s = socket(AF_INET, SOCK_RAW, proto->p_proto);
  474.   if (s<0) errno_crash_and_burn("can't create raw socket");
  475.  
  476.   /*seteuid(getuid());*/
  477.  
  478.   prog = argv[0];
  479.   ident = getpid() & 0xFFFF;
  480.  
  481.   verbose_flag = 1;
  482.   backoff_flag = 1;
  483.  
  484.   opterr = 1;
  485.  
  486.   while ((c = getopt(argc, argv, 
  487.              "edhlmnqusaAvz:t:i:p:f:r:c:b:C:Q:B:")) != EOF) {
  488.      switch (c) {
  489.        case 't': if (!(timeout = (u_int)atoi(optarg)*10))
  490.                    usage();                                   break;
  491.        case 'r': if (!(retry = (u_int)atoi(optarg)))
  492.                    usage();                                   break;
  493.        case 'i': if (!(interval = (u_int)atoi(optarg)*10))
  494.                    usage();                                   break;
  495.        case 'p': if (!(perhost_interval = (u_int)atoi(optarg)*10))
  496.                    usage();                                   break;
  497.        case 'c': if (!(count = (u_int)atoi(optarg)))
  498.                    usage();
  499.                      count_flag = 1;                              break;
  500.        case 'C': if (!(count = (u_int)atoi(optarg)))
  501.                    usage();
  502.                      count_flag = 1;
  503.                      report_all_rtts_flag = 1;                    break;
  504.        case 'b': if(!(ping_data_size = (u_int)atoi(optarg)))
  505.                    usage();                                   break;
  506.        case 'h': usage();                                     break;
  507.        case 'q': verbose_flag = 0; quiet_flag = 1;            break;
  508.        case 'Q': verbose_flag = 0; quiet_flag = 1;
  509.                      if (!(report_interval = (u_int)atoi(optarg) *100000))
  510.                usage();                                   break;
  511.        case 'e': elapsed_flag = 1;                              break;
  512.        case 'd': 
  513.        case 'm': multif_flag = 1;                             break;
  514.        case 'n': name_flag = 1;                               break;
  515.        case 'A': addr_flag = 1;                               break;
  516.        case 'B': if (!(backoff = atof(optarg)))
  517.                        usage();                                   break;
  518.        case 's': stats_flag = 1;                              break;
  519.        case 'l': loop_flag = 1; backoff_flag = 0;             break;
  520.        case 'u': unreachable_flag = 1;                        break;
  521.        case 'a': alive_flag = 1;                              break;
  522. #ifdef DEBUG
  523.        case 'z': if (!(debugging = (u_int)atoi(optarg)))
  524.                    usage();                                   break;
  525. #endif
  526.            case 'v':
  527.                      printf("%s: Version %s $Date: 1997/01/08 20:29:33 $\n",argv[0], VERSION);
  528.                      printf("%s: comments to fping@networking.Stanford.EDU\n",argv[0]);
  529.                      exit(0);
  530.        case 'f': 
  531. #ifdef ENABLE_F_OPTION
  532.              filename= optarg;                            break;
  533. #else
  534.                      if (getuid()) {
  535.           printf("%s: this option can only be used by root.\n",argv[0]);
  536.           printf("%s: fping will read from stdin by default.\n",argv[0]);
  537.                        exit(3);
  538.              } else {
  539.                  filename= optarg; 
  540.                         break;
  541.              }
  542. #endif
  543.            default : usage();                                     break;
  544.        }
  545.   }
  546.  
  547.   /* muck about based on various option settings */
  548.  
  549.   if (unreachable_flag && alive_flag) {
  550.     fprintf(stderr,"%s: specify only one of a, u\n",argv[0]);
  551.     usage();
  552.   }
  553.  
  554.   if (count_flag && loop_flag) {
  555.     fprintf(stderr, "%s: specify only one of c, l\n", argv[0]);
  556.     usage();
  557.   }
  558.  
  559.   if ( (interval < MIN_INTERVAL * 10 || 
  560.     perhost_interval < MIN_PERHOST_INTERVAL * 10 || 
  561.     retry > MAX_RETRY || 
  562.     timeout < MIN_TIMEOUT * 10) 
  563.       && getuid() ) {
  564.     fprintf(stderr,"%s: these options are too risky for mere mortals.\n",prog);
  565.     fprintf(stderr,
  566.         "%s: You need i >= %u, p >= %u, r < %u, and t >= %u\n",
  567.         prog, MIN_INTERVAL, MIN_PERHOST_INTERVAL, 
  568.         MAX_RETRY, MIN_TIMEOUT);
  569.     usage();
  570.   }
  571.  
  572.   if ((ping_data_size > MAX_PING_DATA) ||
  573.       (ping_data_size < MIN_PING_DATA)) {
  574.     fprintf(stderr, 
  575.         "%s: data size %u not valid, must be between %u and %u\n", 
  576.         prog, ping_data_size, MIN_PING_DATA, MAX_PING_DATA);
  577.     usage();
  578.   }
  579.  
  580.   if ((backoff > MAX_BACKOFF_FACTOR) ||
  581.       (backoff < MIN_BACKOFF_FACTOR)) {
  582.     fprintf(stderr,
  583.         "%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n",
  584.         prog, backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR);
  585.     usage();
  586.   }
  587.  
  588.   if (count > MAX_COUNT) {
  589.     fprintf(stderr, 
  590.         "%s: count %u not valid, must be less than %u\n", 
  591.         prog, count, MAX_COUNT);
  592.     usage();
  593.   }
  594.  
  595.   if (alive_flag || unreachable_flag) verbose_flag=0;
  596.   if (count_flag) {
  597.     if (verbose_flag)
  598.       per_recv_flag = 1;
  599.     alive_flag = unreachable_flag = verbose_flag = 0;
  600.   }
  601.   if (loop_flag) {
  602.     if (!report_interval)
  603.       per_recv_flag = 1;
  604.     alive_flag = unreachable_flag = verbose_flag = 0;
  605.   }
  606.  
  607.   trials = (count > retry+1) ? count : retry+1;
  608.  
  609. #ifdef DEBUG
  610.   if (debugging & DBG_TRACE)
  611.     trace_flag = 1;
  612.   if ((debugging & DBG_SENT_TIMES) && !loop_flag)
  613.     sent_times_flag = 1;
  614.   if (debugging & DBG_RANDOM_LOSE_FEW) {
  615.     randomly_lose_flag = 1;
  616.     lose_factor = 1;     /* ie, 1/4 */
  617.   }
  618.   if (debugging & DBG_RANDOM_LOSE_MANY) {
  619.     randomly_lose_flag = 1;
  620.     lose_factor = 5;     /* ie, 3/4 */
  621.   }
  622.   if (debugging & DBG_PRINT_PER_SYSTEM)
  623.     print_per_system_flag = 1;
  624.   if ((debugging & DBG_REPORT_ALL_RTTS) && !loop_flag)
  625.     report_all_rtts_flag = 1;
  626.  
  627.   if (trace_flag) {
  628.     fprintf(stderr, "%s:\n  count: %u, retry: %u, interval: %u\n",
  629.         prog, count, retry, interval/10);
  630.     fprintf(stderr, "  perhost_interval: %u, timeout: %u\n",
  631.         perhost_interval/10, timeout/10);
  632.     fprintf(stderr, "  ping_data_size = %u, trials = %u\n",
  633.         ping_data_size, trials);
  634.     if (verbose_flag) fprintf(stderr, "  verbose_flag set\n");
  635.     if (multif_flag) fprintf(stderr, "  multif_flag set\n");
  636.     if (name_flag) fprintf(stderr, "  name_flag set\n");
  637.     if (addr_flag) fprintf(stderr, "  addr_flag set\n");
  638.     if (stats_flag) fprintf(stderr, "  stats_flag set\n");
  639.     if (unreachable_flag) fprintf(stderr, "  unreachable_flag set\n");
  640.     if (alive_flag) fprintf(stderr, "  alive_flag set\n");
  641.     if (elapsed_flag) fprintf(stderr, "  elapsed_flag set\n");
  642.     if (version_flag) fprintf(stderr, "  version_flag set\n");
  643.     if (count_flag) fprintf(stderr, "  count_flag set\n");
  644.     if (loop_flag) fprintf(stderr, "  loop_flag set\n");
  645.     if (backoff_flag) fprintf(stderr, "  backoff_flag set\n");
  646.     if (per_recv_flag) fprintf(stderr, "  per_recv_flag set\n");
  647.     if (report_all_rtts_flag) fprintf(stderr, "  report_all_rtts_flag set\n");
  648.     if (randomly_lose_flag) fprintf(stderr, "  randomly_lose_flag set\n");
  649.     if (sent_times_flag) fprintf(stderr, "  sent_times_flag set\n");
  650.     if (print_per_system_flag) fprintf(stderr, "  print_per_system_flag set\n");
  651.   }
  652. #endif
  653.  
  654.   /* handle host names supplied on command line or in a file */
  655.  
  656.   argv = &argv[optind];
  657.   if (*argv && filename)   { usage(); }
  658.   if (!*argv && !filename) { filename = "-"; }
  659.  
  660.   if (*argv) while (*argv) {
  661.              add_name(*argv);
  662.              ++argv;
  663.   } else if (filename) {
  664.          FILE *ping_file;
  665.          char line[132];
  666.          char host[132],*p;
  667.          if (strcmp(filename,"-")==0) {
  668.              ping_file=fdopen(0,"r");
  669.          } else {
  670.              ping_file=fopen(filename,"r");
  671.          }
  672.          if (!ping_file) errno_crash_and_burn("fopen");
  673.          while(fgets(line,132,ping_file)) {
  674.            if (sscanf(line,"%s",host) != 1)
  675.          continue;
  676.               if ((!*host) || (host[0]=='#'))  /* magic to avoid comments */
  677.                 continue;
  678.        p = cpystr(host);
  679.            add_name(p);
  680.          }
  681.          fclose(ping_file);
  682.   } else usage();
  683.  
  684.   if (!num_hosts) exit(2);
  685.  
  686.   /* allocate array to hold outstanding ping requests */
  687.  
  688.   table = (HOST_ENTRY **) malloc(sizeof(HOST_ENTRY *)*num_hosts);
  689.   if (!table) crash_and_burn("Can't malloc array of hosts");
  690.  
  691.   cursor=rrlist;
  692.  
  693.   for( num_jobs=0; num_jobs < num_hosts; num_jobs++ ) {
  694.       table[num_jobs]=cursor;
  695.       cursor->i = num_jobs;
  696.       /* as long as we're here, put this in so names print out nicely */
  697.       if (count_flag || loop_flag) {
  698.     n = max_hostname_len - strlen(cursor->host);
  699.     buf = (char *) malloc(n + 1);
  700.     if (!buf) crash_and_burn("can't malloc host pad");
  701.     for (i = 0; i < n; i++)
  702.       buf[i] = ' ';
  703.     buf[n] = '\0';
  704.     cursor->pad = buf;
  705.       }
  706.       cursor=cursor->next;
  707.   }
  708.  
  709.   ping_pkt_size = ping_data_size + SIZE_ICMP_HDR;
  710.   signal(SIGINT, finish);
  711.   gettimeofday(&start_time,&tz);
  712.   current_time = start_time;
  713.   if (report_interval)
  714.     last_report_time = start_time;
  715.   last_send_time.tv_sec = current_time.tv_sec - 10000;
  716. #ifdef DEBUG
  717.   if (randomly_lose_flag) 
  718.     srandom(start_time.tv_usec);
  719. #endif
  720.   cursor=rrlist;
  721.   advance = 0;
  722.  
  723.   /* main loop */
  724.   while (num_jobs) {
  725.     if (num_pingsent)
  726.       while(wait_for_reply()) {  /* call wfr until we timeout */
  727.     /* wait! */
  728.       };
  729.     if (cursor && advance) cursor = cursor->next;
  730.     gettimeofday(¤t_time,&tz);
  731.     lt = timeval_diff(¤t_time, &last_send_time);
  732.     ht = timeval_diff(¤t_time, &cursor->last_send_time);
  733.     if (report_interval && 
  734.     (loop_flag || count_flag) &&
  735.     (timeval_diff(¤t_time, &last_report_time)
  736.     > report_interval)) {
  737.       print_per_system_splits();
  738.       gettimeofday(¤t_time,&tz);
  739.       lt = timeval_diff(¤t_time, &last_send_time);
  740.       ht = timeval_diff(¤t_time, &cursor->last_send_time);
  741.       last_report_time = current_time;
  742.     }
  743.       
  744.     advance = 1;
  745. #ifdef DEBUG
  746.     if (trace_flag)
  747.       printf(
  748.  "main loop:\n  [%s, wait/run/sent/recv/timeout = %u/%u/%u/%u/%u], jobs/lt/ht = %u/%u/%u\n",
  749.          cursor->host, cursor->waiting, cursor->running, cursor->num_sent, 
  750.          cursor->num_recv, cursor->timeout, num_jobs, lt, ht);
  751. #endif
  752.     /* if it's OK to send while counting or looping or starting */
  753.     if ((lt > interval) && (ht > perhost_interval)) {
  754.       /* send if starting or looping */
  755.       if ((cursor->num_sent == 0) || loop_flag) {
  756.     send_ping(s, cursor);
  757.     continue;
  758.       }
  759.       /* send if counting and count not exceeded */
  760.       if (count_flag) {
  761.     if (cursor->num_sent < count) {
  762.       send_ping(s,cursor);
  763.       continue;
  764.     }
  765.       }
  766.     }
  767.     /* is-it-alive mode, and timeout exceeded while waiting for a reply */
  768.     /*   and we haven't exceeded our retries                            */
  769.     if ((lt > interval) && !count_flag && !loop_flag &&
  770.     !cursor->num_recv &&
  771.     (ht > cursor->timeout) &&
  772.     (cursor->waiting < retry+1)) {
  773. #ifdef DEBUG
  774.       if (trace_flag) 
  775.     printf("main loop: timeout for %s\n", cursor->host);
  776. #endif
  777.       num_timeout++;
  778.       /* try again */
  779.       if (backoff_flag)
  780.     cursor->timeout *= backoff;
  781.       send_ping(s,cursor);
  782.       continue;
  783.     }
  784.     /* didn't send, can we remove? */
  785. #ifdef DEBUG
  786.     if (trace_flag)
  787.       printf("main loop: didn't send to %s\n", cursor->host);
  788. #endif
  789.     /* never remove if looping */
  790.     if (loop_flag)
  791.       continue;
  792.     /* remove if counting and count exceeded */
  793.     /* but allow time for the last one to come in */
  794.     if (count_flag) {
  795.       if ((cursor->num_sent >= count) &&
  796.       (ht > cursor->timeout)) {
  797.     remove_job(cursor);
  798.     continue;
  799.       }
  800.     } else {
  801.       /* normal mode, and we got one */
  802.       if (cursor->num_recv) {
  803.     remove_job(cursor);
  804.     continue;
  805.       }
  806.       /* normal mode, and timeout exceeded while waiting for a reply */
  807.       /* and we've run out of retries, so node is unreachable */
  808.       if ((ht > cursor->timeout) &&
  809.       (cursor->waiting >= retry+1)) {
  810. #ifdef DEBUG
  811.     if (trace_flag) 
  812.       printf("main loop: timeout for %s\n", cursor->host);
  813. #endif
  814.     num_timeout++;
  815.     remove_job(cursor);
  816.     continue;
  817.       }
  818.     }
  819.     /* could send to this host, so keep considering it */
  820.     if (ht > interval)
  821.       advance = 0;
  822.   }
  823.   finish();
  824. }
  825.  
  826. #ifdef _NO_PROTO
  827. void finish()
  828. #else
  829. void finish()
  830. #endif
  831. {
  832.   int i;
  833.   HOST_ENTRY *h;
  834.  
  835.   gettimeofday(&end_time,&tz);
  836.  
  837.   /* tot up unreachables */
  838.   for (i = 0; i < num_hosts; i++) {
  839.     h = table[i];
  840.     if (!h->num_recv) {
  841.       num_unreachable++;
  842.       if(verbose_flag || unreachable_flag) {
  843.     printf("%s", h->host);
  844.     if (verbose_flag) 
  845.       printf(" is unreachable");
  846.     printf("\n");
  847.       }
  848.     }
  849.   }
  850.  
  851.   if (count_flag || loop_flag) {
  852.     print_per_system_stats();
  853.   }
  854. #ifdef DEBUG
  855.   else if (print_per_system_flag)
  856.     print_per_system_stats();
  857. #endif
  858.  
  859.   if (stats_flag)
  860.     print_global_stats();
  861.  
  862.   if (num_noaddress) exit(2);
  863.   else if (num_alive != num_hosts) exit(1); 
  864.   
  865.   exit(0);
  866.  
  867. }
  868.  
  869. #ifdef _NO_PROTO
  870. void print_per_system_stats ()
  871. #else
  872. void print_per_system_stats ()
  873. #endif
  874. {
  875.   int i, j, k, avg;
  876.   HOST_ENTRY *h;
  877.   char *buf;
  878.   int bufsize;
  879.   int resp;
  880.  
  881.   bufsize = max_hostname_len + 1;
  882.   buf = (char *) malloc(bufsize);
  883.   if (!buf) crash_and_burn("can't malloc print buf");
  884.   memset(buf, 0, bufsize);
  885.  
  886.   fflush(stdout);
  887.   if (verbose_flag || per_recv_flag) fprintf(stderr,"\n");
  888.   for (i = 0; i < num_hosts; i++) {
  889.     h = table[i];
  890.     fprintf(stderr, "%s%s :", h->host, h->pad);
  891.     if (report_all_rtts_flag) {
  892.       for (j = 0; j < h->num_sent; j++)
  893.     if ((resp = h->resp_times[j]) >= 0)
  894.       fprintf(stderr, " %d.%d", resp/10, resp%10);
  895.     else fprintf(stderr, " -");
  896.       fprintf(stderr, "\n");
  897.     } else {
  898.       if (h->num_recv <= h->num_sent) {
  899.     fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
  900.         h->num_sent, h->num_recv,
  901.         ((h->num_sent - h->num_recv) * 100) / h->num_sent);
  902.       } else {
  903.     fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
  904.         h->num_sent, h->num_recv,
  905.         ((h->num_recv * 100) / h->num_sent));
  906.       }
  907.       if (h->num_recv) {
  908.     avg = h->total_time / h->num_recv;
  909.     fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply));
  910.     fprintf(stderr, "/%s", sprint_tm(avg));
  911.     fprintf(stderr, "/%s", sprint_tm(h->max_reply));
  912.       }
  913.       fprintf(stderr, "\n");
  914.     }
  915. #ifdef DEBUG
  916.     if (sent_times_flag) {
  917.       for (j = 0; j < h->num_sent; j++)
  918.     if ((resp = h->sent_times[j]) >= 0)
  919.       fprintf(stderr, " %s", sprint_tm(resp));
  920.     else fprintf(stderr, " -");
  921.       fprintf(stderr, "\n");
  922.     }
  923. #endif
  924.   }
  925.   free(buf);
  926. }
  927.  
  928.  
  929. #ifdef _NO_PROTO
  930. void print_per_system_splits ()
  931. #else
  932. void print_per_system_splits ()
  933. #endif
  934. {
  935.   int i, j, k, avg;
  936.   HOST_ENTRY *h;
  937.   char *buf;
  938.   int bufsize;
  939.   int resp;
  940.   struct tm *curr_tm;
  941.  
  942.   bufsize = max_hostname_len + 1;
  943.   buf = (char *) malloc(bufsize);
  944.   if (!buf) crash_and_burn("can't malloc print buf");
  945.   memset(buf, 0, bufsize);
  946.  
  947.   fflush(stdout);
  948.   if (verbose_flag || per_recv_flag) fprintf(stderr,"\n");
  949.   curr_tm = localtime((time_t *)¤t_time.tv_sec);
  950.   fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
  951.       curr_tm->tm_min, curr_tm->tm_sec);
  952.   for (i = 0; i < num_hosts; i++) {
  953.     h = table[i];
  954.     fprintf(stderr, "%s%s :", h->host, h->pad);
  955.     if (h->num_recv_i <= h->num_sent_i) {
  956.       fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%",
  957.           h->num_sent_i, h->num_recv_i,
  958.           ((h->num_sent_i - h->num_recv_i) * 100) / h->num_sent_i);
  959.     } else {
  960.       fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%",
  961.           h->num_sent_i, h->num_recv_i,
  962.           ((h->num_recv_i * 100) / h->num_sent_i));
  963.     }
  964.     if (h->num_recv_i) {
  965.       avg = h->total_time_i / h->num_recv_i;
  966.       fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i));
  967.       fprintf(stderr, "/%s", sprint_tm(avg));
  968.       fprintf(stderr, "/%s", sprint_tm(h->max_reply_i));
  969.     }
  970.     fprintf(stderr, "\n");
  971.     h->num_sent_i = h->num_recv_i = h->max_reply_i = h->min_reply_i = 
  972.       h->total_time_i = 0;
  973.   }
  974.   free(buf);
  975. }
  976.  
  977. #ifdef _NO_PROTO
  978. void print_global_stats()
  979. #else
  980. void print_global_stats()
  981. #endif
  982. {
  983.   fflush(stdout);
  984.   fprintf(stderr,"\n");
  985.   fprintf(stderr," %7d targets\n",num_hosts);
  986.   fprintf(stderr," %7d alive\n",num_alive);
  987.   fprintf(stderr," %7d unreachable\n",num_unreachable);
  988.   fprintf(stderr," %7d unknown addresses\n",num_noaddress);
  989.   fprintf(stderr,"\n");
  990.   fprintf(stderr," %7d timeouts (waiting for response)\n",num_timeout);
  991.   fprintf(stderr," %7d ICMP Echos sent\n",num_pingsent);
  992.   fprintf(stderr," %7d ICMP Echo Replies received\n",num_pingreceived);
  993.   fprintf(stderr," %7d other ICMP received\n",num_othericmprcvd);
  994.   fprintf(stderr,"\n");
  995.  
  996.   if (total_replies==0) {
  997.     min_reply=0; max_reply=0; total_replies=1; sum_replies=0;
  998.   }
  999.  
  1000.   fprintf(stderr," %s ms (min round trip time)\n", sprint_tm(min_reply));
  1001.   fprintf(stderr," %s ms (avg round trip time)\n",
  1002.       sprint_tm((int)(sum_replies/total_replies)));
  1003.   fprintf(stderr," %s ms (max round trip time)\n", sprint_tm(max_reply));
  1004.   fprintf(stderr," %12.3f sec (elapsed real time)\n",
  1005.       timeval_diff( &end_time,&start_time)/100000.0);
  1006.   fprintf(stderr,"\n");
  1007. }
  1008.  
  1009. /*
  1010.  * 
  1011.  * Compose and transmit an ICMP_ECHO REQUEST packet.  The IP packet
  1012.  * will be added on by the kernel.  The ID field is our UNIX process ID,
  1013.  * and the sequence number is an index into an array of outstanding
  1014.  * ping requests. The sequence number will later be used to quickly
  1015.  * figure out who the ping reply came from.
  1016.  *
  1017.  */
  1018.  
  1019. #ifdef _NO_PROTO
  1020. void send_ping(s,h)
  1021. int s; HOST_ENTRY *h;
  1022. #else
  1023. void send_ping(int s,HOST_ENTRY *h)
  1024. #endif
  1025. {
  1026.   char *buffer;
  1027.   struct icmp *icp;
  1028.   PING_DATA *pdp;
  1029.   int n;
  1030.  
  1031.   buffer = (char *) malloc ((size_t)ping_pkt_size);
  1032.   if (!buffer) crash_and_burn("can't malloc ping packet");
  1033.   memset(buffer, 0, ping_pkt_size * sizeof(char));
  1034.   icp = (struct icmp *) buffer;
  1035.  
  1036.   gettimeofday(&h->last_send_time,&tz);
  1037.   icp->icmp_type = ICMP_ECHO;
  1038.   icp->icmp_code = 0;
  1039.   icp->icmp_cksum = 0;
  1040.   icp->icmp_seq = h->i;
  1041.   icp->icmp_id = ident;
  1042.  
  1043.   pdp = (PING_DATA *) (buffer + SIZE_ICMP_HDR);
  1044.   pdp->ping_ts = h->last_send_time;
  1045.   pdp->ping_count = h->num_sent;
  1046.  
  1047.   icp->icmp_cksum = in_cksum( (u_short *)icp, ping_pkt_size );
  1048.  
  1049. #ifdef DEBUG
  1050.   if (trace_flag)
  1051.     printf("sending [%d] to %s\n", h->num_sent, h->host);
  1052. #endif
  1053.   n = sendto( s, buffer, ping_pkt_size, 0, (struct sockaddr *)&h->saddr, 
  1054.                                                sizeof(struct sockaddr_in) );
  1055.   if( n < 0 || n != ping_pkt_size ) {
  1056.       if (verbose_flag || unreachable_flag) {
  1057.     printf("%s", h->host);
  1058.     if (verbose_flag) printf(" error while sending ping: %s\n",
  1059.                  sys_errlist[errno]);
  1060.     printf("\n");
  1061.       }
  1062.       num_unreachable++;
  1063.       remove_job(h); 
  1064.   } else {
  1065.        /* mark this trial as outstanding */
  1066.     if (!loop_flag)
  1067.       h->resp_times[h->num_sent] = RESP_WAITING;
  1068. #ifdef DEBUG
  1069.     if (sent_times_flag)
  1070.       h->sent_times[h->num_sent] = 
  1071.     timeval_diff(&h->last_send_time, &start_time);
  1072. #endif
  1073.     h->num_sent++; h->num_sent_i++;
  1074.     h->waiting++;
  1075.     num_pingsent++;
  1076.     last_send_time = h->last_send_time;
  1077.   }
  1078.   free(buffer);
  1079. }
  1080.  
  1081. #ifdef _NO_PROTO
  1082. int wait_for_reply()
  1083. #else
  1084. int wait_for_reply()
  1085. #endif
  1086. {
  1087.   int result;
  1088.   static char buffer[4096];
  1089.   struct sockaddr_in response_addr;
  1090.   struct ip *ip;
  1091.   int hlen;
  1092.   struct icmp *icp;
  1093.   int n, avg;
  1094.   HOST_ENTRY *h;
  1095.   PING_DATA *pdp;
  1096.   long this_reply;
  1097.   int this_count;
  1098.   struct timeval sent_time;
  1099.  
  1100.   result=recvfrom_wto(s,buffer,4096,
  1101.                      (struct sockaddr *)&response_addr,select_time);
  1102.   if (result<0) { return 0; } /* timeout */
  1103.   
  1104. #ifdef DEBUG
  1105.   if (randomly_lose_flag)
  1106.     if ((random() & 0x07) <= lose_factor)
  1107.       return 0;
  1108. #endif
  1109.   ip = (struct ip *) buffer;
  1110. #if defined(__alpha__) && __STDC__
  1111.   /* The alpha headers are decidedly broken.
  1112.    * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
  1113.    * ip_v.  So, to get ip_hl, we mask off the bottom four bits.
  1114.    */
  1115.   hlen = (ip->ip_vhl & 0x0F) << 2;
  1116. #else
  1117.   hlen = ip->ip_hl << 2;
  1118. #endif
  1119.  
  1120.   if (result < hlen+ICMP_MINLEN) { 
  1121.     if (verbose_flag)
  1122.       printf("received packet too short for ICMP (%d bytes from %s)\n",
  1123.          result, inet_ntoa(response_addr.sin_addr));
  1124.     return(1); /* too short */ 
  1125.   }
  1126.  
  1127.   icp = (struct icmp *)(buffer + hlen);
  1128.   if (icp->icmp_type != ICMP_ECHOREPLY) {
  1129.     /* handle some problem */
  1130.     if (handle_random_icmp(icp, result, &response_addr))
  1131.       num_othericmprcvd++;
  1132.     return 1;
  1133.   }
  1134.  
  1135.   if (icp->icmp_id   != ident)
  1136.        return 1; /* packet received, but not the one we are looking for! */
  1137.  
  1138.   num_pingreceived++;
  1139.  
  1140.   if (icp->icmp_seq  >= (n_short)num_hosts)
  1141.     return(1); /* packet received, don't worry about it anymore */
  1142.  
  1143.   n=icp->icmp_seq;
  1144.   h=table[n];
  1145.  
  1146.   /* received ping is cool, so process it */
  1147.   gettimeofday(¤t_time,&tz);
  1148.   h->waiting = 0;
  1149.   h->timeout = timeout;
  1150.   h->num_recv++;  h->num_recv_i++;
  1151.  
  1152.   pdp = (PING_DATA *)icp->icmp_data;
  1153.   sent_time = pdp->ping_ts;
  1154.   this_count = pdp->ping_count;
  1155. #ifdef DEBUG
  1156.   if (trace_flag) 
  1157.     printf("received [%d] from %s\n", this_count, h->host);
  1158. #endif
  1159.   this_reply = timeval_diff(¤t_time,&sent_time);
  1160.   if (this_reply > max_reply) max_reply=this_reply;
  1161.   if (this_reply < min_reply) min_reply=this_reply;
  1162.   if (this_reply > h->max_reply) h->max_reply=this_reply;
  1163.   if (this_reply < h->min_reply) h->min_reply=this_reply;
  1164.   if (this_reply > h->max_reply_i) h->max_reply_i=this_reply;
  1165.   if (this_reply < h->min_reply_i) h->min_reply_i=this_reply;
  1166.   sum_replies += this_reply;
  1167.   h->total_time += this_reply;
  1168.   h->total_time_i += this_reply;
  1169.   total_replies++;
  1170.   /* note reply time in array, probably */
  1171.   if (!loop_flag) {
  1172.     if ((this_count >= 0) && (this_count < trials)) {
  1173.       if (h->resp_times[this_count] != RESP_WAITING) {
  1174.     if (!per_recv_flag) {
  1175.       fprintf(stderr, 
  1176.           "%s : duplicate for [%d], %d bytes, %s ms", 
  1177.           h->host, this_count, result, 
  1178.           sprint_tm(this_reply));
  1179.       if (response_addr.sin_addr.s_addr != h->saddr.sin_addr.s_addr)
  1180.         fprintf(stderr, " [<- %s]", inet_ntoa(response_addr.sin_addr));
  1181.       fprintf(stderr, "\n");
  1182.     }
  1183.       } else {
  1184.     h->resp_times[this_count] = this_reply;
  1185.       }
  1186.     } else {
  1187.       /* count is out of bounds?? */
  1188.       fprintf(stderr, 
  1189.           "%s : duplicate for [%d], %d bytes, %s ms\n", 
  1190.           h->host, this_count, result, 
  1191.           sprint_tm(this_reply));
  1192.     }
  1193.   }
  1194.  
  1195.   if (h->num_recv == 1) {
  1196.     num_alive++;
  1197.     if(verbose_flag||alive_flag) {
  1198.       printf("%s",h->host);
  1199.       if (verbose_flag) printf(" is alive");
  1200.       if (elapsed_flag) printf(" (%s ms)", sprint_tm(this_reply));
  1201.       if (response_addr.sin_addr.s_addr != h->saddr.sin_addr.s_addr)
  1202.     printf(" [<- %s]", inet_ntoa(response_addr.sin_addr));
  1203.       printf("\n");
  1204.     }
  1205.   }
  1206.  
  1207.   if (per_recv_flag) {
  1208.     avg = h->total_time / h->num_recv;
  1209.     printf("%s%s : [%d], %d bytes, %s ms", 
  1210.        h->host, h->pad,
  1211.        this_count,
  1212.        result,
  1213.        sprint_tm(this_reply));
  1214.     printf(" (%s avg, ", sprint_tm(avg));
  1215.     if (h->num_recv <= h->num_sent) {
  1216.       printf("%d%% loss)",        
  1217.        ((h->num_sent - h->num_recv) * 100) 
  1218.        / h->num_sent);
  1219.     } else {
  1220.       printf("%d%% return)",        
  1221.        (h->num_recv * 100) 
  1222.        / h->num_sent);
  1223.     }
  1224.     if (response_addr.sin_addr.s_addr != h->saddr.sin_addr.s_addr)
  1225.       printf(" [<- %s]", inet_ntoa(response_addr.sin_addr));
  1226.     printf("\n");
  1227.   }
  1228.   return num_jobs;
  1229. }
  1230.  
  1231. #ifdef _NO_PROTO
  1232. int handle_random_icmp(p, psize, addr)
  1233.      struct icmp *p;
  1234.      int psize;
  1235.      struct sockaddr_in *addr;
  1236. #else
  1237. int handle_random_icmp(struct icmp *p, int psize, struct sockaddr_in *addr)
  1238. #endif
  1239. {
  1240.   struct icmp *sent_icmp;
  1241.   struct ip *sent_ip;
  1242.   u_char *c;
  1243.   HOST_ENTRY *h;
  1244.  
  1245.   c = (u_char *)p;
  1246.   switch (p->icmp_type) {
  1247.   case ICMP_UNREACH:
  1248.     sent_icmp = (struct icmp *) (c + 28);
  1249.     if ((sent_icmp->icmp_type == ICMP_ECHO) &&
  1250.     (sent_icmp->icmp_id == ident) &&
  1251.     (sent_icmp->icmp_seq < (n_short)num_hosts)) {
  1252.       /* this is a response to a ping we sent */
  1253.       h = table[sent_icmp->icmp_seq];
  1254.       if (p->icmp_code > ICMP_UNREACH_MAXTYPE) {
  1255.     fprintf(stderr, 
  1256.         "ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s", 
  1257.         inet_ntoa(addr->sin_addr), h->host); }
  1258.       else {
  1259.     fprintf(stderr, "%s from %s for ICMP Echo sent to %s", 
  1260.         icmp_unreach_str[p->icmp_code],
  1261.         inet_ntoa(addr->sin_addr), h->host);
  1262.       }
  1263.       if (inet_addr(h->host) == -1)
  1264.     fprintf(stderr, " (%s)", inet_ntoa(h->saddr.sin_addr));
  1265.       fprintf(stderr, "\n");
  1266.     }
  1267.     return 1;
  1268.   case ICMP_SOURCEQUENCH:
  1269.   case ICMP_REDIRECT:
  1270.   case ICMP_TIMXCEED:
  1271.   case ICMP_PARAMPROB:
  1272.     sent_icmp = (struct icmp *) (c + 28);
  1273.     if ((sent_icmp->icmp_type = ICMP_ECHO) &&
  1274.     (sent_icmp->icmp_id = ident) &&
  1275.     (sent_icmp->icmp_seq < (n_short)num_hosts)) {
  1276.       /* this is a response to a ping we sent */
  1277.       h = table[sent_icmp->icmp_seq];
  1278.       fprintf(stderr, "%s from %s for ICMP Echo sent to %s", 
  1279.           icmp_type_str[p->icmp_type],
  1280.           inet_ntoa(addr->sin_addr), h->host);
  1281.       if (inet_addr(h->host) == -1)
  1282.     fprintf(stderr, " (%s)", inet_ntoa(h->saddr.sin_addr));
  1283.       fprintf(stderr, "\n");
  1284.     }
  1285.     return 2;
  1286.   /* no way to tell whether any of these are sent due to our ping */
  1287.   /* or not (shouldn't be, of course), so just discard            */
  1288.   case ICMP_TSTAMP:
  1289.   case ICMP_TSTAMPREPLY:
  1290.   case ICMP_IREQ:
  1291.   case ICMP_IREQREPLY:
  1292.   case ICMP_MASKREQ:
  1293.   case ICMP_MASKREPLY:
  1294.   default:
  1295.     return 0;
  1296.   }
  1297. }
  1298.  
  1299. /*
  1300.  * Checksum routine for Internet Protocol family headers (C Version)
  1301.  * From ping examples in W.Richard Stevens "UNIX NETWORK PROGRAMMING" book.
  1302.  */
  1303.  
  1304. #ifdef _NO_PROTO
  1305. int in_cksum(p,n)
  1306. u_short *p; int n;
  1307. #else
  1308. int in_cksum(u_short *p, int n)
  1309. #endif
  1310. {
  1311.   register u_short answer;
  1312.   register long sum = 0;
  1313.   u_short odd_byte = 0;
  1314.  
  1315.   while( n > 1 )  { sum += *p++; n -= 2; }
  1316.  
  1317.   /* mop up an odd byte, if necessary */
  1318.   if( n == 1 ) {
  1319.       *(u_char *)(&odd_byte) = *(u_char *)p;
  1320.       sum += odd_byte;
  1321.   }
  1322.  
  1323.   sum = (sum >> 16) + (sum & 0xffff);    /* add hi 16 to low 16 */
  1324.   sum += (sum >> 16);            /* add carry */
  1325.   answer = ~sum;            /* ones-complement, truncate*/
  1326.   return (answer);
  1327. }
  1328.  
  1329.  
  1330. /* process input name for addition to target list */
  1331. /*   name can turn into multiple targets via multiple interfaces (-m) */
  1332. /*   or via NIS groups */
  1333.  
  1334. #ifdef _NO_PROTO
  1335. void add_name(name)
  1336. char *name;
  1337. #else
  1338. void add_name(char *name)
  1339. #endif
  1340. {
  1341.   struct hostent *host_ent;
  1342.   u_int ipaddress;
  1343.   struct in_addr *ipa = (struct in_addr *)&ipaddress;
  1344.   struct in_addr *host_add;
  1345.   char *nm;
  1346.   int i = 0;
  1347.  
  1348.   if ((ipaddress = inet_addr(name)) != -1) {
  1349.     /* input name is an IP addr, go with it */
  1350.     if (name_flag) {
  1351.       if (addr_flag) {
  1352.     add_addr(name, na_cat(get_host_by_address(*ipa), *ipa), *ipa);
  1353.       } else {
  1354.     nm = cpystr(get_host_by_address(*ipa));
  1355.     add_addr(name, nm, *ipa);
  1356.       }
  1357.     } else {
  1358.       add_addr(name, name, *ipa);
  1359.     }
  1360.     return;
  1361.   }
  1362.   /* input name is not an IP addr, maybe it's a host name */
  1363.   host_ent = gethostbyname(name); 
  1364.   if (host_ent == NULL) { 
  1365.     if (h_errno == TRY_AGAIN) { 
  1366.       u_sleep(DNS_TIMEOUT) ; 
  1367.       host_ent = gethostbyname(name) ;
  1368.     }
  1369.     if (host_ent == NULL) {
  1370. #ifdef NIS_GROUPS
  1371.       /* maybe it's the name of a NIS netgroup */
  1372.       char *machine, *user_ignored, *domain_ignored;
  1373.       setnetgrent(name);
  1374.       if (getnetgrent(&machine, &user_ignored, &domain_ignored)==0){
  1375.     endnetgrent();
  1376.     if (!quiet_flag) fprintf(stderr,"%s address not found\n", name);
  1377.     num_noaddress++;
  1378.     return;
  1379.       } else {
  1380.     add_name(cpystr(machine));
  1381.       }
  1382.       while(getnetgrent(&machine, &user_ignored, &domain_ignored))
  1383.     add_name(cpystr(machine));
  1384.       endnetgrent();
  1385.       return;
  1386. #else
  1387.       if (!quiet_flag) fprintf(stderr,"%s address not found\n", name);
  1388.       num_noaddress++;
  1389.       return ; 
  1390. #endif
  1391.     }
  1392.   }
  1393.   host_add = (struct in_addr *) *(host_ent->h_addr_list) ; 
  1394.   if (host_add == NULL) { 
  1395.     if (!quiet_flag) fprintf(stderr,"%s has no address data\n", name);
  1396.     num_noaddress++;
  1397.     return ; 
  1398.   } else {
  1399.     /* it is indeed a hostname with a real address */
  1400.     while (host_add) {
  1401.       if (name_flag && addr_flag) {
  1402.     add_addr(name, na_cat(name, *host_add), *host_add);
  1403.       } else if (addr_flag) {
  1404.     nm = cpystr(inet_ntoa(*host_add));
  1405.     add_addr(name, nm, *host_add);
  1406.       } else {
  1407.     add_addr(name, name, *host_add);
  1408.       }
  1409.       if (!multif_flag) break;
  1410.       host_add = (struct in_addr *) (host_ent->h_addr_list[++i]) ; 
  1411.     }
  1412.   }
  1413. }
  1414.  
  1415. #ifdef _NO_PROTO
  1416. char *na_cat(name, ipaddr)
  1417. char *name;
  1418. struct in_addr ipaddr;
  1419. #else
  1420. char *na_cat(char *name, struct in_addr ipaddr)
  1421. #endif
  1422. {
  1423.   char *nm, *as;
  1424.  
  1425.   as = inet_ntoa(ipaddr);
  1426.   nm = (char *) malloc(strlen(name) + strlen(as) + 4);
  1427.   if (!nm) crash_and_burn("can't allocate some space for a string");
  1428.   strcpy(nm, name);
  1429.   strcat(nm, " (");
  1430.   strcat(nm, as);
  1431.   strcat(nm, ")");
  1432.   return(nm);
  1433. }
  1434.  
  1435. /* add address to linked list of targets to be pinged */
  1436. /* assume memory for *name and *host is ours!!!       */
  1437.  
  1438. #ifdef _NO_PROTO
  1439. void add_addr(name, host, ipaddr)
  1440. char *name;
  1441. char *host;
  1442. struct in_addr ipaddr;
  1443. #else
  1444. void add_addr(char *name, char *host, struct in_addr ipaddr)
  1445. #endif
  1446. {
  1447.   HOST_ENTRY *p;
  1448.   int n, *i;
  1449.  
  1450.   p = (HOST_ENTRY *) malloc(sizeof(HOST_ENTRY));
  1451.   if (!p) crash_and_burn("can't allocate HOST_ENTRY");
  1452.   memset((char *) p, 0, sizeof(HOST_ENTRY));
  1453.  
  1454.   p->name = name;
  1455.   p->host = host;
  1456.   p->saddr.sin_family = AF_INET;
  1457.   p->saddr.sin_addr = ipaddr; 
  1458.   p->timeout = timeout;
  1459.   p->running = 1;
  1460.   p->min_reply = 10000000;
  1461.  
  1462.   if (strlen(p->host) > max_hostname_len)
  1463.     max_hostname_len = strlen(p->host);
  1464.  
  1465.   /* array for response time results */
  1466.   if (!loop_flag) {
  1467.     i = (int *) malloc(trials * sizeof(int));
  1468.     if (!i) crash_and_burn("can't allocate resp_times array");
  1469.     for (n = 1; n < trials; n++)
  1470.       i[n] = RESP_UNUSED;
  1471.     p->resp_times = i;
  1472.   }
  1473. #ifdef DEBUG
  1474.   /* likewise for sent times */
  1475.   if (sent_times_flag) {
  1476.     i = (int *) malloc(trials * sizeof(int));
  1477.     if (!i) crash_and_burn("can't allocate sent_times array");
  1478.     for (n = 1; n < trials; n++)
  1479.       i[n] = RESP_UNUSED;
  1480.     p->sent_times = i;
  1481.   }
  1482. #endif
  1483.  
  1484.   if (!rrlist) {
  1485.       rrlist = p;
  1486.       p->next = p;
  1487.       p->prev = p;
  1488.   } else {
  1489.       p->next = rrlist;
  1490.       p->prev = rrlist->prev;
  1491.       p->prev->next = p;
  1492.       p->next->prev = p;
  1493.   }
  1494.   num_hosts++;
  1495. }
  1496.  
  1497. #ifdef _NO_PROTO
  1498. void remove_job(h)
  1499. HOST_ENTRY *h;
  1500. #else
  1501. void remove_job(HOST_ENTRY *h)
  1502. #endif
  1503. {
  1504.  
  1505. #ifdef DEBUG
  1506.   if (trace_flag) 
  1507.     printf("removing job for %s\n", h->host);
  1508. #endif
  1509.   h->running = 0;
  1510.   h->waiting = 0;
  1511.   --num_jobs;
  1512.  
  1513.   if (num_jobs) {                    /* remove us from list of active jobs */
  1514.        h->prev->next = h->next;
  1515.        h->next->prev = h->prev;
  1516.        if (h==cursor) { cursor = h->next; }
  1517.   } else {     
  1518.        cursor=NULL;
  1519.        rrlist=NULL;
  1520.   }
  1521.  
  1522. }
  1523.  
  1524. #ifdef _NO_PROTO
  1525. char *get_host_by_address(in)
  1526. struct in_addr in;
  1527. #else
  1528. char *get_host_by_address(struct in_addr in)
  1529. #endif
  1530. {
  1531.   struct hostent *h;
  1532.    h=gethostbyaddr((char *) &in,sizeof(struct in_addr),AF_INET);
  1533.    if (h==NULL || h->h_name==NULL) return inet_ntoa(in);
  1534.    else return (char*)h->h_name;
  1535. }
  1536.  
  1537.  
  1538. #ifdef _NO_PROTO
  1539. char *cpystr(string)
  1540. char *string;
  1541. #else
  1542. char *cpystr(char *string)
  1543. #endif
  1544. {
  1545.   char *dst;
  1546.  
  1547.   if (string) {         
  1548.       dst = (char *) malloc (1+strlen (string));
  1549.       if (!dst) crash_and_burn("can't allocate some space for a string");
  1550.       strcpy (dst, string);
  1551.       return dst;
  1552.   }
  1553.   else 
  1554.       return NULL;
  1555. }
  1556.   
  1557. #ifdef _NO_PROTO
  1558. void crash_and_burn(message)
  1559. char *message;
  1560. #else
  1561. void crash_and_burn(char *message)
  1562. #endif
  1563. {
  1564.   if (verbose_flag) fprintf(stderr,"%s: %s\n",prog,message);
  1565.   exit(4);
  1566. }
  1567.  
  1568. #ifdef _NO_PROTO
  1569. void errno_crash_and_burn(message)
  1570. char *message;
  1571. #else
  1572. void errno_crash_and_burn(char *message)
  1573. #endif
  1574. {
  1575.   if (verbose_flag)
  1576.         fprintf(stderr,"%s: %s : %s\n",prog,message,sys_errlist[errno]);
  1577.   exit(4);
  1578. }
  1579.  
  1580. /* timeval_diff now returns result in hundredths of milliseconds */
  1581. /*   ie, tens of microseconds                                    */
  1582. #ifdef _NO_PROTO
  1583. long timeval_diff(a,b)
  1584. struct timeval *a,*b;
  1585. #else
  1586. long timeval_diff(struct timeval *a,struct timeval *b)
  1587. #endif
  1588. {
  1589. double temp;
  1590.  
  1591. temp = 
  1592.   (((a->tv_sec*1000000)+ a->tv_usec) - 
  1593.      ((b->tv_sec*1000000)+ b->tv_usec))/10;
  1594.  
  1595. return (long) temp;
  1596.  
  1597. }
  1598.  
  1599. /*
  1600.  * sprint_time: render time into a string with three digits of precision
  1601.  *              input is in tens of microseconds
  1602.  */
  1603.  
  1604. #ifdef _NO_PROTO
  1605. char * sprint_tm(t)
  1606. int t;
  1607. #else
  1608. char * sprint_tm(int t)
  1609. #endif
  1610. {
  1611.   static char buf[10];
  1612.  
  1613.   /* <= 0.99 ms */
  1614.   if (t < 100) {
  1615.     sprintf(buf, "0.%02d", t);
  1616.     return(buf);
  1617.   }
  1618.   /* 1.00 - 9.99 ms */
  1619.   if (t < 1000) {
  1620.     sprintf(buf, "%d.%02d", t/100, t%100);
  1621.     return(buf);
  1622.   }
  1623.   /* 10.0 - 99.9 ms */
  1624.   if (t < 10000) {
  1625.     sprintf(buf, "%d.%d", t/100, (t%100)/10);
  1626.     return(buf);
  1627.   }
  1628.   /* >= 100 ms */
  1629.   sprintf(buf, "%d", t/100);
  1630.   return(buf);
  1631. }
  1632.  
  1633. #ifdef _NO_PROTO
  1634. void u_sleep (u_sec)
  1635. int u_sec;
  1636. #else
  1637. void u_sleep (int u_sec)
  1638. #endif
  1639. {
  1640.   int nfound,slen,n;
  1641.   struct timeval to;
  1642.   fd_set readset,writeset;
  1643.  
  1644.   to.tv_sec  = u_sec/1000000;
  1645.   to.tv_usec = u_sec - (to.tv_sec*1000000);
  1646.  
  1647.   FD_ZERO(&readset);
  1648.   FD_ZERO(&writeset);
  1649.   nfound = select(0, &readset, &writeset, NULL, &to);
  1650.   if (nfound<0) errno_crash_and_burn("select");
  1651.   return;
  1652. }
  1653.  
  1654. /*
  1655.  * recvfrom_wto: receive with timeout
  1656.  *      returns length of data read or -1 if timeout
  1657.  *      crash_and_burn on any other errrors
  1658.  *
  1659.  */
  1660.  
  1661. #ifdef _NO_PROTO
  1662. int recvfrom_wto (s,buf,len, saddr, timo)
  1663. int s; char *buf; int len; struct sockaddr *saddr; int timo;
  1664. #else
  1665. int recvfrom_wto (int s, char *buf, int len, struct sockaddr *saddr, int timo)
  1666. #endif
  1667. {
  1668.   int nfound,slen,n;
  1669.   struct timeval to;
  1670.   fd_set readset,writeset;
  1671.  
  1672.   to.tv_sec  = timo/100000;
  1673.   to.tv_usec = (timo - (to.tv_sec*100000))*10;
  1674.  
  1675.   FD_ZERO(&readset);
  1676.   FD_ZERO(&writeset);
  1677.   FD_SET(s,&readset);
  1678.   nfound = select(s+1,&readset,&writeset,NULL,&to);
  1679.   if (nfound<0) errno_crash_and_burn("select");
  1680.   if (nfound==0) return -1;  /* timeout */
  1681.   slen=sizeof(struct sockaddr);
  1682.   n=recvfrom(s,buf,len,0,saddr,&slen);
  1683.   if (n<0) errno_crash_and_burn("recvfrom");
  1684.   return n;
  1685. }
  1686.  
  1687. #ifdef _NO_PROTO
  1688. void usage()
  1689. #else
  1690. void usage()
  1691. #endif
  1692. {
  1693.   fprintf(stderr,"\n");
  1694.   fprintf(stderr,"Usage: %s [options] [targets...]\n",prog);
  1695.   fprintf(stderr,"   -a         show targets that are alive\n");
  1696.   fprintf(stderr,"   -A         show targets by address\n");
  1697.   fprintf(stderr,"   -b n       amount of ping data to send, in bytes (default %d)\n", ping_data_size);
  1698.   fprintf(stderr,"   -B f       set exponential backoff factor to f\n");
  1699.   fprintf(stderr,"   -c n       count of pings to send to each target (default %d)\n", count);  
  1700.   fprintf(stderr,"   -C n       same as -c, report results in verbose format\n");
  1701.   fprintf(stderr,"   -e         show elapsed time on return packets\n");
  1702.   fprintf(stderr,"   -f file    read list of targets from a file ( - means stdin)\n");
  1703.   fprintf(stderr,"   -i n       interval between sending ping packets (in millisec) (default %d)\n",interval/100);
  1704.   fprintf(stderr,"   -l         loop sending pings forever\n");
  1705.   fprintf(stderr,"   -m         ping multiple interfaces on target host\n");
  1706.   fprintf(stderr,"   -n         show targets by name (-d is equivalent)\n");
  1707.   fprintf(stderr,"   -p n       interval between ping packets to one target (in millisec)\n");
  1708.   fprintf(stderr,"                (in looping and counting modes, default %d)\n", perhost_interval/100);
  1709.   fprintf(stderr,"   -q         quiet (don't show per-target/per-ping results)\n");
  1710.   fprintf(stderr,"   -Q n       same as -q, but show summary every n seconds\n");
  1711.   fprintf(stderr,"   -r n       number of retries (default %d)\n",retry);
  1712.   fprintf(stderr,"   -s         print final stats\n");
  1713.   fprintf(stderr,"   -t n       individual target initial timeout (in millisec) (default %d)\n",timeout/100);
  1714.   fprintf(stderr,"   -u         show targets that are unreachable\n");
  1715.   fprintf(stderr,"   -v         show version\n");
  1716.   fprintf(stderr,"   targets    list of targets to check (if no -f specified)\n");
  1717.   fprintf(stderr,"\n");
  1718.   exit(3);
  1719. }
  1720.