From http://www.jwz.org/xscreensaver/xscreensaver-5.37.tar.gz
[xscreensaver] / hacks / glx / sonar-icmp.c
index fa1ea893df64faca035eaac4ab1381fd8fd26f33..e482ab6a580617061a58ca731922c86c4d5a60a6 100644 (file)
@@ -1,4 +1,4 @@
-/* sonar, Copyright (c) 1998-2012 Jamie Zawinski and Stephen Martin
+/* sonar, Copyright (c) 1998-2017 Jamie Zawinski and Stephen Martin
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
@@ -14,6 +14,7 @@
 #include "screenhackI.h"
 #include "sonar.h"
 #include "version.h"
+#include "async_netdb.h"
 
 #undef usleep /* conflicts with unistd.h on OSX */
 
@@ -39,7 +40,7 @@
   */
 #endif
 
-#ifndef USE_IPHONE
+#ifndef HAVE_MOBILE
 # define READ_FILES
 #endif
 
@@ -52,7 +53,9 @@
 # include <sys/types.h>
 # include <sys/time.h>
 # include <sys/ipc.h>
-# include <sys/shm.h>
+# ifndef HAVE_ANDROID
+#  include <sys/shm.h>
+# endif
 # include <sys/socket.h>
 # include <netinet/in_systm.h>
 # include <netinet/in.h>
@@ -61,6 +64,7 @@
 # include <netinet/udp.h>
 # include <arpa/inet.h>
 # include <netdb.h>
+# include <errno.h>
 # ifdef HAVE_GETIFADDRS
 #  include <ifaddrs.h>
 # endif
 # undef HAVE_PING
 #endif
 
-#ifndef USE_IPHONE
+#ifndef HAVE_MOBILE
 # define LOAD_FILES
 #endif
 
 #ifndef HAVE_PING
 
 sonar_sensor_data *
-init_ping (Display *dpy, char **error_ret, char **desc_ret, 
-           const char *subnet, int timeout,
-           Bool resolve_p, Bool times_p, Bool debug_p)
+sonar_init_ping (Display *dpy, char **error_ret, char **desc_ret, 
+                 const char *subnet, int timeout,
+                 Bool resolve_p, Bool times_p, Bool debug_p)
 {
   if (! (!subnet || !*subnet || !strcmp(subnet, "default")))
     fprintf (stderr, "%s: not compiled with support for pinging hosts.\n",
@@ -130,6 +134,8 @@ static long delta(struct timeval *, struct timeval *);
 
 
 typedef struct {
+  Display *dpy;                 /* Only used to get *useThreads. */
+
   char *version;               /* short version number of xscreensaver */
   int icmpsock;                        /* socket for sending pings */
   int pid;                     /* our process ID */
@@ -149,13 +155,17 @@ typedef struct {
 } ping_data;
 
 typedef struct {
-  struct sockaddr address;     /* ip address */
+  async_name_from_addr_t lookup_name;
+  async_addr_from_name_t lookup_addr;
+  async_netdb_sockaddr_storage_t address;      /* ip address */
+  socklen_t addrlen;
+  char *fallback;
 } ping_bogie;
 
 
 
 /* Packs an IP address quad into bigendian network order. */
-static unsigned long
+static in_addr_t
 pack_addr (unsigned int a, unsigned int b, unsigned int c, unsigned int d)
 {
   unsigned long i = (((a & 255) << 24) |
@@ -181,27 +191,24 @@ unpack_addr (unsigned long addr,
 
 
 
-/* If resolves the bogie's name (either a hostname or ip address string)
-   to a hostent.  Returns 1 if successful, 0 if it failed to resolve.
+/* Resolves the bogie's name (either a hostname or ip address string)
+   to a hostent.  Returns 1 if successful, 0 if something went wrong.
  */
 static int
 resolve_bogie_hostname (ping_data *pd, sonar_bogie *sb, Bool resolve_p)
 {
   ping_bogie *pb = (ping_bogie *) sb->closure;
-  struct hostent *hent;
-  struct sockaddr_in *iaddr;
 
   unsigned int ip[4];
   char c;
 
-  iaddr = (struct sockaddr_in *) &(pb->address);
-  iaddr->sin_family = AF_INET;
-
   if (4 == sscanf (sb->name, " %u.%u.%u.%u %c",
                    &ip[0], &ip[1], &ip[2], &ip[3], &c))
     {
       /* It's an IP address.
        */
+      struct sockaddr_in *iaddr = (struct sockaddr_in *) &(pb->address);
+
       if (ip[3] == 0)
         {
           if (pd->debug_p > 1)
@@ -210,22 +217,22 @@ resolve_bogie_hostname (ping_data *pd, sonar_bogie *sb, Bool resolve_p)
           return 0;
         }
 
+      iaddr->sin_family = AF_INET;
       iaddr->sin_addr.s_addr = pack_addr (ip[0], ip[1], ip[2], ip[3]);
-      if (resolve_p)
-        hent = gethostbyaddr ((const char *) &iaddr->sin_addr.s_addr,
-                              sizeof(iaddr->sin_addr.s_addr),
-                              AF_INET);
-      else
-        hent = 0;
+      pb->addrlen = sizeof(struct sockaddr_in);
 
-      if (pd->debug_p > 1)
-        fprintf (stderr, "%s:   %s => %s\n",
-                 progname, sb->name,
-                 ((hent && hent->h_name && *hent->h_name)
-                  ? hent->h_name : "<unknown>"));
-
-      if (hent && hent->h_name && *hent->h_name)
-        sb->name = strdup (hent->h_name);
+      if (resolve_p)
+        {
+          pb->lookup_name =
+            async_name_from_addr_start (pd->dpy,
+                                        (const struct sockaddr *)&pb->address,
+                                        pb->addrlen);
+          if (!pb->lookup_name)
+            {
+              fprintf (stderr, "%s:   unable to start host resolution.\n",
+                       progname);
+            }
+        }
     }
   else
     {
@@ -236,6 +243,7 @@ resolve_bogie_hostname (ping_data *pd, sonar_bogie *sb, Bool resolve_p)
       if (!strcmp (sb->name, "ssh-rsa") ||
           !strcmp (sb->name, "ssh-dsa") ||
           !strcmp (sb->name, "ssh-dss") ||
+          !strncmp (sb->name, "ecdsa-", 6) ||
           strlen (sb->name) >= 80)
         return 0;
 
@@ -258,53 +266,125 @@ resolve_bogie_hostname (ping_data *pd, sonar_bogie *sb, Bool resolve_p)
           return 0;
         }
 
-      hent = gethostbyname (sb->name);
-      if (!hent)
+      pb->lookup_addr = async_addr_from_name_start(pd->dpy, sb->name);
+      if (!pb->lookup_addr)
         {
           if (pd->debug_p)
-            fprintf (stderr, "%s:   could not resolve host:  %s\n",
-                     progname, sb->name);
+            /* Either address space exhaustion or RAM exhaustion. */
+            fprintf (stderr, "%s:   unable to start host resolution.\n",
+                     progname);
           return 0;
         }
+    }
+  return 1;
+}
 
-      memcpy (&iaddr->sin_addr, hent->h_addr_list[0],
-              sizeof(iaddr->sin_addr));
 
-      if (pd->debug_p > 1)
+static void
+print_address (FILE *out, int width, const void *sockaddr, socklen_t addrlen)
+{
+#ifdef HAVE_GETADDRINFO
+  char buf[NI_MAXHOST];
+#else
+  char buf[50];
+#endif
+
+  const struct sockaddr *addr = (const struct sockaddr *)sockaddr;
+  const char *ips = buf;
+
+  if (!addr->sa_family)
+    ips = "<no address>";
+  else
+    {
+#ifdef HAVE_GETADDRINFO
+      int gai_error = getnameinfo (sockaddr, addrlen, buf, sizeof(buf),
+                                   NULL, 0, NI_NUMERICHOST);
+      if (gai_error == EAI_SYSTEM)
+        ips = strerror(errno);
+      else if (gai_error)
+        ips = gai_strerror(gai_error);
+#else
+      switch (addr->sa_family)
         {
-          unsigned int a, b, c, d;
-          unpack_addr (iaddr->sin_addr.s_addr, &a, &b, &c, &d);
-          fprintf (stderr, "%s:   %s => %d.%d.%d.%d\n",
-                   progname, sb->name, a, b, c, d);
+        case AF_INET:
+          {
+            u_long ip = ((struct sockaddr_in *)sockaddr)->sin_addr.s_addr;
+            unsigned int a, b, c, d;
+            unpack_addr (ip, &a, &b, &c, &d);   /* ip is in network order */
+            sprintf (buf, "%u.%u.%u.%u", a, b, c, d);
+          }
+          break;
+        default:
+          ips = "<unknown>";
+          break;
         }
+#endif
     }
-  return 1;
+
+  fprintf (out, "%-*s", width, ips);
 }
 
 
 static void
-print_host (FILE *out, unsigned long ip, const char *name)
+print_host (FILE *out, const sonar_bogie *sb)
 {
-  char ips[50];
-  unsigned int a, b, c, d;
-  unpack_addr (ip, &a, &b, &c, &d);            /* ip is in network order */
-  sprintf (ips, "%u.%u.%u.%u", a, b, c, d);
+  const ping_bogie *pb = (const ping_bogie *) sb->closure;
+  const char *name = sb->name;
   if (!name || !*name) name = "<unknown>";
-  fprintf (out, "%-16s %s\n", ips, name);
+  print_address (out, 16, &pb->address, pb->addrlen);
+  fprintf (out, " %s\n", name);
 }
 
 
-/* Create a sonar_bogie a host name or ip address string.
+static Bool
+is_address_ok(Bool debug_p, const sonar_bogie *b)
+{
+  const ping_bogie *pb = (const ping_bogie *) b->closure;
+  const struct sockaddr *addr = (const struct sockaddr *)&pb->address;
+
+  switch (addr->sa_family)
+    {
+    case AF_INET:
+      {
+        struct sockaddr_in *iaddr = (struct sockaddr_in *) addr;
+
+        /* Don't ever use loopback (127.0.0.x) hosts */
+        unsigned long ip = iaddr->sin_addr.s_addr;
+        if ((ntohl (ip) & 0xFFFFFF00L) == 0x7f000000L)  /* 127.0.0.x */
+          {
+            if (debug_p)
+              fprintf (stderr, "%s:   ignoring loopback host %s\n",
+                       progname, b->name);
+            return False;
+          }
+
+        /* Don't ever use broadcast (255.x.x.x) hosts */
+        if ((ntohl (ip) & 0xFF000000L) == 0xFF000000L)  /* 255.x.x.x */
+          {
+            if (debug_p)
+              fprintf (stderr, "%s:   ignoring broadcast host %s\n",
+                       progname, b->name);
+            return False;
+          }
+      }
+
+      break;
+    }
+
+  return True;
+}
+
+
+/* Create a sonar_bogie from a host name or ip address string.
    Returns NULL if the name could not be resolved.
  */
 static sonar_bogie *
-bogie_for_host (sonar_sensor_data *ssd, const char *name, Bool resolve_p)
+bogie_for_host (sonar_sensor_data *ssd, const char *name, const char *fallback)
 {
   ping_data *pd = (ping_data *) ssd->closure;
   sonar_bogie *b = (sonar_bogie *) calloc (1, sizeof(*b));
   ping_bogie *pb = (ping_bogie *) calloc (1, sizeof(*pb));
-  struct sockaddr_in *iaddr;
-  unsigned long ip;
+  Bool resolve_p = pd->resolve_p;
 
   b->name = strdup (name);
   b->closure = pb;
@@ -312,37 +392,25 @@ bogie_for_host (sonar_sensor_data *ssd, const char *name, Bool resolve_p)
   if (! resolve_bogie_hostname (pd, b, resolve_p))
     goto FAIL;
 
-  iaddr = (struct sockaddr_in *) &(pb->address);
-
-  /* Don't ever use loopback (127.0.0.x) hosts */
-  ip = iaddr->sin_addr.s_addr;
-  if ((ntohl (ip) & 0xFFFFFF00L) == 0x7f000000L)  /* 127.0.0.x */
-    {
-      if (pd->debug_p)
-        fprintf (stderr, "%s:   ignoring loopback host %s\n", 
-                 progname, b->name);
-      goto FAIL;
-    }
-
-  /* Don't ever use broadcast (255.x.x.x) hosts */
-  if ((ntohl (ip) & 0xFF000000L) == 0xFF000000L)  /* 255.x.x.x */
-    {
-      if (pd->debug_p)
-        fprintf (stderr, "%s:   ignoring broadcast host %s\n",
-                 progname, b->name);
-      goto FAIL;
-    }
+  if (! pb->lookup_addr && ! is_address_ok (pd->debug_p, b))
+    goto FAIL;
 
   if (pd->debug_p > 1)
     {
       fprintf (stderr, "%s:   added ", progname);
-      print_host (stderr, ip, b->name);
+      print_host (stderr, b);
     }
 
+  if (fallback)
+    pb->fallback = strdup (fallback);
   return b;
 
  FAIL:
   if (b) sonar_free_bogie (ssd, b);
+
+  if (fallback)
+    return bogie_for_host (ssd, fallback, NULL);
+
   return 0;
 }
 
@@ -381,12 +449,12 @@ read_hosts_file (sonar_sensor_data *ssd, const char *filename)
   fp = fopen(filename, "r");
   if (!fp)
     {
-      char buf[1024];
-      sprintf(buf, "%s:  %s", progname, filename);
-#ifdef HAVE_COCOA
+      char buf2[1024];
+      sprintf(buf2, "%s:  %s", progname, filename);
+#ifdef HAVE_JWXYZ
       if (pd->debug_p)  /* on OSX don't syslog this */
 #endif
-        perror (buf);
+        perror (buf2);
       return 0;
     }
 
@@ -440,11 +508,13 @@ read_hosts_file (sonar_sensor_data *ssd, const char *filename)
 
       /* Create a new target using first the name then the address */
 
-      new = 0;
-      if (name)
-        new = bogie_for_host (ssd, name, pd->resolve_p);
-      if (!new && addr)
-        new = bogie_for_host (ssd, addr, pd->resolve_p);
+      if (!name)
+        {
+          name = addr;
+          addr = NULL;
+        }
+
+      new = bogie_for_host (ssd, name, addr);
 
       if (new)
         {
@@ -459,50 +529,122 @@ read_hosts_file (sonar_sensor_data *ssd, const char *filename)
 #endif /* READ_FILES */
 
 
+static sonar_bogie **
+found_duplicate_host (const ping_data *pd, sonar_bogie **list,
+                      sonar_bogie *bogie)
+{
+  if (pd->debug_p)
+  {
+    fprintf (stderr, "%s: deleted duplicate: ", progname);
+    print_host (stderr, bogie);
+  }
+
+  return list;
+}
+
+
+static sonar_bogie **
+find_duplicate_host (const ping_data *pd, sonar_bogie **list,
+                     sonar_bogie *bogie)
+{
+  const ping_bogie *pb = (const ping_bogie *) bogie->closure;
+  const struct sockaddr *addr1 = (const struct sockaddr *) &(pb->address);
+
+  while(*list)
+    {
+      const ping_bogie *pb2 = (const ping_bogie *) (*list)->closure;
+
+      if (!pb2->lookup_addr)
+        {
+          const struct sockaddr *addr2 =
+            (const struct sockaddr *) &(pb2->address);
+
+          if (addr1->sa_family == addr2->sa_family)
+            {
+              switch (addr1->sa_family)
+                {
+                case AF_INET:
+                  {
+                    unsigned long ip1 =
+                      ((const struct sockaddr_in *)addr1)->sin_addr.s_addr;
+                    const struct sockaddr_in *i2 =
+                      (const struct sockaddr_in *)addr2;
+                    unsigned long ip2 = i2->sin_addr.s_addr;
+
+                    if (ip1 == ip2)
+                      return found_duplicate_host (pd, list, bogie);
+                  }
+                  break;
+#ifdef AF_INET6
+                case AF_INET6:
+                  {
+                    if (! memcmp(
+                            &((const struct sockaddr_in6 *)addr1)->sin6_addr,
+                            &((const struct sockaddr_in6 *)addr2)->sin6_addr,
+                            16))
+                      return found_duplicate_host (pd, list, bogie);
+                  }
+                  break;
+#endif
+                default:
+                  {
+                    /* Fallback behavior: Just memcmp the two addresses.
+
+                       For this to work, unused space in the sockaddr must be
+                       set to zero. Which may actually be the case:
+                       - async_addr_from_name_finish won't put garbage into
+                         sockaddr_in.sin_zero or elsewhere unless getaddrinfo
+                         does.
+                       - ping_bogie is allocated with calloc(). */
+
+                    if (pb->addrlen == pb2->addrlen &&
+                        ! memcmp(addr1, addr2, pb->addrlen))
+                      return found_duplicate_host (pd, list, bogie);
+                  }
+                  break;
+                }
+            }
+        }
+
+      list = &(*list)->next;
+    }
+
+  return NULL;
+}
+
+
 static sonar_bogie *
 delete_duplicate_hosts (sonar_sensor_data *ssd, sonar_bogie *list)
 {
   ping_data *pd = (ping_data *) ssd->closure;
   sonar_bogie *head = list;
-  sonar_bogie *sb;
+  sonar_bogie *sb = head;
 
-  for (sb = head; sb; sb = sb->next)
+  while (sb)
     {
       ping_bogie *pb = (ping_bogie *) sb->closure;
-      struct sockaddr_in *i1 = (struct sockaddr_in *) &(pb->address);
-      unsigned long ip1 = i1->sin_addr.s_addr;
 
-      sonar_bogie *sb2;
-      for (sb2 = sb; sb2; sb2 = sb2->next)
+      if (!pb->lookup_addr)
         {
-          if (sb2 && sb2->next)
-            {
-              ping_bogie *pb2 = (ping_bogie *) sb2->next->closure;
-              struct sockaddr_in *i2 = (struct sockaddr_in *) &(pb2->address);
-              unsigned long ip2 = i2->sin_addr.s_addr;
-
-              if (ip1 == ip2)
-                {
-                  if (pd->debug_p)
-                    {
-                      fprintf (stderr, "%s: deleted duplicate: ", progname);
-                      print_host (stderr, ip2, sb2->next->name);
-                    }
-                  sb2->next = sb2->next->next;
-                  /* #### sb leaked */
-                }
-            }
+          sonar_bogie **sb2 = find_duplicate_host (pd, &sb->next, sb);
+          if (sb2)
+            *sb2 = (*sb2)->next;
+            /* #### sb leaked */
+          else
+            sb = sb->next;
         }
+      else
+        sb = sb->next;
     }
 
   return head;
 }
 
 
-static unsigned int
-width_mask (int width)
+static unsigned long
+width_mask (unsigned long width)
 {
-  unsigned int m = 0;
+  unsigned long m = 0;
   int i;
   for (i = 0; i < width; i++)
     m |= (1L << (31-i));
@@ -511,8 +653,8 @@ width_mask (int width)
 
 
 #ifdef HAVE_GETIFADDRS
-static int
-mask_width (unsigned int mask)
+static unsigned int
+mask_width (unsigned long mask)
 {
   int i;
   for (i = 0; i < 32; i++)
@@ -590,9 +732,16 @@ subnet_hosts (sonar_sensor_data *ssd, char **error_ret, char **desc_ret,
               if (pd->debug_p)
                 fprintf (stderr, "%s:     if: %4s: %s\n", progname,
                          ifa->ifa_name,
-                         (ifa->ifa_addr->sa_family == AF_UNIX  ? "local" :
+                         (
+# ifdef AF_UNIX
+                          ifa->ifa_addr->sa_family == AF_UNIX  ? "local" :
+# endif
+# ifdef AF_LINK
                           ifa->ifa_addr->sa_family == AF_LINK  ? "link"  :
+# endif
+# ifdef AF_INET6
                           ifa->ifa_addr->sa_family == AF_INET6 ? "ipv6"  :
+# endif
                           "other"));
               continue;
             }
@@ -615,13 +764,12 @@ subnet_hosts (sonar_sensor_data *ssd, char **error_ret, char **desc_ret,
            */
           if (mask_width (mask) == 31)
             {
-              char buf[255];
               sprintf (buf,
                        "Can't ping subnet:\n"
                        "local network is\n"
-                       "%s/%d,\n"
+                       "%.100s/%d,\n"
                        "a p2p bridge\n"
-                       "on if %s.",
+                       "on if %.100s.",
                        inet_ntoa (in2), mask_width (mask), ifa->ifa_name);
               if (*error_ret) free (*error_ret);
               *error_ret = strdup (buf);
@@ -676,7 +824,7 @@ subnet_hosts (sonar_sensor_data *ssd, char **error_ret, char **desc_ret,
         {
           sprintf(buf, 
                   "Unable to resolve\n"
-                  "local host \"%s\"", 
+                  "local host \"%.100s\"", 
                   hostname);
           *error_ret = strdup(buf);
           return 0;
@@ -695,7 +843,7 @@ subnet_hosts (sonar_sensor_data *ssd, char **error_ret, char **desc_ret,
           sprintf (buf,
                    "Unable to determine\n"
                    "local subnet address:\n"
-                   "\"%s\"\n"
+                   "\"%.100s\"\n"
                    "resolves to\n"
                    "loopback address\n"
                    "%u.%u.%u.%u.",
@@ -714,11 +862,15 @@ subnet_hosts (sonar_sensor_data *ssd, char **error_ret, char **desc_ret,
   h_base = ntohl (n_base);
 
   if (desc_ret && !*desc_ret) {
+    char buf2[255];
     unsigned int a, b, c, d;
-    char buf[255];
-    unpack_addr (n_base, &a, &b, &c, &d);
-    sprintf (buf, "%u.%u.%u.%u/%d", a, b, c, d, subnet_width);
-    *desc_ret = strdup (buf);
+    unsigned long bb = n_base & htonl(h_mask);
+    unpack_addr (bb, &a, &b, &c, &d);
+    if (subnet_width > 24)
+      sprintf (buf2, "%u.%u.%u.%u/%d", a, b, c, d, subnet_width);
+    else
+      sprintf (buf2, "%u.%u.%u/%d", a, b, c, subnet_width);
+    *desc_ret = strdup (buf2);
   }
 
   for (i = 255; i >= 0; i--) {
@@ -754,7 +906,7 @@ subnet_hosts (sonar_sensor_data *ssd, char **error_ret, char **desc_ret,
     p = address + strlen(address) + 1;
     sprintf(p, "%d", i);
 
-    new = bogie_for_host (ssd, address, pd->resolve_p);
+    new = bogie_for_host (ssd, address, NULL);
     if (new)
       {
         new->next = list;
@@ -775,10 +927,11 @@ send_ping (ping_data *pd, const sonar_bogie *b)
   u_char *packet;
   struct ICMP *icmph;
   const char *token = "org.jwz.xscreensaver.sonar";
+  char *host_id;
 
-  int pcktsiz = (sizeof(struct ICMP) + sizeof(struct timeval) + 
-                 strlen(b->name) + 1 +
-                 strlen(token) + 1 + 
+  unsigned long pcktsiz = (sizeof(struct ICMP) + sizeof(struct timeval) +
+                 sizeof(socklen_t) + pb->addrlen +
+                 strlen(token) + 1 +
                  strlen(pd->version) + 1);
 
   /* Create the ICMP packet */
@@ -799,26 +952,29 @@ send_ping (ping_data *pd, const sonar_bogie *b)
   gettimeofday((struct timeval *) &packet[sizeof(struct ICMP)]);
 # endif
 
-  /* We store the name of the host we're pinging in the packet, and parse
+  /* We store the sockaddr of the host we're pinging in the packet, and parse
      that out of the return packet later (see get_ping() for why).
      After that, we also include the name and version of this program,
      just to give a clue to anyone sniffing and wondering what's up.
    */
-  sprintf ((char *) &packet[sizeof(struct ICMP) + sizeof(struct timeval)],
-           "%s%c%s %s",
-           b->name, 0, token, pd->version);
+  host_id = (char *) &packet[sizeof(struct ICMP) + sizeof(struct timeval)];
+  *(socklen_t *)host_id = pb->addrlen;
+  host_id += sizeof(socklen_t);
+  memcpy(host_id, &pb->address, pb->addrlen);
+  host_id += pb->addrlen;
+  sprintf (host_id, "%.20s %.20s", token, pd->version);
 
   ICMP_CHECKSUM(icmph) = checksum((u_short *)packet, pcktsiz);
 
   /* Send it */
 
   if (sendto(pd->icmpsock, packet, pcktsiz, 0, 
-             &pb->address, sizeof(pb->address))
+             (struct sockaddr *)&pb->address, sizeof(pb->address))
       != pcktsiz)
     {
 #if 0
       char buf[BUFSIZ];
-      sprintf(buf, "%s: pinging %s", progname, b->name);
+      sprintf(buf, "%s: pinging %.100s", progname, b->name);
       perror(buf);
 #endif
     }
@@ -895,7 +1051,7 @@ get_ping (sonar_sensor_data *ssd)
 {
   ping_data *pd = (ping_data *) ssd->closure;
   struct sockaddr from;
-  unsigned int fromlen;  /* Posix says socklen_t, but that's not portable */
+  socklen_t fromlen;
   int result;
   u_char packet[1024];
   struct timeval now;
@@ -950,7 +1106,7 @@ get_ping (sonar_sensor_data *ssd)
          From Valentijn Sessink <valentyn@openoffice.nl> */
       if (select(pd->icmpsock + 1, &rfds, 0, 0, &tv) >0)
         {
-          result = recvfrom (pd->icmpsock, packet, sizeof(packet),
+          result = (int)recvfrom (pd->icmpsock, packet, sizeof(packet),
                              0, &from, &fromlen);
 
           /* Check the packet */
@@ -982,19 +1138,59 @@ get_ping (sonar_sensor_data *ssd)
              pb->address, but it is possible that, in certain weird router
              or NAT situations, that the reply will come back from a 
              different address than the one we sent it to.  So instead,
-             we parse the name out of the reply packet payload.
+             we parse the sockaddr out of the reply packet payload.
            */
           {
-            const char *name = (char *) &packet[iphdrlen +
-                                                sizeof(struct ICMP) +
-                                                sizeof(struct timeval)];
+            const socklen_t *host_id = (socklen_t *) &packet[
+              iphdrlen + sizeof(struct ICMP) + sizeof(struct timeval)];
+
             sonar_bogie *b;
-            for (b = pd->targets; b; b = b->next)
-              if (!strcmp (name, b->name))
-                {
-                  new = copy_ping_bogie (ssd, b);
-                  break;
-                }
+
+            /* Ensure that a maliciously-crafted return packet can't
+               make us overflow in memcmp. */
+            if (result > 0 && (const u_char *)(host_id + 1) <= packet + result)
+              {
+                const u_char *host_end = (const u_char *)(host_id + 1) +
+                                         *host_id;
+
+                if ((const u_char *)(host_id + 1) <= host_end &&
+                    host_end <= packet + result)
+                  {
+                    for (b = pd->targets; b; b = b->next)
+                      {
+                        ping_bogie *pb = (ping_bogie *)b->closure;
+                        if (*host_id == pb->addrlen &&
+                            !memcmp(&pb->address, host_id + 1, pb->addrlen) )
+                          {
+                            /* Check to see if the name lookup is done. */
+                            if (pb->lookup_name &&
+                                async_name_from_addr_is_done (pb->lookup_name))
+                              {
+                                char *host = NULL;
+
+                                async_name_from_addr_finish (pb->lookup_name,
+                                                             &host, NULL);
+
+                                if (pd->debug_p > 1)
+                                  fprintf (stderr, "%s:   %s => %s\n",
+                                           progname, b->name,
+                                           host ? host : "<unknown>");
+
+                                if (host)
+                                  {
+                                    free(b->name);
+                                    b->name = host;
+                                  }
+
+                                pb->lookup_name = NULL;
+                              }
+
+                            new = copy_ping_bogie (ssd, b);
+                            break;
+                          }
+                      }
+                  }
+              }
           }
 
           if (! new)      /* not in targets? */
@@ -1085,6 +1281,14 @@ ping_free_data (sonar_sensor_data *ssd, void *closure)
 static void
 ping_free_bogie_data (sonar_sensor_data *sd, void *closure)
 {
+  ping_bogie *pb = (ping_bogie *) closure;
+
+  if (pb->lookup_name)
+    async_name_from_addr_cancel (pb->lookup_name);
+  if (pb->lookup_addr)
+    async_addr_from_name_cancel (pb->lookup_addr);
+  free (pb->fallback);
+
   free (closure);
 }
 
@@ -1106,8 +1310,21 @@ double_time (void)
 }
 
 
-/* If a bogie is provided, pings it.
-   Then, returns all outstanding ping replies.
+static void
+free_bogie_after_lookup(sonar_sensor_data *ssd, sonar_bogie **sbp,
+                        sonar_bogie **sb)
+{
+  ping_bogie *pb = (ping_bogie *)(*sb)->closure;
+
+  *sbp = (*sb)->next;
+  pb->lookup_addr = NULL; /* Prevent double-free in sonar_free_bogie. */
+  sonar_free_bogie (ssd, *sb);
+  *sb = NULL;
+}
+
+
+/* Pings the next bogie, if it's time.
+   Returns all outstanding ping replies.
  */
 static sonar_bogie *
 ping_scan (sonar_sensor_data *ssd)
@@ -1119,11 +1336,91 @@ ping_scan (sonar_sensor_data *ssd)
 
   if (now > pd->last_ping_time + ping_interval)   /* time to ping someone */
     {
+      struct sonar_bogie **sbp;
+
       if (pd->last_pinged)
-        pd->last_pinged = pd->last_pinged->next;
-      if (! pd->last_pinged)
-        pd->last_pinged = pd->targets;
-      send_ping (pd, pd->last_pinged);
+        {
+          sbp = &pd->last_pinged->next;
+          if (!*sbp)
+            sbp = &pd->targets;
+        }
+      else
+        sbp = &pd->targets;
+
+      if (!*sbp)
+        /* Aaaaand we're out of bogies. */
+        pd->last_pinged = NULL;
+      else
+        {
+          sonar_bogie *sb = *sbp;
+          ping_bogie *pb = (ping_bogie *)sb->closure;
+          if (pb->lookup_addr &&
+              async_addr_from_name_is_done (pb->lookup_addr))
+            {
+              if (async_addr_from_name_finish (pb->lookup_addr, &pb->address,
+                                               &pb->addrlen, NULL))
+                {
+                  char *fallback = pb->fallback;
+                  pb->fallback = NULL;
+
+                  if (pd->debug_p)
+                    fprintf (stderr, "%s:   could not resolve host:  %s\n",
+                             progname, sb->name);
+
+                  free_bogie_after_lookup (ssd, sbp, &sb);
+
+                  /* Insert the fallback bogie right where the old one was. */
+                  if (fallback)
+                    {
+                      sonar_bogie *new_bogie = bogie_for_host (ssd, fallback,
+                                                               NULL);
+                      if (new_bogie) {
+                        new_bogie->next = *sbp;
+
+                        if (! ((ping_bogie *)new_bogie->closure)->lookup_addr &&
+                            ! find_duplicate_host(pd, &pd->targets, new_bogie))
+                          *sbp = new_bogie;
+                        else
+                          sonar_free_bogie (ssd, new_bogie);
+                      }
+
+                      free (fallback);
+                    }
+                }
+              else
+                {
+                  if (pd->debug_p > 1)
+                    {
+                      fprintf (stderr, "%s:   %s => ", progname, sb->name);
+                      print_address (stderr, 0, &pb->address, pb->addrlen);
+                      putc('\n', stderr);
+                    }
+
+                  if (! is_address_ok (pd->debug_p, sb))
+                    free_bogie_after_lookup (ssd, sbp, &sb);
+                  else if (find_duplicate_host (pd, &pd->targets, sb))
+                    /* Tricky: find_duplicate_host skips the current bogie when
+                       scanning the targets list because pb->lookup_addr hasn't
+                       been NULL'd yet.
+
+                       Not that it matters much, but behavior here is to
+                       keep the existing address.
+                     */
+                    free_bogie_after_lookup (ssd, sbp, &sb);
+                }
+
+              if (sb)
+                pb->lookup_addr = NULL;
+            }
+
+          if (sb && !pb->lookup_addr)
+            {
+              if (!pb->addrlen) abort();
+              send_ping (pd, sb);
+              pd->last_pinged = sb;
+            }
+        }
+
       pd->last_ping_time = now;
     }
 
@@ -1194,7 +1491,7 @@ parse_mode (sonar_sensor_data *ssd, char **error_ret, char **desc_ret,
         {
           /* IP: A.B.C.D
            */
-          new = bogie_for_host (ssd, token, pd->resolve_p);
+          new = bogie_for_host (ssd, token, NULL);
         }
       else if (!strcmp (token, "subnet"))
         {
@@ -1223,7 +1520,7 @@ parse_mode (sonar_sensor_data *ssd, char **error_ret, char **desc_ret,
         {
           /* not an existant file - must be a host name
            */
-          new = bogie_for_host (ssd, token, pd->resolve_p);
+          new = bogie_for_host (ssd, token, NULL);
         }
 
       if (new)
@@ -1267,15 +1564,19 @@ sonar_init_ping (Display *dpy, char **error_ret, char **desc_ret,
                  const char *subnet, int timeout,
                  Bool resolve_p, Bool times_p, Bool debug_p)
 {
+  /* Important! Do not return from this function without disavowing privileges
+     with setuid(getuid()).
+   */
   sonar_sensor_data *ssd = (sonar_sensor_data *) calloc (1, sizeof(*ssd));
   ping_data *pd = (ping_data *) calloc (1, sizeof(*pd));
   sonar_bogie *b;
   char *s;
-  int i;
 
   Bool socket_initted_p = False;
   Bool socket_raw_p     = False;
 
+  pd->dpy = dpy;
+
   pd->resolve_p = resolve_p;
   pd->times_p   = times_p;
   pd->debug_p   = debug_p;
@@ -1353,11 +1654,8 @@ sonar_init_ping (Display *dpy, char **error_ret, char **desc_ret,
       fprintf (stderr, "%s: Target list:\n", progname);
       for (b = pd->targets; b; b = b->next)
         {
-          ping_bogie *pb = (ping_bogie *) b->closure;
-          struct sockaddr_in *iaddr = (struct sockaddr_in *) &(pb->address);
-          unsigned long ip = iaddr->sin_addr.s_addr;
           fprintf (stderr, "%s:   ", progname);
-          print_host (stderr, ip, b->name);
+          print_host (stderr, b);
         }
     }
 
@@ -1381,16 +1679,20 @@ sonar_init_ping (Display *dpy, char **error_ret, char **desc_ret,
      Even on a /24, allocated IPs tend to cluster together, so
      don't put any two hosts closer together than N degrees to
      avoid unnecessary overlap when we have plenty of space due
-     to addresses that probably won't respond.
+     to addresses that probably won't respond.  And don't spread
+     them out too far apart, because that looks too symmetrical
+     when there are a small number of hosts.
    */
   {
-    int min_separation = 23;  /* degrees */
-    int div = pd->target_count;
-    if (div > 360 / min_separation)
-      div = 360 / min_separation;
-    for (i = 0, b = pd->targets; b; b = b->next, i++)
-      b->th = (M_PI/2 -
-               M_PI * 2 * ((div - i) % div) / div);
+    double th = frand(M_PI);
+    double sep = 360.0 / pd->target_count;
+    if (sep < 23) sep = 23;
+    if (sep > 43) sep = 43;
+    sep /= 180/M_PI;
+    for (b = pd->targets; b; b = b->next) {
+      b->th = th;
+      th += sep;
+    }
   }
 
   return ssd;