summaryrefslogtreecommitdiff
path: root/snmplib/tools.c
diff options
context:
space:
mode:
Diffstat (limited to 'snmplib/tools.c')
-rw-r--r--snmplib/tools.c1376
1 files changed, 1376 insertions, 0 deletions
diff --git a/snmplib/tools.c b/snmplib/tools.c
new file mode 100644
index 0000000..8e4852a
--- /dev/null
+++ b/snmplib/tools.c
@@ -0,0 +1,1376 @@
+/*
+ * 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;
+ }
+ }
+
+ 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.
+ * Copies a existing memory location from a pointer to another, newly
+ malloced, pointer.
+
+ * @param to Pointer to allocate and copy memory to.
+ * @param from Pointer to copy memory from.
+ * @param size Size of the data to be copied.
+ *
+ * @return SNMPERR_SUCCESS on success, SNMPERR_GENERR on failure.
+ */
+int
+memdup(u_char ** to, const void * from, size_t size)
+{
+ if (to == NULL)
+ return SNMPERR_GENERR;
+ if (from == NULL) {
+ *to = NULL;
+ return SNMPERR_SUCCESS;
+ }
+ if ((*to = (u_char *) malloc(size)) == NULL)
+ return SNMPERR_GENERR;
+ memcpy(*to, from, size);
+ return SNMPERR_SUCCESS;
+
+} /* end 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 = (char *) calloc(1, (olen) ? olen : 1), *op = s;
+ const u_char *ip = input;
+
+
+ *output = NULL;
+ *op = 0;
+ if (len % 2) {
+ if (!isxdigit(*ip))
+ goto hex_to_binary2_quit;
+ *op++ = HEX2VAL(*ip);
+ ip++;
+ }
+
+ while (ip - input < (int) 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 {
+ netsnmp_assert(FALSE);
+ memset(tv, 0, sizeof(*tv));
+ }
+#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 */