X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=hacks%2Fglx%2Fsonar-icmp.c;fp=hacks%2Fglx%2Fsonar-icmp.c;h=50861259e768e8bca7305d37db57c9b33ecf2da6;hp=6c4029335b4a36ecef9a9c9af6a48335c7336289;hb=d1ae2829ff0fd2a96c16a0c8c5420efaa47d7b30;hpb=7edd66e6bd3209013ee059819747b10b5835635b diff --git a/hacks/glx/sonar-icmp.c b/hacks/glx/sonar-icmp.c index 6c402933..50861259 100644 --- a/hacks/glx/sonar-icmp.c +++ b/hacks/glx/sonar-icmp.c @@ -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 */ @@ -61,6 +62,7 @@ # include # include # include +# include # ifdef HAVE_GETIFADDRS # include # endif @@ -130,6 +132,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,7 +153,11 @@ 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; @@ -182,26 +190,23 @@ unpack_addr (unsigned long addr, /* 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. + 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 +215,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 { @@ -258,39 +263,112 @@ 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); +} + + +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; } @@ -298,13 +376,12 @@ print_host (FILE *out, unsigned long ip, const char *name) 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 +389,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; } @@ -440,11 +505,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,40 +526,112 @@ 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; @@ -764,7 +903,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; @@ -785,9 +924,10 @@ 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 + + sizeof(socklen_t) + pb->addrlen + strlen(token) + 1 + strlen(pd->version) + 1); @@ -809,21 +949,24 @@ 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)], - "%.100s%c%.20s %.20s", - 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 @@ -992,24 +1135,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; /* Ensure that a maliciously-crafted return packet can't - make us overflow in strcmp. */ - packet[sizeof(packet)-1] = 0; + 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; - for (b = pd->targets; b; b = b->next) - if (!strcmp (name, b->name)) - { - new = copy_ping_bogie (ssd, b); - break; - } + 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? */ @@ -1100,6 +1278,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); } @@ -1121,6 +1307,19 @@ double_time (void) } +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. */ @@ -1134,11 +1333,89 @@ 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); + 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) + { + assert (pb->addrlen); + send_ping (pd, sb); + pd->last_pinged = sb; + } + } + pd->last_ping_time = now; } @@ -1209,7 +1486,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")) { @@ -1238,7 +1515,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) @@ -1282,6 +1559,9 @@ 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; @@ -1290,6 +1570,8 @@ sonar_init_ping (Display *dpy, char **error_ret, char **desc_ret, 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; @@ -1367,11 +1649,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); } }