http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.14.tar.gz
[xscreensaver] / hacks / sonar.c
index 5f6510ccce553baec7a6674ad8ee3e1785ae84c5..00f7b3f6fc5e7d1a4da7ca874fe4b6b65212b83a 100644 (file)
@@ -28,7 +28,8 @@
  *   - plot the process table, by process size, cpu usage, or total time;
  *   - plot the logged on users by idle time or cpu usage.
  *
- * Copyright (C) 1998 by Stephen Martin (smartin@canada.com).
+ * Copyright (C) 1998, 2001
+ *  by Stephen Martin (smartin@vanderfleet-martin.net).
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
  * the above copyright notice appear in all copies and that both that
@@ -37,7 +38,7 @@
  * software for any purpose.  It is provided "as is" without express or 
  * implied warranty.
  *
- * $Revision: 1.9 $
+ * $Revision: 1.29 $
  *
  * Version 1.0 April 27, 1998.
  * - Initial version
@@ -86,6 +87,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <math.h>
+#include <sys/stat.h>
 
 #include "screenhack.h"
 #include "colors.h"
 #undef MY_MIN
 #define MY_MIN(a,b) ((a)<(b)?(a - 50):(b - 10))
 
+#ifndef LINE_MAX
+# define LINE_MAX 2048
+#endif
+
 /* Frigging icmp */
 
 #if defined(HAVE_ICMP)
 # undef HAVE_PING
 #endif
 
+
+#ifdef HAVE_PING
+# if defined(__DECC) || defined(_IP_VHL)
+   /* This is how you do it on DEC C, and possibly some BSD systems. */
+#  define IP_HDRLEN(ip)   ((ip)->ip_vhl & 0x0F)
+# else
+   /* This is how you do it on everything else. */
+#  define IP_HDRLEN(ip)   ((ip)->ip_hl)
+# endif
+#endif /* HAVE_PING */
+
+
 /* Forward References */
 
 #ifdef HAVE_PING
@@ -145,12 +163,13 @@ static u_short checksum(u_short *, int);
 #endif
 static long delta(struct timeval *, struct timeval *);
 
+
 /* Data Structures */
 
 /*
  * The Bogie.
  *
- * This represents an object that is visable on the scope.
+ * This represents an object that is visible on the scope.
  */
 
 typedef struct Bogie {
@@ -186,13 +205,18 @@ typedef struct {
     int width, height;         /* Window dimensions */
     int minx, miny, maxx, maxy, /* Bounds of the scope */
        centrex, centrey, radius; /* Parts of the scope circle */
-    Bogie *visable;            /* List of visable objects */
+    Bogie *visible;            /* List of visible objects */
     int current;               /* Current position of sweep */
-
+    int sweepnum;               /* The current id of the sweep */
     int delay;                 /* how long between each frame of the anim */
 
+    int TTL;                   /* The number of ticks that bogies are visible
+                                   on the screen before they fade away. */
 } sonar_info;
 
+static Bool debug_p = False;
+
+
 /* 
  * Variables to support the differnt Sonar modes.
  */
@@ -204,13 +228,16 @@ void *sensor_info;                        /* Information about the sensor */
  * A list of targets to ping.
  */
 
-#ifdef HAVE_PING
 typedef struct ping_target {
     char *name;                        /* The name of the target */
+#ifdef HAVE_PING
     struct sockaddr address;   /* The address of the target */
+#endif /* HAVE_PING */
     struct ping_target *next;  /* The next one in the list */
 } ping_target;
 
+
+#ifdef HAVE_PING
 /*
  * Ping Information.
  *
@@ -230,7 +257,6 @@ typedef struct {
 
 static int timer_expired;
 
-
 #endif /* HAVE_PING */
 
 /*
@@ -241,7 +267,7 @@ typedef struct sim_target {
     char *name;                        /* The name of the target */
     int nexttick;              /* The next tick that this will be seen */
     int nextdist;              /* The distance on that tick */
-    int movedlasttick;         /* Flag to indicate we just moved this one */
+    int movedonsweep;          /* The number of the sweep this last moved */
 } sim_target;
 
 /*
@@ -280,16 +306,15 @@ char *defaults [] = {
     "*textSteps:       80",    /* npixels */
     "*sweepSegments:   80",    /* npixels */
 
-#ifdef HAVE_PING
     "*pingTimeout:     3000",
-    "*pingSource:      file",
-    "*pingFile:        /etc/hosts",
-    "*pingList:        localhost",
-#endif /* HAVE_PING */
+
     "*teamAName:       F18",
     "*teamBName:       MIG",
     "*teamACount:      4",
     "*teamBCount:      4",
+
+    "*ping:           default",
+    ".debug:          false",
     0
 };
 
@@ -302,28 +327,20 @@ XrmOptionDescRec options [] = {
     {"-grid-color",   ".gridColor",    XrmoptionSepArg, 0 },
     {"-text-color",   ".textColor",    XrmoptionSepArg, 0 },
     {"-ttl",          ".ttl",          XrmoptionSepArg, 0 },
-    {"-mode",         ".mode",         XrmoptionSepArg, 0 },
     {"-font",         ".font",         XrmoptionSepArg, 0 },
 #ifdef HAVE_PING
     {"-ping-timeout", ".pingTimeout",  XrmoptionSepArg, 0 },
-    {"-ping-source",  ".pingSource",   XrmoptionSepArg, 0 },
-    {"-ping-file",    ".pingFile",     XrmoptionSepArg, 0 },
-    {"-ping-list",    ".pingList",     XrmoptionSepArg, 0 },
 #endif /* HAVE_PING */
     {"-team-a-name",   ".teamAName",   XrmoptionSepArg, 0 },
     {"-team-b-name",   ".teamBName",   XrmoptionSepArg, 0 },
     {"-team-a-count",  ".teamACount",  XrmoptionSepArg, 0 },
     {"-team-b-count",  ".teamBCount",  XrmoptionSepArg, 0 },
+
+    {"-ping",          ".ping",        XrmoptionSepArg, 0 },
+    {"-debug",         ".debug",       XrmoptionNoArg, "True" },
     { 0, 0, 0, 0 }
 };
 
-/*
- * The number of ticks that bogies are visable on the screen before they
- * fade away.
- */
-
-static int TTL;
-
 /*
  * Create a new Bogie and set some initial values.
  *
@@ -345,6 +362,7 @@ newBogie(char *name, int distance, int tick, int ttl)
 
     Bogie *new;
 
+    distance *= 1000;
     /* Allocate a bogie and initialize it */
 
     if ((new = (Bogie *) calloc(1, sizeof(Bogie))) == NULL) {
@@ -367,6 +385,7 @@ newBogie(char *name, int distance, int tick, int ttl)
  *    b - The bogie to free.
  */
 
+
 static void
 freeBogie(Bogie *b) 
 {
@@ -430,35 +449,91 @@ findNode(Bogie *bl, char *name)
 static int
 lookupHost(ping_target *target) 
 {
+  struct hostent *hent;
+  struct sockaddr_in *iaddr;
 
-    /* Local Variables */
+  int iip[4];
+  char c;
 
-    struct sockaddr_in *iaddr;
+  iaddr = (struct sockaddr_in *) &(target->address);
+  iaddr->sin_family = AF_INET;
 
-    /* Set up the target address we first assume that the name is the
-       IP address as a string */
+  if (4 == sscanf(target->name, "%d.%d.%d.%d%c",
+                  &iip[0], &iip[1], &iip[2], &iip[3], &c))
+    {
+      /* It's an IP address.
+       */
+      unsigned char ip[4];
 
-    iaddr = (struct sockaddr_in *) &(target->address);
-    iaddr->sin_family = AF_INET;
-    if ((iaddr->sin_addr.s_addr = inet_addr(target->name)) == -1) {
+      ip[0] = iip[0];
+      ip[1] = iip[1];
+      ip[2] = iip[2];
+      ip[3] = iip[3];
 
-       /* Conversion of IP address failed, try to look the host up by name */
+      if (ip[3] == 0)
+        {
+          if (debug_p > 1)
+            fprintf (stderr, "%s:   ignoring bogus IP %s\n",
+                     progname, target->name);
+          return 0;
+        }
 
-       struct hostent *hent = gethostbyname(target->name);
-       if (hent == NULL) {
-           fprintf(stderr, "%s: could not resolve host %s\n",
-                    progname, target->name);
-           return 0;
-       }
-       memcpy(&iaddr->sin_addr, hent->h_addr_list[0],
-              sizeof(iaddr->sin_addr));
+      iaddr->sin_addr.s_addr = ((ip[3] << 24) |
+                                (ip[2] << 16) |
+                                (ip[1] <<  8) |
+                                (ip[0]));
+      hent = gethostbyaddr ((const char *) ip, 4, AF_INET);
+
+      if (debug_p > 1)
+        fprintf (stderr, "%s:   %s => %s\n",
+                 progname, target->name,
+                 ((hent && hent->h_name && *hent->h_name)
+                  ? hent->h_name : "<unknown>"));
+
+      if (hent && hent->h_name && *hent->h_name)
+        target->name = strdup (hent->h_name);
     }
+  else
+    {
+      /* It's a host name.
+       */
+      hent = gethostbyname (target->name);
+      if (!hent)
+        {
+          fprintf (stderr, "%s: could not resolve host:  %s\n",
+                   progname, target->name);
+          return 0;
+        }
 
-    /* Done */
+      memcpy (&iaddr->sin_addr, hent->h_addr_list[0],
+              sizeof(iaddr->sin_addr));
 
-    return 1;
+      if (debug_p > 1)
+        fprintf (stderr, "%s:   %s => %d.%d.%d.%d\n",
+                 progname, target->name,
+                 iaddr->sin_addr.s_addr       & 255,
+                 iaddr->sin_addr.s_addr >>  8 & 255,
+                 iaddr->sin_addr.s_addr >> 16 & 255,
+                 iaddr->sin_addr.s_addr >> 24 & 255);
+    }
+  return 1;
 }
 
+
+static void
+print_host (FILE *out, unsigned long ip, const char *name)
+{
+  char ips[50];
+  sprintf (ips, "%lu.%lu.%lu.%lu",
+           (ip)       & 255,
+           (ip >>  8) & 255,
+           (ip >> 16) & 255,
+           (ip >> 24) & 255);
+  if (!name || !*name) name = "<unknown>";
+  fprintf (out, "%-16s %s\n", ips, name);
+}
+
+
 /*
  * Create a target for a host.
  *
@@ -493,8 +568,31 @@ newHost(char *name)
     if (! lookupHost(target))
        goto target_init_error;
 
+    /* Don't ever use loopback (127.0.0) hosts */
+    {
+      struct sockaddr_in *iaddr = (struct sockaddr_in *) &(target->address);
+      unsigned long ip = iaddr->sin_addr.s_addr;
+      if ((ip         & 255) == 127 &&
+          ((ip >>  8) & 255) == 0 &&
+          ((ip >> 16) & 255) == 0)
+        {
+          if (debug_p)
+            fprintf (stderr, "%s:   ignoring loopback host %s\n",
+                     progname, target->name);
+          goto target_init_error;
+        }
+    }
+
     /* Done */
 
+    if (debug_p)
+      {
+        struct sockaddr_in *iaddr = (struct sockaddr_in *) &(target->address);
+        unsigned long ip = iaddr->sin_addr.s_addr;
+        fprintf (stderr, "%s:   added ", progname);
+        print_host (stderr, ip, target->name);
+      }
+
     return target;
 
     /* Handle errors here */
@@ -519,7 +617,6 @@ target_init_error:
 static ping_target *
 readPingHostsFile(char *fname) 
 {
-
     /* Local Variables */
 
     FILE *fp;
@@ -545,6 +642,9 @@ readPingHostsFile(char *fname)
        return NULL;
     }
 
+    if (debug_p)
+      fprintf (stderr, "%s:  reading file %s\n", progname, fname);
+
     /* Read the file line by line */
 
     while ((p = fgets(buf, LINE_MAX, fp)) != NULL) {
@@ -564,17 +664,48 @@ readPingHostsFile(char *fname)
        /* Get the name and address */
 
        name = addr = NULL;
-       if ((addr = strtok(buf, " \t\n")) != NULL)
-           name = strtok(NULL, " \t\n");
+       if ((addr = strtok(buf, " ,;\t\n")) != NULL)
+           name = strtok(NULL, " ,;\t\n");
        else
            continue;
 
+        /* Check to see if the addr looks like an addr.  If not, assume
+           the addr is a name and there is no addr.  This way, we can
+           handle files whose lines have "xx.xx.xx.xx hostname" as their
+           first two tokens, and also files that have a hostname as their
+           first token (like .ssh/known_hosts and .rhosts.)
+         */
+        {
+          int i; char c;
+          if (4 != sscanf(addr, "%d.%d.%d.%d%c", &i, &i, &i, &i, &c))
+            {
+              name = addr;
+              addr = NULL;
+            }
+        }
+
+        /* If the name is all digits, it's not a name. */
+        if (name)
+          {
+            const char *s;
+            for (s = name; *s; s++)
+              if (*s < '0' || *s > '9')
+                break;
+            if (! *s)
+              {
+                if (debug_p > 1)
+                  fprintf (stderr, "%s:  skipping bogus name \"%s\" (%s)\n",
+                           progname, name, addr);
+                name = NULL;
+              }
+          }
+
        /* Create a new target using first the name then the address */
 
        new = NULL;
        if (name != NULL)
            new = newHost(name);
-       if (new == NULL)
+       if (new == NULL && addr != NULL)
            new = newHost(addr);
 
        /* Add it to the list if we got one */
@@ -591,47 +722,45 @@ readPingHostsFile(char *fname)
     return list;
 }
 
-/*
- * Generate a list of ping targets from the entries in a string.
- *
- * Args:
- *    list - A list of comma separated host names.
- *
- * Returns:
- *    A list of targets to ping or null if an error occured.
- */
 
 static ping_target *
-readPingHostsList(char *list) 
+delete_duplicate_hosts (ping_target *list)
 {
+  ping_target *head = list;
+  ping_target *rest;
 
-    /* Local Variables */
-
-    char *host;
-    ping_target *hostlist = NULL;
-    ping_target *new;
+  for (rest = head; rest; rest = rest->next)
+    {
+      struct sockaddr_in *i1 = (struct sockaddr_in *) &(rest->address);
+      unsigned long ip1 = i1->sin_addr.s_addr;
 
-    /* Check that there is a list */
+      static ping_target *rest2;
+      for (rest2 = rest; rest2; rest2 = rest2->next)
+        {
+          if (rest2 && rest2->next)
+            {
+              struct sockaddr_in *i2 = (struct sockaddr_in *)
+                &(rest2->next->address);
+              unsigned long ip2 = i2->sin_addr.s_addr;
 
-    if ((list == NULL) || (list[0] == '\0'))
-       return NULL;
+              if (ip1 == ip2)
+                {
+                  if (debug_p)
+                    {
+                      fprintf (stderr, "%s: deleted duplicate: ", progname);
+                      print_host (stderr, ip2, rest2->next->name);
+                    }
+                  rest2->next = rest2->next->next;
+                }
+            }
+        }
+    }
 
-    /* Loop through the hosts and add them to the list to return */
+  return head;
+}
 
-    host = strtok(list, ",");
-    while (host != NULL) {
-       new = newHost(host);
-       if (new != NULL) {
-           new->next = hostlist;
-           hostlist = new;
-       }
-       host = strtok(NULL, ",");
-    }
 
-    /* Done */
 
-    return hostlist;
-}
 
 /*
  * Generate a list ping targets consisting of all of the entries on
@@ -642,8 +771,9 @@ readPingHostsList(char *list)
  */
 
 static ping_target *
-subnetHostsList(void
+subnetHostsList(int base, int subnet_width
 {
+    unsigned long mask;
 
     /* Local Variables */
 
@@ -655,6 +785,26 @@ subnetHostsList(void)
     ping_target *new;
     ping_target *list = NULL;
 
+    if (subnet_width < 24)
+      {
+        fprintf (stderr,
+   "%s: pinging %lu hosts is a bad idea; please use a subnet mask of 24 bits\n"
+                 "       or more (255 hosts max.)\n",
+                 progname, (unsigned long) (1L << (32 - subnet_width)) - 1);
+        exit (1);
+      }
+    else if (subnet_width > 30)
+      {
+        fprintf (stderr, "%s: a subnet of %d bits doesn't make sense:"
+                 " try \"subnet/24\" or \"subnet/29\".\n",
+                 progname, subnet_width);
+        exit (1);
+      }
+
+
+    if (debug_p)
+      fprintf (stderr, "%s:   adding %d-bit subnet\n", progname, subnet_width);
+
     /* Get our hostname */
 
     if (gethostname(hostname, BUFSIZ)) {
@@ -670,25 +820,67 @@ subnetHostsList(void)
     }
     strcpy(address, inet_ntoa(*((struct in_addr *)hent->h_addr_list[0])));
 
-    /* Get a pointer to the last "." in the string */
+    /* Construct targets for all addresses in this subnet */
 
-    if ((p = strrchr(address, '.')) == NULL) {
-       fprintf(stderr, "%s: can't parse IP address %s\n", progname, address);
-       return NULL;
-    }
-    p++;
+    mask = 0;
+    for (i = 0; i < subnet_width; i++)
+      mask |= (1L << (31-i));
 
-    /* Construct targets for all addresses in this subnet */
+    /* If no base IP specified, assume localhost. */
+    if (base == 0)
+      base = ((((unsigned char) hent->h_addr_list[0][0]) << 24) |
+              (((unsigned char) hent->h_addr_list[0][1]) << 16) |
+              (((unsigned char) hent->h_addr_list[0][2]) <<  8) |
+              (((unsigned char) hent->h_addr_list[0][3])));
+
+    if (base == ((127 << 24) | 1))
+      {
+        fprintf (stderr,
+                 "%s: unable to determine local subnet address: \"%s\"\n"
+                 "       resolves to loopback address %d.%d.%d.%d.\n",
+                 progname, hostname,
+                 (base >> 24) & 255, (base >> 16) & 255,
+                 (base >>  8) & 255, (base      ) & 255);
+        return NULL;
+      }
 
-    for (i = 254; i > 0; i--) {
+    for (i = 255; i >= 0; i--) {
+        int ip = (base & 0xFFFFFF00) | i;
+      
+        if ((ip & mask) != (base & mask))   /* not in the mask range at all */
+          continue;
+        if ((ip & ~mask) == 0)              /* broadcast address */
+          continue;
+        if ((ip & ~mask) == ~mask)          /* broadcast address */
+          continue;
+
+        sprintf (address, "%d.%d.%d.%d", 
+                 (ip>>24)&255, (ip>>16)&255, (ip>>8)&255, (ip)&255);
+
+        if (debug_p > 1)
+          fprintf(stderr, "%s:  subnet: %s (%d.%d.%d.%d & %d.%d.%d.%d / %d)\n",
+                  progname,
+                  address,
+                  (int) (base>>24)&255,
+                  (int) (base>>16)&255,
+                  (int) (base>> 8)&255,
+                  (int) (base&mask&255),
+                  (int) (mask>>24)&255,
+                  (int) (mask>>16)&255,
+                  (int) (mask>> 8)&255,
+                  (int) (mask&255),
+                  (int) subnet_width);
+
+        p = address + strlen(address) + 1;
        sprintf(p, "%d", i);
+
        new = newHost(address);
        if (new != NULL) {
            new->next = list;
            list = new;
        }
     }
-  
+
     /* Done */
 
     return list;
@@ -701,14 +893,17 @@ subnetHostsList(void)
  *    A newly allocated ping_info structure or null if an error occured.
  */
 
+static ping_target *parse_mode (Bool ping_works_p);
+
 static ping_info *
 init_ping(void) 
 {
 
+  Bool socket_initted_p = False;
+
     /* Local Variables */
 
     ping_info *pi = NULL;              /* The new ping_info struct */
-    char *src;                         /* The source of the ping hosts */
     ping_target *pt;                   /* Used to count the targets */
 
     /* Create the ping info structure */
@@ -718,59 +913,44 @@ init_ping(void)
        goto ping_init_error;
     }
 
-    /* Create the ICMP socket and turn off SUID */
+    /* Create the ICMP socket */
 
-    if ((pi->icmpsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
-       char msg[1024];
-       sprintf(msg, "%s: can't create ICMP socket", progname);
-       perror(msg);
-       fprintf(stderr,
-         "%s: this program must be setuid to root for `ping mode' to work.\n",
-                progname);
-       goto ping_init_error;
+    if ((pi->icmpsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) >= 0) {
+      socket_initted_p = True;
     }
+
+    /* Disavow privs */
+
     setuid(getuid());
+
+
     pi->pid = getpid() & 0xFFFF;
     pi->seq = 0;
     pi->timeout = get_integer_resource("pingTimeout", "PingTimeout");
 
     /* Generate a list of targets */
 
-    src = get_string_resource("pingSource", "PingSource");
-    if (strcmp(src, "file") == 0) {
-
-       /*
-         * The list of ping targets is to come from a file in
-        * /etc/hosts format
-        */
-
-       pi->targets = readPingHostsFile(get_string_resource("pingFile",
-                                                           "PingFile"));
-
-    } else if (strcmp(src, "list") == 0) {
-
-       /* The list of hosts is to come from the pinghostlist resource */
-
-       pi->targets = readPingHostsList(get_string_resource("pingList",
-                                                           "PingList"));
-
-    } else if (strcmp(src, "subnet") == 0) {
+    pi->targets = parse_mode (socket_initted_p);
+    pi->targets = delete_duplicate_hosts (pi->targets);
 
-       pi->targets = subnetHostsList();
 
-    } else {
-
-       /* Unknown source */
-
-       fprintf(stderr, "%s: illegal pingSource: %s\n", progname, src);
-       goto ping_init_error;
-    }
+    if (debug_p)
+      {
+        ping_target *t;
+        fprintf (stderr, "%s: Target list:\n", progname);
+        for (t = pi->targets; t; t = t->next)
+          {
+            struct sockaddr_in *iaddr = (struct sockaddr_in *) &(t->address);
+            unsigned long ip = iaddr->sin_addr.s_addr;
+            fprintf (stderr, "%s:   ", progname);
+            print_host (stderr, ip, t->name);
+          }
+      }
 
     /* Make sure there is something to ping */
 
     if (pi->targets == NULL) {
-       fprintf(stderr, "%s: nothing to ping", progname);
-       goto ping_init_error;
+      goto ping_init_error;
     }
 
     /* Count the targets */
@@ -794,6 +974,7 @@ ping_init_error:
     return NULL;
 }
 
+
 /*
  * Ping a host.
  *
@@ -831,8 +1012,13 @@ sendping(ping_info *pi, ping_target *pt)
     ICMP_CHECKSUM(icmph) = 0;
     ICMP_ID(icmph) = pi->pid;
     ICMP_SEQ(icmph) = pi->seq++;
+# ifdef GETTIMEOFDAY_TWO_ARGS
     gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)],
                 (struct timezone *) 0);
+# else
+    gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)]);
+# endif
+
     strcpy((char *) &packet[sizeof(struct ICMP) + sizeof(struct timeval)],
           pt->name);
     ICMP_CHECKSUM(icmph) = checksum((u_short *)packet, pcktsiz);
@@ -930,13 +1116,13 @@ checksum(u_short *packet, int size)
  */
 
 static Bogie *
-getping(sonar_info *si, ping_info *pi, int ttl
+getping(sonar_info *si, ping_info *pi) 
 {
 
     /* Local Variables */
 
     struct sockaddr from;
-    int fromlen;
+    unsigned int fromlen;  /* Posix says socklen_t, but that's not portable */
     int result;
     u_char packet[1024];
     struct timeval now;
@@ -949,6 +1135,8 @@ getping(sonar_info *si, ping_info *pi, int ttl)
     char *name;
     struct sigaction sa;
     struct itimerval it;
+    fd_set rfds;
+    struct timeval tv;
 
     /* Set up a signal to interupt our wait for a packet */
 
@@ -974,15 +1162,31 @@ getping(sonar_info *si, ping_info *pi, int ttl)
     /* Wait for a result packet */
 
     fromlen = sizeof(from);
-    while (! timer_expired &&
-          (result = recvfrom(pi->icmpsock, packet, sizeof(packet),
-                             0, &from, &fromlen)) > 0) {
+    while (! timer_expired) {
+      tv.tv_usec=pi->timeout;
+      tv.tv_sec=0;
+#if 0
+      /* This breaks on BSD, which uses bzero() in the definition of FD_ZERO */
+      FD_ZERO(&rfds);
+#else
+      memset (&rfds, 0, sizeof(rfds));
+#endif
+      FD_SET(pi->icmpsock,&rfds);
+      /* only wait a little while, in case we raced with the timer expiration.
+         From Valentijn Sessink <valentyn@openoffice.nl> */
+      if (select(pi->icmpsock+1, &rfds, NULL, NULL, &tv) >0) {
+        result = recvfrom(pi->icmpsock, packet, sizeof(packet),
+                      0, &from, &fromlen);
 
        /* Check the packet */
 
+# ifdef GETTIMEOFDAY_TWO_ARGS
        gettimeofday(&now, (struct timezone *) 0);
+# else
+       gettimeofday(&now);
+# endif
        ip = (struct ip *) packet;
-       iphdrlen = ip->ip_hl << 2;
+        iphdrlen = IP_HDRLEN(ip) << 2;
        icmph = (struct ICMP *) &packet[iphdrlen];
 
        /* Was the packet a reply?? */
@@ -1030,7 +1234,7 @@ getping(sonar_info *si, ping_info *pi, int ttl)
 
        /* Create the new Bogie and add it to the list we are building */
 
-       if ((new = newBogie(name, 0, si->current, ttl)) == NULL)
+       if ((new = newBogie(name, 0, si->current, si->TTL)) == NULL)
            return bl;
        new->next = bl;
        bl = new;
@@ -1042,6 +1246,7 @@ getping(sonar_info *si, ping_info *pi, int ttl)
        new->distance = delta(then, &now) / 100;
        if (new->distance == 0)
                new->distance = 2; /* HACK */
+      }
     }
 
     /* Done */
@@ -1064,6 +1269,11 @@ static Bogie *
 ping(sonar_info *si, void *vpi) 
 {
 
+    /*
+     * This tries to distribute the targets evely around the field of the
+     * sonar.
+     */
+
     ping_info *pi = (ping_info *) vpi;
     static ping_target *ptr = NULL;
 
@@ -1089,7 +1299,7 @@ ping(sonar_info *si, void *vpi)
 
     /* Get the results */
 
-    return getping(si, pi, TTL);
+    return getping(si, pi);
 }
 
 #endif /* HAVE_PING */
@@ -1150,8 +1360,9 @@ init_sim(void)
            return NULL;
        }
        sprintf(si->teamA[i].name, "%s%03d", si->teamAID, i+1);
-       si->teamA[i].nexttick = (int) (90.0 * random() / RAND_MAX);
-       si->teamA[i].nextdist = (int) (100.0 * random() / RAND_MAX);
+       si->teamA[i].nexttick = random() % 90;
+       si->teamA[i].nextdist = random() % 100;
+       si->teamA[i].movedonsweep = -1;
     }
 
     /* Team B */
@@ -1172,8 +1383,9 @@ init_sim(void)
            return NULL;
        }
        sprintf(si->teamB[i].name, "%s%03d", si->teamBID, i+1);
-       si->teamB[i].nexttick = (int) (90.0 * random() / RAND_MAX);
-       si->teamB[i].nextdist = (int) (100.0 * random() / RAND_MAX);
+       si->teamB[i].nexttick = random() % 90;
+       si->teamB[i].nextdist = random() % 100;
+       si->teamB[i].movedonsweep = -1;
     }
 
     /* Done */
@@ -1181,6 +1393,26 @@ init_sim(void)
     return si;
 }
 
+/*
+ * Creates and returns a drawing mask for the scope:
+ * mask out anything outside of the disc.
+ */
+static Pixmap
+scope_mask (Display *dpy, Window win, sonar_info *si)
+{
+  XGCValues gcv;
+  Pixmap mask = XCreatePixmap(dpy, win, si->width, si->height, 1);
+  GC gc = XCreateGC (dpy, mask, 0, &gcv);
+  XSetFunction (dpy, gc, GXclear);
+  XFillRectangle (dpy, mask, gc, 0, 0, si->width, si->height);
+  XSetFunction (dpy, gc, GXset);
+  XFillArc(dpy, mask, gc, si->minx, si->miny, 
+           si->maxx - si->minx, si->maxy - si->miny,
+           0, 360 * 64);
+  return mask;
+}
+
+
 /*
  * Initialize the Sonar.
  *
@@ -1216,7 +1448,7 @@ init_sonar(Display *dpy, Window win)
 
     si->dpy = dpy;
     si->win = win;
-    si->visable = NULL;
+    si->visible = NULL;
     XGetWindowAttributes(dpy, win, &xwa);
     si->cmap = xwa.colormap;
     si->width = xwa.width;
@@ -1229,6 +1461,7 @@ init_sonar(Display *dpy, Window win)
     si->miny = si->centrey - MY_MIN(si->centrex, si->centrey) + 10;
     si->radius = si->maxx - si->centrex;
     si->current = 0;
+    si->sweepnum = 0;
 
     /* Get the font */
 
@@ -1242,7 +1475,9 @@ init_sonar(Display *dpy, Window win)
     /* Get the delay between animation frames */
 
     si->delay = get_integer_resource ("delay", "Integer");
+
     if (si->delay < 0) si->delay = 0;
+    si->TTL = get_integer_resource("ttl", "TTL");
 
     /* Create the Graphics Contexts that will be used to draw things */
 
@@ -1258,6 +1493,14 @@ init_sonar(Display *dpy, Window win)
                                        dpy, si->cmap);
     si->grid = XCreateGC (dpy, win, GCForeground, &gcv);
 
+    /* Install the clip mask... */
+    {
+      Pixmap mask = scope_mask (dpy, win, si);
+      XSetClipMask(dpy, si->text, mask);
+      XSetClipMask(dpy, si->erase, mask);
+      XFreePixmap (dpy, mask); /* it's been copied into the GCs */
+    }
+
     /* Compute pixel values for fading text on the display */
 
     XParseColor(dpy, si->cmap, 
@@ -1301,6 +1544,9 @@ init_sonar(Display *dpy, Window win)
                      si->sweep_colors, &si->sweep_segs,
                      False, True, False);
 
+    if (si->sweep_segs <= 0)
+      si->sweep_segs = 1;
+
     /* Done */
 
     return si;
@@ -1316,9 +1562,8 @@ updateLocation(sim_target *t)
 
     int xdist, xtick;
 
-    t->movedlasttick = 1;
-    xtick = (int) (3.0 * random() / RAND_MAX) - 1;
-    xdist = (int) (11.0 * random() / RAND_MAX) - 5;
+    xtick = (int) (random() %  3) - 1;
+    xdist = (int) (random() % 11) - 5;
     if (((t->nexttick + xtick) < 90) && ((t->nexttick + xtick) >= 0))
        t->nexttick += xtick;
     else
@@ -1355,29 +1600,30 @@ simulator(sonar_info *si, void *vinfo)
 
     for (i = 0; i < info->numA; i++) {
        t = &info->teamA[i];
-       if (!t->movedlasttick && (t->nexttick == (si->current * -1))) {
-           new = newBogie(strdup(t->name), t->nextdist, si->current, TTL);
+       if ((t->movedonsweep != si->sweepnum) &&
+           (t->nexttick == (si->current * -1))) {
+           new = newBogie(strdup(t->name), t->nextdist, si->current, si->TTL);
            if (list != NULL)
                new->next = list;
            list = new;
            updateLocation(t);
-       } else
-           t->movedlasttick = 0;
+           t->movedonsweep = si->sweepnum;
+       }
     }
 
     /* Team B */
 
     for (i = 0; i < info->numB; i++) {
        t = &info->teamB[i];
-       if (!t->movedlasttick && (t->nexttick == (si->current * -1))) {
-           new = newBogie(strdup(t->name), t->nextdist, si->current, TTL);
+       if ((t->movedonsweep != si->sweepnum) &&
+           (t->nexttick == (si->current * -1))) {
+           new = newBogie(strdup(t->name), t->nextdist, si->current, si->TTL);
            if (list != NULL)
                new->next = list;
            list = new;
-           t->movedlasttick = 1;
            updateLocation(t);
-       } else
-           t->movedlasttick = 0;
+           t->movedonsweep = si->sweepnum;
+       }
     }
 
     /* Done */
@@ -1454,7 +1700,8 @@ DrawBogie(sonar_info *si, int draw, char *name, int degrees,
 
     /* Compute the coordinates of the object */
 
-    distance = (log((double) distance) / 10.0) * si->radius;
+    if (distance != 0)
+      distance = (log((double) distance) / 10.0) * si->radius;
     x = ox + ((double) distance * cos(4.0 * ((double) degrees)/57.29578));
     y = oy - ((double) distance * sin(4.0 * ((double) degrees)/57.29578));
 
@@ -1564,13 +1811,13 @@ Sonar(sonar_info *si, Bogie *bl)
     Bogie *bp, *prev;
     int i;
 
-    /* Check for expired tagets and remove them from the visable list */
+    /* Check for expired tagets and remove them from the visible list */
 
     prev = NULL;
-    for (bp = si->visable; bp != NULL; bp = bp->next) {
+    for (bp = si->visible; bp != NULL; bp = (bp ? bp->next : 0)) {
 
        /*
-        * Remove it from the visable list if it's expired or we have
+        * Remove it from the visible list if it's expired or we have
         * a new target with the same name.
         */
 
@@ -1581,10 +1828,11 @@ Sonar(sonar_info *si, Bogie *bl)
            DrawBogie(si, 0, bp->name, bp->tick,
                      bp->distance, bp->ttl, bp->age);
            if (prev == NULL)
-               si->visable = bp->next;
+               si->visible = bp->next;
            else
                prev->next = bp->next;
            freeBogie(bp);
+            bp = prev;
        } else
            prev = bp;
     }
@@ -1610,17 +1858,17 @@ Sonar(sonar_info *si, Bogie *bl)
                (4 * 64));
     }
 
-    /* Move the new targets to the visable list */
+    /* Move the new targets to the visible list */
 
     for (bp = bl; bp != (Bogie *) 0; bp = bl) {
        bl = bl->next;
-       bp->next = si->visable;
-       si->visable = bp;
+       bp->next = si->visible;
+       si->visible = bp;
     }
 
-    /* Draw the visable targets */
+    /* Draw the visible targets */
 
-    for (bp = si->visable; bp != NULL; bp = bp->next) {
+    for (bp = si->visible; bp != NULL; bp = bp->next) {
        if (bp->age < bp->ttl)          /* grins */
           DrawBogie(si, 1, bp->name, bp->tick, bp->distance, bp->ttl,bp->age);
     }
@@ -1630,6 +1878,124 @@ Sonar(sonar_info *si, Bogie *bl)
     drawGrid(si);
 }
 
+
+static ping_target *
+parse_mode (Bool ping_works_p)
+{
+  char *source = get_string_resource ("ping", "Ping");
+  char *token, *end;
+  char dummy;
+
+  ping_target *hostlist = 0;
+
+  if (!source) source = strdup("");
+
+  if (!*source || !strcmp (source, "default"))
+    {
+# ifdef HAVE_PING
+      if (ping_works_p)                /* if root or setuid, ping will work. */
+        source = strdup("subnet/29,/etc/hosts");
+      else
+# endif
+        source = strdup("simulation");
+    }
+
+  token = source;
+  end = source + strlen(source);
+  while (token < end)
+    {
+      char *next;
+# ifdef HAVE_PING
+      ping_target *new;
+      struct stat st;
+      unsigned int n0=0, n1=0, n2=0, n3=0, m=0;
+      char d;
+# endif /* HAVE_PING */
+
+      for (next = token;
+           *next &&
+           *next != ',' && *next != ' ' && *next != '\t' && *next != '\n';
+           next++)
+        ;
+      *next = 0;
+
+
+      if (debug_p)
+        fprintf (stderr, "%s: parsing %s\n", progname, token);
+
+      if (!strcmp (token, "simulation"))
+        return 0;
+
+      if (!ping_works_p)
+        {
+          fprintf(stderr,
+           "%s: this program must be setuid to root for `ping mode' to work.\n"
+             "       Running in `simulation mode' instead.\n",
+                  progname);
+          return 0;
+        }
+
+#ifdef HAVE_PING
+      if ((4 == sscanf (token, "%u.%u.%u/%u %c",    &n0,&n1,&n2,    &m,&d)) ||
+          (5 == sscanf (token, "%u.%u.%u.%u/%u %c", &n0,&n1,&n2,&n3,&m,&d)))
+        {
+          /* subnet: A.B.C.D/M
+             subnet: A.B.C/M
+           */
+          unsigned long ip = (n0 << 24) | (n1 << 16) | (n2 << 8) | n3;
+          new = subnetHostsList(ip, m);
+        }
+      else if (4 == sscanf (token, "%u.%u.%u.%u %c", &n0, &n1, &n2, &n3, &d))
+        {
+          /* IP: A.B.C.D
+           */
+          new = newHost (token);
+        }
+      else if (!strcmp (token, "subnet"))
+        {
+          new = subnetHostsList(0, 24);
+        }
+      else if (1 == sscanf (token, "subnet/%u %c", &m, &dummy))
+        {
+          new = subnetHostsList(0, m);
+        }
+      else if (*token == '.' || *token == '/' || !stat (token, &st))
+        {
+          /* file name
+           */
+          new = readPingHostsFile (token);
+        }
+      else
+        {
+          /* not an existant file - must be a host name
+           */
+          new = newHost (token);
+        }
+
+      if (new)
+        {
+          ping_target *nn = new;
+          while (nn && nn->next)
+            nn = nn->next;
+          nn->next = hostlist;
+          hostlist = new;
+
+          sensor = ping;
+        }
+#endif /* HAVE_PING */
+
+      token = next + 1;
+      while (token < end &&
+             (*token == ',' || *token == ' ' ||
+              *token == '\t' || *token == '\n'))
+        token++;
+    }
+
+  return hostlist;
+}
+
+
+
 /*
  * Main screen saver hack.
  *
@@ -1648,73 +2014,57 @@ screenhack(Display *dpy, Window win)
     struct timeval start, finish;
     Bogie *bl;
     long sleeptime;
-    char *mode;
 
-    /* 
-     * Initialize 
-     * Adding new sensors would involve supporting more modes other than
-     * ping and initiailizing the sensor in the same way.
-     */
+    debug_p = get_boolean_resource ("debug", "Debug");
 
-    mode = get_string_resource("mode", "Mode");
+    sensor = 0;
+# ifdef HAVE_PING
+    sensor_info = (void *) init_ping();
+# else  /* !HAVE_PING */
+    sensor_info = 0;
+    parse_mode (0);  /* just to check argument syntax */
+# endif /* !HAVE_PING */
 
-    if (!mode || !*mode || !strcmp(mode, "default")) /* Pick a good default. */
+    if (sensor == 0)
       {
-#ifdef HAVE_PING
-        if (geteuid() == 0)    /* we're root or setuid -- ping will work. */
-          mode = "ping";
-        else
-#endif
-          mode = "simulation";
+        sensor = simulator;
+        if ((sensor_info = (void *) init_sim()) == NULL)
+          exit(1);
       }
 
-#ifdef HAVE_PING
-    if (strcmp(mode, "ping") == 0) {
-       sensor = ping;
-       if ((sensor_info = (void *) init_ping()) == (void *) 0)
-          {
-            fprintf (stderr, "%s: running in `simulation mode' instead.\n",
-                     progname);
-           goto SIM;
-          }
-    } else
-#endif /* HAVE_PING */
-    if (strcmp(mode, "simulation") == 0) {
-#ifdef HAVE_PING
-    SIM:
-#endif
-       sensor = simulator;
-       if ((sensor_info = (void *) init_sim()) == NULL)
-           exit(1);
-    } else {
-       fprintf(stderr, "%s: unsupported Sonar mode: %s\n", progname, mode);
-       fprintf(stderr,
-                "\tCurrently supported modes are `ping' and `simulation'\n");
-       exit(1);
-    }
     if ((si = init_sonar(dpy, win)) == (sonar_info *) 0)
        exit(1);
-  
-    /* Sonar loop */
 
-    TTL = get_integer_resource("ttl", "TTL");
+
+    /* Sonar loop */
 
     while (1) {
 
        /* Call the sensor and display the results */
 
+# ifdef GETTIMEOFDAY_TWO_ARGS
        gettimeofday(&start, (struct timezone *) 0);
+# else
+       gettimeofday(&start);
+# endif
        bl = sensor(si, sensor_info);
        Sonar(si, bl);
 
         /* Set up and sleep for the next one */
 
        si->current = (si->current - 1) % 90;
+       if (si->current == 0)
+         si->sweepnum++;
        XSync (dpy, False);
+# ifdef GETTIMEOFDAY_TWO_ARGS
        gettimeofday(&finish, (struct timezone *) 0);
+# else
+       gettimeofday(&finish);
+# endif
        sleeptime = si->delay - delta(&start, &finish);
         screenhack_handle_events (dpy);
        if (sleeptime > 0L)
            usleep(sleeptime);
+
     }
 }