summaryrefslogtreecommitdiffstats
path: root/hacks/glx/sonar-icmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'hacks/glx/sonar-icmp.c')
-rw-r--r--hacks/glx/sonar-icmp.c1789
1 files changed, 0 insertions, 1789 deletions
diff --git a/hacks/glx/sonar-icmp.c b/hacks/glx/sonar-icmp.c
deleted file mode 100644
index b23b6bf..0000000
--- a/hacks/glx/sonar-icmp.c
+++ /dev/null
@@ -1,1789 +0,0 @@
-/* sonar, Copyright © 1998-2021 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
- * the above copyright notice appear in all copies and that both that
- * copyright notice and this permission notice appear in supporting
- * documentation. No representations are made about the suitability of this
- * software for any purpose. It is provided "as is" without express or
- * implied warranty.
- *
- * This implements the "ping" sensor for sonar.
- */
-
-#include "screenhackI.h"
-#include "sonar.h"
-#include "version.h"
-#include "async_netdb.h"
-
-#undef usleep /* conflicts with unistd.h on OSX */
-
-#ifdef HAVE_IPHONE
- /* Note: to get this to compile for iPhone, you need to fix Xcode!
- The icmp headers exist for the simulator build environment, but
- not for the real-device build environment. This appears to
- just be an Apple bug, not intentional. But it has existed for
- many years.
-
- The "ICMP Sanity Check" build phase in the Xcode project checks
- this and tells you what to do.
-
- xc=/Applications/Xcode.app/Contents
- for path in /Developer/Platforms/iPhone*?/Developer/SDKs/?* \
- $xc/Developer/Platforms/iPhone*?/Developer/SDKs/?* ; do
- for file in \
- /usr/include/netinet/ip.h \
- /usr/include/netinet/in_systm.h \
- /usr/include/netinet/ip_icmp.h \
- /usr/include/netinet/ip_var.h \
- /usr/include/netinet/udp.h
- do
- ln -s "$file" "$path$file"
- done
- done
- */
-#endif
-
-#ifndef HAVE_MOBILE
-# define READ_FILES
-#endif
-
-#if defined(HAVE_ICMP) || defined(HAVE_ICMPHDR)
-# include <unistd.h>
-# include <sys/stat.h>
-# include <limits.h>
-# include <signal.h>
-# include <fcntl.h>
-# include <sys/types.h>
-# include <sys/time.h>
-# include <sys/ipc.h>
-# ifndef HAVE_ANDROID
-# include <sys/shm.h>
-# endif
-# include <sys/socket.h>
-# include <netinet/in_systm.h>
-# include <netinet/in.h>
-# include <netinet/ip.h>
-# include <netinet/ip_icmp.h>
-# include <netinet/udp.h>
-# include <arpa/inet.h>
-# include <netdb.h>
-# include <errno.h>
-# ifdef HAVE_GETIFADDRS
-# include <ifaddrs.h>
-# endif
-# ifdef HAVE_LIBCAP
-# include <sys/capability.h>
-# endif
-#endif /* HAVE_ICMP || HAVE_ICMPHDR */
-
-#if defined(HAVE_ICMP)
-# define HAVE_PING
-# define ICMP icmp
-# define ICMP_TYPE(p) (p)->icmp_type
-# define ICMP_CODE(p) (p)->icmp_code
-# define ICMP_CHECKSUM(p) (p)->icmp_cksum
-# define ICMP_ID(p) (p)->icmp_id
-# define ICMP_SEQ(p) (p)->icmp_seq
-#elif defined(HAVE_ICMPHDR)
-# define HAVE_PING
-# define ICMP icmphdr
-# define ICMP_TYPE(p) (p)->type
-# define ICMP_CODE(p) (p)->code
-# define ICMP_CHECKSUM(p) (p)->checksum
-# define ICMP_ID(p) (p)->un.echo.id
-# define ICMP_SEQ(p) (p)->un.echo.sequence
-#else
-# undef HAVE_PING
-#endif
-
-#ifndef HAVE_MOBILE
-# define LOAD_FILES
-#endif
-
-#ifndef HAVE_PING
-
-sonar_sensor_data *
-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",
- progname);
- return 0;
-}
-
-#else /* HAVE_PING -- whole file */
-
-
-#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
-
-/* yes, there is only one, even when multiple savers are running in the
- same address space - since we can only open this socket before dropping
- privs.
- */
-static int global_icmpsock = 0;
-
-/* Set by a signal handler. */
-static int timer_expired;
-
-
-
-static u_short checksum(u_short *, int);
-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 */
- int seq; /* packet sequence number */
- int timeout; /* packet timeout */
-
- int target_count;
- sonar_bogie *targets; /* the hosts we will ping;
- those that pong end up on ssd->pending. */
- sonar_bogie *last_pinged; /* pointer into 'targets' list */
- double last_ping_time;
-
- Bool resolve_p;
- Bool times_p;
- Bool debug_p;
-
-} ping_data;
-
-typedef struct {
- 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 in_addr_t
-pack_addr (unsigned int a, unsigned int b, unsigned int c, unsigned int d)
-{
- unsigned long i = (((a & 255) << 24) |
- ((b & 255) << 16) |
- ((c & 255) << 8) |
- ((d & 255) ));
- return htonl (i);
-}
-
-/* Unpacks an IP address quad from bigendian network order. */
-static void
-unpack_addr (unsigned long addr,
- unsigned int *a, unsigned int *b,
- unsigned int *c, unsigned int *d)
-{
- addr = ntohl (addr);
- *a = (addr >> 24) & 255;
- *b = (addr >> 16) & 255;
- *c = (addr >> 8) & 255;
- *d = (addr ) & 255;
-}
-
-
-
-
-/* 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;
-
- unsigned int ip[4];
- char c;
-
- 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)
- fprintf (stderr, "%s: ignoring bogus IP %s\n",
- progname, sb->name);
- return 0;
- }
-
- iaddr->sin_family = AF_INET;
- iaddr->sin_addr.s_addr = pack_addr (ip[0], ip[1], ip[2], ip[3]);
- pb->addrlen = sizeof(struct sockaddr_in);
-
- 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
- {
- /* It's a host name. */
-
- /* don't waste time being confused by non-hostname tokens
- in .ssh/known_hosts */
- 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;
-
- /* .ssh/known_hosts sometimes contains weirdness like "[host]:port".
- Ignore it. */
- if (strchr (sb->name, '['))
- {
- if (pd->debug_p)
- fprintf (stderr, "%s: ignoring bogus address \"%s\"\n",
- progname, sb->name);
- return 0;
- }
-
- /* If the name contains a colon, it's probably IPv6. */
- if (strchr (sb->name, ':'))
- {
- if (pd->debug_p)
- fprintf (stderr, "%s: ignoring ipv6 address \"%s\"\n",
- progname, sb->name);
- return 0;
- }
-
- pb->lookup_addr = async_addr_from_name_start(pd->dpy, sb->name);
- if (!pb->lookup_addr)
- {
- if (pd->debug_p)
- /* Either address space exhaustion or RAM exhaustion. */
- fprintf (stderr, "%s: unable to start host resolution.\n",
- progname);
- return 0;
- }
- }
- return 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)
- {
- 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
- }
-
- fprintf (out, "%-*s", width, ips);
-}
-
-
-static void
-print_host (FILE *out, const sonar_bogie *sb)
-{
- const ping_bogie *pb = (const ping_bogie *) sb->closure;
- const char *name = sb->name;
- if (!name || !*name) name = "<unknown>";
- 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;
-}
-
-
-/* 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, 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));
- Bool resolve_p = pd->resolve_p;
-
- b->name = strdup (name);
- b->closure = pb;
-
- if (! resolve_bogie_hostname (pd, b, resolve_p))
- 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, 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;
-}
-
-
-#ifdef READ_FILES
-
-/* Return a list of bogies read from a file.
- The file can be like /etc/hosts or .ssh/known_hosts or probably
- just about anything that has host names in it.
- */
-static sonar_bogie *
-read_hosts_file (sonar_sensor_data *ssd, const char *filename)
-{
- ping_data *pd = (ping_data *) ssd->closure;
- FILE *fp;
- char buf[LINE_MAX];
- char *p;
- sonar_bogie *list = 0;
- char *addr, *name;
- sonar_bogie *new;
-
- /* Kludge: on OSX, variables have not been expanded in the command
- line arguments, so as a special case, allow the string to begin
- with literal "$HOME/" or "~/".
-
- This is so that the "Known Hosts" menu item in sonar.xml works.
- */
- if (!strncmp(filename, "~/", 2) || !strncmp(filename, "$HOME/", 6))
- {
- char *s = strchr (filename, '/');
- strcpy (buf, getenv("HOME"));
- strcat (buf, s);
- filename = buf;
- }
-
- fp = fopen(filename, "r");
- if (!fp)
- {
-#ifdef HAVE_JWXYZ
- if (pd->debug_p) /* on OSX don't syslog this */
-#endif
- {
- char *str_error = strerror(errno);
- fprintf(stderr, "%s: %s: %s", progname, filename, str_error);
- }
- return 0;
- }
-
- if (pd->debug_p)
- fprintf (stderr, "%s: reading \"%s\"\n", progname, filename);
-
- while ((p = fgets(buf, LINE_MAX, fp)))
- {
- while ((*p == ' ') || (*p == '\t')) /* skip whitespace */
- p++;
- if (*p == '#') /* skip comments */
- continue;
-
- /* Get the name and address */
-
- if ((addr = strtok(buf, " ,;\t\n")))
- name = strtok(0, " ,;\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 = 0;
- }
- }
-
- /* 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 (pd->debug_p > 1)
- fprintf (stderr, "%s: skipping bogus name \"%s\" (%s)\n",
- progname, name, addr);
- name = 0;
- }
- }
-
- /* Create a new target using first the name then the address */
-
- if (!name)
- {
- name = addr;
- addr = NULL;
- }
-
- new = bogie_for_host (ssd, name, addr);
-
- if (new)
- {
- new->next = list;
- list = new;
- }
- }
-
- fclose(fp);
- return list;
-}
-#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 = head;
-
- while (sb)
- {
- ping_bogie *pb = (ping_bogie *) sb->closure;
-
- if (!pb->lookup_addr)
- {
- 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 long
-width_mask (unsigned long width)
-{
- unsigned long m = 0;
- int i;
- for (i = 0; i < width; i++)
- m |= (1L << (31-i));
- return m;
-}
-
-
-#ifdef HAVE_GETIFADDRS
-static unsigned int
-mask_width (unsigned long mask)
-{
- int i;
- for (i = 0; i < 32; i++)
- if (mask & (1 << i))
- break;
- return 32-i;
-}
-#endif
-
-
-/* Generate a list of bogies consisting of all of the entries on
- the same subnet. 'base' ip is in network order; 0 means localhost.
- */
-static sonar_bogie *
-subnet_hosts (sonar_sensor_data *ssd, char **error_ret, char **desc_ret,
- unsigned long n_base, int subnet_width)
-{
- ping_data *pd = (ping_data *) ssd->closure;
- unsigned long h_mask; /* host order */
- unsigned long h_base; /* host order */
- char address[BUFSIZ];
- char *p;
- int i;
- sonar_bogie *new;
- sonar_bogie *list = 0;
- char buf[1024];
-
- if (subnet_width < 24)
- {
- sprintf (buf,
- "Pinging %lu hosts is a bad\n"
- "idea. Please use a subnet\n"
- "mask of 24 bits or more.",
- (unsigned long) (1L << (32 - subnet_width)) - 1);
- *error_ret = strdup(buf);
- return 0;
- }
- else if (subnet_width > 30)
- {
- sprintf (buf,
- "An %d-bit subnet\n"
- "doesn't make sense.\n"
- "Try \"subnet/24\"\n"
- "or \"subnet/29\".\n",
- subnet_width);
- *error_ret = strdup(buf);
- return 0;
- }
-
-
- if (pd->debug_p)
- fprintf (stderr, "%s: adding %d-bit subnet\n", progname, subnet_width);
-
-
- if (! n_base)
- {
-# ifdef HAVE_GETIFADDRS
-
- /* To determine the local subnet, we need to know the local IP address.
- Do this by looking at the IPs of every network interface.
- */
- struct in_addr in = { 0, };
- struct ifaddrs *all = 0, *ifa;
-
- if (pd->debug_p)
- fprintf (stderr, "%s: listing network interfaces\n", progname);
-
- getifaddrs (&all);
- for (ifa = all; ifa; ifa = ifa->ifa_next)
- {
- struct in_addr in2;
- unsigned long mask;
- if (! ifa->ifa_addr)
- continue;
- else if (ifa->ifa_addr->sa_family != AF_INET)
- {
- if (pd->debug_p)
- fprintf (stderr, "%s: if: %4s: %s\n", progname,
- ifa->ifa_name,
- (
-# 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;
- }
- in2 = ((struct sockaddr_in *) ifa->ifa_addr)->sin_addr;
- mask = ntohl (((struct sockaddr_in *) ifa->ifa_netmask)
- ->sin_addr.s_addr);
- if (pd->debug_p)
- fprintf (stderr, "%s: if: %4s: inet = %s /%d 0x%08lx\n",
- progname,
- ifa->ifa_name,
- inet_ntoa (in2),
- mask_width (mask),
- mask);
- if (in2.s_addr == 0x0100007f || /* 127.0.0.1 in network order */
- ((in2.s_addr & 0x000000ff) == 0x7f) || /* 127.0.0.0/24 */
- mask == 0)
- /* Assume all 127 addresses are loopback, not just 127.0.0.1. */
- continue;
-
- /* At least on the AT&T 3G network, pinging either of the two
- hosts on a /31 network doesn't work, so don't try.
- */
- if (mask_width (mask) == 31)
- {
- sprintf (buf,
- "Can't ping subnet:\n"
- "local network is\n"
- "%.100s/%d,\n"
- "a p2p bridge\n"
- "on if %.100s.",
- inet_ntoa (in2), mask_width (mask), ifa->ifa_name);
- if (*error_ret) free (*error_ret);
- *error_ret = strdup (buf);
- continue;
- }
-
- in = in2;
- subnet_width = mask_width (mask);
-
- /* Take the first non-loopback network: prefer en0 over en1. */
- if (in.s_addr && subnet_width)
- break;
- }
-
- if (in.s_addr)
- {
- if (*error_ret) free (*error_ret);
- *error_ret = 0;
- n_base = in.s_addr; /* already in network order, I think? */
- }
- else if (!*error_ret)
- *error_ret = strdup ("Unable to determine\nlocal IP address\n");
-
- if (all)
- freeifaddrs (all);
-
- if (*error_ret)
- return 0;
-
-# else /* !HAVE_GETIFADDRS */
-
- /* If we can't walk the list of network interfaces to figure out
- our local IP address, try to do it by finding the local host
- name, then resolving that.
- */
- char hostname[BUFSIZ];
- struct hostent *hent = 0;
-
- if (gethostname(hostname, BUFSIZ))
- {
- *error_ret = strdup ("Unable to determine\n"
- "local host name!");
- return 0;
- }
-
- /* Get our IP address and convert it to a string */
-
- hent = gethostbyname(hostname);
- if (! hent)
- {
- strcat (hostname, ".local"); /* Necessary on iphone */
- hent = gethostbyname(hostname);
- }
-
- if (! hent)
- {
- sprintf(buf,
- "Unable to resolve\n"
- "local host \"%.100s\"",
- hostname);
- *error_ret = strdup(buf);
- return 0;
- }
-
- strcpy (address, inet_ntoa(*((struct in_addr *)hent->h_addr_list[0])));
- n_base = pack_addr (hent->h_addr_list[0][0],
- hent->h_addr_list[0][1],
- hent->h_addr_list[0][2],
- hent->h_addr_list[0][3]);
-
- if (n_base == 0x0100007f) /* 127.0.0.1 in network order */
- {
- unsigned int a, b, c, d;
- unpack_addr (n_base, &a, &b, &c, &d);
- sprintf (buf,
- "Unable to determine\n"
- "local subnet address:\n"
- "\"%.100s\"\n"
- "resolves to\n"
- "loopback address\n"
- "%u.%u.%u.%u.",
- hostname, a, b, c, d);
- *error_ret = strdup(buf);
- return 0;
- }
-
-# endif /* !HAVE_GETIFADDRS */
- }
-
-
- /* Construct targets for all addresses in this subnet */
-
- h_mask = width_mask (subnet_width);
- h_base = ntohl (n_base);
-
- if (desc_ret && !*desc_ret) {
- char buf2[255];
- unsigned int a, b, c, d;
- 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--) {
- unsigned int a, b, c, d;
- int ip = (h_base & 0xFFFFFF00L) | i; /* host order */
-
- if ((ip & h_mask) != (h_base & h_mask)) /* skip out-of-subnet host */
- continue;
- else if (subnet_width == 31) /* 1-bit bridge: 2 hosts */
- ;
- else if ((ip & ~h_mask) == 0) /* skip network address */
- continue;
- else if ((ip & ~h_mask) == ~h_mask) /* skip broadcast address */
- continue;
-
- unpack_addr (htonl (ip), &a, &b, &c, &d);
- sprintf (address, "%u.%u.%u.%u", a, b, c, d);
-
- if (pd->debug_p > 1)
- {
- unsigned int aa, ab, ac, ad;
- unsigned int ma, mb, mc, md;
- unpack_addr (htonl (h_base & h_mask), &aa, &ab, &ac, &ad);
- unpack_addr (htonl (h_mask), &ma, &mb, &mc, &md);
- fprintf (stderr,
- "%s: subnet: %s (%u.%u.%u.%u & %u.%u.%u.%u / %d)\n",
- progname, address,
- aa, ab, ac, ad,
- ma, mb, mc, md,
- subnet_width);
- }
-
- p = address + strlen(address) + 1;
- sprintf(p, "%d", i);
-
- new = bogie_for_host (ssd, address, NULL);
- if (new)
- {
- new->next = list;
- list = new;
- }
- }
-
- return list;
-}
-
-
-/* Send a ping packet.
- */
-static void
-send_ping (ping_data *pd, const sonar_bogie *b)
-{
- ping_bogie *pb = (ping_bogie *) b->closure;
- u_char *packet;
- struct ICMP *icmph;
- const char *token = "org.jwz.xscreensaver.sonar";
- char *host_id;
- struct timeval tval;
-
- 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 */
-
- if (! (packet = (u_char *) calloc(1, pcktsiz)))
- return; /* Out of memory */
-
- icmph = (struct ICMP *) packet;
- ICMP_TYPE(icmph) = ICMP_ECHO;
- ICMP_CODE(icmph) = 0;
- ICMP_CHECKSUM(icmph) = 0;
- ICMP_ID(icmph) = pd->pid;
- ICMP_SEQ(icmph) = pd->seq++;
- /* struct timeval needs alignment, so we first use aligned buffer for
- gettimeofday() and later copy the result to packet buffer
- */
-# ifdef GETTIMEOFDAY_TWO_ARGS
- gettimeofday((struct timeval *) &tval,
- (struct timezone *) 0);
-# else
- gettimeofday((struct timeval *) &tval);
-# endif
- memcpy(&packet[sizeof(struct ICMP)], &tval, sizeof tval);
-
- /* 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.
- */
- 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,
- (struct sockaddr *)&pb->address, sizeof(pb->address))
- != pcktsiz)
- {
-#if 0
- char buf[BUFSIZ];
- sprintf(buf, "%s: pinging %.100s", progname, b->name);
- perror(buf);
-#endif
- }
-
- free (packet);
-}
-
-/* signal handler */
-static void
-sigcatcher (int sig)
-{
- timer_expired = 1;
-}
-
-
-/* Compute the checksum on a ping packet.
- */
-static u_short
-checksum (u_short *packet, int size)
-{
- register int nleft = size;
- register u_short *w = packet;
- register int sum = 0;
- u_short answer = 0;
-
- /* Using a 32 bit accumulator (sum), we add sequential 16 bit words
- to it, and at the end, fold back all the carry bits from the
- top 16 bits into the lower 16 bits.
- */
- while (nleft > 1)
- {
- sum += *w++;
- nleft -= 2;
- }
-
- /* mop up an odd byte, if necessary */
-
- if (nleft == 1)
- {
- *(u_char *)(&answer) = *(u_char *)w ;
- *(1 + (u_char *)(&answer)) = 0;
- sum += answer;
- }
-
- /* add back carry outs from top 16 bits to low 16 bits */
-
- sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
- sum += (sum >> 16); /* add carry */
- answer = ~sum; /* truncate to 16 bits */
-
- return(answer);
-}
-
-
-/* Copies the sonar_bogie and the underlying ping_bogie.
- */
-static sonar_bogie *
-copy_ping_bogie (sonar_sensor_data *ssd, const sonar_bogie *b)
-{
- sonar_bogie *b2 = sonar_copy_bogie (ssd, b);
- if (b->closure)
- {
- ping_bogie *pb = (ping_bogie *) b->closure;
- ping_bogie *pb2 = (ping_bogie *) calloc (1, sizeof(*pb));
- pb2->address = pb->address;
- b2->closure = pb2;
- }
- return b2;
-}
-
-
-/* Look for all outstanding ping replies.
- */
-static sonar_bogie *
-get_ping (sonar_sensor_data *ssd)
-{
- ping_data *pd = (ping_data *) ssd->closure;
- struct sockaddr from;
- socklen_t fromlen;
- int result;
- u_char packet[1024];
- struct timeval now;
- struct timeval then;
- struct ip *ip;
- int iphdrlen;
- struct ICMP *icmph;
- sonar_bogie *bl = 0;
- sonar_bogie *new = 0;
- struct sigaction sa;
- struct itimerval it;
- fd_set rfds;
- struct timeval tv;
-
- /* Set up a signal to interrupt our wait for a packet */
-
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = 0;
- sa.sa_handler = sigcatcher;
- if (sigaction(SIGALRM, &sa, 0) == -1)
- {
- char msg[1024];
- sprintf(msg, "%s: unable to trap SIGALRM", progname);
- perror(msg);
- exit(1);
- }
-
- /* Set up a timer to interupt us if we don't get a packet */
-
- it.it_interval.tv_sec = 0;
- it.it_interval.tv_usec = 0;
- it.it_value.tv_sec = 0;
- it.it_value.tv_usec = pd->timeout;
- timer_expired = 0;
- setitimer(ITIMER_REAL, &it, 0);
-
- /* Wait for a result packet */
-
- fromlen = sizeof(from);
- while (! timer_expired)
- {
- tv.tv_usec = pd->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(pd->icmpsock, &rfds);
- /* only wait a little while, in case we raced with the timer expiration.
- From Valentijn Sessink <valentyn@openoffice.nl> */
- if (select(pd->icmpsock + 1, &rfds, 0, 0, &tv) >0)
- {
- result = (int)recvfrom (pd->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_HDRLEN(ip) << 2;
- icmph = (struct ICMP *) &packet[iphdrlen];
- /* struct timeval data in packet is not aligned, move the data to
- the aligned buffer
- */
- memcpy(&then, &packet[iphdrlen + sizeof(struct ICMP)], sizeof then);
-
-
- /* Ignore anything but ICMP Replies */
- if (ICMP_TYPE(icmph) != ICMP_ECHOREPLY)
- continue;
-
- /* Ignore packets not set from us */
- if (ICMP_ID(icmph) != pd->pid)
- continue;
-
- /* Find the bogie in 'targets' that corresponds to this packet
- and copy it, so that this bogie stays in the same spot (th)
- on the screen, and so that we don't have to resolve it again.
-
- We could find the bogie by comparing ip->ip_src.s_addr to
- 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 sockaddr out of the reply packet payload.
- */
- {
- 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 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? */
- {
- unsigned int a, b, c, d;
- unpack_addr (ip->ip_src.s_addr, &a, &b, &c, &d);
- fprintf (stderr,
- "%s: UNEXPECTED PING REPLY! "
- "%4d bytes, icmp_seq=%-4d from %d.%d.%d.%d\n",
- progname, result, ICMP_SEQ(icmph), a, b, c, d);
- continue;
- }
-
- new->next = bl;
- bl = new;
-
- {
- double msec = delta(&then, &now) / 1000.0;
-
- if (pd->times_p)
- {
- if (new->desc) free (new->desc);
- new->desc = (char *) malloc (30);
- if (msec > 99) sprintf (new->desc, "%.0f ms", msec);
- else if (msec > 9) sprintf (new->desc, "%.1f ms", msec);
- else if (msec > 1) sprintf (new->desc, "%.2f ms", msec);
- else sprintf (new->desc, "%.3f ms", msec);
- }
-
- if (pd->debug_p && pd->times_p) /* ping-like stdout log */
- {
- char *s = strdup(new->name);
- char *s2 = s;
- if (strlen(s) > 28)
- {
- s2 = s + strlen(s) - 28;
- memcpy (s2, "...", 3);
- }
- fprintf (stdout,
- "%3d bytes from %28s: icmp_seq=%-4d time=%s\n",
- result, s2, ICMP_SEQ(icmph), new->desc);
- fflush (stdout);
- free(s);
- }
-
- /* The radius must be between 0.0 and 1.0.
- We want to display ping times on a logarithmic scale,
- with the three rings being 2.5, 70 and 2,000 milliseconds.
- */
- if (msec <= 0) msec = 0.001;
- new->r = log (msec * 10) / log (20000);
-
- /* Don't put anyone *too* close to the center of the screen. */
- if (new->r < 0) new->r = 0;
- if (new->r < 0.1) new->r += 0.1;
- }
- }
- }
-
- return bl;
-}
-
-
-/* difference between the two times in microseconds.
- */
-static long
-delta (struct timeval *then, struct timeval *now)
-{
- return (((now->tv_sec - then->tv_sec) * 1000000) +
- (now->tv_usec - then->tv_usec));
-}
-
-
-static void
-ping_free_data (sonar_sensor_data *ssd, void *closure)
-{
- ping_data *pd = (ping_data *) closure;
- sonar_bogie *b = pd->targets;
- while (b)
- {
- sonar_bogie *b2 = b->next;
- sonar_free_bogie (ssd, b);
- b = b2;
- }
- free (pd);
-}
-
-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);
-}
-
-
-/* Returns the current time in seconds as a double.
- */
-static double
-double_time (void)
-{
- struct timeval now;
-# ifdef GETTIMEOFDAY_TWO_ARGS
- struct timezone tzp;
- gettimeofday(&now, &tzp);
-# else
- gettimeofday(&now);
-# endif
-
- return (now.tv_sec + ((double) now.tv_usec * 0.000001));
-}
-
-
-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)
-{
- ping_data *pd = (ping_data *) ssd->closure;
- double now = double_time();
- double ping_cycle = 10; /* re-ping a given host every 10 seconds */
- double ping_interval = ping_cycle / pd->target_count;
-
- if (now > pd->last_ping_time + ping_interval) /* time to ping someone */
- {
- struct sonar_bogie **sbp;
-
- if (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;
- }
-
- return get_ping (ssd);
-}
-
-
-/* Returns a list of hosts to ping based on the "-ping" argument.
- */
-static sonar_bogie *
-parse_mode (sonar_sensor_data *ssd, char **error_ret, char **desc_ret,
- const char *ping_arg, Bool ping_works_p)
-{
- ping_data *pd = (ping_data *) ssd->closure;
- char *source, *token, *end, dummy;
- sonar_bogie *hostlist = 0;
- const char *fallback = "subnet";
-
- AGAIN:
-
- if (fallback && (!ping_arg || !*ping_arg || !strcmp (ping_arg, "default")))
- source = strdup(fallback);
- else if (ping_arg)
- source = strdup(ping_arg);
- else
- return 0;
-
- token = source;
- end = source + strlen(source);
- while (token < end)
- {
- char *next;
- sonar_bogie *new = 0;
-# ifdef READ_FILES
- struct stat st;
-# endif
- unsigned int n0=0, n1=0, n2=0, n3=0, m=0;
- char d;
-
- for (next = token;
- *next &&
- *next != ',' && *next != ' ' && *next != '\t' && *next != '\n';
- next++)
- ;
- *next = 0;
-
-
- if (pd->debug_p)
- fprintf (stderr, "%s: parsing %s\n", progname, token);
-
- if (!ping_works_p)
- {
-# ifdef HAVE_LIBCAP
- *error_ret = strdup ("Sonar must be setuid or libcap to ping!\n"
- "Running simulation instead.");
-# else
- *error_ret = strdup ("Sonar must be setuid to ping!\n"
- "Running simulation instead.");
-# endif
- return 0;
- }
-
- 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 = pack_addr (n0, n1, n2, n3);
- new = subnet_hosts (ssd, error_ret, desc_ret, ip, m);
- }
- else if (4 == sscanf (token, "%u.%u.%u.%u %c", &n0, &n1, &n2, &n3, &d))
- {
- /* IP: A.B.C.D
- */
- new = bogie_for_host (ssd, token, NULL);
- }
- else if (!strcmp (token, "subnet"))
- {
- new = subnet_hosts (ssd, error_ret, desc_ret, 0, 24);
- }
- else if (1 == sscanf (token, "subnet/%u %c", &m, &dummy))
- {
- new = subnet_hosts (ssd, error_ret, desc_ret, 0, m);
- }
- else if (*token == '.' || *token == '/' ||
- *token == '$' || *token == '~')
- {
-# ifdef READ_FILES
- new = read_hosts_file (ssd, token);
-# else
- if (pd->debug_p) fprintf (stderr, "%s: skipping file\n", progname);
-# endif
- }
-# ifdef READ_FILES
- else if (!stat (token, &st))
- {
- new = read_hosts_file (ssd, token);
- }
-# endif /* READ_FILES */
- else
- {
- /* not an existant file - must be a host name
- */
- new = bogie_for_host (ssd, token, NULL);
- }
-
- if (new)
- {
- sonar_bogie *nn = new;
- while (nn->next)
- nn = nn->next;
- nn->next = hostlist;
- hostlist = new;
- }
-
- token = next + 1;
- while (token < end &&
- (*token == ',' || *token == ' ' ||
- *token == '\t' || *token == '\n'))
- token++;
- }
-
- free (source);
-
- /* If the arg was completely unparsable, fall back to the local subnet.
- This happens if the default is "/etc/hosts" but READ_FILES is off.
- Or if we're on a /31 network, in which case we try twice then fail.
- */
- if (!hostlist && fallback)
- {
- if (pd->debug_p)
- fprintf (stderr, "%s: no hosts parsed! Trying %s\n",
- progname, fallback);
- ping_arg = fallback;
- fallback = 0;
- goto AGAIN;
- }
-
- return hostlist;
-}
-
-
-static Bool
-set_net_raw_capalibity(int enable_p)
-{
- Bool ret_status = False;
-# ifdef HAVE_LIBCAP
- cap_t cap_status;
- cap_value_t cap_value[] = { CAP_NET_RAW, };
- cap_flag_value_t cap_flag_value;
- cap_flag_value_t new_value = enable_p ? CAP_SET : CAP_CLEAR;
-
- cap_status = cap_get_proc();
- do {
- cap_flag_value = CAP_CLEAR;
- if (cap_get_flag (cap_status, CAP_NET_RAW, CAP_EFFECTIVE, &cap_flag_value))
- break;
- if (cap_flag_value == new_value)
- {
- ret_status = True;
- break;
- }
-
- cap_set_flag (cap_status, CAP_EFFECTIVE, 1, cap_value, new_value);
- if (!cap_set_proc(cap_status))
- ret_status = True;
- } while (0);
-
- if (cap_status) cap_free (cap_status);
-# endif /* HAVE_LIBCAP */
-
- return ret_status;
-}
-
-static Bool
-set_ping_capability (void)
-{
- if (geteuid() == 0) return True;
- return set_net_raw_capalibity (True);
-}
-
-
-sonar_sensor_data *
-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;
-
- 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;
-
- ssd->closure = pd;
- ssd->scan_cb = ping_scan;
- ssd->free_data_cb = ping_free_data;
- ssd->free_bogie_cb = ping_free_bogie_data;
-
- /* Get short version number. */
- s = strchr (screensaver_id, ' ');
- pd->version = strdup (s+1);
- s = strchr (pd->version, ' ');
- *s = 0;
-
-
- /* Create the ICMP socket. Do this before dropping privs.
-
- Raw sockets can only be opened by root (or setuid root), so we only try
- to do this when the effective uid is 0.
-
- We used to just always try, and notice the failure. But apparently that
- causes "SELinux" to log spurious warnings when running with the "strict"
- policy. So to avoid that, we just don't try unless we know it will work.
-
- On MacOS X, we can avoid the whole problem by using a non-privileged
- datagram instead of a raw socket.
-
- On recent Linux systems (2012-ish?) we can avoid setuid by instead using
- cap_set_flag(... CAP_NET_RAW). To make that call the executable needs to
- have "sudo setcap cap_net_raw=p sonar" done to it first.
-
- Except, it turns out that $MESA_LOADER_DRIVER_OVERRIDE would then let you
- run arbitrary other programs with access to raw sockets. It's possible
- that un-setting $MESA_LOADER_DRIVER_OVERRIDE early in main() would prevent
- this, but Mesa uses a ton of environment variables, and who knows what
- other crap is lurking in there. So that's just great.
-
- Ironically, being setuid root is *more* secure, as Mesa happens to contain
- code that says, "Hey, maybe I shouldn't link in arbitrary other .so files
- when I'm root".
-
- This trick does not work with $LD_PRELOAD because the kernel checks auxv
- for AT_SECURE and won't run $LD_PRELOAD if setcap is in use, whereas Mesa
- only checks geteuid.
- */
- if (global_icmpsock)
- {
- pd->icmpsock = global_icmpsock;
- socket_initted_p = True;
- if (debug_p)
- fprintf (stderr, "%s: re-using icmp socket\n", progname);
-
- }
- else if ((pd->icmpsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)) >= 0)
- {
- socket_initted_p = True;
- }
- else if (set_ping_capability() &&
- (pd->icmpsock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) >= 0)
- {
- socket_initted_p = True;
- socket_raw_p = True;
- }
-
- if (socket_initted_p)
- {
- global_icmpsock = pd->icmpsock;
- socket_initted_p = True;
- if (debug_p)
- fprintf (stderr, "%s: opened %s icmp socket\n", progname,
- (socket_raw_p ? "raw" : "dgram"));
- }
- else if (debug_p)
- fprintf (stderr, "%s: unable to open icmp socket\n", progname);
-
- /* Disavow privs */
- if (setuid(getuid()) == -1) abort();
-
- pd->pid = getpid() & 0xFFFF;
- pd->seq = 0;
- pd->timeout = timeout;
-
- /* Generate a list of targets */
-
- pd->targets = parse_mode (ssd, error_ret, desc_ret, subnet,
- socket_initted_p);
- pd->targets = delete_duplicate_hosts (ssd, pd->targets);
-
- if (debug_p)
- {
- fprintf (stderr, "%s: Target list:\n", progname);
- for (b = pd->targets; b; b = b->next)
- {
- fprintf (stderr, "%s: ", progname);
- print_host (stderr, b);
- }
- }
-
- /* Make sure there is something to ping */
-
- pd->target_count = 0;
- for (b = pd->targets; b; b = b->next)
- pd->target_count++;
-
- if (pd->target_count == 0)
- {
- if (! *error_ret)
- *error_ret = strdup ("No hosts to ping!\n"
- "Simulating instead.");
- ping_free_data (ssd, pd);
- if (ssd) free (ssd);
- return 0;
- }
-
- /* Distribute them evenly around the display field, clockwise.
- 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. And don't spread
- them out too far apart, because that looks too symmetrical
- when there are a small number of hosts.
- */
- {
- 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;
-}
-
-#endif /* HAVE_PING -- whole file */