/* * tools.c * * Portions of this file are copyrighted by: * Copyright (c) 2016 VMware, Inc. All rights reserved. * Use is subject to license terms specified in the COPYING file * distributed with the Net-SNMP package. */ #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 #endif #include #include #include #include #include #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #if HAVE_STRING_H #include #else #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifdef HAVE_VALGRIND_MEMCHECK_H #include #endif #if defined(cygwin) || defined(mingw32) #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_DMALLOC_H #include #endif #include #include #include #include /* for "internal" definitions */ #include #include #include 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() */ /** * Duplicates a memory block, adding a NULL at the end. * * NOTE: the returned size DOES NOT include the extra byte for the NULL * termination, just the raw data (i.e. from_size). * * This is mainly to protect agains code that uses str* functions on * a fixed buffer that may not have a terminating NULL. * * @param[in] from Pointer to copy memory from. * @param[in] from_size Size of the data to be copied. * @param[out] new_size Pointer to size var for new block (OPTIONAL) * * @return Pointer to the duplicated memory block, or NULL if memory allocation * failed. */ void *netsnmp_memdup_nt(const void *from, size_t from_size, size_t *to_size) { char *to = NULL; if (from) { to = malloc(from_size+1); if (to) { memcpy(to, from, from_size); to[from_size] = 0; if (to_size) *to_size = from_size; } } return to; } /* end netsnmp_memdupNT() */ #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') from_len--; ret = malloc(from_len + 1); if (ret) { memcpy(ret, from, from_len); ret[from_len] = '\0'; } 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 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)) { goto 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, "!!! "); /* FALLTHROUGH */ 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 */