diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 14:11:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 14:11:00 +0000 |
commit | af754e596a8dbb05ed8580c342e7fe02e08b28e0 (patch) | |
tree | b2f334c2b55ede42081aa6710a72da784547d8ea /src/lib/misc.c | |
parent | Initial commit. (diff) | |
download | freeradius-upstream.tar.xz freeradius-upstream.zip |
Adding upstream version 3.2.3+dfsg.upstream/3.2.3+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib/misc.c')
-rw-r--r-- | src/lib/misc.c | 2192 |
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 |