noip.c (get_local_ipaddrs): Rewrite to use getifaddrs(3).
[preload-hacks] / noip.c
diff --git a/noip.c b/noip.c
index acb60ef..aaf21b4 100644 (file)
--- a/noip.c
+++ b/noip.c
  * 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.
+ * Preload-hacks are 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.
+ * with preload-hacks; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
 #define _GNU_SOURCE
@@ -51,7 +51,7 @@
 #include <arpa/inet.h>
 #include <netinet/tcp.h>
 #include <netinet/udp.h>
-#include <net/if.h>
+#include <ifaddrs.h>
 
 /*----- Data structures ---------------------------------------------------*/
 
@@ -98,10 +98,9 @@ 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;
@@ -266,7 +265,7 @@ static int unix_socket_status(struct sockaddr_un *sun, int quickp)
   rc = USED;
   if ((fp = fopen("/proc/net/unix", "r")) == 0)
     goto done;
-  fgets(buf, sizeof(buf), fp); /* skip header */
+  if (!fgets(buf, sizeof(buf), fp)) goto done; /* skip header */
   len = strlen(sun->sun_path);
   while (fgets(buf, sizeof(buf), fp)) {
     n = strlen(buf);
@@ -340,15 +339,19 @@ static int encode_inet_addr(struct sockaddr_un *sun,
   return (0);
 }
 
-/* Decode the Unix address SUN to an Internet address SIN.  Returns zero on
- * success; -1 on failure (e.g., it wasn't one of our addresses). */
+/* 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;
 
@@ -356,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;
@@ -425,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 ||
@@ -436,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);
@@ -446,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();
@@ -502,7 +506,8 @@ static void return_fake_name(struct sockaddr *sa, socklen_t len,
   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);
   }
@@ -831,11 +836,19 @@ done:
 
 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:
+#ifdef PF_NETLINK
+    case PF_NETLINK:
+#endif
+      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)
@@ -869,14 +882,12 @@ 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 fixup_p = 0;
   int rc;
 
   switch (sa->sa_family) {
     case AF_INET:
       PRESERVING_ERRNO({
        do_implicit_bind(sk, &sa, &len, &sun);
-       fixup_p = 1;
       });
       rc = real_connect(sk, sa, len);
       if (rc < 0) {
@@ -1049,7 +1060,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",
@@ -1070,31 +1081,36 @@ static void cleanup_sockdir(void)
  */
 static void get_local_ipaddrs(void)
 {
-  struct if_nameindex *ifn;
-  struct ifreq ifr;
-  int sk;
+  struct ifaddrs *ifa_head, *ifa;
+  const struct in_addr *a;
   int i;
 
-  ifn = if_nameindex();
-  if ((sk = real_socket(PF_INET, SOCK_STREAM, 00)) < 0)
-    return;
-  for (i = n_local_ipaddrs = 0;
-       n_local_ipaddrs < MAX_LOCAL_IPADDRS &&
-         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)
+  if (getifaddrs(&ifa_head)) { perror("getifaddrs"); return; }
+  for (n_local_ipaddrs = 0, ifa = ifa_head;
+       n_local_ipaddrs < MAX_LOCAL_IPADDRS && ifa;
+       ifa = ifa->ifa_next) {
+    if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET)
       continue;
-    local_ipaddrs[n_local_ipaddrs++] =
-      SIN(&ifr.ifr_addr)->sin_addr;
-    D( fprintf(stderr, "noip: local addr %s = %s\n", ifn[i].if_name,
-              inet_ntoa(local_ipaddrs[n_local_ipaddrs - 1])); )
+    a = &SIN(ifa->ifa_addr)->sin_addr;
+    D( fprintf(stderr, "noip: local addr %s = %s",
+              ifa->ifa_name, inet_ntoa(*a)); )
+    for (i = 0; i < n_local_ipaddrs; i++) {
+      if (local_ipaddrs[i].s_addr == a->s_addr) {
+       D( fprintf(stderr, " (duplicate)\n"); )
+       goto skip;
+      }
+    }
+    D( fprintf(stderr, "\n"); )
+    local_ipaddrs[n_local_ipaddrs] = *a;
+    n_local_ipaddrs++;
+  skip:;
   }
-  close(sk);
+  freeifaddrs(ifa_head);
 }
 
 /* Print the given message to standard error.  Avoids stdio. */
-static void printerr(const char *p) { write(STDERR_FILENO, p, strlen(p)); }
+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)