noip.c: Shut up some GCC warnings.
[preload-hacks] / noip.c
diff --git a/noip.c b/noip.c
index 152540d..1dac168 100644 (file)
--- a/noip.c
+++ b/noip.c
@@ -1,8 +1,36 @@
+/* -*-c-*-
+ *
+ * Make programs use Unix-domain sockets instead of IP
+ *
+ * (c) 2008 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the preload-hacks package.
+ *
+ * Preload-hacks are free software; you can redistribute it and/or modify
+ * them under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * Preload-hacks distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mLib; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
 #define _GNU_SOURCE
 #undef sun
 #undef SUN
 #define DEBUG
 
+/*----- Header files ------------------------------------------------------*/
+
 #include <ctype.h>
 #include <errno.h>
 #include <stdio.h>
 #include <netinet/udp.h>
 #include <net/if.h>
 
-enum { UNUSED, STALE, USED };
-enum { WANT_FRESH, WANT_EXISTING };
-enum { DENY, ALLOW };
+/*----- Data structures ---------------------------------------------------*/
+
+enum { UNUSED, STALE, USED };          /* Unix socket status values */
+enum { WANT_FRESH, WANT_EXISTING };    /* Socket address dispositions */
+enum { DENY, ALLOW };                  /* ACL verdicts */
+
+/* Access control list nodes */
 typedef struct aclnode {
   struct aclnode *next;
   int act;
@@ -35,20 +67,24 @@ typedef struct aclnode {
   unsigned short minport, maxport;
 } aclnode;
 
+/* Local address records */
 #define MAX_LOCAL_IPADDRS 16
 static struct in_addr local_ipaddrs[MAX_LOCAL_IPADDRS];
 static int n_local_ipaddrs;
 
+/* General configuration */
 static uid_t uid;
 static char *sockdir = 0;
 static int debug = 0;
 static unsigned minautoport = 16384, maxautoport = 65536;
 
+/* Access control lists */
 static aclnode *bind_real, **bind_tail = &bind_real;
 static aclnode *connect_real,  **connect_tail = &connect_real;
 
-/* --- Import magic --- */
+/*----- Import the real versions of functions -----------------------------*/
 
+/* The list of functions to immport. */
 #define IMPORTS(_)                                                     \
   _(socket, int, (int, int, int))                                      \
   _(socketpair, int, (int, int, int, int *))                           \
@@ -62,16 +98,16 @@ static aclnode *connect_real,  **connect_tail = &connect_real;
   _(sendto, ssize_t, (int, const void *buf, size_t, int,               \
                      const struct sockaddr *to, socklen_t tolen))      \
   _(recvfrom, ssize_t, (int, void *buf, size_t, int,                   \
-                       struct sockaddr *from, socklen_t *fromlen))     \
+                       struct sockaddr *from, socklen_t *fromlen))     \
   _(sendmsg, ssize_t, (int, const struct msghdr *, int))               \
-  _(recvmsg, ssize_t, (int, struct msghdr *, int))                     \
-  _(close, int, (int))
+  _(recvmsg, ssize_t, (int, struct msghdr *, int))
 
+/* Function pointers to set up. */
 #define DECL(imp, ret, args) static ret (*real_##imp) args;
 IMPORTS(DECL)
 #undef DECL
 
-static void setup(void) __attribute__((constructor));
+/* Import the system calls. */
 static void import(void)
 {
 #define IMPORT(imp, ret, args)                                         \
@@ -80,27 +116,33 @@ static void import(void)
 #undef IMPORT
 }
 
-/* --- Support --- */
+/*----- Utilities ---------------------------------------------------------*/
 
+/* Socket address casts */
 #define SA(sa) ((struct sockaddr *)(sa))
 #define SIN(sa) ((struct sockaddr_in *)(sa))
 #define SUN(sa) ((struct sockaddr_un *)(sa))
 
+/* Raw bytes */
 #define UC(ch) ((unsigned char)(ch))
 
+/* Memory allocation */
 #define NEW(x) ((x) = xmalloc(sizeof(*x)))
 #define NEWV(x, n) ((x) = xmalloc(sizeof(*x) * (n)))
 
+/* Debugging */
 #ifdef DEBUG
 #  define D(body) { if (debug) { body } }
 #else
 #  define D(body) ;
 #endif
 
+/* Preservation of error status */
 #define PRESERVING_ERRNO(body) do {                                    \
   int _err = errno; { body } errno = _err;                             \
 } while (0)
 
+/* Allocate N bytes of memory; abort on failure. */
 static void *xmalloc(size_t n)
 {
   void *p;
@@ -109,6 +151,7 @@ static void *xmalloc(size_t n)
   return (p);
 }
 
+/* Allocate a copy of the null-terminated string P; abort on failure. */
 static char *xstrdup(const char *p)
 {
   size_t n = strlen(p) + 1;
@@ -116,40 +159,11 @@ static char *xstrdup(const char *p)
   memcpy(q, p, n);
   return (q);
 }
-
-static int unix_socket_status(struct sockaddr_un *sun, int quickp)
-{
-  struct stat st;
-  FILE *fp = 0;
-  size_t len, n;
-  int rc;
-  char buf[256];
-
-  if (stat(sun->sun_path, &st))
-    return (errno == ENOENT ? UNUSED : USED);
-  if (!S_ISSOCK(st.st_mode) || quickp)
-    return (USED);
-  rc = USED;
-  if ((fp = fopen("/proc/net/unix", "r")) == 0)
-    goto done;
-  fgets(buf, sizeof(buf), fp); /* skip header */
-  len = strlen(sun->sun_path);
-  while (fgets(buf, sizeof(buf), fp)) {
-    n = strlen(buf);
-    if (n >= len + 2 && buf[n - len - 2] == ' ' && buf[n - 1] == '\n' &&
-       memcmp(buf + n - len - 1, sun->sun_path, len) == 0)
-      goto done;
-  }
-  if (ferror(fp))
-    goto done;
-  rc = STALE;
-done:
-  if (fp) fclose(fp);
-  return (rc);
-}
+/*----- Access control lists ----------------------------------------------*/
 
 #ifdef DEBUG
 
+/* Write to standard error a description of the ACL node A. */
 static void dump_aclnode(aclnode *a)
 {
   char minbuf[16], maxbuf[16];
@@ -176,8 +190,21 @@ static void dump_aclnode(aclnode *a)
   fputc('\n', stderr);
 }
 
+static void dump_acl(aclnode *a)
+{
+  int act = ALLOW;
+
+  for (; a; a = a->next) {
+    dump_aclnode(a);
+    act = a->act;
+  }
+  fprintf(stderr, "noip:   [default policy: %s]\n",
+         act == ALLOW ? "DENY" : "ALLOW");
+}
+
 #endif
 
+/* Returns nonzero if the ACL A allows the IP socket SIN. */
 static int acl_allows_p(aclnode *a, const struct sockaddr_in *sin)
 {
   unsigned long addr = ntohl(sin->sin_addr.s_addr);
@@ -201,22 +228,9 @@ static int acl_allows_p(aclnode *a, const struct sockaddr_in *sin)
   return (!act);
 }
 
-#ifdef DEBUG
-
-static void dump_acl(aclnode *a)
-{
-  int act = ALLOW;
-  
-  for (; a; a = a->next) {
-    dump_aclnode(a);
-    act = a->act;
-  }
-  fprintf(stderr, "noip:   [default policy: %s]\n",
-         act == ALLOW ? "DENY" : "ALLOW");
-}
-
-#endif
+/*----- Socket address conversion -----------------------------------------*/
 
+/* Return a uniformly distributed integer between MIN and MAX inclusive. */
 static unsigned randrange(unsigned min, unsigned max)
 {
   unsigned mask, i;
@@ -230,6 +244,48 @@ static unsigned randrange(unsigned min, unsigned max)
   return (i + min);
 }
 
+/* Return the status of Unix-domain socket address SUN.  Returns: UNUSED if
+ * the socket doesn't exist; USED if the path refers to an active socket, or
+ * isn't really a socket at all, or we can't tell without a careful search
+ * and QUICKP is set; or STALE if the file refers to a socket which isn't
+ * being used any more.
+ */
+static int unix_socket_status(struct sockaddr_un *sun, int quickp)
+{
+  struct stat st;
+  FILE *fp = 0;
+  size_t len, n;
+  int rc;
+  char buf[256];
+
+  if (stat(sun->sun_path, &st))
+    return (errno == ENOENT ? UNUSED : USED);
+  if (!S_ISSOCK(st.st_mode) || quickp)
+    return (USED);
+  rc = USED;
+  if ((fp = fopen("/proc/net/unix", "r")) == 0)
+    goto done;
+  if (!fgets(buf, sizeof(buf), fp)) goto done; /* skip header */
+  len = strlen(sun->sun_path);
+  while (fgets(buf, sizeof(buf), fp)) {
+    n = strlen(buf);
+    if (n >= len + 2 && buf[n - len - 2] == ' ' && buf[n - 1] == '\n' &&
+       memcmp(buf + n - len - 1, sun->sun_path, len) == 0)
+      goto done;
+  }
+  if (ferror(fp))
+    goto done;
+  rc = STALE;
+done:
+  if (fp) fclose(fp);
+  return (rc);
+}
+
+/* Encode the Internet address SIN as a Unix-domain address SUN.  If WANT is
+ * WANT_FRESH, and SIN->sin_port is zero, then we pick an arbitrary local
+ * port.  Otherwise we pick the port given.  There's an unpleasant hack to
+ * find servers bound to INADDR_ANY.  Returns zero on success; -1 on failure.
+ */
 static int encode_inet_addr(struct sockaddr_un *sun,
                            const struct sockaddr_in *sin,
                            int want)
@@ -283,13 +339,19 @@ static int encode_inet_addr(struct sockaddr_un *sun,
   return (0);
 }
 
+/* Decode the Unix address SUN to an Internet address SIN.  If
+ * DECODE_UNBOUND_P is nonzero, an empty address (indicative of an unbound
+ * Unix-domain socket) is translated to a wildcard Internet address.  Returns
+ * zero on success; -1 on failure (e.g., it wasn't one of our addresses).
+ */
 static int decode_inet_addr(struct sockaddr_in *sin,
                            const struct sockaddr_un *sun,
-                           socklen_t len)
+                           socklen_t len,
+                           int decode_unbound_p)
 {
   char buf[INET_ADDRSTRLEN + 16];
   char *p;
-  size_t n = strlen(sockdir), nn = strlen(sun->sun_path);
+  size_t n = strlen(sockdir), nn;
   struct sockaddr_in sin_mine;
   unsigned long port;
 
@@ -297,10 +359,11 @@ static int decode_inet_addr(struct sockaddr_in *sin,
     sin = &sin_mine;
   if (sun->sun_family != AF_UNIX)
     return (-1);
+  nn = strlen(sun->sun_path);
   if (len < sizeof(sun)) ((char *)sun)[len] = 0;
   D( fprintf(stderr, "noip: decode (%d) `%s'",
             *sun->sun_path, sun->sun_path); )
-  if (!sun->sun_path[0]) {
+  if (decode_unbound_p && !sun->sun_path[0]) {
     sin->sin_family = AF_INET;
     sin->sin_addr.s_addr = INADDR_ANY;
     sin->sin_port = 0;
@@ -335,6 +398,10 @@ static int decode_inet_addr(struct sockaddr_in *sin,
   return (0);
 }
 
+/* SK is (or at least might be) a Unix-domain socket we created when an
+ * Internet socket was asked for.  We've decided it should be an Internet
+ * socket after all, so convert it.
+ */
 static int fixup_real_ip_socket(int sk)
 {
   int nsk;
@@ -362,7 +429,7 @@ static int fixup_real_ip_socket(int sk)
   len = sizeof(sun);
   if (real_getsockname(sk, SA(&sun), &len))
     return (-1);
-  if (decode_inet_addr(&sin, &sun, len))
+  if (decode_inet_addr(&sin, &sun, len, 1))
     return (0); /* Not one of ours */
   len = sizeof(type);
   if (real_getsockopt(sk, SOL_SOCKET, SO_TYPE, &type, &len) < 0 ||
@@ -373,7 +440,7 @@ static int fixup_real_ip_socket(int sk)
   len = sizeof(ov_);                                                   \
   if (real_getsockopt(sk, SOL_SOCKET, SO_##opt, &ov_, &len) < 0 ||     \
       real_setsockopt(nsk, SOL_SOCKET, SO_##opt, &ov_, len)) {         \
-    real_close(nsk);                                                   \
+    close(nsk);                                                                \
     return (-1);                                                       \
   }                                                                    \
 } while (0);
@@ -383,11 +450,11 @@ static int fixup_real_ip_socket(int sk)
       (fd = fcntl(sk, F_GETFD)) < 0 ||
       fcntl(nsk, F_SETFL, f) < 0 ||
       dup2(nsk, sk) < 0) {
-    real_close(nsk);
+    close(nsk);
     return (-1);
   }
   unlink(sun.sun_path);
-  real_close(nsk);
+  close(nsk);
   if (fcntl(sk, F_SETFD, fd) < 0) {
     perror("noip: fixup_real_ip_socket F_SETFD");
     abort();
@@ -395,6 +462,9 @@ static int fixup_real_ip_socket(int sk)
   return (0);
 }
 
+/* The socket SK is about to be used to communicate with the remote address
+ * SA.  Assign it a local address so that getpeername does something useful.
+ */
 static int do_implicit_bind(int sk, const struct sockaddr **sa,
                            socklen_t *len, struct sockaddr_un *sun)
 {
@@ -425,13 +495,19 @@ static int do_implicit_bind(int sk, const struct sockaddr **sa,
   return (0);
 }
 
+/* We found the real address SA, with length LEN; if it's a Unix-domain
+ * address corresponding to a fake socket, convert it to cover up the
+ * deception.  Whatever happens, put the result at FAKE and store its length
+ * at FAKELEN.
+ */
 static void return_fake_name(struct sockaddr *sa, socklen_t len,
                             struct sockaddr *fake, socklen_t *fakelen)
 {
   struct sockaddr_in sin;
   socklen_t alen;
 
-  if (sa->sa_family == AF_UNIX && !decode_inet_addr(&sin, SUN(sa), len)) {
+  if (sa->sa_family == AF_UNIX &&
+      !decode_inet_addr(&sin, SUN(sa), len, 0)) {
     sa = SA(&sin);
     len = sizeof(sin);
   }
@@ -443,8 +519,9 @@ static void return_fake_name(struct sockaddr *sa, socklen_t len,
   *fakelen = alen;
 }
 
-/* --- Configuration --- */
+/*----- Configuration -----------------------------------------------------*/
 
+/* Return the process owner's home directory. */
 static char *home(void)
 {
   char *p;
@@ -459,6 +536,7 @@ static char *home(void)
     return "/notexist";
 }
 
+/* Return a good temporary directory to use. */
 static char *tmpdir(void)
 {
   char *p;
@@ -468,6 +546,7 @@ static char *tmpdir(void)
   else return ("/tmp");
 }
 
+/* Return the user's name, or at least something distinctive. */
 static char *user(void)
 {
   static char buf[16];
@@ -483,13 +562,20 @@ static char *user(void)
   }
 }
 
+/* Skip P over space characters. */
 #define SKIPSPC do { while (*p && isspace(UC(*p))) p++; } while (0)
+
+/* Set Q to point to the next word following P, null-terminate it, and step P
+ * past it. */
 #define NEXTWORD(q) do {                                               \
   SKIPSPC;                                                             \
   q = p;                                                               \
   while (*p && !isspace(UC(*p))) p++;                                  \
   if (*p) *p++ = 0;                                                    \
 } while (0)
+
+/* Set Q to point to the next dotted-quad address, store the ending delimiter
+ * in DEL, null-terminate it, and step P past it. */
 #define NEXTADDR(q, del) do {                                          \
   SKIPSPC;                                                             \
   q = p;                                                               \
@@ -497,6 +583,9 @@ static char *user(void)
   del = *p;                                                            \
   if (*p) *p++ = 0;                                                    \
 } while (0)
+
+/* Set Q to point to the next decimal number, store the ending delimiter in
+ * DEL, null-terminate it, and step P past it. */
 #define NEXTNUMBER(q, del) do {                                                \
   SKIPSPC;                                                             \
   q = p;                                                               \
@@ -504,11 +593,23 @@ static char *user(void)
   del = *p;                                                            \
   if (*p) *p++ = 0;                                                    \
 } while (0)
+
+/* Push the character DEL back so we scan it again, unless it's zero
+ * (end-of-file). */
 #define RESCAN(del) do { if (del) *--p = del; } while (0)
+
+/* Evaluate true if P is pointing to the word KW (and not some longer string
+ * of which KW is a prefix). */
+
 #define KWMATCHP(kw) (strncmp(p, kw, sizeof(kw) - 1) == 0 &&           \
                      !isalnum(UC(p[sizeof(kw) - 1])) &&                \
                      (p += sizeof(kw) - 1))
-  
+
+/* Parse a port list, starting at *PP.  Port lists have the form
+ * [:LOW[-HIGH]]: if omitted, all ports are included; if HIGH is omitted,
+ * it's as if HIGH = LOW.  Store LOW in *MIN, HIGH in *MAX and set *PP to the
+ * rest of the string.
+ */
 static void parse_ports(char **pp, unsigned short *min, unsigned short *max)
 {
   char *p = *pp, *q;
@@ -529,6 +630,10 @@ static void parse_ports(char **pp, unsigned short *min, unsigned short *max)
   *pp = p;
 }
 
+/* Make a new ACL node.  ACT is the verdict; MINADDR and MAXADDR are the
+ * ranges on IP addresses; MINPORT and MAXPORT are the ranges on port
+ * numbers; TAIL is the list tail to attach the new node to.
+ */
 #define ACLNODE(tail_, act_,                                           \
                minaddr_, maxaddr_, minport_, maxport_) do {            \
   aclnode *a_;                                                         \
@@ -539,6 +644,12 @@ static void parse_ports(char **pp, unsigned short *min, unsigned short *max)
   *tail_ = a_; tail_ = &a_->next;                                      \
 } while (0)
 
+/* Parse an ACL line.  *PP points to the end of the line; *TAIL points to
+ * the list tail (i.e., the final link in the list).  An ACL entry has the
+ * form +|- [any | local | ADDR | ADDR - ADDR | ADDR/ADDR | ADDR/INT] PORTS
+ * where PORTS is parsed by parse_ports above; an ACL line consists of a
+ * comma-separated sequence of entries..
+ */
 static void parse_acl_line(char **pp, aclnode ***tail)
 {
   struct in_addr addr;
@@ -617,6 +728,7 @@ bad:
   return;
 }
 
+/* Parse the autoports configuration directive.  Syntax is MIN - MAX. */
 static void parse_autoports(char **pp)
 {
   char *p = *pp, *q;
@@ -636,6 +748,8 @@ bad:
   return;
 }
 
+/* Parse an ACL from an environment variable VAR, attaching it to the list
+ * TAIL. */
 static void parse_acl_env(const char *var, aclnode ***tail)
 {
   char *p, *q;
@@ -647,6 +761,7 @@ static void parse_acl_env(const char *var, aclnode ***tail)
   }
 }
 
+/* Read the configuration from the config file and environment. */
 static void readconfig(void)
 {
   FILE *fp;
@@ -717,15 +832,20 @@ done:
      dump_acl(connect_real); )
 }
 
-/* --- Hooks --- */
+/*----- Overridden system calls -------------------------------------------*/
 
 int socket(int pf, int ty, int proto)
 {
-  if (pf == PF_INET) {
-    pf = PF_UNIX;
-    proto = 0;
+  switch (pf) {
+    case PF_INET:
+      pf = PF_UNIX;
+      proto = 0;
+    case PF_UNIX:
+      return real_socket(pf, ty, proto);
+    default:
+      errno = EAFNOSUPPORT;
+      return -1;
   }
-  return real_socket(pf, ty, proto);
 }
 
 int socketpair(int pf, int ty, int proto, int *sk)
@@ -741,7 +861,7 @@ int bind(int sk, const struct sockaddr *sa, socklen_t len)
 {
   struct sockaddr_un sun;
 
-  if (sa->sa_family == AF_INET) { 
+  if (sa->sa_family == AF_INET) {
     PRESERVING_ERRNO({
       if (acl_allows_p(bind_real, SIN(sa))) {
        if (fixup_real_ip_socket(sk))
@@ -759,13 +879,25 @@ int bind(int sk, const struct sockaddr *sa, socklen_t len)
 int connect(int sk, const struct sockaddr *sa, socklen_t len)
 {
   struct sockaddr_un sun;
+  int rc;
 
-  if (sa->sa_family == AF_INET) {
-    PRESERVING_ERRNO({
-      do_implicit_bind(sk, &sa, &len, &sun);
-    });
+  switch (sa->sa_family) {
+    case AF_INET:
+      PRESERVING_ERRNO({
+       do_implicit_bind(sk, &sa, &len, &sun);
+      });
+      rc = real_connect(sk, sa, len);
+      if (rc < 0) {
+       switch (errno) {
+         case ENOENT:  errno = ECONNREFUSED;   break;
+       }
+      }
+      break;
+    default:
+      rc = real_connect(sk, sa, len);
+      break;
   }
-  return real_connect(sk, sa, len);
+  return rc;
 }
 
 ssize_t sendto(int sk, const void *buf, size_t len, int flags,
@@ -907,8 +1039,9 @@ int setsockopt(int sk, int lev, int opt, const void *p, socklen_t len)
   return real_setsockopt(sk, lev, opt, p, len);
 }
 
-/* --- Initialization --- */
+/*----- Initialization ----------------------------------------------------*/
 
+/* Clean up the socket directory, deleting stale sockets. */
 static void cleanup_sockdir(void)
 {
   DIR *dir;
@@ -924,7 +1057,7 @@ static void cleanup_sockdir(void)
     if (d->d_name[0] == '.') continue;
     snprintf(sun.sun_path, sizeof(sun.sun_path),
             "%s/%s", sockdir, d->d_name);
-    if (decode_inet_addr(&sin, &sun, SUN_LEN(&sun)) ||
+    if (decode_inet_addr(&sin, &sun, SUN_LEN(&sun), 0) ||
        stat(sun.sun_path, &st) ||
        !S_ISSOCK(st.st_mode)) {
       D( fprintf(stderr, "noip: ignoring unknown socketdir entry `%s'\n",
@@ -940,6 +1073,9 @@ static void cleanup_sockdir(void)
   closedir(dir);
 }
 
+/* Find the addresses attached to local network interfaces, and remember them
+ * in a table.
+ */
 static void get_local_ipaddrs(void)
 {
   struct if_nameindex *ifn;
@@ -952,7 +1088,7 @@ static void get_local_ipaddrs(void)
     return;
   for (i = n_local_ipaddrs = 0;
        n_local_ipaddrs < MAX_LOCAL_IPADDRS &&
-         ifn[i].if_name && *ifn[i].if_name;
+        ifn[i].if_name && *ifn[i].if_name;
        i++) {
     strcpy(ifr.ifr_name, ifn[i].if_name);
     if (ioctl(sk, SIOCGIFADDR, &ifr) || ifr.ifr_addr.sa_family != AF_INET)
@@ -965,8 +1101,11 @@ static void get_local_ipaddrs(void)
   close(sk);
 }
 
-static void printerr(const char *p) { write(STDERR_FILENO, p, strlen(p)); }
+/* Print the given message to standard error.  Avoids stdio. */
+static void printerr(const char *p)
+  { if (write(STDERR_FILENO, p, strlen(p))) ; }
 
+/* Create the socket directory, being careful about permissions. */
 static void create_sockdir(void)
 {
   struct stat st;
@@ -998,6 +1137,8 @@ check:
   }
 }
 
+/* Initialization function. */
+static void setup(void) __attribute__((constructor));
 static void setup(void)
 {
   PRESERVING_ERRNO({
@@ -1013,3 +1154,5 @@ static void setup(void)
     cleanup_sockdir();
   });
 }
+
+/*----- That's all, folks -------------------------------------------------*/