X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hacks%2Fglx%2Fsonar-icmp.c;h=e482ab6a580617061a58ca731922c86c4d5a60a6;hb=4361b69d3178d7fc98d0388f9a223af6c2651aba;hp=fa1ea893df64faca035eaac4ab1381fd8fd26f33;hpb=6f5482d73adb0165c0130bb47d852644ab0c4869;p=xscreensaver diff --git a/hacks/glx/sonar-icmp.c b/hacks/glx/sonar-icmp.c index fa1ea893..e482ab6a 100644 --- a/hacks/glx/sonar-icmp.c +++ b/hacks/glx/sonar-icmp.c @@ -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 # include # include -# include +# ifndef HAVE_ANDROID +# include +# endif # include # include # include @@ -61,6 +64,7 @@ # include # include # include +# include # ifdef HAVE_GETIFADDRS # include # endif @@ -86,16 +90,16 @@ # 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 : "")); - - 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 = ""; + 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 = ""; + 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 = ""; - 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 */ 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 : ""); + + 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;