summaryrefslogtreecommitdiffstats
path: root/src/lib/misc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/misc.c')
-rw-r--r--src/lib/misc.c2192
1 files changed, 2192 insertions, 0 deletions
diff --git a/src/lib/misc.c b/src/lib/misc.c
new file mode 100644
index 0000000..b80b9ce
--- /dev/null
+++ b/src/lib/misc.c
@@ -0,0 +1,2192 @@
+/*
+ * misc.c Various miscellaneous functions.
+ *
+ * Version: $Id$
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright 2000,2006 The FreeRADIUS server project
+ */
+
+RCSID("$Id$")
+
+#include <freeradius-devel/libradius.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 {\
+ a[1] = ((uint16_t) (val)) >> 8;\
+ a[0] = ((uint16_t) (val)) & 0xff;\
+ } while (0)
+
+bool fr_dns_lookups = false; /* IP -> hostname lookups? */
+bool fr_hostname_lookups = true; /* hostname -> IP lookups? */
+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 */
+
+typedef struct fr_talloc_link {
+ bool armed;
+ TALLOC_CTX *child;
+} fr_talloc_link_t;
+
+/** Sets a signal handler using sigaction if available, else signal
+ *
+ * @param sig to set handler for.
+ * @param func handler to set.
+ */
+DIAG_OPTIONAL
+DIAG_OFF(disabled-macro-expansion)
+int fr_set_signal(int sig, sig_t func)
+{
+#ifdef HAVE_SIGACTION
+ struct sigaction act;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_flags = 0;
+ sigemptyset(&act.sa_mask);
+ act.sa_handler = func;
+
+ if (sigaction(sig, &act, NULL) < 0) {
+ fr_strerror_printf("Failed setting signal %i handler via sigaction(): %s", sig, fr_syserror(errno));
+ return -1;
+ }
+#else
+ if (signal(sig, func) < 0) {
+ fr_strerror_printf("Failed setting signal %i handler via signal(): %s", sig, fr_syserror(errno));
+ return -1;
+ }
+#endif
+ return 0;
+}
+DIAG_ON(disabled-macro-expansion)
+
+/** Uninstall a signal for a specific handler
+ *
+ * man sigaction says these are fine to call from a signal handler.
+ *
+ * @param sig SIGNAL
+ */
+DIAG_OPTIONAL
+DIAG_OFF(disabled-macro-expansion)
+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
+}
+DIAG_ON(disabled-macro-expansion)
+
+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
+ *
+ * @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.
+ *
+ * @param parent who's fate the child should share.
+ * @param child bound to parent's lifecycle.
+ * @return 0 on success -1 on failure.
+ */
+int fr_link_talloc_ctx_free(TALLOC_CTX *parent, TALLOC_CTX *child)
+{
+ 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;
+ }
+
+ 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 0;
+}
+
+/*
+ * Explicitly cleanup the memory allocated to the error inet_ntop
+ * buffer.
+ */
+static void _fr_inet_ntop_free(void *arg)
+{
+ free(arg);
+}
+
+/** Wrapper around inet_ntop, prints IPv4/IPv6 addresses
+ *
+ * inet_ntop requires the caller pass in a buffer for the address.
+ * This would be annoying and cumbersome, seeing as quite often the ASCII
+ * address is only used for logging output.
+ *
+ * So as with lib/log.c use TLS to allocate thread specific buffers, and
+ * write the IP address there instead.
+ *
+ * @param af address family, either AF_INET or AF_INET6.
+ * @param src pointer to network address structure.
+ * @return NULL on error, else pointer to ASCII buffer containing text version of address.
+ */
+char const *fr_inet_ntop(int af, void const *src)
+{
+ char *buffer;
+
+ if (!src) {
+ return NULL;
+ }
+
+ buffer = fr_thread_local_init(fr_inet_ntop_buffer, _fr_inet_ntop_free);
+ if (!buffer) {
+ int ret;
+
+ /*
+ * malloc is thread safe, talloc is not
+ */
+ buffer = malloc(sizeof(char) * INET6_ADDRSTRLEN);
+ if (!buffer) {
+ fr_perror("Failed allocating memory for inet_ntop buffer");
+ return NULL;
+ }
+
+ ret = fr_thread_local_set(fr_inet_ntop_buffer, buffer);
+ if (ret != 0) {
+ fr_perror("Failed setting up TLS for inet_ntop buffer: %s", fr_syserror(ret));
+ free(buffer);
+ return NULL;
+ }
+ }
+ buffer[0] = '\0';
+
+ return inet_ntop(af, src, buffer, INET6_ADDRSTRLEN);
+}
+
+/*
+ * Return an IP address in standard dot notation
+ *
+ * FIXME: DELETE THIS
+ */
+char const *ip_ntoa(char *buffer, uint32_t ipaddr)
+{
+ ipaddr = ntohl(ipaddr);
+
+ sprintf(buffer, "%d.%d.%d.%d",
+ (ipaddr >> 24) & 0xff,
+ (ipaddr >> 16) & 0xff,
+ (ipaddr >> 8) & 0xff,
+ (ipaddr ) & 0xff);
+ return buffer;
+}
+
+/*
+ * 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), 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_pton4(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resolve, bool fallback)
+{
+ char *p;
+ unsigned int mask;
+ char *eptr;
+
+ /* 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 >= (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) || ((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 addreess string \"%s\"", value);
+ return -1;
+ }
+ } else if (ip_hton(out, AF_INET, value, fallback) < 0) return -1;
+
+ return 0;
+ }
+
+ /*
+ * Copy the IP portion into a temporary buffer if we haven't already.
+ */
+ if (inlen < 0) memcpy(buffer, value, p - value);
+ buffer[p - value] = '\0';
+
+ 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;
+ }
+
+ 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 (mask < 32) {
+ out->ipaddr.ip4addr = fr_inaddr_mask(&out->ipaddr.ip4addr, mask);
+ }
+
+ out->prefix = (uint8_t) mask;
+ out->af = AF_INET;
+
+ return 0;
+}
+
+/**
+ * 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 -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, ssize_t inlen, bool resolve, bool fallback)
+{
+ char const *p;
+ unsigned int prefix;
+ char *eptr;
+
+ /* 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 >= (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));
+ } else if (!resolve) {
+ 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(out, AF_INET6, value, fallback) < 0) return -1;
+
+ return 0;
+ }
+
+ if ((p - value) >= INET6_ADDRSTRLEN) {
+ fr_strerror_printf("Invalid IPv6 address string \"%s\"", value);
+ return -1;
+ }
+
+ /*
+ * Copy string to temporary buffer if we didn't do it earlier
+ */
+ 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) {
+ fr_strerror_printf("Failed to parse IPv6 address string \"%s\"", value);
+ return -1;
+ }
+ } else if (ip_hton(out, AF_INET6, buffer, fallback) < 0) return -1;
+
+ prefix = strtoul(p + 1, &eptr, 10);
+ if (prefix > 128) {
+ fr_strerror_printf("Invalid IPv6 mask length \"%s\". Should be between 0-128", p);
+ return -1;
+ }
+ if (eptr[0] != '\0') {
+ fr_strerror_printf("Failed to parse IPv6 address string \"%s\", "
+ "got garbage after mask length \"%s\"", value, eptr);
+ return -1;
+ }
+
+ if (prefix < 128) {
+ struct in6_addr 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;
+ out->af = AF_INET6;
+
+ 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;
+ bool hostname = true;
+ bool ipv4 = true;
+ bool ipv6 = true;
+
+ len = (inlen >= 0) ? (size_t)inlen : strlen(value);
+
+ for (i = 0; i < len; i++) {
+ /*
+ * These are valid for IPv4, IPv6, and host names.
+ */
+ if ((value[i] >= '0') && (value[i] <= '9')) {
+ continue;
+ }
+
+ /*
+ * These are invalid for IPv4, but OK for IPv6
+ * and host names.
+ */
+ if ((value[i] >= 'a') && (value[i] <= 'f')) {
+ ipv4 = false;
+ continue;
+ }
+
+ /*
+ * These are invalid for IPv4, but OK for IPv6
+ * and host names.
+ */
+ if ((value[i] >= 'A') && (value[i] <= 'F')) {
+ ipv4 = false;
+ continue;
+ }
+
+ /*
+ * This is only valid for IPv6 addresses.
+ */
+ if (value[i] == ':') {
+ ipv4 = false;
+ hostname = false;
+ continue;
+ }
+
+ /*
+ * Valid for IPv4 and host names, not for IPv6.
+ */
+ if (value[i] == '.') {
+ ipv6 = false;
+ continue;
+ }
+
+ /*
+ * Netmasks are allowed by us, and MUST come at
+ * the end of the address.
+ */
+ if (value[i] == '/') {
+ break;
+ }
+
+ /*
+ * Any characters other than what are checked for
+ * above can't be IPv4 or IPv6 addresses.
+ */
+ ipv4 = false;
+ ipv6 = false;
+ }
+
+ /*
+ * It's not an IPv4 or IPv6 address. It MUST be a host
+ * name.
+ */
+ if (!ipv4 && !ipv6) {
+ /*
+ * Not an IPv4 or IPv6 address, and we weren't
+ * asked to do DNS resolution, we can't do it.
+ */
+ if (!resolve) {
+ fr_strerror_printf("Not IPv4/6 address, and asked not to resolve");
+ return -1;
+ }
+
+ /*
+ * It's not a hostname, either, so bail out
+ * early.
+ */
+ if (!hostname) {
+ fr_strerror_printf("Invalid address");
+ return -1;
+ }
+ }
+
+ /*
+ * The name has a ':' in it. Therefore it must be an
+ * IPv6 address. Error out if the caller specified IPv4.
+ * Otherwise, force IPv6.
+ */
+ if (ipv6 && !hostname) {
+ if (af == AF_INET) {
+ fr_strerror_printf("Invalid address");
+ return -1;
+ }
+
+ af = AF_INET6;
+ }
+
+ /*
+ * Use whatever the caller specified, OR what we
+ * insinuated above from looking at the name string.
+ */
+ 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:
+ break;
+ }
+
+ /*
+ * No idea what it is...
+ */
+ fr_strerror_printf("Invalid address family %i", af);
+ return -1;
+}
+
+/** 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 const *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
+ *
+ * Use fcntl or error
+ */
+int rad_lockfd(int fd, int lock_len)
+{
+#ifdef F_WRLCK
+ struct flock fl;
+
+ fl.l_start = 0;
+ fl.l_len = lock_len;
+ fl.l_pid = getpid();
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_CUR;
+
+ return fcntl(fd, F_SETLKW, (void *)&fl);
+#else
+#error "missing definition for F_WRLCK, all file locks will fail"
+
+ return -1;
+#endif
+}
+
+/*
+ * Internal wrapper for locking, to minimize the number of ifdef's
+ *
+ * Lock an fd, prefer lockf() over flock()
+ * Nonblocking version.
+ */
+int rad_lockfd_nonblock(int fd, int lock_len)
+{
+#ifdef F_WRLCK
+ struct flock fl;
+
+ fl.l_start = 0;
+ fl.l_len = lock_len;
+ fl.l_pid = getpid();
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_CUR;
+
+ return fcntl(fd, F_SETLK, (void *)&fl);
+#else
+#error "missing definition for F_WRLCK, all file locks will fail"
+
+ return -1;
+#endif
+}
+
+/*
+ * Internal wrapper for unlocking, to minimize the number of ifdef's
+ * in the source.
+ *
+ * Unlock an fd, prefer lockf() over flock()
+ */
+int rad_unlockfd(int fd, int lock_len)
+{
+#ifdef F_WRLCK
+ struct flock fl;
+
+ fl.l_start = 0;
+ fl.l_len = lock_len;
+ fl.l_pid = getpid();
+ fl.l_type = F_UNLCK;
+ fl.l_whence = SEEK_CUR;
+
+ return fcntl(fd, F_SETLK, (void *)&fl);
+#else
+#error "missing definition for F_WRLCK, all file locks will fail"
+
+ return -1;
+#endif
+}
+
+/*
+ * Return an interface-id in standard colon notation
+ */
+char *ifid_ntoa(char *buffer, size_t size, uint8_t const *ifid)
+{
+ snprintf(buffer, size, "%x:%x:%x:%x",
+ (ifid[0] << 8) + ifid[1], (ifid[2] << 8) + ifid[3],
+ (ifid[4] << 8) + ifid[5], (ifid[6] << 8) + ifid[7]);
+ return buffer;
+}
+
+
+/*
+ * Return an interface-id from
+ * one supplied in standard colon notation.
+ */
+uint8_t *ifid_aton(char const *ifid_str, uint8_t *ifid)
+{
+ static char const xdigits[] = "0123456789abcdef";
+ char const *p, *pch;
+ int num_id = 0, val = 0, idx = 0;
+
+ for (p = ifid_str; ; ++p) {
+ if (*p == ':' || *p == '\0') {
+ if (num_id <= 0)
+ return NULL;
+
+ /*
+ * Drop 'val' into the array.
+ */
+ ifid[idx] = (val >> 8) & 0xff;
+ ifid[idx + 1] = val & 0xff;
+ if (*p == '\0') {
+ /*
+ * Must have all entries before
+ * end of the string.
+ */
+ if (idx != 6)
+ return NULL;
+ break;
+ }
+ val = 0;
+ num_id = 0;
+ if ((idx += 2) > 6)
+ return NULL;
+ } else if ((pch = strchr(xdigits, tolower((uint8_t) *p))) != NULL) {
+ if (++num_id > 4)
+ return NULL;
+ /*
+ * Dumb version of 'scanf'
+ */
+ val <<= 4;
+ val |= (pch - xdigits);
+ } else
+ return NULL;
+ }
+ return ifid;
+}
+
+
+#ifndef HAVE_INET_PTON
+static int inet_pton4(char const *src, struct in_addr *dst)
+{
+ int octet;
+ unsigned int num;
+ char const *p, *off;
+ uint8_t tmp[4];
+ static char const digits[] = "0123456789";
+
+ octet = 0;
+ p = src;
+ while (1) {
+ num = 0;
+ while (*p && ((off = strchr(digits, *p)) != NULL)) {
+ num *= 10;
+ num += (off - digits);
+
+ if (num > 255) return 0;
+
+ p++;
+ }
+ if (!*p) break;
+
+ /*
+ * Not a digit, MUST be a dot, else we
+ * die.
+ */
+ if (*p != '.') {
+ return 0;
+ }
+
+ tmp[octet++] = num;
+ p++;
+ }
+
+ /*
+ * End of the string. At the fourth
+ * octet is OK, anything else is an
+ * error.
+ */
+ if (octet != 3) {
+ return 0;
+ }
+ tmp[3] = num;
+
+ memcpy(dst, &tmp, sizeof(tmp));
+ return 1;
+}
+
+
+#ifdef HAVE_STRUCT_SOCKADDR_IN6
+/** 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)
+{
+ static char const xdigits_l[] = "0123456789abcdef",
+ xdigits_u[] = "0123456789ABCDEF";
+ u_char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
+ char const *xdigits, *curtok;
+ int ch, saw_xdigit;
+ u_int val;
+
+ memset((tp = tmp), 0, IN6ADDRSZ);
+ endp = tp + IN6ADDRSZ;
+ colonp = NULL;
+ /* Leading :: requires some special handling. */
+ if (*src == ':')
+ if (*++src != ':')
+ return (0);
+ curtok = src;
+ saw_xdigit = 0;
+ val = 0;
+ while ((ch = *src++) != '\0') {
+ char const *pch;
+
+ if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+ pch = strchr((xdigits = xdigits_u), ch);
+ if (pch != NULL) {
+ val <<= 4;
+ val |= (pch - xdigits);
+ if (val > 0xffff)
+ return (0);
+ saw_xdigit = 1;
+ continue;
+ }
+ if (ch == ':') {
+ curtok = src;
+ if (!saw_xdigit) {
+ if (colonp)
+ return (0);
+ colonp = tp;
+ continue;
+ }
+ if (tp + INT16SZ > endp)
+ return (0);
+ *tp++ = (u_char) (val >> 8) & 0xff;
+ *tp++ = (u_char) val & 0xff;
+ saw_xdigit = 0;
+ val = 0;
+ continue;
+ }
+ if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
+ inet_pton4(curtok, (struct in_addr *) tp) > 0) {
+ tp += INADDRSZ;
+ saw_xdigit = 0;
+ break; /* '\0' was seen by inet_pton4(). */
+ }
+ return (0);
+ }
+ if (saw_xdigit) {
+ if (tp + INT16SZ > endp)
+ return (0);
+ *tp++ = (u_char) (val >> 8) & 0xff;
+ *tp++ = (u_char) val & 0xff;
+ }
+ if (colonp != NULL) {
+ /*
+ * Since some memmove()'s erroneously fail to handle
+ * overlapping regions, we'll do the shift by hand.
+ */
+ int const n = tp - colonp;
+ int i;
+
+ for (i = 1; i <= n; i++) {
+ endp[- i] = colonp[n - i];
+ colonp[n - i] = 0;
+ }
+ tp = endp;
+ }
+ if (tp != endp)
+ return (0);
+ /* bcopy(tmp, dst, IN6ADDRSZ); */
+ memcpy(dst, tmp, IN6ADDRSZ);
+ return (1);
+}
+#endif
+
+/*
+ * Utility function, so that the rest of the server doesn't
+ * have ifdef's around IPv6 support
+ */
+int inet_pton(int af, char const *src, void *dst)
+{
+ if (af == AF_INET) {
+ return inet_pton4(src, dst);
+ }
+#ifdef HAVE_STRUCT_SOCKADDR_IN6
+
+ if (af == AF_INET6) {
+ return inet_pton6(src, dst);
+ }
+#endif
+
+ return -1;
+}
+#endif
+
+#ifndef HAVE_INET_NTOP
+/*
+ * Utility function, so that the rest of the server doesn't
+ * have ifdef's around IPv6 support
+ */
+char const *inet_ntop(int af, void const *src, char *dst, size_t cnt)
+{
+ if (af == AF_INET) {
+ uint8_t const *ipaddr = src;
+
+ if (cnt <= INET_ADDRSTRLEN) return NULL;
+
+ snprintf(dst, cnt, "%d.%d.%d.%d",
+ ipaddr[0], ipaddr[1],
+ ipaddr[2], ipaddr[3]);
+ return dst;
+ }
+
+ /*
+ * If the system doesn't define this, we define it
+ * in missing.h
+ */
+ if (af == AF_INET6) {
+ struct in6_addr const *ipaddr = src;
+
+ if (cnt <= INET6_ADDRSTRLEN) return NULL;
+
+ snprintf(dst, cnt, "%x:%x:%x:%x:%x:%x:%x:%x",
+ (ipaddr->s6_addr[0] << 8) | ipaddr->s6_addr[1],
+ (ipaddr->s6_addr[2] << 8) | ipaddr->s6_addr[3],
+ (ipaddr->s6_addr[4] << 8) | ipaddr->s6_addr[5],
+ (ipaddr->s6_addr[6] << 8) | ipaddr->s6_addr[7],
+ (ipaddr->s6_addr[8] << 8) | ipaddr->s6_addr[9],
+ (ipaddr->s6_addr[10] << 8) | ipaddr->s6_addr[11],
+ (ipaddr->s6_addr[12] << 8) | ipaddr->s6_addr[13],
+ (ipaddr->s6_addr[14] << 8) | ipaddr->s6_addr[15]);
+ return dst;
+ }
+
+ return NULL; /* don't support IPv6 */
+}
+#endif
+
+/** 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(fr_ipaddr_t *out, int af, char const *hostname, bool fallback)
+{
+ int rcode;
+ 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 = hostname; *p != '\0'; p++) {
+ if ((*p == ':') ||
+ (*p == '[') ||
+ (*p == ']')) {
+ af = AF_INET6;
+ break;
+ }
+ }
+ }
+#endif
+
+ if (af == AF_UNSPEC) af = AF_INET;
+
+ if (!inet_pton(af, hostname, &(out->ipaddr))) return -1;
+
+ out->af = af;
+ return 0;
+ }
+
+ memset(&hints, 0, sizeof(hints));
+
+ /*
+ * If we're falling back we need both IPv4 and IPv6 records
+ */
+ if (fallback) {
+ hints.ai_family = AF_UNSPEC;
+ } else {
+ hints.ai_family = af;
+ }
+
+ 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 (!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", hostname);
+ freeaddrinfo(res);
+ return -1;
+ }
+
+ rcode = fr_sockaddr2ipaddr((struct sockaddr_storage *)ai->ai_addr,
+ ai->ai_addrlen, out, NULL);
+ freeaddrinfo(res);
+ if (!rcode) {
+ fr_strerror_printf("Failed converting sockaddr to ipaddr");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Look IP addresses up, and print names (depending on DNS config)
+ */
+char const *ip_ntoh(fr_ipaddr_t const *src, char *dst, size_t cnt)
+{
+ struct sockaddr_storage ss;
+ int error;
+ socklen_t salen;
+
+ /*
+ * No DNS lookups
+ */
+ if (!fr_dns_lookups) {
+ return inet_ntop(src->af, &(src->ipaddr), dst, cnt);
+ }
+
+ if (!fr_ipaddr2sockaddr(src, 0, &ss, &salen)) {
+ return NULL;
+ }
+
+ if ((error = getnameinfo((struct sockaddr *)&ss, salen, dst, cnt, NULL, 0,
+ NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
+ fr_strerror_printf("ip_ntoh: %s", gai_strerror(error));
+ return NULL;
+ }
+ return dst;
+}
+
+/** Mask off a portion of an IPv4 address
+ *
+ * @param ipaddr to mask.
+ * @param prefix Number of contiguous bits to mask.
+ * @return an ipv4 address with the host portion zeroed out.
+ */
+struct in_addr fr_inaddr_mask(struct in_addr const *ipaddr, uint8_t prefix)
+{
+ uint32_t ret;
+
+ if (prefix > 32) prefix = 32;
+
+ /* Short circuit */
+ if (prefix == 32) return *ipaddr;
+
+ if (prefix == 0) ret = 0;
+ else ret = htonl(~((0x00000001UL << (32 - prefix)) - 1)) & ipaddr->s_addr;
+
+ return (*(struct in_addr *)&ret);
+}
+
+/** Mask off a portion of an IPv6 address
+ *
+ * @param ipaddr to mask.
+ * @param prefix Number of contiguous bits to mask.
+ * @return an ipv6 address with the host portion zeroed out.
+ */
+struct in6_addr fr_in6addr_mask(struct in6_addr const *ipaddr, uint8_t prefix)
+{
+ uint64_t const *p = (uint64_t const *) ipaddr;
+ uint64_t addr; /* Needed for alignment */
+ uint64_t ret[2], *o = ret;
+
+ if (prefix > 128) prefix = 128;
+
+ /* Short circuit */
+ if (prefix == 128) return *ipaddr;
+
+ if (prefix >= 64) {
+ prefix -= 64;
+ memcpy(&addr, p, sizeof(addr)); /* Needed for aligned access (ubsan) */
+ *o++ = 0xffffffffffffffffULL & addr; /* lhs portion masked */
+ p++;
+ } else {
+ ret[1] = 0; /* rhs portion zeroed */
+ }
+
+ /* Max left shift is 63 else we get overflow */
+ if (prefix > 0) {
+ memcpy(&addr, p, sizeof(addr)); /* Needed for aligned access (ubsan) */
+ *o = htonll(~((uint64_t)(0x0000000000000001ULL << (64 - prefix)) - 1)) & addr;
+ } else {
+ *o = 0;
+ }
+
+ return *(struct in6_addr *) &ret;
+}
+
+/** 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 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, size_t outlen, char const *hex, size_t inlen)
+{
+ size_t i;
+ size_t len;
+ char *c1, *c2;
+
+ /*
+ * 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((uint8_t) hex[i << 1]), sizeof(hextab))) ||
+ !(c2 = memchr(hextab, tolower((uint8_t) hex[(i << 1) + 1]), sizeof(hextab))))
+ break;
+ 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'
+ *
+ * @warning If the output buffer isn't long enough, we have a buffer overflow.
+ *
+ * @param[out] hex Buffer to write hex output.
+ * @param[in] bin input.
+ * @param[in] inlen of bin input.
+ * @return length of data written to buffer.
+ */
+size_t fr_bin2hex(char *hex, uint8_t const *bin, size_t inlen)
+{
+ size_t i;
+
+ for (i = 0; i < inlen; i++) {
+ hex[0] = hextab[((*bin) >> 4) & 0x0f];
+ hex[1] = hextab[*bin & 0x0f];
+ hex += 2;
+ bin++;
+ }
+
+ *hex = '\0';
+ 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
+ *
+ * @param value string to parse.
+ * @param end pointer to the first non numeric char.
+ * @return integer value.
+ */
+uint32_t fr_strtoul(char const *value, char **end)
+{
+ if ((value[0] == '0') && (value[1] == 'x')) {
+ return strtoul(value, end, 16);
+ }
+
+ return strtoul(value, end, 10);
+}
+
+/** Check whether the string is all whitespace
+ *
+ * @return true if the entirety of the string is whitespace, else false.
+ */
+bool is_whitespace(char const *value)
+{
+ do {
+ if (!isspace((uint8_t) *value)) return false;
+ value++;
+ } while (*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 all numbers, else false.
+ */
+bool is_integer(char const *value)
+{
+#ifndef __clang_analyzer__
+ do {
+ if (!isdigit((uint8_t) *value)) return false;
+ value++;
+ } while (*value);
+
+ /*
+ * Clang analyzer complains about the above line: "Branch
+ * depends on a garbage value", even though that's
+ * clearly not true. And, it doesn't complain about the
+ * other functions doing similar things.
+ */
+#else
+ if (!isdigit((uint8_t) *value)) return false;
+#endif
+
+ return true;
+}
+
+/** Check whether the string is allzeros
+ *
+ * @return true if the entirety of the string is all zeros, else false.
+ */
+bool is_zero(char const *value)
+{
+ do {
+ if (*value != '0') return false;
+ value++;
+ } while (*value);
+
+ return true;
+}
+
+/*
+ * So we don't have ifdef's in the rest of the code
+ */
+#ifndef HAVE_CLOSEFROM
+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;
+ }
+#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?
+ */
+ for (i = fd; i < maxfd; i++) {
+ close(i);
+ }
+
+ return 0;
+}
+#endif
+
+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));
+
+#ifdef HAVE_STRUCT_SOCKADDR_IN6
+ case AF_INET6:
+ if (a->scope < b->scope) return -1;
+ if (a->scope > b->scope) return +1;
+
+ return memcmp(&a->ipaddr.ip6addr,
+ &b->ipaddr.ip6addr,
+ sizeof(a->ipaddr.ip6addr));
+#endif
+
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+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;
+
+ *salen = sizeof(s4);
+
+ memset(&s4, 0, sizeof(s4));
+ s4.sin_family = AF_INET;
+ s4.sin_addr = ipaddr->ipaddr.ip4addr;
+ s4.sin_port = htons(port);
+ memset(sa, 0, sizeof(*sa));
+ memcpy(sa, &s4, sizeof(s4));
+
+#ifdef HAVE_STRUCT_SOCKADDR_IN6
+ } else if (ipaddr->af == AF_INET6) {
+ struct sockaddr_in6 s6;
+
+ *salen = sizeof(s6);
+
+ memset(&s6, 0, sizeof(s6));
+ s6.sin6_family = AF_INET6;
+ s6.sin6_addr = ipaddr->ipaddr.ip6addr;
+ s6.sin6_port = htons(port);
+ s6.sin6_scope_id = ipaddr->scope;
+ memset(sa, 0, sizeof(*sa));
+ memcpy(sa, &s6, sizeof(s6));
+#endif
+ } else {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+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;
+
+ if (salen < sizeof(s4)) {
+ fr_strerror_printf("IPv4 address is too small");
+ return 0;
+ }
+
+ 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);
+
+#ifdef HAVE_STRUCT_SOCKADDR_IN6
+ } else if (sa->ss_family == AF_INET6) {
+ struct sockaddr_in6 s6;
+
+ if (salen < sizeof(s6)) {
+ fr_strerror_printf("IPv6 address is too small");
+ return 0;
+ }
+
+ 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;
+#endif
+
+ } else {
+ fr_strerror_printf("Unsupported address famility %d",
+ sa->ss_family);
+ return 0;
+ }
+
+ 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/)
+ *
+ * @param[out] out Where to write the ucs2 string.
+ * @param[in] outlen Size of output buffer.
+ * @param[in] in UTF8 string to convert.
+ * @param[in] inlen length of UTF8 string.
+ * @return the size of the UCS2 string written to the output buffer (in bytes).
+ */
+ssize_t fr_utf8_to_ucs2(uint8_t *out, size_t outlen, char const *in, size_t inlen)
+{
+ size_t i;
+ uint8_t *start = out;
+
+ for (i = 0; i < inlen; i++) {
+ uint8_t c, c2, c3;
+
+ c = in[i];
+ if ((size_t)(out - start) >= outlen) {
+ /* input too long */
+ return -1;
+ }
+
+ /* One-byte encoding */
+ if (c <= 0x7f) {
+ FR_PUT_LE16(out, c);
+ out += 2;
+ continue;
+ } else if ((i == (inlen - 1)) || ((size_t)(out - start) >= (outlen - 1))) {
+ /* Incomplete surrogate */
+ return -1;
+ }
+
+ c2 = in[++i];
+ /* Two-byte encoding */
+ if ((c & 0xe0) == 0xc0) {
+ FR_PUT_LE16(out, ((c & 0x1f) << 6) | (c2 & 0x3f));
+ out += 2;
+ continue;
+ }
+ if ((i == inlen) || ((size_t)(out - start) >= (outlen - 1))) {
+ /* Incomplete surrogate */
+ return -1;
+ }
+
+ /* Three-byte encoding */
+ c3 = in[++i];
+ FR_PUT_LE16(out, ((c & 0xf) << 12) | ((c2 & 0x3f) << 6) | (c3 & 0x3f));
+ out += 2;
+ }
+
+ return out - start;
+}
+
+/** Write 128bit unsigned integer to buffer
+ *
+ * @author Alexey Frunze
+ *
+ * @param out where to write result to.
+ * @param outlen size of out.
+ * @param num 128 bit integer.
+ */
+size_t fr_prints_uint128(char *out, size_t outlen, uint128_t const num)
+{
+ char buff[128 / 3 + 1 + 1];
+ 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';
+
+ memcpy(n, &num, sizeof(n));
+
+ for (i = 0; i < 128; i++) {
+ ssize_t j;
+ int carry;
+
+ carry = (n[h] >= 0x8000000000000000);
+
+ // Shift n[] left, doubling it
+ 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--) {
+ buff[j] += buff[j] - '0' + carry;
+ carry = (buff[j] > '9');
+ if (carry) {
+ buff[j] -= 10;
+ }
+ }
+ }
+
+ while ((*p == '0') && (p < &buff[sizeof(buff) - 2])) {
+ p++;
+ }
+
+ return strlcpy(out, p, outlen);
+}
+
+/*
+ * Sort of strtok/strsep function.
+ */
+static char *mystrtok(char **ptr, char const *sep)
+{
+ char *res;
+
+ if (**ptr == 0) {
+ return NULL;
+ }
+
+ while (**ptr && strchr(sep, **ptr)) {
+ (*ptr)++;
+ }
+ if (**ptr == 0) {
+ return NULL;
+ }
+
+ res = *ptr;
+ while (**ptr && strchr(sep, **ptr) == NULL) {
+ (*ptr)++;
+ }
+
+ if (**ptr != 0) {
+ *(*ptr)++ = 0;
+ }
+ return res;
+}
+
+/** Convert string in various formats to a time_t
+ *
+ * @param date_str input date string.
+ * @param date time_t to write result to.
+ * @return 0 on success or -1 on error.
+ */
+int fr_get_time(char const *date_str, time_t *date)
+{
+ int i, j;
+ time_t t;
+ struct tm *tm, s_tm;
+ char buf[64];
+ char *p;
+ char *f[4];
+ char *tail = NULL;
+
+ /*
+ * Test for unix timestamp date
+ */
+ *date = strtoul(date_str, &tail, 10);
+ if (*tail == '\0') {
+ return 0;
+ }
+
+ tm = &s_tm;
+ memset(tm, 0, sizeof(*tm));
+ tm->tm_isdst = -1; /* don't know, and don't care about DST */
+
+ strlcpy(buf, date_str, sizeof(buf));
+
+ p = buf;
+ f[0] = mystrtok(&p, " \t");
+ f[1] = mystrtok(&p, " \t");
+ f[2] = mystrtok(&p, " \t");
+ f[3] = mystrtok(&p, " \t"); /* may, or may not, be present */
+ if (!f[0] || !f[1] || !f[2]) return -1;
+
+ /*
+ * The time has a colon, where nothing else does.
+ * So if we find it, bubble it to the back of the list.
+ */
+ if (f[3]) {
+ for (i = 0; i < 3; i++) {
+ if (strchr(f[i], ':')) {
+ p = f[3];
+ f[3] = f[i];
+ f[i] = p;
+ break;
+ }
+ }
+ }
+
+ /*
+ * The month is text, which allows us to find it easily.
+ */
+ tm->tm_mon = 12;
+ for (i = 0; i < 3; i++) {
+ if (isalpha( (int) *f[i])) {
+ /*
+ * Bubble the month to the front of the list
+ */
+ p = f[0];
+ f[0] = f[i];
+ f[i] = p;
+
+ for (j = 0; j < 12; j++) {
+ if (strncasecmp(months[j], f[0], 3) == 0) {
+ tm->tm_mon = j;
+ break;
+ }
+ }
+ }
+ }
+
+ /* month not found? */
+ if (tm->tm_mon == 12) return -1;
+
+ /*
+ * The year may be in f[1], or in f[2]
+ */
+ tm->tm_year = atoi(f[1]);
+ tm->tm_mday = atoi(f[2]);
+
+ if (tm->tm_year >= 1900) {
+ tm->tm_year -= 1900;
+
+ } else {
+ /*
+ * We can't use 2-digit years any more, they make it
+ * impossible to tell what's the day, and what's the year.
+ */
+ if (tm->tm_mday < 1900) return -1;
+
+ /*
+ * Swap the year and the day.
+ */
+ i = tm->tm_year;
+ tm->tm_year = tm->tm_mday - 1900;
+ tm->tm_mday = i;
+ }
+
+ /*
+ * If the day is out of range, die.
+ */
+ if ((tm->tm_mday < 1) || (tm->tm_mday > 31)) {
+ return -1;
+ }
+
+ /*
+ * There may be %H:%M:%S. Parse it in a hacky way.
+ */
+ if (f[3]) {
+ f[0] = f[3]; /* HH */
+ f[1] = strchr(f[0], ':'); /* find : separator */
+ if (!f[1]) return -1;
+
+ *(f[1]++) = '\0'; /* nuke it, and point to MM:SS */
+
+ f[2] = strchr(f[1], ':'); /* find : separator */
+ if (f[2]) {
+ *(f[2]++) = '\0'; /* nuke it, and point to SS */
+ tm->tm_sec = atoi(f[2]);
+ } /* else leave it as zero */
+
+ tm->tm_hour = atoi(f[0]);
+ tm->tm_min = atoi(f[1]);
+ }
+
+ /*
+ * Returns -1 on error.
+ */
+ t = mktime(tm);
+ if (t == (time_t) -1) return -1;
+
+ *date = t;
+
+ return 0;
+}
+
+/** Compares two pointers
+ *
+ * @param a first pointer to compare.
+ * @param b second pointer to compare.
+ * @return -1 if a < b, +1 if b > a, or 0 if both equal.
+ */
+int8_t fr_pointer_cmp(void const *a, void const *b)
+{
+ if (a < b) return -1;
+ if (a == b) return 0;
+
+ return 1;
+}
+
+static int _quick_partition(void const *to_sort[], int min, int max, fr_cmp_t cmp) {
+ void const *pivot = to_sort[min];
+ int i = min;
+ int j = max + 1;
+ void const *tmp;
+
+ for (;;) {
+ do ++i; while((cmp(to_sort[i], pivot) <= 0) && i <= max);
+ do --j; while(cmp(to_sort[j], pivot) > 0);
+
+ if (i >= j) break;
+
+ tmp = to_sort[i];
+ to_sort[i] = to_sort[j];
+ to_sort[j] = tmp;
+ }
+
+ tmp = to_sort[min];
+ to_sort[min] = to_sort[j];
+ to_sort[j] = tmp;
+
+ return j;
+}
+
+/** Quick sort an array of pointers using a comparator
+ *
+ * @param to_sort array of pointers to sort.
+ * @param min_idx the lowest index (usually 0).
+ * @param max_idx the highest index (usually length of array - 1).
+ * @param cmp the comparison function to use to sort the array elements.
+ */
+void fr_quick_sort(void const *to_sort[], int min_idx, int max_idx, fr_cmp_t cmp)
+{
+ int part;
+
+ if (min_idx >= max_idx) return;
+
+ part = _quick_partition(to_sort, min_idx, max_idx, cmp);
+ fr_quick_sort(to_sort, min_idx, part - 1, cmp);
+ fr_quick_sort(to_sort, part + 1, max_idx, cmp);
+}
+
+#define USEC 1000000
+
+/** Convert a time specified in milliseconds to a timeval
+ *
+ * @param[out] out Where to write the result.
+ * @param[in] ms To convert to a timeval struct.
+ */
+void fr_timeval_from_ms(struct timeval *out, uint64_t ms)
+{
+ out->tv_sec = ms / 1000;
+ out->tv_usec = (ms % 1000) * 1000;
+}
+
+/** Convert a time specified in microseconds to a timeval
+ *
+ * @param[out] out Where to write the result.
+ * @param[in] usec To convert to a timeval struct.
+ */
+void fr_timeval_from_usec(struct timeval *out, uint64_t usec)
+{
+ out->tv_sec = usec / USEC;
+ out->tv_usec = usec % USEC;
+}
+
+#ifdef TALLOC_DEBUG
+void fr_talloc_verify_cb(UNUSED const void *ptr, UNUSED int depth,
+ UNUSED int max_depth, UNUSED int is_ref,
+ UNUSED void *private_data)
+{
+ /* do nothing */
+}
+#endif