For sources specified by an IP address, keep the original address as the source's name and pass it to the NCR instance. Allow the sources to go through the replacement process if their address has changed. This will be useful with NTS-KE negotiation. The IP-based source names are now provided via cmdmon. This means chronyc -n and -N can show two different addresses for a source.
1495 lines
32 KiB
C
1495 lines
32 KiB
C
/*
|
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
|
|
|
**********************************************************************
|
|
* Copyright (C) Richard P. Curnow 1997-2003
|
|
* Copyright (C) Miroslav Lichvar 2009, 2012-2020
|
|
*
|
|
* 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(const struct timeval *tv, struct timespec *ts)
|
|
{
|
|
ts->tv_sec = tv->tv_sec;
|
|
ts->tv_nsec = 1000 * tv->tv_usec;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
void
|
|
UTI_TimespecToTimeval(const struct timespec *ts, struct timeval *tv)
|
|
{
|
|
tv->tv_sec = ts->tv_sec;
|
|
tv->tv_usec = ts->tv_nsec / 1000;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
double
|
|
UTI_TimespecToDouble(const 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(const struct timeval *tv)
|
|
{
|
|
return tv->tv_sec + 1.0e-6 * tv->tv_usec;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
void
|
|
UTI_DoubleToTimeval(double a, struct timeval *b)
|
|
{
|
|
double frac_part;
|
|
|
|
b->tv_sec = a;
|
|
frac_part = 1.0e6 * (a - b->tv_sec);
|
|
b->tv_usec = frac_part > 0 ? frac_part + 0.5 : frac_part - 0.5;
|
|
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(const struct timespec *a, const 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, const struct timespec *a, const 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(const struct timespec *a, const struct timespec *b)
|
|
{
|
|
return ((double)a->tv_sec - (double)b->tv_sec) + 1.0e-9 * (a->tv_nsec - b->tv_nsec);
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
void
|
|
UTI_AddDoubleToTimespec(const 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(const struct timespec *earlier, const struct timespec *later,
|
|
struct timespec *average, double *diff)
|
|
{
|
|
*diff = UTI_DiffTimespecsToDouble(later, earlier);
|
|
UTI_AddDoubleToTimespec(earlier, *diff / 2.0, average);
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
void
|
|
UTI_AddDiffToTimespec(const struct timespec *a, const struct timespec *b,
|
|
const 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(const 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(const 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(const IPAddr *addr)
|
|
{
|
|
unsigned long a, b, c, d, ip;
|
|
const 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, "%lu.%lu.%lu.%lu", a, b, c, d);
|
|
break;
|
|
case IPADDR_INET6:
|
|
ip6 = addr->addr.in6;
|
|
#ifdef FEAT_IPV6
|
|
inet_ntop(AF_INET6, ip6, result, BUFFER_LENGTH);
|
|
#else
|
|
assert(BUFFER_LENGTH >= 40);
|
|
for (a = 0; a < 8; a++)
|
|
snprintf(result + a * 5, 40 - a * 5, "%04x:",
|
|
(unsigned int)(ip6[2 * a] << 8 | ip6[2 * a + 1]));
|
|
#endif
|
|
break;
|
|
case IPADDR_ID:
|
|
snprintf(result, BUFFER_LENGTH, "ID#%010"PRIu32, addr->addr.id);
|
|
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;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
int
|
|
UTI_IsStringIP(const char *string)
|
|
{
|
|
IPAddr ip;
|
|
|
|
return UTI_StringToIP(string, &ip);
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
int
|
|
UTI_StringToIdIP(const char *addr, IPAddr *ip)
|
|
{
|
|
if (sscanf(addr, "ID#%"SCNu32, &ip->addr.id) == 1) {
|
|
ip->family = IPADDR_ID;
|
|
ip->_pad = 0;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
int
|
|
UTI_IsIPReal(const IPAddr *ip)
|
|
{
|
|
switch (ip->family) {
|
|
case IPADDR_INET4:
|
|
case IPADDR_INET6:
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
uint32_t
|
|
UTI_IPToRefid(const 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(HSH_MD5);
|
|
|
|
if (MD5_hash < 0 ||
|
|
HSH_Hash(MD5_hash, (const unsigned char *)ip->addr.in6, sizeof (ip->addr.in6),
|
|
NULL, 0, buf, sizeof (buf)) != sizeof (buf))
|
|
LOG_FATAL("Could not get MD5");
|
|
|
|
return (uint32_t)buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
uint32_t
|
|
UTI_IPToHash(const IPAddr *ip)
|
|
{
|
|
static uint32_t seed = 0;
|
|
const 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;
|
|
case IPADDR_ID:
|
|
addr = (unsigned char *)&ip->addr.id;
|
|
len = sizeof (ip->addr.id);
|
|
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(const 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;
|
|
case IPADDR_ID:
|
|
dest->addr.id = htonl(src->addr.id);
|
|
break;
|
|
default:
|
|
dest->family = htons(IPADDR_UNSPEC);
|
|
}
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
void
|
|
UTI_IPNetworkToHost(const 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;
|
|
case IPADDR_ID:
|
|
dest->addr.id = ntohl(src->addr.id);
|
|
break;
|
|
default:
|
|
dest->family = IPADDR_UNSPEC;
|
|
}
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
int
|
|
UTI_CompareIPs(const IPAddr *a, const IPAddr *b, const 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;
|
|
case IPADDR_ID:
|
|
return a->addr.id - b->addr.id;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
char *
|
|
UTI_IPSockAddrToString(const IPSockAddr *sa)
|
|
{
|
|
char *result;
|
|
|
|
result = NEXT_BUFFER;
|
|
snprintf(result, BUFFER_LENGTH,
|
|
sa->ip_addr.family != IPADDR_INET6 ? "%s:%hu" : "[%s]:%hu",
|
|
UTI_IPToString(&sa->ip_addr), sa->port);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
char *
|
|
UTI_TimeToLogForm(time_t t)
|
|
{
|
|
struct tm *stm;
|
|
char *result;
|
|
|
|
result = NEXT_BUFFER;
|
|
|
|
stm = gmtime(&t);
|
|
|
|
if (stm)
|
|
strftime(result, BUFFER_LENGTH, "%Y-%m-%d %H:%M:%S", stm);
|
|
else
|
|
snprintf(result, BUFFER_LENGTH, "INVALID INVALID ");
|
|
|
|
return result;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
void
|
|
UTI_AdjustTimespec(const struct timespec *old_ts, const 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);
|
|
assert(sizeof (*ts) == 8);
|
|
|
|
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(const NTP_int64 *ts)
|
|
{
|
|
return !ts->hi && !ts->lo;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
int
|
|
UTI_CompareNtp64(const NTP_int64 *a, const 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;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
int
|
|
UTI_IsEqualAnyNtp64(const NTP_int64 *a, const NTP_int64 *b1, const NTP_int64 *b2,
|
|
const NTP_int64 *b3)
|
|
{
|
|
if (b1 && a->lo == b1->lo && a->hi == b1->hi)
|
|
return 1;
|
|
|
|
if (b2 && a->lo == b2->lo && a->hi == b2->hi)
|
|
return 1;
|
|
|
|
if (b3 && a->lo == b3->lo && a->hi == b3->hi)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
/* 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(const struct timespec *src, NTP_int64 *dest, const 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(const 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;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
/* 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(const 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(const Timespec *src, struct timespec *dest)
|
|
{
|
|
uint32_t sec_low, nsec;
|
|
#ifdef HAVE_LONG_TIME_T
|
|
uint32_t sec_high;
|
|
#endif
|
|
|
|
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
|
|
|
|
nsec = ntohl(src->tv_nsec);
|
|
dest->tv_nsec = MIN(nsec, 999999999U);
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
void
|
|
UTI_TimespecHostToNetwork(const 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;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
CMC_Algorithm
|
|
UTI_CmacNameToAlgorithm(const char *name)
|
|
{
|
|
if (strcmp(name, "AES128") == 0)
|
|
return CMC_AES128;
|
|
else if (strcmp(name, "AES256") == 0)
|
|
return CMC_AES256;
|
|
return CMC_INVALID;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
HSH_Algorithm
|
|
UTI_HashNameToAlgorithm(const char *name)
|
|
{
|
|
if (strcmp(name, "MD5") == 0)
|
|
return HSH_MD5;
|
|
else if (strcmp(name, "SHA1") == 0)
|
|
return HSH_SHA1;
|
|
else if (strcmp(name, "SHA256") == 0)
|
|
return HSH_SHA256;
|
|
else if (strcmp(name, "SHA384") == 0)
|
|
return HSH_SHA384;
|
|
else if (strcmp(name, "SHA512") == 0)
|
|
return HSH_SHA512;
|
|
else if (strcmp(name, "SHA3-224") == 0)
|
|
return HSH_SHA3_224;
|
|
else if (strcmp(name, "SHA3-256") == 0)
|
|
return HSH_SHA3_256;
|
|
else if (strcmp(name, "SHA3-384") == 0)
|
|
return HSH_SHA3_384;
|
|
else if (strcmp(name, "SHA3-512") == 0)
|
|
return HSH_SHA3_512;
|
|
else if (strcmp(name, "TIGER") == 0)
|
|
return HSH_TIGER;
|
|
else if (strcmp(name, "WHIRLPOOL") == 0)
|
|
return HSH_WHIRLPOOL;
|
|
return HSH_INVALID;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
int
|
|
UTI_FdSetCloexec(int fd)
|
|
{
|
|
int flags;
|
|
|
|
flags = fcntl(fd, F_GETFD);
|
|
if (flags == -1) {
|
|
DEBUG_LOG("fcntl() failed : %s", strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
flags |= FD_CLOEXEC;
|
|
|
|
if (fcntl(fd, F_SETFD, flags) < 0) {
|
|
DEBUG_LOG("fcntl() failed : %s", strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
void
|
|
UTI_SetQuitSignalsHandler(void (*handler)(int), int ignore_sigpipe)
|
|
{
|
|
struct sigaction sa;
|
|
|
|
sa.sa_handler = handler;
|
|
sa.sa_flags = SA_RESTART;
|
|
if (sigemptyset(&sa.sa_mask) < 0)
|
|
LOG_FATAL("sigemptyset() failed");
|
|
|
|
#ifdef SIGINT
|
|
if (sigaction(SIGINT, &sa, NULL) < 0)
|
|
LOG_FATAL("sigaction(%d) failed", SIGINT);
|
|
#endif
|
|
#ifdef SIGTERM
|
|
if (sigaction(SIGTERM, &sa, NULL) < 0)
|
|
LOG_FATAL("sigaction(%d) failed", SIGTERM);
|
|
#endif
|
|
#ifdef SIGQUIT
|
|
if (sigaction(SIGQUIT, &sa, NULL) < 0)
|
|
LOG_FATAL("sigaction(%d) failed", SIGQUIT);
|
|
#endif
|
|
#ifdef SIGHUP
|
|
if (sigaction(SIGHUP, &sa, NULL) < 0)
|
|
LOG_FATAL("sigaction(%d) failed", SIGHUP);
|
|
#endif
|
|
|
|
if (ignore_sigpipe)
|
|
sa.sa_handler = SIG_IGN;
|
|
|
|
if (sigaction(SIGPIPE, &sa, NULL) < 0)
|
|
LOG_FATAL("sigaction(%d) failed", SIGPIPE);
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
char *
|
|
UTI_PathToDir(const char *path)
|
|
{
|
|
char *dir, *slash;
|
|
size_t dir_len;
|
|
|
|
slash = strrchr(path, '/');
|
|
|
|
if (!slash)
|
|
return Strdup(".");
|
|
|
|
if (slash == path)
|
|
return Strdup("/");
|
|
|
|
dir_len = slash - path;
|
|
|
|
dir = Malloc(dir_len + 1);
|
|
memcpy(dir, path, dir_len);
|
|
dir[dir_len] = '\0';
|
|
|
|
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, "Could not access %s : %s", p, strerror(errno));
|
|
return 0;
|
|
}
|
|
} else {
|
|
if (S_ISDIR(buf.st_mode))
|
|
return 1;
|
|
LOG(LOGS_ERR, "%s is not directory", p);
|
|
return 0;
|
|
}
|
|
|
|
/* Create the directory */
|
|
if (mkdir(p, mode) < 0) {
|
|
LOG(LOGS_ERR, "Could not create directory %s : %s", p, strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
/* Set its owner */
|
|
if (chown(p, uid, gid) < 0) {
|
|
LOG(LOGS_ERR, "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, "Could not access %s : %s", path, strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
if (!S_ISDIR(buf.st_mode)) {
|
|
LOG(LOGS_ERR, "%s is not directory", path);
|
|
return 0;
|
|
}
|
|
|
|
if ((buf.st_mode & 0777) & ~perm) {
|
|
LOG(LOGS_ERR, "Wrong permissions on %s", path);
|
|
return 0;
|
|
}
|
|
|
|
if (buf.st_uid != uid) {
|
|
LOG(LOGS_ERR, "Wrong owner of %s (%s != %u)", path, "UID", uid);
|
|
return 0;
|
|
}
|
|
|
|
if (buf.st_gid != gid) {
|
|
LOG(LOGS_ERR, "Wrong owner of %s (%s != %u)", path, "GID", gid);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
static int
|
|
join_path(const char *basedir, const char *name, const char *suffix,
|
|
char *buffer, size_t length, LOG_Severity severity)
|
|
{
|
|
const char *sep;
|
|
|
|
if (!basedir) {
|
|
basedir = "";
|
|
sep = "";
|
|
} else {
|
|
sep = "/";
|
|
}
|
|
|
|
if (!suffix)
|
|
suffix = "";
|
|
|
|
if (snprintf(buffer, length, "%s%s%s%s", basedir, sep, name, suffix) >= length) {
|
|
LOG(severity, "File path %s%s%s%s too long", basedir, sep, name, suffix);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
FILE *
|
|
UTI_OpenFile(const char *basedir, const char *name, const char *suffix,
|
|
char mode, mode_t perm)
|
|
{
|
|
const char *file_mode;
|
|
char path[PATH_MAX];
|
|
LOG_Severity severity;
|
|
int fd, flags;
|
|
FILE *file;
|
|
|
|
severity = mode >= 'A' && mode <= 'Z' ? LOGS_FATAL : LOGS_ERR;
|
|
|
|
if (!join_path(basedir, name, suffix, path, sizeof (path), severity))
|
|
return NULL;
|
|
|
|
switch (mode) {
|
|
case 'r':
|
|
case 'R':
|
|
flags = O_RDONLY;
|
|
file_mode = "r";
|
|
if (severity != LOGS_FATAL)
|
|
severity = LOGS_DEBUG;
|
|
break;
|
|
case 'w':
|
|
case 'W':
|
|
flags = O_WRONLY | O_CREAT | O_EXCL;
|
|
file_mode = "w";
|
|
break;
|
|
case 'a':
|
|
case 'A':
|
|
flags = O_WRONLY | O_CREAT | O_APPEND | O_NOFOLLOW;
|
|
file_mode = "a";
|
|
break;
|
|
default:
|
|
assert(0);
|
|
return NULL;
|
|
}
|
|
|
|
try_again:
|
|
fd = open(path, flags, perm);
|
|
if (fd < 0) {
|
|
if (errno == EEXIST) {
|
|
if (unlink(path) < 0) {
|
|
LOG(severity, "Could not remove %s : %s", path, strerror(errno));
|
|
return NULL;
|
|
}
|
|
DEBUG_LOG("Removed %s", path);
|
|
goto try_again;
|
|
}
|
|
LOG(severity, "Could not open %s : %s", path, strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
UTI_FdSetCloexec(fd);
|
|
|
|
file = fdopen(fd, file_mode);
|
|
if (!file) {
|
|
LOG(severity, "Could not open %s : %s", path, strerror(errno));
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
DEBUG_LOG("Opened %s fd=%d mode=%c", path, fd, mode);
|
|
|
|
return file;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
int
|
|
UTI_RenameTempFile(const char *basedir, const char *name,
|
|
const char *old_suffix, const char *new_suffix)
|
|
{
|
|
char old_path[PATH_MAX], new_path[PATH_MAX];
|
|
|
|
if (!join_path(basedir, name, old_suffix, old_path, sizeof (old_path), LOGS_ERR))
|
|
return 0;
|
|
|
|
if (!join_path(basedir, name, new_suffix, new_path, sizeof (new_path), LOGS_ERR))
|
|
goto error;
|
|
|
|
if (rename(old_path, new_path) < 0) {
|
|
LOG(LOGS_ERR, "Could not replace %s with %s : %s", new_path, old_path, strerror(errno));
|
|
goto error;
|
|
}
|
|
|
|
DEBUG_LOG("Renamed %s to %s", old_path, new_path);
|
|
|
|
return 1;
|
|
|
|
error:
|
|
if (unlink(old_path) < 0)
|
|
LOG(LOGS_ERR, "Could not remove %s : %s", old_path, strerror(errno));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
int
|
|
UTI_RemoveFile(const char *basedir, const char *name, const char *suffix)
|
|
{
|
|
char path[PATH_MAX];
|
|
struct stat buf;
|
|
|
|
if (!join_path(basedir, name, suffix, path, sizeof (path), LOGS_ERR))
|
|
return 0;
|
|
|
|
/* Avoid logging an error message if the file is not accessible */
|
|
if (stat(path, &buf) < 0) {
|
|
DEBUG_LOG("Could not remove %s : %s", path, strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
if (unlink(path) < 0) {
|
|
LOG(LOGS_ERR, "Could not remove %s : %s", path, strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
DEBUG_LOG("Removed %s", path);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
void
|
|
UTI_DropRoot(uid_t uid, gid_t gid)
|
|
{
|
|
/* Drop supplementary groups */
|
|
if (setgroups(0, NULL))
|
|
LOG_FATAL("setgroups() failed : %s", strerror(errno));
|
|
|
|
/* Set effective, saved and real group ID */
|
|
if (setgid(gid))
|
|
LOG_FATAL("setgid(%u) failed : %s", gid, strerror(errno));
|
|
|
|
/* Set effective, saved and real user ID */
|
|
if (setuid(uid))
|
|
LOG_FATAL("setuid(%u) failed : %s", uid, strerror(errno));
|
|
|
|
DEBUG_LOG("Dropped root privileges: UID %u GID %u", uid, gid);
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
#define DEV_URANDOM "/dev/urandom"
|
|
|
|
void
|
|
UTI_GetRandomBytesUrandom(void *buf, unsigned int len)
|
|
{
|
|
static FILE *f = NULL;
|
|
|
|
if (!f)
|
|
f = UTI_OpenFile(NULL, DEV_URANDOM, NULL, 'R', 0);
|
|
if (fread(buf, 1, len, f) != len)
|
|
LOG_FATAL("Can't read from %s", DEV_URANDOM);
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
#ifdef HAVE_GETRANDOM
|
|
static void
|
|
get_random_bytes_getrandom(char *buf, unsigned int len)
|
|
{
|
|
static char rand_buf[256];
|
|
static unsigned int available = 0, disabled = 0;
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (!available) {
|
|
if (disabled)
|
|
break;
|
|
|
|
if (getrandom(rand_buf, sizeof (rand_buf), GRND_NONBLOCK) != sizeof (rand_buf)) {
|
|
disabled = 1;
|
|
break;
|
|
}
|
|
|
|
available = sizeof (rand_buf);
|
|
}
|
|
|
|
buf[i] = rand_buf[--available];
|
|
}
|
|
|
|
if (i < len)
|
|
UTI_GetRandomBytesUrandom(buf, len);
|
|
}
|
|
#endif
|
|
|
|
/* ================================================== */
|
|
|
|
void
|
|
UTI_GetRandomBytes(void *buf, unsigned int len)
|
|
{
|
|
#ifdef HAVE_ARC4RANDOM
|
|
arc4random_buf(buf, len);
|
|
#elif defined(HAVE_GETRANDOM)
|
|
get_random_bytes_getrandom(buf, len);
|
|
#else
|
|
UTI_GetRandomBytesUrandom(buf, len);
|
|
#endif
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
int
|
|
UTI_BytesToHex(const void *buf, unsigned int buf_len, char *hex, unsigned int hex_len)
|
|
{
|
|
unsigned int i, l;
|
|
|
|
if (hex_len < 1)
|
|
return 0;
|
|
|
|
hex[0] = '\0';
|
|
|
|
for (i = l = 0; i < buf_len; i++, l += 2) {
|
|
if (l + 2 >= hex_len ||
|
|
snprintf(hex + l, hex_len - l, "%02hhX", ((const char *)buf)[i]) != 2)
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
unsigned int
|
|
UTI_HexToBytes(const char *hex, void *buf, unsigned int len)
|
|
{
|
|
char *p, byte[3];
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < len && *hex != '\0'; i++) {
|
|
byte[0] = *hex++;
|
|
if (*hex == '\0')
|
|
return 0;
|
|
byte[1] = *hex++;
|
|
byte[2] = '\0';
|
|
((char *)buf)[i] = strtol(byte, &p, 16);
|
|
|
|
if (p != byte + 2)
|
|
return 0;
|
|
}
|
|
|
|
return *hex == '\0' ? i : 0;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
int
|
|
UTI_SplitString(char *string, char **words, int max_saved_words)
|
|
{
|
|
char *s = string;
|
|
int i;
|
|
|
|
for (i = 0; i < max_saved_words; i++)
|
|
words[i] = NULL;
|
|
|
|
for (i = 0; ; i++) {
|
|
/* Zero white-space characters before the word */
|
|
while (*s != '\0' && isspace((unsigned char)*s))
|
|
*s++ = '\0';
|
|
|
|
if (*s == '\0')
|
|
break;
|
|
|
|
if (i < max_saved_words)
|
|
words[i] = s;
|
|
|
|
/* Find the next word */
|
|
while (*s != '\0' && !isspace((unsigned char)*s))
|
|
s++;
|
|
}
|
|
|
|
return i;
|
|
}
|