chrony/util.c
Miroslav Lichvar 510b22e96b util: fix more coverity warnings
Coverity doesn't seem to like the new field in the IPAddr struct (used
as explicit padding of the structure) to be left uninitialized, even
though it's never used for anything and is cleared by memset() in
UTI_IPHostToNetwork() before leaving the process.
2017-01-31 11:22:10 +01:00

1202 lines
26 KiB
C

/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009, 2012-2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Various utility functions
*/
#include "config.h"
#include "sysincl.h"
#include "logging.h"
#include "memory.h"
#include "util.h"
#include "hash.h"
#define NSEC_PER_SEC 1000000000
/* ================================================== */
void
UTI_ZeroTimespec(struct timespec *ts)
{
ts->tv_sec = 0;
ts->tv_nsec = 0;
}
/* ================================================== */
int
UTI_IsZeroTimespec(struct timespec *ts)
{
return !ts->tv_sec && !ts->tv_nsec;
}
/* ================================================== */
void
UTI_TimevalToTimespec(struct timeval *tv, struct timespec *ts)
{
ts->tv_sec = tv->tv_sec;
ts->tv_nsec = 1000 * tv->tv_usec;
}
/* ================================================== */
void
UTI_TimespecToTimeval(struct timespec *ts, struct timeval *tv)
{
tv->tv_sec = ts->tv_sec;
tv->tv_usec = ts->tv_nsec / 1000;
}
/* ================================================== */
double
UTI_TimespecToDouble(struct timespec *ts)
{
return ts->tv_sec + 1.0e-9 * ts->tv_nsec;
}
/* ================================================== */
void
UTI_DoubleToTimespec(double d, struct timespec *ts)
{
ts->tv_sec = d;
ts->tv_nsec = 1.0e9 * (d - ts->tv_sec);
UTI_NormaliseTimespec(ts);
}
/* ================================================== */
void
UTI_NormaliseTimespec(struct timespec *ts)
{
if (ts->tv_nsec >= NSEC_PER_SEC || ts->tv_nsec < 0) {
ts->tv_sec += ts->tv_nsec / NSEC_PER_SEC;
ts->tv_nsec = ts->tv_nsec % NSEC_PER_SEC;
/* If seconds are negative nanoseconds would end up negative too */
if (ts->tv_nsec < 0) {
ts->tv_sec--;
ts->tv_nsec += NSEC_PER_SEC;
}
}
}
/* ================================================== */
double
UTI_TimevalToDouble(struct timeval *tv)
{
return tv->tv_sec + 1.0e-6 * tv->tv_usec;
}
/* ================================================== */
void
UTI_DoubleToTimeval(double a, struct timeval *b)
{
long int_part;
double frac_part;
int_part = (long)(a);
frac_part = 1.0e6 * (a - (double)(int_part));
frac_part = frac_part > 0 ? frac_part + 0.5 : frac_part - 0.5;
b->tv_sec = int_part;
b->tv_usec = (long)frac_part;
UTI_NormaliseTimeval(b);
}
/* ================================================== */
void
UTI_NormaliseTimeval(struct timeval *x)
{
/* Reduce tv_usec to within +-1000000 of zero. JGH */
if ((x->tv_usec >= 1000000) || (x->tv_usec <= -1000000)) {
x->tv_sec += x->tv_usec/1000000;
x->tv_usec = x->tv_usec%1000000;
}
/* Make tv_usec positive. JGH */
if (x->tv_usec < 0) {
--x->tv_sec;
x->tv_usec += 1000000;
}
}
/* ================================================== */
int
UTI_CompareTimespecs(struct timespec *a, struct timespec *b)
{
if (a->tv_sec < b->tv_sec)
return -1;
if (a->tv_sec > b->tv_sec)
return 1;
if (a->tv_nsec < b->tv_nsec)
return -1;
if (a->tv_nsec > b->tv_nsec)
return 1;
return 0;
}
/* ================================================== */
void
UTI_DiffTimespecs(struct timespec *result, struct timespec *a, struct timespec *b)
{
result->tv_sec = a->tv_sec - b->tv_sec;
result->tv_nsec = a->tv_nsec - b->tv_nsec;
UTI_NormaliseTimespec(result);
}
/* ================================================== */
/* Calculate result = a - b and return as a double */
double
UTI_DiffTimespecsToDouble(struct timespec *a, struct timespec *b)
{
return (a->tv_sec - b->tv_sec) + 1.0e-9 * (a->tv_nsec - b->tv_nsec);
}
/* ================================================== */
void
UTI_AddDoubleToTimespec(struct timespec *start, double increment, struct timespec *end)
{
time_t int_part;
int_part = increment;
end->tv_sec = start->tv_sec + int_part;
end->tv_nsec = start->tv_nsec + 1.0e9 * (increment - int_part);
UTI_NormaliseTimespec(end);
}
/* ================================================== */
/* Calculate the average and difference (as a double) of two timespecs */
void
UTI_AverageDiffTimespecs(struct timespec *earlier, struct timespec *later,
struct timespec *average, double *diff)
{
*diff = UTI_DiffTimespecsToDouble(later, earlier);
UTI_AddDoubleToTimespec(earlier, *diff / 2.0, average);
}
/* ================================================== */
void
UTI_AddDiffToTimespec(struct timespec *a, struct timespec *b,
struct timespec *c, struct timespec *result)
{
double diff;
diff = UTI_DiffTimespecsToDouble(a, b);
UTI_AddDoubleToTimespec(c, diff, result);
}
/* ================================================== */
#define POOL_ENTRIES 16
#define BUFFER_LENGTH 64
static char buffer_pool[POOL_ENTRIES][BUFFER_LENGTH];
static int pool_ptr = 0;
#define NEXT_BUFFER (buffer_pool[pool_ptr = ((pool_ptr + 1) % POOL_ENTRIES)])
/* ================================================== */
/* Convert a timespec into a temporary string, largely for diagnostic display */
char *
UTI_TimespecToString(struct timespec *ts)
{
char *result;
result = NEXT_BUFFER;
#ifdef HAVE_LONG_TIME_T
snprintf(result, BUFFER_LENGTH, "%"PRId64".%09lu",
(int64_t)ts->tv_sec, (unsigned long)ts->tv_nsec);
#else
snprintf(result, BUFFER_LENGTH, "%ld.%09lu",
(long)ts->tv_sec, (unsigned long)ts->tv_nsec);
#endif
return result;
}
/* ================================================== */
/* Convert an NTP timestamp into a temporary string, largely
for diagnostic display */
char *
UTI_Ntp64ToString(NTP_int64 *ntp_ts)
{
struct timespec ts;
UTI_Ntp64ToTimespec(ntp_ts, &ts);
return UTI_TimespecToString(&ts);
}
/* ================================================== */
char *
UTI_RefidToString(uint32_t ref_id)
{
unsigned int i, j, c;
char *result;
result = NEXT_BUFFER;
for (i = j = 0; i < 4 && i < BUFFER_LENGTH - 1; i++) {
c = (ref_id >> (24 - i * 8)) & 0xff;
if (isprint(c))
result[j++] = c;
}
result[j] = '\0';
return result;
}
/* ================================================== */
char *
UTI_IPToString(IPAddr *addr)
{
unsigned long a, b, c, d, ip;
uint8_t *ip6;
char *result;
result = NEXT_BUFFER;
switch (addr->family) {
case IPADDR_UNSPEC:
snprintf(result, BUFFER_LENGTH, "[UNSPEC]");
break;
case IPADDR_INET4:
ip = addr->addr.in4;
a = (ip>>24) & 0xff;
b = (ip>>16) & 0xff;
c = (ip>> 8) & 0xff;
d = (ip>> 0) & 0xff;
snprintf(result, BUFFER_LENGTH, "%ld.%ld.%ld.%ld", a, b, c, d);
break;
case IPADDR_INET6:
ip6 = addr->addr.in6;
#ifdef FEAT_IPV6
inet_ntop(AF_INET6, ip6, result, BUFFER_LENGTH);
#else
snprintf(result, BUFFER_LENGTH, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
ip6[0], ip6[1], ip6[2], ip6[3], ip6[4], ip6[5], ip6[6], ip6[7],
ip6[8], ip6[9], ip6[10], ip6[11], ip6[12], ip6[13], ip6[14], ip6[15]);
#endif
break;
default:
snprintf(result, BUFFER_LENGTH, "[UNKNOWN]");
}
return result;
}
/* ================================================== */
int
UTI_StringToIP(const char *addr, IPAddr *ip)
{
#ifdef FEAT_IPV6
struct in_addr in4;
struct in6_addr in6;
if (inet_pton(AF_INET, addr, &in4) > 0) {
ip->family = IPADDR_INET4;
ip->_pad = 0;
ip->addr.in4 = ntohl(in4.s_addr);
return 1;
}
if (inet_pton(AF_INET6, addr, &in6) > 0) {
ip->family = IPADDR_INET6;
ip->_pad = 0;
memcpy(ip->addr.in6, in6.s6_addr, sizeof (ip->addr.in6));
return 1;
}
#else
unsigned long a, b, c, d, n;
n = sscanf(addr, "%lu.%lu.%lu.%lu", &a, &b, &c, &d);
if (n == 4) {
ip->family = IPADDR_INET4;
ip->_pad = 0;
ip->addr.in4 = ((a & 0xff) << 24) | ((b & 0xff) << 16) |
((c & 0xff) << 8) | (d & 0xff);
return 1;
}
#endif
return 0;
}
/* ================================================== */
uint32_t
UTI_IPToRefid(IPAddr *ip)
{
static int MD5_hash = -1;
unsigned char buf[16];
switch (ip->family) {
case IPADDR_INET4:
return ip->addr.in4;
case IPADDR_INET6:
if (MD5_hash < 0) {
MD5_hash = HSH_GetHashId("MD5");
assert(MD5_hash >= 0);
}
if (HSH_Hash(MD5_hash, (unsigned const char *)ip->addr.in6, sizeof
(ip->addr.in6), NULL, 0, buf, 16) != 16) {
assert(0);
return 0;
};
return (uint32_t)buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
}
return 0;
}
/* ================================================== */
uint32_t
UTI_IPToHash(IPAddr *ip)
{
static uint32_t seed = 0;
unsigned char *addr;
unsigned int i, len;
uint32_t hash;
switch (ip->family) {
case IPADDR_INET4:
addr = (unsigned char *)&ip->addr.in4;
len = sizeof (ip->addr.in4);
break;
case IPADDR_INET6:
addr = ip->addr.in6;
len = sizeof (ip->addr.in6);
break;
default:
return 0;
}
/* Include a random seed in the hash to randomize collisions
and order of addresses in hash tables */
while (!seed)
UTI_GetRandomBytes(&seed, sizeof (seed));
for (i = 0, hash = seed; i < len; i++)
hash = 71 * hash + addr[i];
return hash + seed;
}
/* ================================================== */
void
UTI_IPHostToNetwork(IPAddr *src, IPAddr *dest)
{
/* Don't send uninitialized bytes over network */
memset(dest, 0, sizeof (IPAddr));
dest->family = htons(src->family);
switch (src->family) {
case IPADDR_INET4:
dest->addr.in4 = htonl(src->addr.in4);
break;
case IPADDR_INET6:
memcpy(dest->addr.in6, src->addr.in6, sizeof (dest->addr.in6));
break;
default:
dest->family = htons(IPADDR_UNSPEC);
}
}
/* ================================================== */
void
UTI_IPNetworkToHost(IPAddr *src, IPAddr *dest)
{
dest->family = ntohs(src->family);
dest->_pad = 0;
switch (dest->family) {
case IPADDR_INET4:
dest->addr.in4 = ntohl(src->addr.in4);
break;
case IPADDR_INET6:
memcpy(dest->addr.in6, src->addr.in6, sizeof (dest->addr.in6));
break;
default:
dest->family = IPADDR_UNSPEC;
}
}
/* ================================================== */
int
UTI_CompareIPs(IPAddr *a, IPAddr *b, IPAddr *mask)
{
int i, d;
if (a->family != b->family)
return a->family - b->family;
if (mask && mask->family != b->family)
mask = NULL;
switch (a->family) {
case IPADDR_UNSPEC:
return 0;
case IPADDR_INET4:
if (mask)
return (a->addr.in4 & mask->addr.in4) - (b->addr.in4 & mask->addr.in4);
else
return a->addr.in4 - b->addr.in4;
case IPADDR_INET6:
for (i = 0, d = 0; !d && i < 16; i++) {
if (mask)
d = (a->addr.in6[i] & mask->addr.in6[i]) -
(b->addr.in6[i] & mask->addr.in6[i]);
else
d = a->addr.in6[i] - b->addr.in6[i];
}
return d;
}
return 0;
}
/* ================================================== */
void
UTI_SockaddrToIPAndPort(struct sockaddr *sa, IPAddr *ip, unsigned short *port)
{
switch (sa->sa_family) {
case AF_INET:
ip->family = IPADDR_INET4;
ip->addr.in4 = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr);
*port = ntohs(((struct sockaddr_in *)sa)->sin_port);
break;
#ifdef FEAT_IPV6
case AF_INET6:
ip->family = IPADDR_INET6;
memcpy(ip->addr.in6, ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr,
sizeof (ip->addr.in6));
*port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
break;
#endif
default:
ip->family = IPADDR_UNSPEC;
*port = 0;
}
}
/* ================================================== */
int
UTI_IPAndPortToSockaddr(IPAddr *ip, unsigned short port, struct sockaddr *sa)
{
switch (ip->family) {
case IPADDR_INET4:
memset(sa, 0, sizeof (struct sockaddr_in));
sa->sa_family = AF_INET;
((struct sockaddr_in *)sa)->sin_addr.s_addr = htonl(ip->addr.in4);
((struct sockaddr_in *)sa)->sin_port = htons(port);
#ifdef SIN6_LEN
((struct sockaddr_in *)sa)->sin_len = sizeof (struct sockaddr_in);
#endif
return sizeof (struct sockaddr_in);
#ifdef FEAT_IPV6
case IPADDR_INET6:
memset(sa, 0, sizeof (struct sockaddr_in6));
sa->sa_family = AF_INET6;
memcpy(((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr, ip->addr.in6,
sizeof (ip->addr.in6));
((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
#ifdef SIN6_LEN
((struct sockaddr_in6 *)sa)->sin6_len = sizeof (struct sockaddr_in6);
#endif
return sizeof (struct sockaddr_in6);
#endif
default:
memset(sa, 0, sizeof (struct sockaddr));
sa->sa_family = AF_UNSPEC;
return 0;
}
}
/* ================================================== */
char *UTI_SockaddrToString(struct sockaddr *sa)
{
unsigned short port;
IPAddr ip;
char *result;
result = NEXT_BUFFER;
switch (sa->sa_family) {
case AF_INET:
#ifdef AF_INET6
case AF_INET6:
#endif
UTI_SockaddrToIPAndPort(sa, &ip, &port);
snprintf(result, BUFFER_LENGTH, "%s:%hu", UTI_IPToString(&ip), port);
break;
case AF_UNIX:
snprintf(result, BUFFER_LENGTH, "%s", ((struct sockaddr_un *)sa)->sun_path);
break;
default:
snprintf(result, BUFFER_LENGTH, "[UNKNOWN]");
}
return result;
}
/* ================================================== */
const char *
UTI_SockaddrFamilyToString(int family)
{
switch (family) {
case AF_INET:
return "IPv4";
#ifdef AF_INET6
case AF_INET6:
return "IPv6";
#endif
case AF_UNIX:
return "Unix";
case AF_UNSPEC:
return "UNSPEC";
default:
return "?";
}
}
/* ================================================== */
char *
UTI_TimeToLogForm(time_t t)
{
struct tm stm;
char *result;
result = NEXT_BUFFER;
stm = *gmtime(&t);
strftime(result, BUFFER_LENGTH, "%Y-%m-%d %H:%M:%S", &stm);
return result;
}
/* ================================================== */
void
UTI_AdjustTimespec(struct timespec *old_ts, struct timespec *when, struct timespec *new_ts, double *delta_time, double dfreq, double doffset)
{
double elapsed;
elapsed = UTI_DiffTimespecsToDouble(when, old_ts);
*delta_time = elapsed * dfreq - doffset;
UTI_AddDoubleToTimespec(old_ts, *delta_time, new_ts);
}
/* ================================================== */
void
UTI_GetNtp64Fuzz(NTP_int64 *ts, int precision)
{
int start, bits;
assert(precision >= -32 && precision <= 32);
start = sizeof (*ts) - (precision + 32 + 7) / 8;
ts->hi = ts->lo = 0;
UTI_GetRandomBytes((unsigned char *)ts + start, sizeof (*ts) - start);
bits = (precision + 32) % 8;
if (bits)
((unsigned char *)ts)[start] %= 1U << bits;
}
/* ================================================== */
double
UTI_Ntp32ToDouble(NTP_int32 x)
{
return (double) ntohl(x) / 65536.0;
}
/* ================================================== */
#define MAX_NTP_INT32 (4294967295.0 / 65536.0)
NTP_int32
UTI_DoubleToNtp32(double x)
{
NTP_int32 r;
if (x >= MAX_NTP_INT32) {
r = 0xffffffff;
} else if (x <= 0.0) {
r = 0;
} else {
x *= 65536.0;
r = x;
/* Round up */
if (r < x)
r++;
}
return htonl(r);
}
/* ================================================== */
void
UTI_ZeroNtp64(NTP_int64 *ts)
{
ts->hi = ts->lo = htonl(0);
}
/* ================================================== */
int
UTI_IsZeroNtp64(NTP_int64 *ts)
{
return !ts->hi && !ts->lo;
}
/* ================================================== */
int
UTI_CompareNtp64(NTP_int64 *a, NTP_int64 *b)
{
int32_t diff;
if (a->hi == b->hi && a->lo == b->lo)
return 0;
diff = ntohl(a->hi) - ntohl(b->hi);
if (diff < 0)
return -1;
if (diff > 0)
return 1;
return ntohl(a->lo) < ntohl(b->lo) ? -1 : 1;
}
/* ================================================== */
/* Seconds part of NTP timestamp correponding to the origin of the time_t format */
#define JAN_1970 0x83aa7e80UL
#define NSEC_PER_NTP64 4.294967296
void
UTI_TimespecToNtp64(struct timespec *src, NTP_int64 *dest, NTP_int64 *fuzz)
{
uint32_t hi, lo, sec, nsec;
sec = (uint32_t)src->tv_sec;
nsec = (uint32_t)src->tv_nsec;
/* Recognize zero as a special case - it always signifies
an 'unknown' value */
if (!nsec && !sec) {
hi = lo = 0;
} else {
hi = htonl(sec + JAN_1970);
lo = htonl(NSEC_PER_NTP64 * nsec);
/* Add the fuzz */
if (fuzz) {
hi ^= fuzz->hi;
lo ^= fuzz->lo;
}
}
dest->hi = hi;
dest->lo = lo;
}
/* ================================================== */
void
UTI_Ntp64ToTimespec(NTP_int64 *src, struct timespec *dest)
{
uint32_t ntp_sec, ntp_frac;
/* Zero is a special value */
if (UTI_IsZeroNtp64(src)) {
UTI_ZeroTimespec(dest);
return;
}
ntp_sec = ntohl(src->hi);
ntp_frac = ntohl(src->lo);
#ifdef HAVE_LONG_TIME_T
dest->tv_sec = ntp_sec - (uint32_t)(NTP_ERA_SPLIT + JAN_1970) +
(time_t)NTP_ERA_SPLIT;
#else
dest->tv_sec = ntp_sec - JAN_1970;
#endif
dest->tv_nsec = ntp_frac / NSEC_PER_NTP64 + 0.5;
UTI_NormaliseTimespec(dest);
}
/* ================================================== */
/* Maximum offset between two sane times */
#define MAX_OFFSET 4294967296.0
/* Minimum allowed distance from maximum 32-bit time_t */
#define MIN_ENDOFTIME_DISTANCE (365 * 24 * 3600)
int
UTI_IsTimeOffsetSane(struct timespec *ts, double offset)
{
double t;
/* Handle nan correctly here */
if (!(offset > -MAX_OFFSET && offset < MAX_OFFSET))
return 0;
t = UTI_TimespecToDouble(ts) + offset;
/* Time before 1970 is not considered valid */
if (t < 0.0)
return 0;
#ifdef HAVE_LONG_TIME_T
/* Check if it's in the interval to which NTP time is mapped */
if (t < (double)NTP_ERA_SPLIT || t > (double)(NTP_ERA_SPLIT + (1LL << 32)))
return 0;
#else
/* Don't get too close to 32-bit time_t overflow */
if (t > (double)(0x7fffffff - MIN_ENDOFTIME_DISTANCE))
return 0;
#endif
return 1;
}
/* ================================================== */
double
UTI_Log2ToDouble(int l)
{
if (l >= 0) {
if (l > 31)
l = 31;
return (uint32_t)1 << l;
} else {
if (l < -31)
l = -31;
return 1.0 / ((uint32_t)1 << -l);
}
}
/* ================================================== */
void
UTI_TimespecNetworkToHost(Timespec *src, struct timespec *dest)
{
uint32_t sec_low;
#ifdef HAVE_LONG_TIME_T
uint32_t sec_high;
#endif
dest->tv_nsec = ntohl(src->tv_nsec);
sec_low = ntohl(src->tv_sec_low);
#ifdef HAVE_LONG_TIME_T
sec_high = ntohl(src->tv_sec_high);
if (sec_high == TV_NOHIGHSEC)
sec_high = 0;
dest->tv_sec = (uint64_t)sec_high << 32 | sec_low;
#else
dest->tv_sec = sec_low;
#endif
UTI_NormaliseTimespec(dest);
}
/* ================================================== */
void
UTI_TimespecHostToNetwork(struct timespec *src, Timespec *dest)
{
dest->tv_nsec = htonl(src->tv_nsec);
#ifdef HAVE_LONG_TIME_T
dest->tv_sec_high = htonl((uint64_t)src->tv_sec >> 32);
#else
dest->tv_sec_high = htonl(TV_NOHIGHSEC);
#endif
dest->tv_sec_low = htonl(src->tv_sec);
}
/* ================================================== */
#define FLOAT_EXP_BITS 7
#define FLOAT_EXP_MIN (-(1 << (FLOAT_EXP_BITS - 1)))
#define FLOAT_EXP_MAX (-FLOAT_EXP_MIN - 1)
#define FLOAT_COEF_BITS ((int)sizeof (int32_t) * 8 - FLOAT_EXP_BITS)
#define FLOAT_COEF_MIN (-(1 << (FLOAT_COEF_BITS - 1)))
#define FLOAT_COEF_MAX (-FLOAT_COEF_MIN - 1)
double
UTI_FloatNetworkToHost(Float f)
{
int32_t exp, coef;
uint32_t x;
x = ntohl(f.f);
exp = x >> FLOAT_COEF_BITS;
if (exp >= 1 << (FLOAT_EXP_BITS - 1))
exp -= 1 << FLOAT_EXP_BITS;
exp -= FLOAT_COEF_BITS;
coef = x % (1U << FLOAT_COEF_BITS);
if (coef >= 1 << (FLOAT_COEF_BITS - 1))
coef -= 1 << FLOAT_COEF_BITS;
return coef * pow(2.0, exp);
}
Float
UTI_FloatHostToNetwork(double x)
{
int32_t exp, coef, neg;
Float f;
if (x < 0.0) {
x = -x;
neg = 1;
} else if (x >= 0.0) {
neg = 0;
} else {
/* Save NaN as zero */
x = 0.0;
neg = 0;
}
if (x < 1.0e-100) {
exp = coef = 0;
} else if (x > 1.0e100) {
exp = FLOAT_EXP_MAX;
coef = FLOAT_COEF_MAX + neg;
} else {
exp = log(x) / log(2) + 1;
coef = x * pow(2.0, -exp + FLOAT_COEF_BITS) + 0.5;
assert(coef > 0);
/* we may need to shift up to two bits down */
while (coef > FLOAT_COEF_MAX + neg) {
coef >>= 1;
exp++;
}
if (exp > FLOAT_EXP_MAX) {
/* overflow */
exp = FLOAT_EXP_MAX;
coef = FLOAT_COEF_MAX + neg;
} else if (exp < FLOAT_EXP_MIN) {
/* underflow */
if (exp + FLOAT_COEF_BITS >= FLOAT_EXP_MIN) {
coef >>= FLOAT_EXP_MIN - exp;
exp = FLOAT_EXP_MIN;
} else {
exp = coef = 0;
}
}
}
/* negate back */
if (neg)
coef = (uint32_t)-coef << FLOAT_EXP_BITS >> FLOAT_EXP_BITS;
f.f = htonl((uint32_t)exp << FLOAT_COEF_BITS | coef);
return f;
}
/* ================================================== */
int
UTI_FdSetCloexec(int fd)
{
int flags;
flags = fcntl(fd, F_GETFD);
if (flags != -1) {
flags |= FD_CLOEXEC;
return !fcntl(fd, F_SETFD, flags);
}
return 0;
}
/* ================================================== */
int
UTI_SetQuitSignalsHandler(void (*handler)(int))
{
struct sigaction sa;
sa.sa_handler = handler;
sa.sa_flags = SA_RESTART;
if (sigemptyset(&sa.sa_mask) < 0)
return 0;
#ifdef SIGINT
if (sigaction(SIGINT, &sa, NULL) < 0)
return 0;
#endif
#ifdef SIGTERM
if (sigaction(SIGTERM, &sa, NULL) < 0)
return 0;
#endif
#ifdef SIGQUIT
if (sigaction(SIGQUIT, &sa, NULL) < 0)
return 0;
#endif
#ifdef SIGHUP
if (sigaction(SIGHUP, &sa, NULL) < 0)
return 0;
#endif
return 1;
}
/* ================================================== */
char *
UTI_PathToDir(const char *path)
{
char *dir, *slash;
slash = strrchr(path, '/');
if (!slash)
return Strdup(".");
if (slash == path)
return Strdup("/");
dir = Malloc(slash - path + 1);
snprintf(dir, slash - path + 1, "%s", path);
return dir;
}
/* ================================================== */
static int
create_dir(char *p, mode_t mode, uid_t uid, gid_t gid)
{
int status;
struct stat buf;
/* See if directory exists */
status = stat(p, &buf);
if (status < 0) {
if (errno != ENOENT) {
LOG(LOGS_ERR, LOGF_Util, "Could not access %s : %s", p, strerror(errno));
return 0;
}
} else {
if (S_ISDIR(buf.st_mode))
return 1;
LOG(LOGS_ERR, LOGF_Util, "%s is not directory", p);
return 0;
}
/* Create the directory */
if (mkdir(p, mode) < 0) {
LOG(LOGS_ERR, LOGF_Util, "Could not create directory %s : %s", p, strerror(errno));
return 0;
}
/* Set its owner */
if (chown(p, uid, gid) < 0) {
LOG(LOGS_ERR, LOGF_Util, "Could not change ownership of %s : %s", p, strerror(errno));
/* Don't leave it there with incorrect ownership */
rmdir(p);
return 0;
}
return 1;
}
/* ================================================== */
/* Return 0 if the directory couldn't be created, 1 if it could (or
already existed) */
int
UTI_CreateDirAndParents(const char *path, mode_t mode, uid_t uid, gid_t gid)
{
char *p;
int i, j, k, last;
/* Don't try to create current directory */
if (!strcmp(path, "."))
return 1;
p = (char *)Malloc(1 + strlen(path));
i = k = 0;
while (1) {
p[i++] = path[k++];
if (path[k] == '/' || !path[k]) {
/* Check whether its end of string, a trailing / or group of / */
last = 1;
j = k;
while (path[j]) {
if (path[j] != '/') {
/* Pick up a / into p[] thru the assignment at the top of the loop */
k = j - 1;
last = 0;
break;
}
j++;
}
p[i] = 0;
if (!create_dir(p, last ? mode : 0755, last ? uid : 0, last ? gid : 0)) {
Free(p);
return 0;
}
if (last)
break;
}
if (!path[k])
break;
}
Free(p);
return 1;
}
/* ================================================== */
int
UTI_CheckDirPermissions(const char *path, mode_t perm, uid_t uid, gid_t gid)
{
struct stat buf;
if (stat(path, &buf)) {
LOG(LOGS_ERR, LOGF_Util, "Could not access %s : %s", path, strerror(errno));
return 0;
}
if (!S_ISDIR(buf.st_mode)) {
LOG(LOGS_ERR, LOGF_Util, "%s is not directory", path);
return 0;
}
if ((buf.st_mode & 0777) & ~perm) {
LOG(LOGS_ERR, LOGF_Util, "Wrong permissions on %s", path);
return 0;
}
if (buf.st_uid != uid) {
LOG(LOGS_ERR, LOGF_Util, "Wrong owner of %s (%s != %d)", path, "UID", uid);
return 0;
}
if (buf.st_gid != gid) {
LOG(LOGS_ERR, LOGF_Util, "Wrong owner of %s (%s != %d)", path, "GID", gid);
return 0;
}
return 1;
}
/* ================================================== */
void
UTI_DropRoot(uid_t uid, gid_t gid)
{
/* Drop supplementary groups */
if (setgroups(0, NULL))
LOG_FATAL(LOGF_Util, "setgroups() failed : %s", strerror(errno));
/* Set effective, saved and real group ID */
if (setgid(gid))
LOG_FATAL(LOGF_Util, "setgid(%d) failed : %s", gid, strerror(errno));
/* Set effective, saved and real user ID */
if (setuid(uid))
LOG_FATAL(LOGF_Util, "setuid(%d) failed : %s", uid, strerror(errno));
DEBUG_LOG(LOGF_Util, "Dropped root privileges: UID %d GID %d", uid, gid);
}
/* ================================================== */
#define DEV_URANDOM "/dev/urandom"
void
UTI_GetRandomBytesUrandom(void *buf, unsigned int len)
{
static FILE *f = NULL;
if (!f)
f = fopen(DEV_URANDOM, "r");
if (!f)
LOG_FATAL(LOGF_Util, "Can't open %s : %s", DEV_URANDOM, strerror(errno));
if (fread(buf, 1, len, f) != len)
LOG_FATAL(LOGF_Util, "Can't read from %s", DEV_URANDOM);
}
/* ================================================== */
void
UTI_GetRandomBytes(void *buf, unsigned int len)
{
#ifdef HAVE_ARC4RANDOM
arc4random_buf(buf, len);
#else
UTI_GetRandomBytesUrandom(buf, len);
#endif
}