blob: 29e5fefb03c8b73f0c27feeabbfe5e5ac118378a [file] [log] [blame]
/*
* tools.c
*/
#define NETSNMP_TOOLS_C 1 /* dont re-define malloc wrappers here */
#ifdef HAVE_CRTDBG_H
/*
* Define _CRTDBG_MAP_ALLOC such that in debug builds (when _DEBUG has been
* defined) e.g. malloc() is rerouted to _malloc_dbg().
*/
#define _CRTDBG_MAP_ALLOC 1
#include <crtdbg.h>
#endif
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-features.h>
#include <ctype.h>
#include <stdio.h>
#include <sys/types.h>
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif
#ifdef cygwin
#include <windows.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_DMALLOC_H
#include <dmalloc.h>
#endif
#include <net-snmp/types.h>
#include <net-snmp/output_api.h>
#include <net-snmp/utilities.h>
#include <net-snmp/library/tools.h> /* for "internal" definitions */
#include <net-snmp/library/snmp_api.h>
#include <net-snmp/library/mib.h>
#include <net-snmp/library/scapi.h>
netsnmp_feature_child_of(tools_all, libnetsnmp)
netsnmp_feature_child_of(memory_wrappers, tools_all)
netsnmp_feature_child_of(valgrind, tools_all)
netsnmp_feature_child_of(string_time_to_secs, tools_all)
netsnmp_feature_child_of(netsnmp_check_definedness, valgrind)
netsnmp_feature_child_of(uatime_ready, netsnmp_unused)
netsnmp_feature_child_of(timeval_tticks, netsnmp_unused)
netsnmp_feature_child_of(memory_strdup, memory_wrappers)
netsnmp_feature_child_of(memory_calloc, memory_wrappers)
netsnmp_feature_child_of(memory_malloc, memory_wrappers)
netsnmp_feature_child_of(memory_realloc, memory_wrappers)
netsnmp_feature_child_of(memory_free, memory_wrappers)
#ifndef NETSNMP_FEATURE_REMOVE_MEMORY_WRAPPERS
/**
* This function is a wrapper for the strdup function.
*
* @note The strdup() implementation calls _malloc_dbg() when linking with
* MSVCRT??D.dll and malloc() when linking with MSVCRT??.dll
*/
char * netsnmp_strdup( const char * ptr)
{
return strdup(ptr);
}
#endif /* NETSNMP_FEATURE_REMOVE_MEMORY_STRDUP */
#ifndef NETSNMP_FEATURE_REMOVE_MEMORY_CALLOC
/**
* This function is a wrapper for the calloc function.
*/
void * netsnmp_calloc(size_t nmemb, size_t size)
{
return calloc(nmemb, size);
}
#endif /* NETSNMP_FEATURE_REMOVE_MEMORY_CALLOC */
#ifndef NETSNMP_FEATURE_REMOVE_MEMORY_MALLOC
/**
* This function is a wrapper for the malloc function.
*/
void * netsnmp_malloc(size_t size)
{
return malloc(size);
}
#endif /* NETSNMP_FEATURE_REMOVE_MEMORY_MALLOC */
#ifndef NETSNMP_FEATURE_REMOVE_MEMORY_REALLOC
/**
* This function is a wrapper for the realloc function.
*/
void * netsnmp_realloc( void * ptr, size_t size)
{
return realloc(ptr, size);
}
#endif /* NETSNMP_FEATURE_REMOVE_MEMORY_REALLOC */
#ifndef NETSNMP_FEATURE_REMOVE_MEMORY_FREE
/**
* This function is a wrapper for the free function.
* It calls free only if the calling parameter has a non-zero value.
*/
void netsnmp_free( void * ptr)
{
if (ptr)
free(ptr);
}
#endif /* NETSNMP_FEATURE_REMOVE_MEMORY_FREE */
/**
* This function increase the size of the buffer pointed at by *buf, which is
* initially of size *buf_len. Contents are preserved **AT THE BOTTOM END OF
* THE BUFFER**. If memory can be (re-)allocated then it returns 1, else it
* returns 0.
*
* @param buf pointer to a buffer pointer
* @param buf_len pointer to current size of buffer in bytes
*
* @note
* The current re-allocation algorithm is to increase the buffer size by
* whichever is the greater of 256 bytes or the current buffer size, up to
* a maximum increase of 8192 bytes.
*/
int
snmp_realloc(u_char ** buf, size_t * buf_len)
{
u_char *new_buf = NULL;
size_t new_buf_len = 0;
if (buf == NULL) {
return 0;
}
if (*buf_len <= 255) {
new_buf_len = *buf_len + 256;
} else if (*buf_len > 255 && *buf_len <= 8191) {
new_buf_len = *buf_len * 2;
} else if (*buf_len > 8191) {
new_buf_len = *buf_len + 8192;
}
if (*buf == NULL) {
new_buf = (u_char *) malloc(new_buf_len);
} else {
new_buf = (u_char *) realloc(*buf, new_buf_len);
}
if (new_buf != NULL) {
*buf = new_buf;
*buf_len = new_buf_len;
return 1;
} else {
return 0;
}
}
int
snmp_strcat(u_char ** buf, size_t * buf_len, size_t * out_len,
int allow_realloc, const u_char * s)
{
if (buf == NULL || buf_len == NULL || out_len == NULL) {
return 0;
}
if (s == NULL) {
/*
* Appending a NULL string always succeeds since it is a NOP.
*/
return 1;
}
while ((*out_len + strlen((const char *) s) + 1) >= *buf_len) {
if (!(allow_realloc && snmp_realloc(buf, buf_len))) {
return 0;
}
}
if (!*buf)
return 0;
strcpy((char *) (*buf + *out_len), (const char *) s);
*out_len += strlen((char *) (*buf + *out_len));
return 1;
}
/** zeros memory before freeing it.
*
* @param *buf Pointer at bytes to free.
* @param size Number of bytes in buf.
*/
void
free_zero(void *buf, size_t size)
{
if (buf) {
memset(buf, 0, size);
free(buf);
}
} /* end free_zero() */
#ifndef NETSNMP_FEATURE_REMOVE_USM_SCAPI
/**
* Returns pointer to allocaed & set buffer on success, size contains
* number of random bytes filled. buf is NULL and *size set to KMT
* error value upon failure.
*
* @param size Number of bytes to malloc() and fill with random bytes.
*
* @return a malloced buffer
*
*/
u_char *
malloc_random(size_t * size)
{
int rval = SNMPERR_SUCCESS;
u_char *buf = (u_char *) calloc(1, *size);
if (buf) {
rval = sc_random(buf, size);
if (rval < 0) {
free_zero(buf, *size);
buf = NULL;
} else {
*size = rval;
}
}
return buf;
} /* end malloc_random() */
#endif /* NETSNMP_FEATURE_REMOVE_USM_SCAPI */
/**
* Duplicates a memory block.
*
* @param[in] from Pointer to copy memory from.
* @param[in] size Size of the data to be copied.
*
* @return Pointer to the duplicated memory block, or NULL if memory allocation
* failed.
*/
void *netsnmp_memdup(const void *from, size_t size)
{
void *to = NULL;
if (from) {
to = malloc(size);
if (to)
memcpy(to, from, size);
}
return to;
} /* end netsnmp_memdup() */
#ifndef NETSNMP_FEATURE_REMOVE_NETSNMP_CHECK_DEFINEDNESS
/**
* When running under Valgrind, check whether all bytes in the range [packet,
* packet+length) are defined. Let Valgrind print a backtrace if one or more
* bytes with uninitialized values have been found. This function can help to
* find the cause of undefined value errors if --track-origins=yes is not
* sufficient. Does nothing when not running under Valgrind.
*
* Note: this requires a fairly recent valgrind.
*/
void
netsnmp_check_definedness(const void *packet, size_t length)
{
#if defined(__VALGRIND_MAJOR__) && defined(__VALGRIND_MINOR__) \
&& (__VALGRIND_MAJOR__ > 3 \
|| (__VALGRIND_MAJOR__ == 3 && __VALGRIND_MINOR__ >= 6))
if (RUNNING_ON_VALGRIND) {
int i;
char vbits;
for (i = 0; i < length; ++i) {
if (VALGRIND_GET_VBITS((const char *)packet + i, &vbits, 1) == 1
&& vbits)
VALGRIND_PRINTF_BACKTRACE("Undefined: byte %d/%d", i,
(int)length);
}
}
#endif
}
#endif /* NETSNMP_FEATURE_REMOVE_NETSNMP_CHECK_DEFINEDNESS */
/** copies a (possible) unterminated string of a given length into a
* new buffer and null terminates it as well (new buffer MAY be one
* byte longer to account for this */
char *
netsnmp_strdup_and_null(const u_char * from, size_t from_len)
{
char *ret;
if (from_len == 0 || from[from_len - 1] != '\0') {
ret = (char *)malloc(from_len + 1);
if (!ret)
return NULL;
ret[from_len] = '\0';
} else {
ret = (char *)malloc(from_len);
if (!ret)
return NULL;
ret[from_len - 1] = '\0';
}
memcpy(ret, from, from_len);
return ret;
}
/** converts binary to hexidecimal
*
* @param *input Binary data.
* @param len Length of binary data.
* @param **dest NULL terminated string equivalent in hex.
* @param *dest_len size of destination buffer
* @param allow_realloc flag indicating if buffer can be realloc'd
*
* @return olen Length of output string not including NULL terminator.
*/
u_int
netsnmp_binary_to_hex(u_char ** dest, size_t *dest_len, int allow_realloc,
const u_char * input, size_t len)
{
u_int olen = (len * 2) + 1;
u_char *s, *op;
const u_char *ip = input;
if (dest == NULL || dest_len == NULL || input == NULL)
return 0;
if (NULL == *dest) {
s = (unsigned char *) calloc(1, olen);
*dest_len = olen;
}
else
s = *dest;
if (*dest_len < olen) {
if (!allow_realloc)
return 0;
*dest_len = olen;
if (snmp_realloc(dest, dest_len))
return 0;
}
op = s;
while (ip - input < (int) len) {
*op++ = VAL2HEX((*ip >> 4) & 0xf);
*op++ = VAL2HEX(*ip & 0xf);
ip++;
}
*op = '\0';
if (s != *dest)
*dest = s;
*dest_len = olen;
return olen;
} /* end netsnmp_binary_to_hex() */
/** converts binary to hexidecimal
*
* @param *input Binary data.
* @param len Length of binary data.
* @param **output NULL terminated string equivalent in hex.
*
* @return olen Length of output string not including NULL terminator.
*
* FIX Is there already one of these in the UCD SNMP codebase?
* The old one should be used, or this one should be moved to
* snmplib/snmp_api.c.
*/
u_int
binary_to_hex(const u_char * input, size_t len, char **output)
{
size_t out_len = 0;
*output = NULL; /* will alloc new buffer */
return netsnmp_binary_to_hex((u_char**)output, &out_len, 1, input, len);
} /* end binary_to_hex() */
/**
* hex_to_binary2
* @param *input Printable data in base16.
* @param len Length in bytes of data.
* @param **output Binary data equivalent to input.
*
* @return SNMPERR_GENERR on failure, otherwise length of allocated string.
*
* Input of an odd length is right aligned.
*
* FIX Another version of "hex-to-binary" which takes odd length input
* strings. It also allocates the memory to hold the binary data.
* Should be integrated with the official hex_to_binary() function.
*/
int
hex_to_binary2(const u_char * input, size_t len, char **output)
{
u_int olen = (len / 2) + (len % 2);
char *s = calloc(1, olen ? olen : 1), *op = s;
const u_char *ip = input;
*output = NULL;
if (!s)
goto hex_to_binary2_quit;
*op = 0;
if (len % 2) {
if (!isxdigit(*ip))
goto hex_to_binary2_quit;
*op++ = HEX2VAL(*ip);
ip++;
}
while (ip < input + len) {
if (!isxdigit(*ip))
goto hex_to_binary2_quit;
*op = HEX2VAL(*ip) << 4;
ip++;
if (!isxdigit(*ip))
goto hex_to_binary2_quit;
*op++ += HEX2VAL(*ip);
ip++;
}
*output = s;
return olen;
hex_to_binary2_quit:
free_zero(s, olen);
return -1;
} /* end hex_to_binary2() */
int
snmp_decimal_to_binary(u_char ** buf, size_t * buf_len, size_t * out_len,
int allow_realloc, const char *decimal)
{
int subid = 0;
const char *cp = decimal;
if (buf == NULL || buf_len == NULL || out_len == NULL
|| decimal == NULL) {
return 0;
}
while (*cp != '\0') {
if (isspace((int) *cp) || *cp == '.') {
cp++;
continue;
}
if (!isdigit((int) *cp)) {
return 0;
}
if ((subid = atoi(cp)) > 255) {
return 0;
}
if ((*out_len >= *buf_len) &&
!(allow_realloc && snmp_realloc(buf, buf_len))) {
return 0;
}
*(*buf + *out_len) = (u_char) subid;
(*out_len)++;
while (isdigit((int) *cp)) {
cp++;
}
}
return 1;
}
/**
* convert an ASCII hex string (with specified delimiters) to binary
*
* @param buf address of a pointer (pointer to pointer) for the output buffer.
* If allow_realloc is set, the buffer may be grown via snmp_realloc
* to accomodate the data.
*
* @param buf_len pointer to a size_t containing the initial size of buf.
*
* @param offset On input, a pointer to a size_t indicating an offset into buf.
* The binary data will be stored at this offset.
* On output, this pointer will have updated the offset to be
* the first byte after the converted data.
*
* @param allow_realloc If true, the buffer can be reallocated. If false, and
* the buffer is not large enough to contain the string,
* an error will be returned.
*
* @param hex pointer to hex string to be converted. May be prefixed by
* "0x" or "0X".
*
* @param delim point to a string of allowed delimiters between bytes.
* If not specified, any non-hex characters will be an error.
*
* @retval 1 success
* @retval 0 error
*/
int
netsnmp_hex_to_binary(u_char ** buf, size_t * buf_len, size_t * offset,
int allow_realloc, const char *hex, const char *delim)
{
unsigned int subid = 0;
const char *cp = hex;
if (buf == NULL || buf_len == NULL || offset == NULL || hex == NULL) {
return 0;
}
if ((*cp == '0') && ((*(cp + 1) == 'x') || (*(cp + 1) == 'X'))) {
cp += 2;
}
while (*cp != '\0') {
if (!isxdigit((int) *cp) ||
!isxdigit((int) *(cp+1))) {
if ((NULL != delim) && (NULL != strchr(delim, *cp))) {
cp++;
continue;
}
return 0;
}
if (sscanf(cp, "%2x", &subid) == 0) {
return 0;
}
/*
* if we dont' have enough space, realloc.
* (snmp_realloc will adjust buf_len to new size)
*/
if ((*offset >= *buf_len) &&
!(allow_realloc && snmp_realloc(buf, buf_len))) {
return 0;
}
*(*buf + *offset) = (u_char) subid;
(*offset)++;
if (*++cp == '\0') {
/*
* Odd number of hex digits is an error.
*/
return 0;
} else {
cp++;
}
}
return 1;
}
/**
* convert an ASCII hex string to binary
*
* @note This is a wrapper which calls netsnmp_hex_to_binary with a
* delimiter string of " ".
*
* See netsnmp_hex_to_binary for parameter descriptions.
*
* @retval 1 success
* @retval 0 error
*/
int
snmp_hex_to_binary(u_char ** buf, size_t * buf_len, size_t * offset,
int allow_realloc, const char *hex)
{
return netsnmp_hex_to_binary(buf, buf_len, offset, allow_realloc, hex, " ");
}
/*******************************************************************-o-******
* dump_chunk
*
* Parameters:
* *title (May be NULL.)
* *buf
* size
*/
void
dump_chunk(const char *debugtoken, const char *title, const u_char * buf,
int size)
{
int printunit = 64; /* XXX Make global. */
char chunk[SNMP_MAXBUF], *s, *sp;
if (title && (*title != '\0')) {
DEBUGMSGTL((debugtoken, "%s\n", title));
}
memset(chunk, 0, SNMP_MAXBUF);
size = binary_to_hex(buf, size, &s);
sp = s;
while (size > 0) {
if (size > printunit) {
memcpy(chunk, sp, printunit);
chunk[printunit] = '\0';
DEBUGMSGTL((debugtoken, "\t%s\n", chunk));
} else {
DEBUGMSGTL((debugtoken, "\t%s\n", sp));
}
sp += printunit;
size -= printunit;
}
SNMP_FREE(s);
} /* end dump_chunk() */
/*******************************************************************-o-******
* dump_snmpEngineID
*
* Parameters:
* *estring
* *estring_len
*
* Returns:
* Allocated memory pointing to a string of buflen char representing
* a printf'able form of the snmpEngineID.
*
* -OR- NULL on error.
*
*
* Translates the snmpEngineID TC into a printable string. From RFC 2271,
* Section 5 (pp. 36-37):
*
* First bit: 0 Bit string structured by means non-SNMPv3.
* 1 Structure described by SNMPv3 SnmpEngineID TC.
*
* Bytes 1-4: Enterprise ID. (High bit of first byte is ignored.)
*
* Byte 5: 0 (RESERVED by IANA.)
* 1 IPv4 address. ( 4 octets)
* 2 IPv6 address. ( 16 octets)
* 3 MAC address. ( 6 octets)
* 4 Locally defined text. (0-27 octets)
* 5 Locally defined octets. (0-27 octets)
* 6-127 (RESERVED for enterprise.)
*
* Bytes 6-32: (Determined by byte 5.)
*
*
* Non-printable characters are given in hex. Text is given in quotes.
* IP and MAC addresses are given in standard (UN*X) conventions. Sections
* are comma separated.
*
* esp, remaining_len and s trace the state of the constructed buffer.
* s will be defined if there is something to return, and it will point
* to the end of the constructed buffer.
*
*
* ASSUME "Text" means printable characters.
*
* XXX Must the snmpEngineID always have a minimum length of 12?
* (Cf. part 2 of the TC definition.)
* XXX Does not enforce upper-bound of 32 bytes.
* XXX Need a switch to decide whether to use DNS name instead of a simple
* IP address.
*
* FIX Use something other than snprint_hexstring which doesn't add
* trailing spaces and (sometimes embedded) newlines...
*/
#ifdef NETSNMP_ENABLE_TESTING_CODE
char *
dump_snmpEngineID(const u_char * estring, size_t * estring_len)
{
#define eb(b) ( *(esp+b) & 0xff )
int rval = SNMPERR_SUCCESS, gotviolation = 0, slen = 0;
u_int remaining_len;
char buf[SNMP_MAXBUF], *s = NULL, *t;
const u_char *esp = estring;
struct in_addr iaddr;
/*
* Sanity check.
*/
if (!estring || (*estring_len <= 0)) {
QUITFUN(SNMPERR_GENERR, dump_snmpEngineID_quit);
}
remaining_len = *estring_len;
memset(buf, 0, SNMP_MAXBUF);
/*
* Test first bit. Return immediately with a hex string, or
* begin by formatting the enterprise ID.
*/
if (!(*esp & 0x80)) {
snprint_hexstring(buf, SNMP_MAXBUF, esp, remaining_len);
s = strchr(buf, '\0');
s -= 1;
goto dump_snmpEngineID_quit;
}
s = buf;
s += sprintf(s, "enterprise %d, ", ((*(esp + 0) & 0x7f) << 24) |
((*(esp + 1) & 0xff) << 16) |
((*(esp + 2) & 0xff) << 8) | ((*(esp + 3) & 0xff)));
/*
* XXX Ick.
*/
if (remaining_len < 5) { /* XXX Violating string. */
goto dump_snmpEngineID_quit;
}
esp += 4; /* Incremented one more in the switch below. */
remaining_len -= 5;
/*
* Act on the fifth byte.
*/
switch ((int) *esp++) {
case 1: /* IPv4 address. */
if (remaining_len < 4)
goto dump_snmpEngineID_violation;
memcpy(&iaddr.s_addr, esp, 4);
if (!(t = inet_ntoa(iaddr)))
goto dump_snmpEngineID_violation;
s += sprintf(s, "%s", t);
esp += 4;
remaining_len -= 4;
break;
case 2: /* IPv6 address. */
if (remaining_len < 16)
goto dump_snmpEngineID_violation;
s += sprintf(s,
"%02X%02X %02X%02X %02X%02X %02X%02X::"
"%02X%02X %02X%02X %02X%02X %02X%02X",
eb(0), eb(1), eb(2), eb(3),
eb(4), eb(5), eb(6), eb(7),
eb(8), eb(9), eb(10), eb(11),
eb(12), eb(13), eb(14), eb(15));
esp += 16;
remaining_len -= 16;
break;
case 3: /* MAC address. */
if (remaining_len < 6)
goto dump_snmpEngineID_violation;
s += sprintf(s, "%02X:%02X:%02X:%02X:%02X:%02X",
eb(0), eb(1), eb(2), eb(3), eb(4), eb(5));
esp += 6;
remaining_len -= 6;
break;
case 4: /* Text. */
s += sprintf(s, "\"%.*s\"", (int) (sizeof(buf)-strlen(buf)-3), esp);
goto dump_snmpEngineID_quit;
break;
/*NOTREACHED*/ case 5: /* Octets. */
snprint_hexstring(s, (SNMP_MAXBUF - (s-buf)),
esp, remaining_len);
s = strchr(buf, '\0');
s -= 1;
goto dump_snmpEngineID_quit;
break;
/*NOTREACHED*/ dump_snmpEngineID_violation:
case 0: /* Violation of RESERVED,
* * -OR- of expected length.
*/
gotviolation = 1;
s += sprintf(s, "!!! ");
default: /* Unknown encoding. */
if (!gotviolation) {
s += sprintf(s, "??? ");
}
snprint_hexstring(s, (SNMP_MAXBUF - (s-buf)),
esp, remaining_len);
s = strchr(buf, '\0');
s -= 1;
goto dump_snmpEngineID_quit;
} /* endswitch */
/*
* Cases 1-3 (IP and MAC addresses) should not have trailing
* octets, but perhaps they do. Throw them in too. XXX
*/
if (remaining_len > 0) {
s += sprintf(s, " (??? ");
snprint_hexstring(s, (SNMP_MAXBUF - (s-buf)),
esp, remaining_len);
s = strchr(buf, '\0');
s -= 1;
s += sprintf(s, ")");
}
dump_snmpEngineID_quit:
if (s) {
slen = s - buf + 1;
s = calloc(1, slen);
memcpy(s, buf, (slen) - 1);
}
memset(buf, 0, SNMP_MAXBUF); /* XXX -- Overkill? XXX: Yes! */
return s;
#undef eb
} /* end dump_snmpEngineID() */
#endif /* NETSNMP_ENABLE_TESTING_CODE */
/**
* Create a new real-time marker.
*
* \deprecated Use netsnmp_set_monotonic_marker() instead.
*
* @note Caller must free time marker when no longer needed.
*/
marker_t
atime_newMarker(void)
{
marker_t pm = (marker_t) calloc(1, sizeof(struct timeval));
gettimeofday((struct timeval *) pm, NULL);
return pm;
}
/**
* Set a time marker to the current value of the real-time clock.
* \deprecated Use netsnmp_set_monotonic_marker() instead.
*/
void
atime_setMarker(marker_t pm)
{
if (!pm)
return;
gettimeofday((struct timeval *) pm, NULL);
}
/**
* Query the current value of the monotonic clock.
*
* Returns the current value of a monotonic clock if such a clock is provided by
* the operating system or the wall clock time if no such clock is provided by
* the operating system. A monotonic clock is a clock that is never adjusted
* backwards and that proceeds at the same rate as wall clock time.
*
* @param[out] tv Pointer to monotonic clock time.
*/
void netsnmp_get_monotonic_clock(struct timeval* tv)
{
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
struct timespec ts;
int res;
res = clock_gettime(CLOCK_MONOTONIC, &ts);
if (res >= 0) {
tv->tv_sec = ts.tv_sec;
tv->tv_usec = ts.tv_nsec / 1000;
} else {
gettimeofday(tv, NULL);
}
#elif defined(WIN32)
/*
* Windows: return tick count. Note: the rate at which the tick count
* increases is not adjusted by the time synchronization algorithm, so
* expect an error of <= 100 ppm for the rate at which this clock
* increases.
*/
typedef ULONGLONG (WINAPI * pfGetTickCount64)(void);
static int s_initialized;
static pfGetTickCount64 s_pfGetTickCount64;
uint64_t now64;
if (!s_initialized) {
HMODULE hKernel32 = GetModuleHandle("kernel32");
s_pfGetTickCount64 =
(pfGetTickCount64) GetProcAddress(hKernel32, "GetTickCount64");
s_initialized = TRUE;
}
if (s_pfGetTickCount64) {
/* Windows Vista, Windows 2008 or any later Windows version */
now64 = (*s_pfGetTickCount64)();
} else {
/* Windows XP, Windows 2003 or any earlier Windows version */
static uint32_t s_wraps, s_last;
uint32_t now;
now = GetTickCount();
if (now < s_last)
s_wraps++;
s_last = now;
now64 = ((uint64_t)s_wraps << 32) | now;
}
tv->tv_sec = now64 / 1000;
tv->tv_usec = (now64 % 1000) * 1000;
#else
/* At least FreeBSD 4 doesn't provide monotonic clock support. */
#warning Not sure how to query a monotonically increasing clock on your system. \
Timers will not work correctly if the system clock is adjusted by e.g. ntpd.
gettimeofday(tv, NULL);
#endif
}
/**
* Set a time marker to the current value of the monotonic clock.
*/
void
netsnmp_set_monotonic_marker(marker_t *pm)
{
if (!*pm)
*pm = malloc(sizeof(struct timeval));
if (*pm)
netsnmp_get_monotonic_clock(*pm);
}
/**
* Returns the difference (in msec) between the two markers
*
* \deprecated Don't use in new code.
*/
long
atime_diff(const_marker_t first, const_marker_t second)
{
struct timeval diff;
NETSNMP_TIMERSUB((const struct timeval *) second, (const struct timeval *) first, &diff);
return (long)(diff.tv_sec * 1000 + diff.tv_usec / 1000);
}
/**
* Returns the difference (in u_long msec) between the two markers
*
* \deprecated Don't use in new code.
*/
u_long
uatime_diff(const_marker_t first, const_marker_t second)
{
struct timeval diff;
NETSNMP_TIMERSUB((const struct timeval *) second, (const struct timeval *) first, &diff);
return (((u_long) diff.tv_sec) * 1000 + diff.tv_usec / 1000);
}
/**
* Returns the difference (in u_long 1/100th secs) between the two markers
* (functionally this is what sysUpTime needs)
*
* \deprecated Don't use in new code.
*/
u_long
uatime_hdiff(const_marker_t first, const_marker_t second)
{
struct timeval diff;
NETSNMP_TIMERSUB((const struct timeval *) second, (const struct timeval *) first, &diff);
return ((u_long) diff.tv_sec) * 100 + diff.tv_usec / 10000;
}
/**
* Test: Has (marked time plus delta) exceeded current time ?
* Returns 0 if test fails or cannot be tested (no marker).
*
* \deprecated Use netsnmp_ready_monotonic() instead.
*/
int
atime_ready(const_marker_t pm, int delta_ms)
{
marker_t now;
long diff;
if (!pm)
return 0;
now = atime_newMarker();
diff = atime_diff(pm, now);
free(now);
if (diff < delta_ms)
return 0;
return 1;
}
#ifndef NETSNMP_FEATURE_REMOVE_UATIME_READY
/**
* Test: Has (marked time plus delta) exceeded current time ?
* Returns 0 if test fails or cannot be tested (no marker).
*
* \deprecated Use netsnmp_ready_monotonic() instead.
*/
int
uatime_ready(const_marker_t pm, unsigned int delta_ms)
{
marker_t now;
u_long diff;
if (!pm)
return 0;
now = atime_newMarker();
diff = uatime_diff(pm, now);
free(now);
if (diff < delta_ms)
return 0;
return 1;
}
#endif /* NETSNMP_FEATURE_REMOVE_UATIME_READY */
/**
* Is the current time past (marked time plus delta) ?
*
* @param[in] pm Pointer to marked time as obtained via
* netsnmp_set_monotonic_marker().
* @param[in] delta_ms Time delta in milliseconds.
*
* @return pm != NULL && now >= (*pm + delta_ms)
*/
int
netsnmp_ready_monotonic(const_marker_t pm, int delta_ms)
{
struct timeval now, diff, delta;
netsnmp_assert(delta_ms >= 0);
if (pm) {
netsnmp_get_monotonic_clock(&now);
NETSNMP_TIMERSUB(&now, (const struct timeval *) pm, &diff);
delta.tv_sec = delta_ms / 1000;
delta.tv_usec = (delta_ms % 1000) * 1000UL;
return timercmp(&diff, &delta, >=) ? TRUE : FALSE;
} else {
return FALSE;
}
}
/*
* Time-related utility functions
*/
/**
* Return the number of timeTicks since the given marker
*
* \deprecated Don't use in new code.
*/
int
marker_tticks(const_marker_t pm)
{
int res;
marker_t now = atime_newMarker();
res = atime_diff(pm, now);
free(now);
return res / 10; /* atime_diff works in msec, not csec */
}
#ifndef NETSNMP_FEATURE_REMOVE_TIMEVAL_TTICKS
/**
* \deprecated Don't use in new code.
*/
int
timeval_tticks(const struct timeval *tv)
{
return marker_tticks((const_marker_t) tv);
}
#endif /* NETSNMP_FEATURE_REMOVE_TIMEVAL_TTICKS */
/**
* Non Windows: Returns a pointer to the desired environment variable
* or NULL if the environment variable does not exist.
*
* Windows: Returns a pointer to the desired environment variable
* if it exists. If it does not, the variable is looked up
* in the registry in HKCU\\Net-SNMP or HKLM\\Net-SNMP
* (whichever it finds first) and stores the result in the
* environment variable. It then returns a pointer to
* environment variable.
*/
char *netsnmp_getenv(const char *name)
{
#if !defined (WIN32) && !defined (cygwin)
return (getenv(name));
#else
char *temp = NULL;
HKEY hKey;
unsigned char * key_value = NULL;
DWORD key_value_size = 0;
DWORD key_value_type = 0;
DWORD getenv_worked = 0;
DEBUGMSGTL(("read_config", "netsnmp_getenv called with name: %s\n",name));
if (!(name))
return NULL;
/* Try environment variable first */
temp = getenv(name);
if (temp) {
getenv_worked = 1;
DEBUGMSGTL(("read_config", "netsnmp_getenv will return from ENV: %s\n",temp));
}
/* Next try HKCU */
if (temp == NULL)
{
if (getenv("SNMP_IGNORE_WINDOWS_REGISTRY"))
return NULL;
if (RegOpenKeyExA(
HKEY_CURRENT_USER,
"SOFTWARE\\Net-SNMP",
0,
KEY_QUERY_VALUE,
&hKey) == ERROR_SUCCESS) {
if (RegQueryValueExA(
hKey,
name,
NULL,
&key_value_type,
NULL, /* Just get the size */
&key_value_size) == ERROR_SUCCESS) {
SNMP_FREE(key_value);
/* Allocate memory needed +1 to allow RegQueryValueExA to NULL terminate the
* string data in registry is missing one (which is unlikely).
*/
key_value = malloc((sizeof(char) * key_value_size)+sizeof(char));
if (RegQueryValueExA(
hKey,
name,
NULL,
&key_value_type,
key_value,
&key_value_size) == ERROR_SUCCESS) {
}
temp = (char *) key_value;
}
RegCloseKey(hKey);
if (temp)
DEBUGMSGTL(("read_config", "netsnmp_getenv will return from HKCU: %s\n",temp));
}
}
/* Next try HKLM */
if (temp == NULL)
{
if (RegOpenKeyExA(
HKEY_LOCAL_MACHINE,
"SOFTWARE\\Net-SNMP",
0,
KEY_QUERY_VALUE,
&hKey) == ERROR_SUCCESS) {
if (RegQueryValueExA(
hKey,
name,
NULL,
&key_value_type,
NULL, /* Just get the size */
&key_value_size) == ERROR_SUCCESS) {
SNMP_FREE(key_value);
/* Allocate memory needed +1 to allow RegQueryValueExA to NULL terminate the
* string data in registry is missing one (which is unlikely).
*/
key_value = malloc((sizeof(char) * key_value_size)+sizeof(char));
if (RegQueryValueExA(
hKey,
name,
NULL,
&key_value_type,
key_value,
&key_value_size) == ERROR_SUCCESS) {
}
temp = (char *) key_value;
}
RegCloseKey(hKey);
if (temp)
DEBUGMSGTL(("read_config", "netsnmp_getenv will return from HKLM: %s\n",temp));
}
}
if (temp && !getenv_worked) {
setenv(name, temp, 1);
SNMP_FREE(temp);
}
DEBUGMSGTL(("read_config", "netsnmp_getenv returning: %s\n",getenv(name)));
return(getenv(name));
#endif
}
/**
* Set an environment variable.
*
* This function is only necessary on Windows for the MSVC and MinGW
* environments. If the process that uses the Net-SNMP DLL (e.g. a Perl
* interpreter) and the Net-SNMP have been built with a different compiler
* version then each will have a separate set of environment variables.
* This function allows to set an environment variable such that it gets
* noticed by the Net-SNMP DLL.
*/
int netsnmp_setenv(const char *envname, const char *envval, int overwrite)
{
return setenv(envname, envval, overwrite);
}
/*
* swap the order of an inet addr string
*/
int
netsnmp_addrstr_hton(char *ptr, size_t len)
{
#ifndef WORDS_BIGENDIAN
char tmp[8];
if (8 == len) {
tmp[0] = ptr[6];
tmp[1] = ptr[7];
tmp[2] = ptr[4];
tmp[3] = ptr[5];
tmp[4] = ptr[2];
tmp[5] = ptr[3];
tmp[6] = ptr[0];
tmp[7] = ptr[1];
memcpy (ptr, &tmp, 8);
}
else if (32 == len) {
netsnmp_addrstr_hton(ptr , 8);
netsnmp_addrstr_hton(ptr+8 , 8);
netsnmp_addrstr_hton(ptr+16, 8);
netsnmp_addrstr_hton(ptr+24, 8);
}
else
return -1;
#endif
return 0;
}
#ifndef NETSNMP_FEATURE_REMOVE_STRING_TIME_TO_SECS
/**
* Takes a time string like 4h and converts it to seconds.
* The string time given may end in 's' for seconds (the default
* anyway if no suffix is specified),
* 'm' for minutes, 'h' for hours, 'd' for days, or 'w' for weeks. The
* upper case versions are also accepted.
*
* @param time_string The time string to convert.
*
* @return seconds converted from the string
* @return -1 : on failure
*/
int
netsnmp_string_time_to_secs(const char *time_string) {
int secs = -1;
if (!time_string || !time_string[0])
return secs;
secs = atoi(time_string);
if (isdigit((unsigned char)time_string[strlen(time_string)-1]))
return secs; /* no letter specified, it's already in seconds */
switch (time_string[strlen(time_string)-1]) {
case 's':
case 'S':
/* already in seconds */
break;
case 'm':
case 'M':
secs = secs * 60;
break;
case 'h':
case 'H':
secs = secs * 60 * 60;
break;
case 'd':
case 'D':
secs = secs * 60 * 60 * 24;
break;
case 'w':
case 'W':
secs = secs * 60 * 60 * 24 * 7;
break;
default:
snmp_log(LOG_ERR, "time string %s contains an invalid suffix letter\n",
time_string);
return -1;
}
DEBUGMSGTL(("string_time_to_secs", "Converted time string %s to %d\n",
time_string, secs));
return secs;
}
#endif /* NETSNMP_FEATURE_REMOVE_STRING_TIME_TO_SECS */