quiet compiler
[freeradius.git] / src / lib / misc.c
index 6d6e7e8..587482f 100644 (file)
 
 RCSID("$Id$")
 
-#include       <freeradius-devel/libradius.h>
+#include <freeradius-devel/libradius.h>
 
-#include       <ctype.h>
-#include       <sys/file.h>
-#include       <fcntl.h>
+#include <ctype.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/uio.h>
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+
+/*
+ *     Some versions of Linux don't have closefrom(), but they will
+ *     have /proc.
+ *
+ *     BSD systems will generally have closefrom(), but not proc.
+ *
+ *     OSX doesn't have closefrom() or /proc/self/fd, but it does
+ *     have /dev/fd
+ */
+#ifdef __linux__
+#define CLOSEFROM_DIR "/proc/self/fd"
+#elif defined(__APPLE__)
+#define CLOSEFROM_DIR "/dev/fd"
+#else
+#undef HAVE_DIRENT_H
+#endif
+
+#endif
 
 #define FR_PUT_LE16(a, val)\
        do {\
@@ -34,24 +59,20 @@ RCSID("$Id$")
                a[0] = ((uint16_t) (val)) & 0xff;\
        } while (0)
 
-#ifdef HAVE_PTHREAD_H
-static pthread_mutex_t autofree_context = PTHREAD_MUTEX_INITIALIZER;
-#  define PTHREAD_MUTEX_LOCK pthread_mutex_lock
-#  define PTHREAD_MUTEX_UNLOCK pthread_mutex_unlock
-#else
-#  define PTHREAD_MUTEX_LOCK(_x)
-#  define PTHREAD_MUTEX_UNLOCK(_x)
-#endif
-
 bool   fr_dns_lookups = false;     /* IP -> hostname lookups? */
 bool    fr_hostname_lookups = true; /* hostname -> IP lookups? */
-int    fr_debug_flag = 0;
+int    fr_debug_lvl = 0;
 
 static char const *months[] = {
        "jan", "feb", "mar", "apr", "may", "jun",
        "jul", "aug", "sep", "oct", "nov", "dec" };
 
-fr_thread_local_setup(char *, fr_inet_ntop_buffer);    /* macro */
+fr_thread_local_setup(char *, fr_inet_ntop_buffer)     /* macro */
+
+typedef struct fr_talloc_link {
+       bool armed;
+       TALLOC_CTX *child;
+} fr_talloc_link_t;
 
 /** Sets a signal handler using sigaction if available, else signal
  *
@@ -81,25 +102,72 @@ int fr_set_signal(int sig, sig_t func)
        return 0;
 }
 
-/** Allocates a new talloc context from the root autofree context
+/** Uninstall a signal for a specific handler
+ *
+ * man sigaction says these are fine to call from a signal handler.
+ *
+ * @param sig SIGNAL
+ */
+int fr_unset_signal(int sig)
+{
+#ifdef HAVE_SIGACTION
+        struct sigaction act;
+
+        memset(&act, 0, sizeof(act));
+        act.sa_flags = 0;
+        sigemptyset(&act.sa_mask);
+        act.sa_handler = SIG_DFL;
+
+        return sigaction(sig, &act, NULL);
+#else
+        return signal(sig, SIG_DFL);
+#endif
+}
+
+static int _fr_trigger_talloc_ctx_free(fr_talloc_link_t *trigger)
+{
+       if (trigger->armed) talloc_free(trigger->child);
+
+       return 0;
+}
+
+static int _fr_disarm_talloc_ctx_free(bool **armed)
+{
+       **armed = false;
+       return 0;
+}
+
+/** Link a parent and a child context, so the child is freed before the parent
  *
- * This function is threadsafe, whereas using the NULL context is not.
+ * @note This is not thread safe. Do not free parent before threads are joined, do not call from a child thread.
+ * @note It's OK to free the child before threads are joined, but this will leak memory until the parent is freed.
  *
- * @note The returned context must be freed by the caller.
- * @returns a new talloc context parented by the root autofree context.
+ * @param parent who's fate the child should share.
+ * @param child bound to parent's lifecycle.
+ * @return 0 on success -1 on failure.
  */
-TALLOC_CTX *fr_autofree_ctx(void)
+int fr_link_talloc_ctx_free(TALLOC_CTX *parent, TALLOC_CTX *child)
 {
-       static TALLOC_CTX *ctx = NULL, *child;
-       PTHREAD_MUTEX_LOCK(&autofree_context);
-       if (!ctx) {
-               ctx = talloc_autofree_context();
+       fr_talloc_link_t *trigger;
+       bool **disarm;
+
+       trigger = talloc(parent, fr_talloc_link_t);
+       if (!trigger) return -1;
+
+       disarm = talloc(child, bool *);
+       if (!disarm) {
+               talloc_free(trigger);
+               return -1;
        }
 
-       child = talloc_new(ctx);
-       PTHREAD_MUTEX_UNLOCK(&autofree_context);
+       trigger->child = child;
+       trigger->armed = true;
+       *disarm = &trigger->armed;
+
+       talloc_set_destructor(trigger, _fr_trigger_talloc_ctx_free);
+       talloc_set_destructor(disarm, _fr_disarm_talloc_ctx_free);
 
-       return child;
+       return 0;
 }
 
 /*
@@ -174,153 +242,215 @@ char const *ip_ntoa(char *buffer, uint32_t ipaddr)
        return buffer;
 }
 
-/** Parse an IPv4 address or IPv4 prefix in presentation format (and others)
+/*
+ *     Parse decimal digits until we run out of decimal digits.
+ */
+static int ip_octet_from_str(char const *str, uint32_t *poctet)
+{
+       uint32_t octet;
+       char const *p = str;
+
+       if ((*p < '0') || (*p > '9')) {
+               return -1;
+       }
+
+       octet = 0;
+
+       while ((*p >= '0') && (*p <= '9')) {
+               octet *= 10;
+               octet += *p - '0';
+               p++;
+
+               if (octet > 255) return -1;
+       }
+
+
+       *poctet = octet;
+       return p - str;
+}
+
+static int ip_prefix_from_str(char const *str, uint32_t *paddr)
+{
+       int shift, length;
+       uint32_t octet;
+       uint32_t addr;
+       char const *p = str;
+
+       addr = 0;
+
+       for (shift = 24; shift >= 0; shift -= 8) {
+               length = ip_octet_from_str(p, &octet);
+               if (length <= 0) return -1;
+
+               addr |= octet << shift;
+               p += length;
+
+               /*
+                *      EOS or / means we're done.
+                */
+               if (!*p || (*p == '/')) break;
+
+               /*
+                *      We require dots between octets.
+                */
+               if (*p != '.') return -1;
+               p++;
+       }
+
+       *paddr = htonl(addr);
+       return p - str;
+}
+
+
+/**
+ * Parse an IPv4 address, IPv4 prefix in presentation format (and others), or
+ * a hostname.
  *
  * @param out Where to write the ip address value.
- * @param value to parse, may be dotted quad [+ prefix], or integer, or octal number, or '*' (INADDR_ANY).
- * @param inlen Length of value, if value is \0 terminated inlen may be 0.
+ * @param value to parse, may be dotted quad [+ prefix], or integer, or octal number, or '*' (INADDR_ANY), or a hostname.
+ * @param inlen Length of value, if value is \0 terminated inlen may be -1.
  * @param resolve If true and value doesn't look like an IP address, try and resolve value as a hostname.
+ * @param fallback to IPv6 resolution if no A records can be found.
  * @return 0 if ip address was parsed successfully, else -1 on error.
  */
-int fr_pton(fr_ipaddr_t *out, char const *value, size_t inlen, bool resolve)
+int fr_pton4(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resolve, bool fallback)
 {
        char *p;
-       unsigned int prefix;
+       unsigned int mask;
        char *eptr;
 
-       /* Dotted quad + / + [0-9]{1,2} */
-       char buffer[INET_ADDRSTRLEN + 3];
+       /* Dotted quad + / + [0-9]{1,2} or a hostname (RFC1035 2.3.4 Size limits) */
+       char buffer[256];
 
        /*
         *      Copy to intermediary buffer if we were given a length
         */
-       if (inlen > 0) {
-               if (inlen >= sizeof(buffer)) {
+       if (inlen >= 0) {
+               if (inlen >= (ssize_t)sizeof(buffer)) {
                        fr_strerror_printf("Invalid IPv4 address string \"%s\"", value);
                        return -1;
                }
                memcpy(buffer, value, inlen);
                buffer[inlen] = '\0';
+               value = buffer;
        }
 
        p = strchr(value, '/');
+
        /*
         *      192.0.2.2 is parsed as if it was /32
         */
        if (!p) {
+               out->prefix = 32;
+               out->af = AF_INET;
+
                /*
                 *      Allow '*' as the wildcard address usually 0.0.0.0
                 */
                if ((value[0] == '*') && (value[1] == '\0')) {
                        out->ipaddr.ip4addr.s_addr = htonl(INADDR_ANY);
+
                /*
                 *      Convert things which are obviously integers to IP addresses
                 *
                 *      We assume the number is the bigendian representation of the
                 *      IP address.
                 */
-               } else if (is_integer(value)) {
+               } else if (is_integer(value) || ((value[0] == '0') && (value[1] == 'x'))) {
                        out->ipaddr.ip4addr.s_addr = htonl(strtoul(value, NULL, 0));
+
                } else if (!resolve) {
-                       if (inet_pton(AF_INET, value, &(out->ipaddr.ip4addr.s_addr)) <= 0) {
-                               fr_strerror_printf("Failed to parse IPv4 address string \"%s\"", value);
+                       if (inet_pton(AF_INET, value, &out->ipaddr.ip4addr.s_addr) <= 0) {
+                               fr_strerror_printf("Failed to parse IPv4 addreess string \"%s\"", value);
                                return -1;
                        }
-               } else if (ip_hton(AF_INET, value, out) < 0) return -1;
-
-               out->prefix = 32;
-               out->af = AF_INET;
+               } else if (ip_hton(out, AF_INET, value, fallback) < 0) return -1;
 
                return 0;
        }
 
        /*
-        *      Otherwise parse the prefix
-        */
-       if ((size_t)(p - value) >= INET_ADDRSTRLEN) {
-               fr_strerror_printf("Invalid IPv4 address string \"%s\"", value);
-               return -1;
-       }
-
-       /*
         *      Copy the IP portion into a temporary buffer if we haven't already.
         */
-       if (inlen == 0) memcpy(buffer, value, p - value);
+       if (inlen < 0) memcpy(buffer, value, p - value);
        buffer[p - value] = '\0';
 
-       if (!resolve) {
-               if (inet_pton(AF_INET, buffer, &(out->ipaddr.ip4addr.s_addr)) <= 0) {
-                       fr_strerror_printf("Failed to parse IPv4 address string \"%s\"", value);
-                       return -1;
-               }
-       } else if (ip_hton(AF_INET, buffer, out) < 0) return -1;
+       if (ip_prefix_from_str(buffer, &out->ipaddr.ip4addr.s_addr) <= 0) {
+               fr_strerror_printf("Failed to parse IPv4 address string \"%s\"", value);
+               return -1;
+       }
 
-       prefix = strtoul(p + 1, &eptr, 10);
-       if (prefix > 32) {
+       mask = strtoul(p + 1, &eptr, 10);
+       if (mask > 32) {
                fr_strerror_printf("Invalid IPv4 mask length \"%s\".  Should be between 0-32", p);
                return -1;
        }
+
        if (eptr[0] != '\0') {
                fr_strerror_printf("Failed to parse IPv4 address string \"%s\", "
                                   "got garbage after mask length \"%s\"", value, eptr);
                return -1;
        }
 
-       if (prefix < 32) {
-               out->ipaddr.ip4addr = fr_ipaddr_mask(&(out->ipaddr.ip4addr), prefix);
+       if (mask < 32) {
+               out->ipaddr.ip4addr = fr_inaddr_mask(&out->ipaddr.ip4addr, mask);
        }
 
-       out->prefix = (uint8_t) prefix;
+       out->prefix = (uint8_t) mask;
        out->af = AF_INET;
 
        return 0;
 }
 
-/** Parse an IPv6 address or IPv6 prefix in presentation format (and others)
+/**
+ * Parse an IPv6 address or IPv6 prefix in presentation format (and others),
+ * or a hostname.
  *
  * @param out Where to write the ip address value.
  * @param value to parse.
- * @param inlen Length of value, if value is \0 terminated inlen may be 0.
+ * @param inlen Length of value, if value is \0 terminated inlen may be -1.
  * @param resolve If true and value doesn't look like an IP address, try and resolve value as a hostname.
+ * @param fallback to IPv4 resolution if no AAAA records can be found.
  * @return 0 if ip address was parsed successfully, else -1 on error.
  */
-int fr_pton6(fr_ipaddr_t *out, char const *value, size_t inlen, bool resolve)
+int fr_pton6(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resolve, bool fallback)
 {
        char const *p;
        unsigned int prefix;
        char *eptr;
 
-       /* IPv6  + / + [0-9]{1,3} */
-       char buffer[INET6_ADDRSTRLEN + 4];
+       /* IPv6  + / + [0-9]{1,3} or a hostname (RFC1035 2.3.4 Size limits) */
+       char buffer[256];
 
        /*
         *      Copy to intermediary buffer if we were given a length
         */
-       if (inlen > 0) {
-               if (inlen >= sizeof(buffer)) {
+       if (inlen >= 0) {
+               if (inlen >= (ssize_t)sizeof(buffer)) {
                        fr_strerror_printf("Invalid IPv6 address string \"%s\"", value);
                        return -1;
                }
                memcpy(buffer, value, inlen);
                buffer[inlen] = '\0';
+               value = buffer;
        }
 
        p = strchr(value, '/');
        if (!p) {
+               out->prefix = 128;
+               out->af = AF_INET6;
+
                /*
                 *      Allow '*' as the wildcard address
                 */
                if ((value[0] == '*') && (value[1] == '\0')) {
-                       memset(&out->ipaddr.ip6addr.s6_addr, 0, sizeof(out->ipaddr.ip6addr.s6_addr));
+                       memset(out->ipaddr.ip6addr.s6_addr, 0, sizeof(out->ipaddr.ip6addr.s6_addr));
                } else if (!resolve) {
-                       if (inet_pton(AF_INET6, value, &(out->ipaddr.ip6addr.s6_addr)) <= 0) {
+                       if (inet_pton(AF_INET6, value, out->ipaddr.ip6addr.s6_addr) <= 0) {
                                fr_strerror_printf("Failed to parse IPv6 address string \"%s\"", value);
                                return -1;
                        }
-               } else if (ip_hton(AF_INET6, value, out) < 0) return -1;
-
-               out->prefix = 128;
-               out->af = AF_INET6;
+               } else if (ip_hton(out, AF_INET6, value, fallback) < 0) return -1;
 
                return 0;
        }
@@ -333,15 +463,15 @@ int fr_pton6(fr_ipaddr_t *out, char const *value, size_t inlen, bool resolve)
        /*
         *      Copy string to temporary buffer if we didn't do it earlier
         */
-       if (inlen == 0) memcpy(buffer, value, p - value);
+       if (inlen < 0) memcpy(buffer, value, p - value);
        buffer[p - value] = '\0';
 
        if (!resolve) {
-               if (inet_pton(AF_INET6, buffer, &(out->ipaddr.ip6addr.s6_addr)) <= 0) {
+               if (inet_pton(AF_INET6, buffer, out->ipaddr.ip6addr.s6_addr) <= 0) {
                        fr_strerror_printf("Failed to parse IPv6 address string \"%s\"", value);
                        return -1;
                }
-       } else if (ip_hton(AF_INET6, buffer, out) < 0) return -1;
+       } else if (ip_hton(out, AF_INET6, buffer, fallback) < 0) return -1;
 
        prefix = strtoul(p + 1, &eptr, 10);
        if (prefix > 128) {
@@ -357,8 +487,8 @@ int fr_pton6(fr_ipaddr_t *out, char const *value, size_t inlen, bool resolve)
        if (prefix < 128) {
                struct in6_addr addr;
 
-               addr = fr_ipaddr_mask6(&(out->ipaddr.ip6addr), prefix);
-               memcpy(&(out->ipaddr.ip6addr.s6_addr), &addr, sizeof(addr));
+               addr = fr_in6addr_mask(&out->ipaddr.ip6addr, prefix);
+               memcpy(out->ipaddr.ip6addr.s6_addr, addr.s6_addr, sizeof(out->ipaddr.ip6addr.s6_addr));
        }
 
        out->prefix = (uint8_t) prefix;
@@ -367,6 +497,172 @@ int fr_pton6(fr_ipaddr_t *out, char const *value, size_t inlen, bool resolve)
        return 0;
 }
 
+/** Simple wrapper to decide whether an IP value is v4 or v6 and call the appropriate parser.
+ *
+ * @param[out] out Where to write the ip address value.
+ * @param[in] value to parse.
+ * @param[in] inlen Length of value, if value is \0 terminated inlen may be -1.
+ * @param[in] resolve If true and value doesn't look like an IP address, try and resolve value as a
+ *     hostname.
+ * @param[in] af If the address type is not obvious from the format, and resolve is true, the DNS
+ *     record (A or AAAA) we require.  Also controls which parser we pass the address to if
+ *     we have no idea what it is.
+ * @return
+ *     - 0 if ip address was parsed successfully.
+ *     - -1 on failure.
+ */
+int fr_pton(fr_ipaddr_t *out, char const *value, ssize_t inlen, int af, bool resolve)
+{
+       size_t len, i;
+
+       len = (inlen >= 0) ? (size_t)inlen : strlen(value);
+       for (i = 0; i < len; i++) switch (value[i]) {
+       /*
+        *      ':' is illegal in domain names and IPv4 addresses.
+        *      Must be v6 and cannot be a domain.
+        */
+       case ':':
+               return fr_pton6(out, value, inlen, false, false);
+
+       /*
+        *      Chars which don't really tell us anything
+        */
+       case '.':
+       case '/':
+               continue;
+
+       default:
+               /*
+                *      Outside the range of IPv4 chars, must be a domain
+                *      Use A record in preference to AAAA record.
+                */
+               if ((value[i] < '0') || (value[i] > '9')) {
+                       if (!resolve) {
+                               fr_strerror_printf("Not IPv4/6 address, and asked not to resolve");
+                               return -1;
+                       }
+                       switch (af) {
+                       case AF_UNSPEC:
+                               return fr_pton4(out, value, inlen, resolve, true);
+
+                       case AF_INET:
+                               return fr_pton4(out, value, inlen, resolve, false);
+
+                       case AF_INET6:
+                               return fr_pton6(out, value, inlen, resolve, false);
+
+                       default:
+                               fr_strerror_printf("Invalid address family %i", af);
+                               return -1;
+                       }
+               }
+               break;
+       }
+
+       /*
+        *      All chars were in the IPv4 set [0-9/.], must be an IPv4
+        *      address.
+        */
+       return fr_pton4(out, value, inlen, false, false);
+}
+
+/** Parses IPv4/6 address + port, to fr_ipaddr_t and integer
+ *
+ * @param[out] out Where to write the ip address value.
+ * @param[out] port_out Where to write the port (0 if no port found).
+ * @param[in] value to parse.
+ * @param[in] inlen Length of value, if value is \0 terminated inlen may be -1.
+ * @param[in] af If the address type is not obvious from the format, and resolve is true, the DNS
+ *     record (A or AAAA) we require.  Also controls which parser we pass the address to if
+ *     we have no idea what it is.
+ * @param[in] resolve If true and value doesn't look like an IP address, try and resolve value as a
+ *     hostname.
+ */
+int fr_pton_port(fr_ipaddr_t *out, uint16_t *port_out, char const *value, ssize_t inlen, int af, bool resolve)
+{
+       char const      *p = value, *q;
+       char            *end;
+       unsigned long   port;
+       char            buffer[6];
+       size_t          len;
+
+       *port_out = 0;
+
+       len = (inlen >= 0) ? (size_t)inlen : strlen(value);
+
+       if (*p == '[') {
+               if (!(q = memchr(p + 1, ']', len - 1))) {
+                       fr_strerror_printf("Missing closing ']' for IPv6 address");
+                       return -1;
+               }
+
+               /*
+                *      inet_pton doesn't like the address being wrapped in []
+                */
+               if (fr_pton6(out, p + 1, (q - p) - 1, false, false) < 0) return -1;
+
+               if (q[1] == ':') {
+                       q++;
+                       goto do_port;
+               }
+
+               return 0;
+       }
+
+       /*
+        *      Host, IPv4 or IPv6 with no port
+        */
+       q = memchr(p, ':', len);
+       if (!q) return fr_pton(out, p, len, af, resolve);
+
+       /*
+        *      IPv4 or host, with port
+        */
+       if (fr_pton(out, p, (q - p), af, resolve) < 0) return -1;
+
+do_port:
+       /*
+        *      Valid ports are a maximum of 5 digits, so if the
+        *      input length indicates there are more than 5 chars
+        *      after the ':' then there's an issue.
+        */
+       if (len > (size_t) ((q + sizeof(buffer)) - value)) {
+       error:
+               fr_strerror_printf("IP string contains trailing garbage after port delimiter");
+               return -1;
+       }
+
+       p = q + 1;                      /* Move to first digit */
+
+       strlcpy(buffer, p, (len - (p - value)) + 1);
+       port = strtoul(buffer, &end, 10);
+       if (*end != '\0') goto error;   /* Trailing garbage after integer */
+
+       if ((port > UINT16_MAX) || (port == 0)) {
+               fr_strerror_printf("Port %lu outside valid port range 1-" STRINGIFY(UINT16_MAX), port);
+               return -1;
+       }
+       *port_out = port;
+
+       return 0;
+}
+
+int fr_ntop(char *out, size_t outlen, fr_ipaddr_t *addr)
+{
+       char buffer[INET6_ADDRSTRLEN];
+
+       if (inet_ntop(addr->af, &(addr->ipaddr), buffer, sizeof(buffer)) == NULL) return -1;
+
+       return snprintf(out, outlen, "%s/%i", buffer, addr->prefix);
+}
+
+/*
+ *     cppcheck apparently can't pick this up from the system headers.
+ */
+#ifdef CPPCHECK
+#define F_WRLCK
+#endif
+
 /*
  *     Internal wrapper for locking, to minimize the number of ifdef's
  *
@@ -552,18 +848,16 @@ static int inet_pton4(char const *src, struct in_addr *dst)
 
 
 #ifdef HAVE_STRUCT_SOCKADDR_IN6
-/* int
- * inet_pton6(src, dst)
- *     convert presentation level address to network order binary form.
- * return:
- *     1 if `src' is a valid [RFC1884 2.2] address, else 0.
- * notice:
- *     (1) does not touch `dst' unless it's returning 1.
- *     (2) :: in a full address is silently ignored.
- * credit:
- *     inspired by Mark Andrews.
- * author:
- *     Paul Vixie, 1996.
+/** Convert presentation level address to network order binary form
+ *
+ * @note Does not touch dst unless it's returning 1.
+ * @note :: in a full address is silently ignored.
+ * @note Inspired by Mark Andrews.
+ * @author Paul Vixie, 1996.
+ *
+ * @param src presentation level address.
+ * @param dst where to write output address.
+ * @return 1 if `src' is a valid [RFC1884 2.2] address, else 0.
  */
 static int inet_pton6(char const *src, unsigned char *dst)
 {
@@ -712,46 +1006,42 @@ char const *inet_ntop(int af, void const *src, char *dst, size_t cnt)
 }
 #endif
 
-
-/*
- *     Try to convert the address to v4 then v6
- */
-int ip_ptonx(char const *src, fr_ipaddr_t *dst)
-{
-       if (inet_pton(AF_INET, src, &dst->ipaddr.ip4addr) == 1) {
-               dst->af = AF_INET;
-               return 1;
-       }
-
-#ifdef HAVE_STRUCT_SOCKADDR_IN6
-       if (inet_pton(AF_INET6, src, &dst->ipaddr.ip6addr) == 1) {
-               dst->af = AF_INET6;
-               return 1;
-       }
-#endif
-
-       dst->scope = 0;
-
-       return 0;
-}
-
-/*
- *     Wrappers for IPv4/IPv6 host to IP address lookup.
- *     This API returns only one IP address, of the specified
- *     address family, or the first address (of whatever family),
- *     if AF_UNSPEC is used.
+/** Wrappers for IPv4/IPv6 host to IP address lookup
+ *
+ * This function returns only one IP address, of the specified address family,
+ * or the first address (of whatever family), if AF_UNSPEC is used.
+ *
+ * If fallback is specified and af is AF_INET, but no AF_INET records were
+ * found and a record for AF_INET6 exists that record will be returned.
+ *
+ * If fallback is specified and af is AF_INET6, and a record with AF_INET4 exists
+ * that record will be returned instead.
+ *
+ * @param out Where to write result.
+ * @param af To search for in preference.
+ * @param hostname to search for.
+ * @param fallback to the other adress family, if no records matching af, found.
+ * @return 0 on success, else -1 on failure.
  */
-int ip_hton(int af, char const *src, fr_ipaddr_t *out)
+int ip_hton(fr_ipaddr_t *out, int af, char const *hostname, bool fallback)
 {
        int rcode;
-       struct addrinfo hints, *ai = NULL, *res = NULL;
+       struct addrinfo hints, *ai = NULL, *alt = NULL, *res = NULL;
 
+       /*
+        *      Avoid malloc for IP addresses.  This helps us debug
+        *      memory errors when using talloc.
+        */
+#ifdef TALLOC_DEBUG
+       if (true) {
+#else
        if (!fr_hostname_lookups) {
+#endif
 #ifdef HAVE_STRUCT_SOCKADDR_IN6
                if (af == AF_UNSPEC) {
                        char const *p;
 
-                       for (p = src; *p != '\0'; p++) {
+                       for (p = hostname; *p != '\0'; p++) {
                                if ((*p == ':') ||
                                    (*p == '[') ||
                                    (*p == ']')) {
@@ -764,52 +1054,62 @@ int ip_hton(int af, char const *src, fr_ipaddr_t *out)
 
                if (af == AF_UNSPEC) af = AF_INET;
 
-               if (!inet_pton(af, src, &(out->ipaddr))) {
-                       return -1;
-               }
+               if (!inet_pton(af, hostname, &(out->ipaddr))) return -1;
 
                out->af = af;
                return 0;
        }
 
        memset(&hints, 0, sizeof(hints));
-       hints.ai_family = af;
 
-#ifdef TALLOC_DEBUG
        /*
-        *      Avoid malloc for IP addresses.  This helps us debug
-        *      memory errors when using talloc.
+        *      If we're falling back we need both IPv4 and IPv6 records
         */
-       if (af == AF_INET) {
-               /*
-                *      If it's all numeric, avoid getaddrinfo()
-                */
-               if (inet_pton(af, src, &out->ipaddr.ip4addr) == 1) {
-                       return 0;
-               }
+       if (fallback) {
+               hints.ai_family = AF_UNSPEC;
+       } else {
+               hints.ai_family = af;
        }
-#endif
 
-       if ((rcode = getaddrinfo(src, NULL, &hints, &res)) != 0) {
-               fr_strerror_printf("ip_hton: %s", gai_strerror(rcode));
-               return -1;
+       if ((rcode = getaddrinfo(hostname, NULL, &hints, &res)) != 0) {
+               switch (af) {
+               default:
+               case AF_UNSPEC:
+                       fr_strerror_printf("Failed resolving \"%s\" to IP address: %s",
+                                          hostname, gai_strerror(rcode));
+                       return -1;
+
+               case AF_INET:
+                       fr_strerror_printf("Failed resolving \"%s\" to IPv4 address: %s",
+                                          hostname, gai_strerror(rcode));
+                       return -1;
+
+               case AF_INET6:
+                       fr_strerror_printf("Failed resolving \"%s\" to IPv6 address: %s",
+                                          hostname, gai_strerror(rcode));
+                       return -1;
+               }
        }
 
        for (ai = res; ai; ai = ai->ai_next) {
-               if ((af == ai->ai_family) || (af == AF_UNSPEC))
-                       break;
+               if ((af == ai->ai_family) || (af == AF_UNSPEC)) break;
+               if (!alt && fallback && ((ai->ai_family == AF_INET) || (ai->ai_family == AF_INET6))) alt = ai;
        }
 
+       if (!ai) ai = alt;
        if (!ai) {
-               fr_strerror_printf("ip_hton failed to find requested information for host %.100s", src);
-               freeaddrinfo(ai);
+               fr_strerror_printf("ip_hton failed to find requested information for host %.100s", hostname);
+               freeaddrinfo(res);
                return -1;
        }
 
        rcode = fr_sockaddr2ipaddr((struct sockaddr_storage *)ai->ai_addr,
                                   ai->ai_addrlen, out, NULL);
-       freeaddrinfo(ai);
-       if (!rcode) return -1;
+       freeaddrinfo(res);
+       if (!rcode) {
+               fr_strerror_printf("Failed converting sockaddr to ipaddr");
+               return -1;
+       }
 
        return 0;
 }
@@ -846,22 +1146,20 @@ char const *ip_ntoh(fr_ipaddr_t const *src, char *dst, size_t cnt)
  *
  * @param ipaddr to mask.
  * @param prefix Number of contiguous bits to mask.
- * @return an ipv6 address with the host portion zeroed out.
+ * @return an ipv4 address with the host portion zeroed out.
  */
-struct in_addr fr_ipaddr_mask(struct in_addr const *ipaddr, uint8_t prefix)
+struct in_addr fr_inaddr_mask(struct in_addr const *ipaddr, uint8_t prefix)
 {
        uint32_t ret;
 
-       if (prefix > 32) {
-               prefix = 32;
-       }
+       if (prefix > 32) prefix = 32;
 
        /* Short circuit */
-       if (prefix == 32) {
-               return *ipaddr;
-       }
+       if (prefix == 32) return *ipaddr;
+
+       if (prefix == 0) ret = 0;
+       else ret = htonl(~((0x00000001UL << (32 - prefix)) - 1)) & ipaddr->s_addr;
 
-       ret = htonl(~((0x00000001UL << (32 - prefix)) - 1)) & ipaddr->s_addr;
        return (*(struct in_addr *)&ret);
 }
 
@@ -871,57 +1169,89 @@ struct in_addr fr_ipaddr_mask(struct in_addr const *ipaddr, uint8_t prefix)
  * @param prefix Number of contiguous bits to mask.
  * @return an ipv6 address with the host portion zeroed out.
  */
-struct in6_addr fr_ipaddr_mask6(struct in6_addr const *ipaddr, uint8_t prefix)
+struct in6_addr fr_in6addr_mask(struct in6_addr const *ipaddr, uint8_t prefix)
 {
        uint64_t const *p = (uint64_t const *) ipaddr;
        uint64_t ret[2], *o = ret;
 
-       if (prefix > 128) {
-               prefix = 128;
-       }
+       if (prefix > 128) prefix = 128;
 
        /* Short circuit */
-       if (prefix == 128) {
-               return *ipaddr;
-       }
+       if (prefix == 128) return *ipaddr;
 
        if (prefix >= 64) {
                prefix -= 64;
-               *o++ = 0xffffffffffffffffULL & *p++;
+               *o++ = 0xffffffffffffffffULL & *p++;    /* lhs portion masked */
        } else {
-               ret[1] = 0;
+               ret[1] = 0;                             /* rhs portion zeroed */
        }
 
-       *o = htonll(~((0x0000000000000001ULL << (64 - prefix)) - 1)) & *p;
+       /* Max left shift is 63 else we get overflow */
+       if (prefix > 0) {
+               *o = htonll(~((uint64_t)(0x0000000000000001ULL << (64 - prefix)) - 1)) & *p;
+       } else {
+               *o = 0;
+       }
 
        return *(struct in6_addr *) &ret;
 }
 
-static char const *hextab = "0123456789abcdef";
+/** Zeroes out the host portion of an fr_ipaddr_t
+ *
+ * @param[in,out] addr to mask
+ * @param[in] prefix Length of the network portion.
+ */
+void fr_ipaddr_mask(fr_ipaddr_t *addr, uint8_t prefix)
+{
+
+       switch (addr->af) {
+       case AF_INET:
+               addr->ipaddr.ip4addr = fr_inaddr_mask(&addr->ipaddr.ip4addr, prefix);
+               break;
+
+       case AF_INET6:
+               addr->ipaddr.ip6addr = fr_in6addr_mask(&addr->ipaddr.ip6addr, prefix);
+               break;
+
+       default:
+               return;
+       }
+       addr->prefix = prefix;
+}
+
+static char const hextab[] = "0123456789abcdef";
 
 /** Convert hex strings to binary data
  *
  * @param bin Buffer to write output to.
- * @param hex input string.
  * @param outlen length of output buffer (or length of input string / 2).
+ * @param hex input string.
+ * @param inlen length of the input string
  * @return length of data written to buffer.
  */
-size_t fr_hex2bin(uint8_t *bin, char const *hex, size_t outlen)
+size_t fr_hex2bin(uint8_t *bin, size_t outlen, char const *hex, size_t inlen)
 {
        size_t i;
+       size_t len;
        char *c1, *c2;
 
-       for (i = 0; i < outlen; i++) {
-               if(!(c1 = memchr(hextab, tolower((int) hex[i << 1]), 16)) ||
-                  !(c2 = memchr(hextab, tolower((int) hex[(i << 1) + 1]), 16)))
+       /*
+        *      Smartly truncate output, caller should check number of bytes
+        *      written.
+        */
+       len = inlen >> 1;
+       if (len > outlen) len = outlen;
+
+       for (i = 0; i < len; i++) {
+               if(!(c1 = memchr(hextab, tolower((int) hex[i << 1]), sizeof(hextab))) ||
+                  !(c2 = memchr(hextab, tolower((int) hex[(i << 1) + 1]), sizeof(hextab))))
                        break;
-                bin[i] = ((c1-hextab)<<4) + (c2-hextab);
+               bin[i] = ((c1-hextab)<<4) + (c2-hextab);
        }
 
        return i;
 }
 
-
 /** Convert binary data to a hex string
  *
  * Ascii encoded hex string will not be prefixed with '0x'
@@ -948,7 +1278,26 @@ size_t fr_bin2hex(char *hex, uint8_t const *bin, size_t inlen)
        return inlen * 2;
 }
 
+/** Convert binary data to a hex string
+ *
+ * Ascii encoded hex string will not be prefixed with '0x'
+ *
+ * @param[in] ctx to alloc buffer in.
+ * @param[in] bin input.
+ * @param[in] inlen of bin input.
+ * @return length of data written to buffer.
+ */
+char *fr_abin2hex(TALLOC_CTX *ctx, uint8_t const *bin, size_t inlen)
+{
+       char *buff;
+
+       buff = talloc_array(ctx, char, (inlen << 2));
+       if (!buff) return NULL;
 
+       fr_bin2hex(buff, bin, inlen);
+
+       return buff;
+}
 
 /** Consume the integer (or hex) portion of a value string
  *
@@ -978,9 +1327,33 @@ bool is_whitespace(char const *value)
        return true;
 }
 
+/** Check whether the string is made up of printable UTF8 chars
+ *
+ * @param value to check.
+ * @param len of value.
+ *
+ * @return
+ *     - true if the string is printable.
+ *     - false if the string contains non printable chars
+ */
+ bool is_printable(void const *value, size_t len)
+ {
+       uint8_t const *p = value;
+       int     clen;
+       size_t  i;
+
+       for (i = 0; i < len; i++) {
+               clen = fr_utf8_char(p, len - i);
+               if (clen == 0) return false;
+               i += (size_t)clen;
+               p += clen;
+       }
+       return true;
+ }
+
 /** Check whether the string is all numbers
  *
- * @return true if the entirety of the string is are numebrs, else false.
+ * @return true if the entirety of the string is all numbers, else false.
  */
 bool is_integer(char const *value)
 {
@@ -993,7 +1366,7 @@ bool is_integer(char const *value)
 
 /** Check whether the string is allzeros
  *
- * @return true if the entirety of the string is are numebrs, else false.
+ * @return true if the entirety of the string is all zeros, else false.
  */
 bool is_zero(char const *value)
 {
@@ -1012,20 +1385,63 @@ int closefrom(int fd)
 {
        int i;
        int maxfd = 256;
+#ifdef HAVE_DIRENT_H
+       DIR *dir;
+#endif
+
+#ifdef F_CLOSEM
+       if (fcntl(fd, F_CLOSEM) == 0) {
+               return 0;
+       }
+#endif
+
+#ifdef F_MAXFD
+       maxfd = fcntl(fd, F_F_MAXFD);
+       if (maxfd >= 0) goto do_close;
+#endif
 
 #ifdef _SC_OPEN_MAX
        maxfd = sysconf(_SC_OPEN_MAX);
        if (maxfd < 0) {
-         maxfd = 256;
+               maxfd = 256;
+       }
+#endif
+
+#ifdef HAVE_DIRENT_H
+       /*
+        *      Use /proc/self/fd directory if it exists.
+        */
+       dir = opendir(CLOSEFROM_DIR);
+       if (dir != NULL) {
+               long my_fd;
+               char *endp;
+               struct dirent *dp;
+
+               while ((dp = readdir(dir)) != NULL) {
+                       my_fd = strtol(dp->d_name, &endp, 10);
+                       if (my_fd <= 0) continue;
+
+                       if (*endp) continue;
+
+                       if (my_fd == dirfd(dir)) continue;
+
+                       if ((my_fd >= fd) && (my_fd <= maxfd)) {
+                               (void) close((int) my_fd);
+                       }
+               }
+               (void) closedir(dir);
+               return 0;
        }
 #endif
 
+#ifdef F_MAXFD
+do_close:
+#endif
+
        if (fd > maxfd) return 0;
 
        /*
         *      FIXME: return EINTR?
-        *
-        *      Use F_CLOSEM?
         */
        for (i = fd; i < maxfd; i++) {
                close(i);
@@ -1040,12 +1456,14 @@ int fr_ipaddr_cmp(fr_ipaddr_t const *a, fr_ipaddr_t const *b)
        if (a->af < b->af) return -1;
        if (a->af > b->af) return +1;
 
+       if (a->prefix < b->prefix) return -1;
+       if (a->prefix > b->prefix) return +1;
+
        switch (a->af) {
        case AF_INET:
                return memcmp(&a->ipaddr.ip4addr,
                              &b->ipaddr.ip4addr,
                              sizeof(a->ipaddr.ip4addr));
-               break;
 
 #ifdef HAVE_STRUCT_SOCKADDR_IN6
        case AF_INET6:
@@ -1055,7 +1473,6 @@ int fr_ipaddr_cmp(fr_ipaddr_t const *a, fr_ipaddr_t const *b)
                return memcmp(&a->ipaddr.ip6addr,
                              &b->ipaddr.ip6addr,
                              sizeof(a->ipaddr.ip6addr));
-               break;
 #endif
 
        default:
@@ -1068,6 +1485,8 @@ int fr_ipaddr_cmp(fr_ipaddr_t const *a, fr_ipaddr_t const *b)
 int fr_ipaddr2sockaddr(fr_ipaddr_t const *ipaddr, uint16_t port,
                       struct sockaddr_storage *sa, socklen_t *salen)
 {
+       memset(sa, 0, sizeof(*sa));
+
        if (ipaddr->af == AF_INET) {
                struct sockaddr_in s4;
 
@@ -1105,6 +1524,8 @@ int fr_ipaddr2sockaddr(fr_ipaddr_t const *ipaddr, uint16_t port,
 int fr_sockaddr2ipaddr(struct sockaddr_storage const *sa, socklen_t salen,
                       fr_ipaddr_t *ipaddr, uint16_t *port)
 {
+       memset(ipaddr, 0, sizeof(*ipaddr));
+
        if (sa->ss_family == AF_INET) {
                struct sockaddr_in      s4;
 
@@ -1115,6 +1536,7 @@ int fr_sockaddr2ipaddr(struct sockaddr_storage const *sa, socklen_t salen,
 
                memcpy(&s4, sa, sizeof(s4));
                ipaddr->af = AF_INET;
+               ipaddr->prefix = 32;
                ipaddr->ipaddr.ip4addr = s4.sin_addr;
                if (port) *port = ntohs(s4.sin_port);
 
@@ -1129,6 +1551,7 @@ int fr_sockaddr2ipaddr(struct sockaddr_storage const *sa, socklen_t salen,
 
                memcpy(&s6, sa, sizeof(s6));
                ipaddr->af = AF_INET6;
+               ipaddr->prefix = 128;
                ipaddr->ipaddr.ip6addr = s6.sin6_addr;
                if (port) *port = ntohs(s6.sin6_port);
                ipaddr->scope = s6.sin6_scope_id;
@@ -1143,6 +1566,165 @@ int fr_sockaddr2ipaddr(struct sockaddr_storage const *sa, socklen_t salen,
        return 1;
 }
 
+#ifdef O_NONBLOCK
+/** Set O_NONBLOCK on a socket
+ *
+ * @note O_NONBLOCK is POSIX.
+ *
+ * @param fd to set nonblocking flag on.
+ * @return flags set on the socket, or -1 on error.
+ */
+int fr_nonblock(int fd)
+{
+       int flags;
+
+       flags = fcntl(fd, F_GETFL, NULL);
+       if (flags < 0)  {
+               fr_strerror_printf("Failure getting socket flags: %s", fr_syserror(errno));
+               return -1;
+       }
+
+       flags |= O_NONBLOCK;
+       if (fcntl(fd, F_SETFL, flags) < 0) {
+               fr_strerror_printf("Failure setting socket flags: %s", fr_syserror(errno));
+               return -1;
+       }
+
+       return flags;
+}
+
+/** Unset O_NONBLOCK on a socket
+ *
+ * @note O_NONBLOCK is POSIX.
+ *
+ * @param fd to set nonblocking flag on.
+ * @return flags set on the socket, or -1 on error.
+ */
+int fr_blocking(int fd)
+{
+       int flags;
+
+       flags = fcntl(fd, F_GETFL, NULL);
+       if (flags < 0)  {
+               fr_strerror_printf("Failure getting socket flags: %s", fr_syserror(errno));
+               return -1;
+       }
+
+       flags ^= O_NONBLOCK;
+       if (fcntl(fd, F_SETFL, flags) < 0) {
+               fr_strerror_printf("Failure setting socket flags: %s", fr_syserror(errno));
+               return -1;
+       }
+
+       return flags;
+}
+#else
+int fr_nonblock(UNUSED int fd)
+{
+       fr_strerror_printf("Non blocking sockets are not supported");
+       return -1;
+}
+int fr_blocking(UNUSED int fd)
+{
+       fr_strerror_printf("Non blocking sockets are not supported");
+       return -1;
+}
+#endif
+
+/** Write out a vector to a file descriptor
+ *
+ * Wraps writev, calling it as necessary. If timeout is not NULL,
+ * timeout is applied to each call that returns EAGAIN or EWOULDBLOCK
+ *
+ * @note Should only be used on nonblocking file descriptors.
+ * @note Socket should likely be closed on timeout.
+ * @note iovec may be modified in such a way that it's not re-usable.
+ * @note Leaves errno set to the last error that ocurred.
+ *
+ * @param fd to write to.
+ * @param vector to write.
+ * @param iovcnt number of elements in iovec.
+ * @param timeout how long to wait for fd to become writeable before timing out.
+ * @return number of bytes written, -1 on error.
+ */
+ssize_t fr_writev(int fd, struct iovec vector[], int iovcnt, struct timeval *timeout)
+{
+       struct iovec *vector_p = vector;
+       ssize_t total = 0;
+
+       while (iovcnt > 0) {
+               ssize_t wrote;
+
+               wrote = writev(fd, vector_p, iovcnt);
+               if (wrote > 0) {
+                       total += wrote;
+                       while (wrote > 0) {
+                               /*
+                                *      An entire vector element was written
+                                */
+                               if (wrote >= (ssize_t)vector_p->iov_len) {
+                                       iovcnt--;
+                                       wrote -= vector_p->iov_len;
+                                       vector_p++;
+                                       continue;
+                               }
+
+                               /*
+                                *      Partial vector element was written
+                                */
+                               vector_p->iov_len -= wrote;
+                               vector_p->iov_base = ((char *)vector_p->iov_base) + wrote;
+                               break;
+                       }
+                       continue;
+               } else if (wrote == 0) return total;
+
+               switch (errno) {
+               /* Write operation would block, use select() to implement a timeout */
+#if EWOULDBLOCK != EAGAIN
+               case EWOULDBLOCK:
+               case EAGAIN:
+#else
+               case EAGAIN:
+#endif
+               {
+                       int     ret;
+                       fd_set  write_set;
+
+                       FD_ZERO(&write_set);
+                       FD_SET(fd, &write_set);
+
+                       /* Don't let signals mess up the select */
+                       do {
+                               ret = select(fd + 1, NULL, &write_set, NULL, timeout);
+                       } while ((ret == -1) && (errno == EINTR));
+
+                       /* Select returned 0 which means it reached the timeout */
+                       if (ret == 0) {
+                               fr_strerror_printf("Write timed out");
+                               return -1;
+                       }
+
+                       /* Other select error */
+                       if (ret < 0) {
+                               fr_strerror_printf("Failed waiting on socket: %s", fr_syserror(errno));
+                               return -1;
+                       }
+
+                       /* select said a file descriptor was ready for writing */
+                       if (!fr_assert(FD_ISSET(fd, &write_set))) return -1;
+
+                       break;
+               }
+
+               default:
+                       return -1;
+               }
+       }
+
+       return total;
+}
+
 /** Convert UTF8 string to UCS2 encoding
  *
  * @note Borrowed from src/crypto/ms_funcs.c of wpa_supplicant project (http://hostap.epitest.fi/wpa_supplicant/)
@@ -1212,6 +1794,13 @@ size_t fr_prints_uint128(char *out, size_t outlen, uint128_t const num)
        uint64_t n[2];
        char *p = buff;
        int i;
+#ifdef FR_LITTLE_ENDIAN
+       const size_t l = 0;
+       const size_t h = 1;
+#else
+       const size_t l = 1;
+       const size_t h = 0;
+#endif
 
        memset(buff, '0', sizeof(buff) - 1);
        buff[sizeof(buff) - 1] = '\0';
@@ -1222,11 +1811,11 @@ size_t fr_prints_uint128(char *out, size_t outlen, uint128_t const num)
                ssize_t j;
                int carry;
 
-               carry = (n[1] >= 0x8000000000000000);
+               carry = (n[h] >= 0x8000000000000000);
 
                // Shift n[] left, doubling it
-               n[1] = ((n[1] << 1) & 0xffffffffffffffff) + (n[0] >= 0x8000000000000000);
-               n[0] = ((n[0] << 1) & 0xffffffffffffffff);
+               n[h] = ((n[h] << 1) & 0xffffffffffffffff) + (n[l] >= 0x8000000000000000);
+               n[l] = ((n[l] << 1) & 0xffffffffffffffff);
 
                // Add s[] to itself in decimal, doubling it
                for (j = sizeof(buff) - 2; j >= 0; j--) {
@@ -1245,90 +1834,6 @@ size_t fr_prints_uint128(char *out, size_t outlen, uint128_t const num)
        return strlcpy(out, p, outlen);
 }
 
-/** Calculate powers
- *
- * @author Orson Peters
- * @note Borrowed from the gist here: https://gist.github.com/nightcracker/3551590.
- *
- * @param base a 32bit signed integer.
- * @param exp amount to raise base by.
- * @return base ^ pow, or 0 on underflow/overflow.
- */
-int64_t fr_pow(int32_t base, uint8_t exp) {
-       static const uint8_t highest_bit_set[] = {
-               0, 1, 2, 2, 3, 3, 3, 3,
-               4, 4, 4, 4, 4, 4, 4, 4,
-               5, 5, 5, 5, 5, 5, 5, 5,
-               5, 5, 5, 5, 5, 5, 5, 5,
-               6, 6, 6, 6, 6, 6, 6, 6,
-               6, 6, 6, 6, 6, 6, 6, 6,
-               6, 6, 6, 6, 6, 6, 6, 6,
-               6, 6, 6, 6, 6, 6, 6, 255, // anything past 63 is a guaranteed overflow with base > 1
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-               255, 255, 255, 255, 255, 255, 255, 255,
-       };
-
-       uint64_t result = 1;
-
-       switch (highest_bit_set[exp]) {
-       case 255: // we use 255 as an overflow marker and return 0 on overflow/underflow
-               if (base == 1) {
-                       return 1;
-               }
-
-               if (base == -1) {
-                       return 1 - 2 * (exp & 1);
-               }
-               return 0;
-       case 6:
-               if (exp & 1) result *= base;
-               exp >>= 1;
-               base *= base;
-       case 5:
-               if (exp & 1) result *= base;
-               exp >>= 1;
-               base *= base;
-       case 4:
-               if (exp & 1) result *= base;
-               exp >>= 1;
-               base *= base;
-       case 3:
-               if (exp & 1) result *= base;
-               exp >>= 1;
-               base *= base;
-       case 2:
-               if (exp & 1) result *= base;
-               exp >>= 1;
-               base *= base;
-       case 1:
-               if (exp & 1) result *= base;
-       default:
-               return result;
-       }
-}
-
 /*
  *     Sort of strtok/strsep function.
  */