RCSID("$Id$")
-#include <freeradius-devel/libradius.h>
+#include <freeradius-devel/libradius.h>
-#include <ctype.h>
-#include <sys/file.h>
-#include <fcntl.h>
+#include <ctype.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/uio.h>
#define FR_PUT_LE16(a, val)\
do {\
a[0] = ((uint16_t) (val)) & 0xff;\
} while (0)
-#ifdef HAVE_PTHREAD_H
-static pthread_mutex_t autofree_context = PTHREAD_MUTEX_INITIALIZER;
-# define PTHREAD_MUTEX_LOCK pthread_mutex_lock
-# define PTHREAD_MUTEX_UNLOCK pthread_mutex_unlock
-#else
-# define PTHREAD_MUTEX_LOCK(_x)
-# define PTHREAD_MUTEX_UNLOCK(_x)
-#endif
-
bool fr_dns_lookups = false; /* IP -> hostname lookups? */
bool fr_hostname_lookups = true; /* hostname -> IP lookups? */
-int fr_debug_flag = 0;
+int fr_debug_lvl = 0;
static char const *months[] = {
"jan", "feb", "mar", "apr", "may", "jun",
"jul", "aug", "sep", "oct", "nov", "dec" };
-fr_thread_local_setup(char *, fr_inet_ntop_buffer); /* macro */
+fr_thread_local_setup(char *, fr_inet_ntop_buffer) /* macro */
+
+typedef struct fr_talloc_link {
+ bool armed;
+ TALLOC_CTX *child;
+} fr_talloc_link_t;
/** Sets a signal handler using sigaction if available, else signal
*
return 0;
}
-/** Allocates a new talloc context from the root autofree context
+/** Uninstall a signal for a specific handler
*
- * This function is threadsafe, whereas using the NULL context is not.
+ * man sigaction says these are fine to call from a signal handler.
*
- * @note The returned context must be freed by the caller.
- * @returns a new talloc context parented by the root autofree context.
+ * @param sig SIGNAL
*/
-TALLOC_CTX *fr_autofree_ctx(void)
+int fr_unset_signal(int sig)
+{
+#ifdef HAVE_SIGACTION
+ struct sigaction act;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_flags = 0;
+ sigemptyset(&act.sa_mask);
+ act.sa_handler = SIG_DFL;
+
+ return sigaction(sig, &act, NULL);
+#else
+ return signal(sig, SIG_DFL);
+#endif
+}
+
+static int _fr_trigger_talloc_ctx_free(fr_talloc_link_t *trigger)
+{
+ if (trigger->armed) talloc_free(trigger->child);
+
+ return 0;
+}
+
+static int _fr_disarm_talloc_ctx_free(bool **armed)
{
- static TALLOC_CTX *ctx = NULL, *child;
- PTHREAD_MUTEX_LOCK(&autofree_context);
- if (!ctx) {
- ctx = talloc_autofree_context();
+ **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;
}
- child = talloc_new(ctx);
- PTHREAD_MUTEX_UNLOCK(&autofree_context);
+ trigger->child = child;
+ trigger->armed = true;
+ *disarm = &trigger->armed;
+
+ talloc_set_destructor(trigger, _fr_trigger_talloc_ctx_free);
+ talloc_set_destructor(disarm, _fr_disarm_talloc_ctx_free);
- return child;
+ return 0;
}
/*
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 or IPv4 prefix in presentation format (and others)
*
* @param out Where to write the ip address value.
* @param value to parse, may be dotted quad [+ prefix], or integer, or octal number, or '*' (INADDR_ANY).
- * @param inlen Length of value, if value is \0 terminated inlen may be 0.
+ * @param 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 A records can be found.
+ * @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, size_t inlen, bool resolve, bool fallback)
+int fr_pton4(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resolve, bool fallback)
{
char *p;
- unsigned int prefix;
+ unsigned int mask;
char *eptr;
/* Dotted quad + / + [0-9]{1,2} */
/*
* Copy to intermediary buffer if we were given a length
*/
- if (inlen > 0) {
- if (inlen >= sizeof(buffer)) {
+ if (inlen >= 0) {
+ if (inlen >= (ssize_t)sizeof(buffer)) {
fr_strerror_printf("Invalid IPv4 address string \"%s\"", value);
return -1;
}
memcpy(buffer, value, inlen);
buffer[inlen] = '\0';
+ value = buffer;
}
p = strchr(value, '/');
+
/*
* 192.0.2.2 is parsed as if it was /32
*/
if (!p) {
+ out->prefix = 32;
+ out->af = AF_INET;
+
/*
* Allow '*' as the wildcard address usually 0.0.0.0
*/
if ((value[0] == '*') && (value[1] == '\0')) {
out->ipaddr.ip4addr.s_addr = htonl(INADDR_ANY);
+
/*
* Convert things which are obviously integers to IP addresses
*
* We assume the number is the bigendian representation of the
* IP address.
*/
- } else if (is_integer(value)) {
+ } else if (is_integer(value) || ((value[0] == '0') && (value[1] == 'x'))) {
out->ipaddr.ip4addr.s_addr = htonl(strtoul(value, NULL, 0));
+
} else if (!resolve) {
- if (inet_pton(AF_INET, value, &(out->ipaddr.ip4addr.s_addr)) <= 0) {
- fr_strerror_printf("Failed to parse IPv4 address string \"%s\"", value);
+ if (inet_pton(AF_INET, value, &out->ipaddr.ip4addr.s_addr) <= 0) {
+ fr_strerror_printf("Failed to parse IPv4 addreess string \"%s\"", value);
return -1;
}
} else if (ip_hton(out, AF_INET, value, fallback) < 0) return -1;
- out->prefix = 32;
- out->af = AF_INET;
-
return 0;
}
/*
- * Otherwise parse the prefix
- */
- if ((size_t)(p - value) >= INET_ADDRSTRLEN) {
- fr_strerror_printf("Invalid IPv4 address string \"%s\"", value);
- return -1;
- }
-
- /*
* Copy the IP portion into a temporary buffer if we haven't already.
*/
- if (inlen == 0) memcpy(buffer, value, p - value);
+ if (inlen < 0) memcpy(buffer, value, p - value);
buffer[p - value] = '\0';
- if (!resolve) {
- if (inet_pton(AF_INET, buffer, &(out->ipaddr.ip4addr.s_addr)) <= 0) {
- fr_strerror_printf("Failed to parse IPv4 address string \"%s\"", value);
- return -1;
- }
- } else if (ip_hton(out, AF_INET, buffer, fallback) < 0) return -1;
+ if (ip_prefix_from_str(buffer, &out->ipaddr.ip4addr.s_addr) <= 0) {
+ fr_strerror_printf("Failed to parse IPv4 address string \"%s\"", value);
+ return -1;
+ }
- prefix = strtoul(p + 1, &eptr, 10);
- if (prefix > 32) {
+ mask = strtoul(p + 1, &eptr, 10);
+ if (mask > 32) {
fr_strerror_printf("Invalid IPv4 mask length \"%s\". Should be between 0-32", p);
return -1;
}
+
if (eptr[0] != '\0') {
fr_strerror_printf("Failed to parse IPv4 address string \"%s\", "
"got garbage after mask length \"%s\"", value, eptr);
return -1;
}
- if (prefix < 32) {
- out->ipaddr.ip4addr = fr_inaddr_mask(&(out->ipaddr.ip4addr), prefix);
+ if (mask < 32) {
+ out->ipaddr.ip4addr = fr_inaddr_mask(&out->ipaddr.ip4addr, mask);
}
- out->prefix = (uint8_t) prefix;
+ out->prefix = (uint8_t) mask;
out->af = AF_INET;
return 0;
*
* @param out Where to write the ip address value.
* @param value to parse.
- * @param inlen Length of value, if value is \0 terminated inlen may be 0.
+ * @param inlen Length of value, if value is \0 terminated inlen may be -1.
* @param resolve If true and value doesn't look like an IP address, try and resolve value as a hostname.
* @param fallback to IPv4 resolution if no AAAA records can be found.
* @return 0 if ip address was parsed successfully, else -1 on error.
*/
-int fr_pton6(fr_ipaddr_t *out, char const *value, size_t inlen, bool resolve, bool fallback)
+int fr_pton6(fr_ipaddr_t *out, char const *value, ssize_t inlen, bool resolve, bool fallback)
{
char const *p;
unsigned int prefix;
/*
* Copy to intermediary buffer if we were given a length
*/
- if (inlen > 0) {
- if (inlen >= sizeof(buffer)) {
+ if (inlen >= 0) {
+ if (inlen >= (ssize_t)sizeof(buffer)) {
fr_strerror_printf("Invalid IPv6 address string \"%s\"", value);
return -1;
}
memcpy(buffer, value, inlen);
buffer[inlen] = '\0';
+ value = buffer;
}
p = strchr(value, '/');
if (!p) {
+ out->prefix = 128;
+ out->af = AF_INET6;
+
/*
* Allow '*' as the wildcard address
*/
if ((value[0] == '*') && (value[1] == '\0')) {
- memset(&out->ipaddr.ip6addr.s6_addr, 0, sizeof(out->ipaddr.ip6addr.s6_addr));
+ memset(out->ipaddr.ip6addr.s6_addr, 0, sizeof(out->ipaddr.ip6addr.s6_addr));
} else if (!resolve) {
- if (inet_pton(AF_INET6, value, &(out->ipaddr.ip6addr.s6_addr)) <= 0) {
+ if (inet_pton(AF_INET6, value, out->ipaddr.ip6addr.s6_addr) <= 0) {
fr_strerror_printf("Failed to parse IPv6 address string \"%s\"", value);
return -1;
}
} else if (ip_hton(out, AF_INET6, value, fallback) < 0) return -1;
- out->prefix = 128;
- out->af = AF_INET6;
-
return 0;
}
/*
* Copy string to temporary buffer if we didn't do it earlier
*/
- if (inlen == 0) memcpy(buffer, value, p - value);
+ if (inlen < 0) memcpy(buffer, value, p - value);
buffer[p - value] = '\0';
if (!resolve) {
- if (inet_pton(AF_INET6, buffer, &(out->ipaddr.ip6addr.s6_addr)) <= 0) {
+ if (inet_pton(AF_INET6, buffer, out->ipaddr.ip6addr.s6_addr) <= 0) {
fr_strerror_printf("Failed to parse IPv6 address string \"%s\"", value);
return -1;
}
if (prefix < 128) {
struct in6_addr addr;
- addr = fr_in6addr_mask(&(out->ipaddr.ip6addr), prefix);
- memcpy(&(out->ipaddr.ip6addr.s6_addr), &addr, sizeof(addr));
+ addr = fr_in6addr_mask(&out->ipaddr.ip6addr, prefix);
+ memcpy(out->ipaddr.ip6addr.s6_addr, addr.s6_addr, sizeof(out->ipaddr.ip6addr.s6_addr));
}
out->prefix = (uint8_t) prefix;
/** Simple wrapper to decide whether an IP value is v4 or v6 and call the appropriate parser.
*
- * @param out Where to write the ip address value.
- * @param value to parse.
- * @param inlen Length of value, if value is \0 terminated inlen may be 0.
- * @param resolve If true and value doesn't look like an IP address, try and resolve value as a hostname.
- * @return 0 if ip address was parsed successfully, else -1 on error.
+ * @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, size_t inlen, bool resolve)
+int fr_pton(fr_ipaddr_t *out, char const *value, ssize_t inlen, int af, bool resolve)
{
- char const *p;
- int af = AF_INET;
-
- for (p = value; *p != '\0'; p++) {
- if ((*p == ':') ||
- (*p == '[') ||
- (*p == ']')) {
- af = AF_INET6;
- break;
- }
- }
-
- switch (af) {
- case AF_INET:
- return fr_pton4(out, value, inlen, resolve, true);
+ size_t len, i;
+ len = (inlen >= 0) ? (size_t)inlen : strlen(value);
+ for (i = 0; i < len; i++) switch (value[i]) {
/*
- * If we found ':' or '[' or ']' in the above string, there's
- * no way this can be a hostname, so don't try to resolve.
+ * ':' is illegal in domain names and IPv4 addresses.
+ * Must be v6 and cannot be a domain.
*/
- case AF_INET6:
+ case ':':
return fr_pton6(out, value, inlen, false, false);
+ /*
+ * Chars which don't really tell us anything
+ */
+ case '.':
+ case '/':
+ continue;
+
default:
- return -1;
+ /*
+ * Outside the range of IPv4 chars, must be a domain
+ * Use A record in preference to AAAA record.
+ */
+ if ((value[i] < '0') || (value[i] > '9')) {
+ if (!resolve) {
+ fr_strerror_printf("Not IPv4/6 address, and asked not to resolve");
+ return -1;
+ }
+ switch (af) {
+ case AF_UNSPEC:
+ return fr_pton4(out, value, inlen, resolve, true);
+
+ case AF_INET:
+ return fr_pton4(out, value, inlen, resolve, false);
+
+ case AF_INET6:
+ return fr_pton6(out, value, inlen, resolve, false);
+
+ default:
+ fr_strerror_printf("Invalid address family %i", af);
+ return -1;
+ }
+ }
+ break;
}
+
+ /*
+ * All chars were in the IPv4 set [0-9/.], must be an IPv4
+ * address.
+ */
+ return fr_pton4(out, value, inlen, false, false);
}
-/** Check if the IP address is equivalent to INADDR_ANY
+/** Parses IPv4/6 address + port, to fr_ipaddr_t and integer
*
- * @param addr to chec.
- * @return true if IP address matches INADDR_ANY or INADDR6_ANY (assumed to be 0), else false.
+ * @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.
*/
-bool is_wildcard(fr_ipaddr_t *addr)
+int fr_pton_port(fr_ipaddr_t *out, uint16_t *port_out, char const *value, ssize_t inlen, int af, bool resolve)
{
- static struct in6_addr in6_addr;
+ char const *p = value, *q;
+ char *end;
+ unsigned long port;
+ char buffer[6];
+ size_t len;
- switch (addr->af) {
- case AF_INET:
- return (addr->ipaddr.ip4addr.s_addr == htons(INADDR_ANY));
+ *port_out = 0;
- case AF_INET6:
- return (memcmp(addr->ipaddr.ip6addr.s6_addr, in6_addr.s6_addr, sizeof(in6_addr.s6_addr)) == 0) ? true :false;
+ len = (inlen >= 0) ? (size_t)inlen : strlen(value);
- default:
- fr_assert(0);
- return false;
+ 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 (inlen > ((q + sizeof(buffer)) - value)) {
+ error:
+ fr_strerror_printf("IP string contains trailing garbage after port delimiter");
+ return -1;
+ }
+
+ p = q + 1; /* Move to first digit */
+
+ strlcpy(buffer, p, (len - (p - value)) + 1);
+ port = strtoul(buffer, &end, 10);
+ if (*end != '\0') goto error; /* Trailing garbage after integer */
+
+ if ((port > UINT16_MAX) || (port == 0)) {
+ fr_strerror_printf("Port %lu outside valid port range 1-" STRINGIFY(UINT16_MAX), port);
+ return -1;
+ }
+ *port_out = port;
+
+ return 0;
}
int fr_ntop(char *out, size_t outlen, fr_ipaddr_t *addr)
}
/*
+ * 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
#ifdef HAVE_STRUCT_SOCKADDR_IN6
-/* int
- * inet_pton6(src, dst)
- * convert presentation level address to network order binary form.
- * return:
- * 1 if `src' is a valid [RFC1884 2.2] address, else 0.
- * notice:
- * (1) does not touch `dst' unless it's returning 1.
- * (2) :: in a full address is silently ignored.
- * credit:
- * inspired by Mark Andrews.
- * author:
- * Paul Vixie, 1996.
+/** Convert presentation level address to network order binary form
+ *
+ * @note Does not touch dst unless it's returning 1.
+ * @note :: in a full address is silently ignored.
+ * @note Inspired by Mark Andrews.
+ * @author Paul Vixie, 1996.
+ *
+ * @param src presentation level address.
+ * @param dst where to write output address.
+ * @return 1 if `src' is a valid [RFC1884 2.2] address, else 0.
*/
static int inet_pton6(char const *src, unsigned char *dst)
{
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;
if (af == AF_UNSPEC) af = AF_INET;
- if (!inet_pton(af, hostname, &(out->ipaddr))) {
- return -1;
- }
+ if (!inet_pton(af, hostname, &(out->ipaddr))) return -1;
out->af = af;
return 0;
}
memset(&hints, 0, sizeof(hints));
- hints.ai_family = af;
-#ifdef TALLOC_DEBUG
/*
- * Avoid malloc for IP addresses. This helps us debug
- * memory errors when using talloc.
+ * If we're falling back we need both IPv4 and IPv6 records
*/
- if (af == AF_INET) {
- /*
- * If it's all numeric, avoid getaddrinfo()
- */
- if (inet_pton(af, hostname, &out->ipaddr.ip4addr) == 1) {
- return 0;
- }
+ if (fallback) {
+ hints.ai_family = AF_UNSPEC;
+ } else {
+ hints.ai_family = af;
}
-#endif
if ((rcode = getaddrinfo(hostname, NULL, &hints, &res)) != 0) {
- fr_strerror_printf("ip_hton: %s", gai_strerror(rcode));
- return -1;
+ 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 (!ai) ai = alt;
if (!ai) {
fr_strerror_printf("ip_hton failed to find requested information for host %.100s", hostname);
- freeaddrinfo(ai);
+ freeaddrinfo(res);
return -1;
}
rcode = fr_sockaddr2ipaddr((struct sockaddr_storage *)ai->ai_addr,
ai->ai_addrlen, out, NULL);
- freeaddrinfo(ai);
- if (!rcode) return -1;
+ freeaddrinfo(res);
+ if (!rcode) {
+ fr_strerror_printf("Failed converting sockaddr to ipaddr");
+ return -1;
+ }
return 0;
}
*
* @param ipaddr to mask.
* @param prefix Number of contiguous bits to mask.
- * @return an ipv6 address with the host portion zeroed out.
+ * @return an ipv4 address with the host portion zeroed out.
*/
struct in_addr fr_inaddr_mask(struct in_addr const *ipaddr, uint8_t prefix)
{
uint32_t ret;
- if (prefix > 32) {
- prefix = 32;
- }
+ if (prefix > 32) prefix = 32;
/* Short circuit */
- if (prefix == 32) {
- return *ipaddr;
- }
+ if (prefix == 32) return *ipaddr;
+
+ if (prefix == 0) ret = 0;
+ else ret = htonl(~((0x00000001UL << (32 - prefix)) - 1)) & ipaddr->s_addr;
- ret = htonl(~((0x00000001UL << (32 - prefix)) - 1)) & ipaddr->s_addr;
return (*(struct in_addr *)&ret);
}
uint64_t const *p = (uint64_t const *) ipaddr;
uint64_t ret[2], *o = ret;
- if (prefix > 128) {
- prefix = 128;
- }
+ if (prefix > 128) prefix = 128;
/* Short circuit */
- if (prefix == 128) {
- return *ipaddr;
- }
+ if (prefix == 128) return *ipaddr;
if (prefix >= 64) {
prefix -= 64;
- *o++ = 0xffffffffffffffffULL & *p++;
+ *o++ = 0xffffffffffffffffULL & *p++; /* lhs portion masked */
} else {
- ret[1] = 0;
+ ret[1] = 0; /* rhs portion zeroed */
}
- *o = htonll(~((0x0000000000000001ULL << (64 - prefix)) - 1)) & *p;
+ /* Max left shift is 63 else we get overflow */
+ if (prefix > 0) {
+ *o = htonll(~((uint64_t)(0x0000000000000001ULL << (64 - prefix)) - 1)) & *p;
+ } else {
+ *o = 0;
+ }
return *(struct in6_addr *) &ret;
}
addr->prefix = prefix;
}
-static char const *hextab = "0123456789abcdef";
+static char const hextab[] = "0123456789abcdef";
/** Convert hex strings to binary data
*
* @param bin Buffer to write output to.
- * @param hex input string.
* @param outlen length of output buffer (or length of input string / 2).
+ * @param hex input string.
+ * @param inlen length of the input string
* @return length of data written to buffer.
*/
-size_t fr_hex2bin(uint8_t *bin, char const *hex, size_t outlen)
+size_t fr_hex2bin(uint8_t *bin, size_t outlen, char const *hex, size_t inlen)
{
size_t i;
+ size_t len;
char *c1, *c2;
- for (i = 0; i < outlen; i++) {
- if(!(c1 = memchr(hextab, tolower((int) hex[i << 1]), 16)) ||
- !(c2 = memchr(hextab, tolower((int) hex[(i << 1) + 1]), 16)))
+ /*
+ * Smartly truncate output, caller should check number of bytes
+ * written.
+ */
+ len = inlen >> 1;
+ if (len > outlen) len = outlen;
+
+ for (i = 0; i < len; i++) {
+ if(!(c1 = memchr(hextab, tolower((int) hex[i << 1]), sizeof(hextab))) ||
+ !(c2 = memchr(hextab, tolower((int) hex[(i << 1) + 1]), sizeof(hextab))))
break;
- bin[i] = ((c1-hextab)<<4) + (c2-hextab);
+ bin[i] = ((c1-hextab)<<4) + (c2-hextab);
}
return i;
}
-
/** Convert binary data to a hex string
*
* Ascii encoded hex string will not be prefixed with '0x'
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
*
return true;
}
+/** Check whether the string is made up of printable UTF8 chars
+ *
+ * @param value to check.
+ * @param len of value.
+ *
+ * @return
+ * - true if the string is printable.
+ * - false if the string contains non printable chars
+ */
+ bool is_printable(void const *value, size_t len)
+ {
+ uint8_t const *p = value;
+ int clen;
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ clen = fr_utf8_char(p, len - i);
+ if (clen == 0) return false;
+ i += (size_t)clen;
+ p += clen;
+ }
+ return true;
+ }
+
/** Check whether the string is all numbers
*
- * @return true if the entirety of the string is are numebrs, else false.
+ * @return true if the entirety of the string is all numbers, else false.
*/
bool is_integer(char const *value)
{
/** Check whether the string is allzeros
*
- * @return true if the entirety of the string is are numebrs, else false.
+ * @return true if the entirety of the string is all zeros, else false.
*/
bool is_zero(char const *value)
{
int i;
int maxfd = 256;
+#ifdef F_CLOSEM
+ if (fcntl(fd, F_CLOSEM) == 0) {
+ return 0;
+ }
+#endif
+
+#ifdef F_MAXFD
+ maxfd = fcntl(fd, F_F_MAXFD);
+ if (maxfd >= 0) goto do_close;
+#endif
+
#ifdef _SC_OPEN_MAX
maxfd = sysconf(_SC_OPEN_MAX);
if (maxfd < 0) {
- maxfd = 256;
+ maxfd = 256;
}
#endif
+#ifdef F_MAXFD
+do_close:
+#endif
+
if (fd > maxfd) return 0;
/*
* FIXME: return EINTR?
- *
- * Use F_CLOSEM?
*/
for (i = fd; i < maxfd; i++) {
close(i);
if (a->af < b->af) return -1;
if (a->af > b->af) return +1;
+ if (a->prefix < b->prefix) return -1;
+ if (a->prefix > b->prefix) return +1;
+
switch (a->af) {
case AF_INET:
return memcmp(&a->ipaddr.ip4addr,
&b->ipaddr.ip4addr,
sizeof(a->ipaddr.ip4addr));
- break;
#ifdef HAVE_STRUCT_SOCKADDR_IN6
case AF_INET6:
return memcmp(&a->ipaddr.ip6addr,
&b->ipaddr.ip6addr,
sizeof(a->ipaddr.ip6addr));
- break;
#endif
default:
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;
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;
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);
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;
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/)
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';
ssize_t j;
int carry;
- carry = (n[1] >= 0x8000000000000000);
+ carry = (n[h] >= 0x8000000000000000);
// Shift n[] left, doubling it
- n[1] = ((n[1] << 1) & 0xffffffffffffffff) + (n[0] >= 0x8000000000000000);
- n[0] = ((n[0] << 1) & 0xffffffffffffffff);
+ n[h] = ((n[h] << 1) & 0xffffffffffffffff) + (n[l] >= 0x8000000000000000);
+ n[l] = ((n[l] << 1) & 0xffffffffffffffff);
// Add s[] to itself in decimal, doubling it
for (j = sizeof(buff) - 2; j >= 0; j--) {
return strlcpy(out, p, outlen);
}
-/** Calculate powers
- *
- * @author Orson Peters
- * @note Borrowed from the gist here: https://gist.github.com/nightcracker/3551590.
- *
- * @param base a 32bit signed integer.
- * @param exp amount to raise base by.
- * @return base ^ pow, or 0 on underflow/overflow.
- */
-int64_t fr_pow(int32_t base, uint8_t exp) {
- static const uint8_t highest_bit_set[] = {
- 0, 1, 2, 2, 3, 3, 3, 3,
- 4, 4, 4, 4, 4, 4, 4, 4,
- 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5,
- 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 255, // anything past 63 is a guaranteed overflow with base > 1
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- 255, 255, 255, 255, 255, 255, 255, 255,
- };
-
- uint64_t result = 1;
-
- switch (highest_bit_set[exp]) {
- case 255: // we use 255 as an overflow marker and return 0 on overflow/underflow
- if (base == 1) {
- return 1;
- }
-
- if (base == -1) {
- return 1 - 2 * (exp & 1);
- }
- return 0;
- case 6:
- if (exp & 1) result *= base;
- exp >>= 1;
- base *= base;
- case 5:
- if (exp & 1) result *= base;
- exp >>= 1;
- base *= base;
- case 4:
- if (exp & 1) result *= base;
- exp >>= 1;
- base *= base;
- case 3:
- if (exp & 1) result *= base;
- exp >>= 1;
- base *= base;
- case 2:
- if (exp & 1) result *= base;
- exp >>= 1;
- base *= base;
- case 1:
- if (exp & 1) result *= base;
- default:
- return result;
- }
-}
-
/*
* Sort of strtok/strsep function.
*/