1 /*
2  * tools.c
3  *
4  * Portions of this file are copyrighted by:
5  * Copyright (c) 2016 VMware, Inc. All rights reserved.
6  * Use is subject to license terms specified in the COPYING file
7  * distributed with the Net-SNMP package.
8  */
9 
10 #define NETSNMP_TOOLS_C 1 /* dont re-define malloc wrappers here */
11 
12 #ifdef HAVE_CRTDBG_H
13 /*
14  * Define _CRTDBG_MAP_ALLOC such that in debug builds (when _DEBUG has been
15  * defined) e.g. malloc() is rerouted to _malloc_dbg().
16  */
17 #define _CRTDBG_MAP_ALLOC 1
18 #include <crtdbg.h>
19 #endif
20 
21 #include <net-snmp/net-snmp-config.h>
22 #include <net-snmp/net-snmp-features.h>
23 
24 #include <ctype.h>
25 #ifdef HAVE_INTTYPES_H
26 #include <inttypes.h>
27 #endif
28 #include <stdio.h>
29 #include <sys/types.h>
30 #if TIME_WITH_SYS_TIME
31 # include <sys/time.h>
32 # include <time.h>
33 #else
34 # if HAVE_SYS_TIME_H
35 #  include <sys/time.h>
36 # else
37 #  include <time.h>
38 # endif
39 #endif
40 #ifdef HAVE_SYS_SOCKET_H
41 #include <sys/socket.h>
42 #endif
43 #ifdef HAVE_SYS_TIME_H
44 #include <sys/time.h>
45 #endif
46 #ifdef HAVE_STDLIB_H
47 #include <stdlib.h>
48 #endif
49 #if HAVE_STRING_H
50 #include <string.h>
51 #else
52 #include <strings.h>
53 #endif
54 #ifdef HAVE_NETINET_IN_H
55 #include <netinet/in.h>
56 #endif
57 #ifdef HAVE_ARPA_INET_H
58 #include <arpa/inet.h>
59 #endif
60 #ifdef HAVE_VALGRIND_MEMCHECK_H
61 #include <valgrind/memcheck.h>
62 #endif
63 #if defined(cygwin) || defined(mingw32)
64 #include <windows.h>
65 #endif
66 
67 #if HAVE_UNISTD_H
68 #include <unistd.h>
69 #endif
70 
71 #include <net-snmp/types.h>
72 #include <net-snmp/output_api.h>
73 #include <net-snmp/utilities.h>
74 #include <net-snmp/library/tools.h>     /* for "internal" definitions */
75 
76 #include <net-snmp/library/snmp_api.h>
77 #include <net-snmp/library/mib.h>
78 #include <net-snmp/library/scapi.h>
79 
80 netsnmp_feature_child_of(tools_all, libnetsnmp);
81 
82 netsnmp_feature_child_of(memory_wrappers, tools_all);
83 netsnmp_feature_child_of(valgrind, tools_all);
84 netsnmp_feature_child_of(string_time_to_secs, tools_all);
85 netsnmp_feature_child_of(netsnmp_check_definedness, valgrind);
86 
87 netsnmp_feature_child_of(uatime_ready, netsnmp_unused);
88 netsnmp_feature_child_of(timeval_tticks, netsnmp_unused);
89 
90 netsnmp_feature_child_of(memory_strdup, memory_wrappers);
91 netsnmp_feature_child_of(memory_calloc, memory_wrappers);
92 netsnmp_feature_child_of(memory_malloc, memory_wrappers);
93 netsnmp_feature_child_of(memory_realloc, memory_wrappers);
94 netsnmp_feature_child_of(memory_free, memory_wrappers);
95 
96 #ifndef NETSNMP_FEATURE_REMOVE_MEMORY_STRDUP
97 /**
98  * This function is a wrapper for the strdup function.
99  *
100  * @note The strdup() implementation calls _malloc_dbg() when linking with
101  * MSVCRT??D.dll and malloc() when linking with MSVCRT??.dll
102  */
netsnmp_strdup(const char * ptr)103 char * netsnmp_strdup( const char * ptr)
104 {
105     return strdup(ptr);
106 }
107 #endif /* NETSNMP_FEATURE_REMOVE_MEMORY_STRDUP */
108 #ifndef NETSNMP_FEATURE_REMOVE_MEMORY_CALLOC
109 /**
110  * This function is a wrapper for the calloc function.
111  */
netsnmp_calloc(size_t nmemb,size_t size)112 void * netsnmp_calloc(size_t nmemb, size_t size)
113 {
114     return calloc(nmemb, size);
115 }
116 #endif /* NETSNMP_FEATURE_REMOVE_MEMORY_CALLOC */
117 #ifndef NETSNMP_FEATURE_REMOVE_MEMORY_MALLOC
118 /**
119  * This function is a wrapper for the malloc function.
120  */
netsnmp_malloc(size_t size)121 void * netsnmp_malloc(size_t size)
122 {
123     return malloc(size);
124 }
125 #endif /* NETSNMP_FEATURE_REMOVE_MEMORY_MALLOC */
126 #ifndef NETSNMP_FEATURE_REMOVE_MEMORY_REALLOC
127 /**
128  * This function is a wrapper for the realloc function.
129  */
netsnmp_realloc(void * ptr,size_t size)130 void * netsnmp_realloc( void * ptr, size_t size)
131 {
132     return realloc(ptr, size);
133 }
134 #endif /* NETSNMP_FEATURE_REMOVE_MEMORY_REALLOC */
135 #ifndef NETSNMP_FEATURE_REMOVE_MEMORY_FREE
136 /**
137  * This function is a wrapper for the free function.
138  * It calls free only if the calling parameter has a non-zero value.
139  */
netsnmp_free(void * ptr)140 void netsnmp_free( void * ptr)
141 {
142     if (ptr)
143         free(ptr);
144 }
145 #endif /* NETSNMP_FEATURE_REMOVE_MEMORY_FREE */
146 
147 /**
148  * This function increase the size of the buffer pointed at by *buf, which is
149  * initially of size *buf_len.  Contents are preserved **AT THE BOTTOM END OF
150  * THE BUFFER**.  If memory can be (re-)allocated then it returns 1, else it
151  * returns 0.
152  *
153  * @param buf  pointer to a buffer pointer
154  * @param buf_len      pointer to current size of buffer in bytes
155  *
156  * @note
157  * The current re-allocation algorithm is to increase the buffer size by
158  * whichever is the greater of 256 bytes or the current buffer size, up to
159  * a maximum increase of 8192 bytes.
160  */
161 int
snmp_realloc(u_char ** buf,size_t * buf_len)162 snmp_realloc(u_char ** buf, size_t * buf_len)
163 {
164     u_char         *new_buf = NULL;
165     size_t          new_buf_len = 0;
166 
167     if (buf == NULL) {
168         return 0;
169     }
170 
171     if (*buf_len <= 255) {
172         new_buf_len = *buf_len + 256;
173     } else if (*buf_len > 255 && *buf_len <= 8191) {
174         new_buf_len = *buf_len * 2;
175     } else if (*buf_len > 8191) {
176         new_buf_len = *buf_len + 8192;
177     }
178 
179     if (*buf == NULL) {
180         new_buf = (u_char *) malloc(new_buf_len);
181     } else {
182         new_buf = (u_char *) realloc(*buf, new_buf_len);
183     }
184 
185     if (new_buf != NULL) {
186         *buf = new_buf;
187         *buf_len = new_buf_len;
188         return 1;
189     } else {
190         return 0;
191     }
192 }
193 
194 int
snmp_strcat(u_char ** buf,size_t * buf_len,size_t * out_len,int allow_realloc,const u_char * s)195 snmp_strcat(u_char ** buf, size_t * buf_len, size_t * out_len,
196             int allow_realloc, const u_char * s)
197 {
198     if (buf == NULL || buf_len == NULL || out_len == NULL) {
199         return 0;
200     }
201 
202     if (s == NULL) {
203         /*
204          * Appending a NULL string always succeeds since it is a NOP.
205          */
206         return 1;
207     }
208 
209     while ((*out_len + strlen((const char *) s) + 1) >= *buf_len) {
210         if (!(allow_realloc && snmp_realloc(buf, buf_len))) {
211             return 0;
212         }
213     }
214 
215     if (!*buf)
216         return 0;
217 
218     strcpy((char *) (*buf + *out_len), (const char *) s);
219     *out_len += strlen((char *) (*buf + *out_len));
220     return 1;
221 }
222 
223 /** zeros memory before freeing it.
224  *
225  *	@param *buf	Pointer at bytes to free.
226  *	@param size	Number of bytes in buf.
227  */
228 void
free_zero(void * buf,size_t size)229 free_zero(void *buf, size_t size)
230 {
231     if (buf) {
232         memset(buf, 0, size);
233         free(buf);
234     }
235 
236 }                               /* end free_zero() */
237 
238 #ifndef NETSNMP_FEATURE_REMOVE_USM_SCAPI
239 /**
240  * Returns pointer to allocaed & set buffer on success, size contains
241  * number of random bytes filled.  buf is NULL and *size set to KMT
242  * error value upon failure.
243  *
244  *	@param size	Number of bytes to malloc() and fill with random bytes.
245  *
246  * @return a malloced buffer
247  *
248  */
249 u_char         *
malloc_random(size_t * size)250 malloc_random(size_t * size)
251 {
252     int             rval = SNMPERR_SUCCESS;
253     u_char         *buf = (u_char *) calloc(1, *size);
254 
255     if (buf) {
256         rval = sc_random(buf, size);
257 
258         if (rval < 0) {
259             free_zero(buf, *size);
260             buf = NULL;
261         } else {
262             *size = rval;
263         }
264     }
265 
266     return buf;
267 
268 }                               /* end malloc_random() */
269 #endif /* NETSNMP_FEATURE_REMOVE_USM_SCAPI */
270 
271 /**
272  * Duplicates a memory block.
273  *
274  * @param[in] from Pointer to copy memory from.
275  * @param[in] size Size of the data to be copied.
276  *
277  * @return Pointer to the duplicated memory block, or NULL if memory allocation
278  * failed.
279  */
netsnmp_memdup(const void * from,size_t size)280 void *netsnmp_memdup(const void *from, size_t size)
281 {
282     void *to = NULL;
283 
284     if (from) {
285         to = malloc(size);
286         if (to)
287             memcpy(to, from, size);
288     }
289     return to;
290 }                               /* end netsnmp_memdup() */
291 
292 /**
293  * Duplicates a memory block, adding a NULL at the end.
294  *
295  * NOTE: the returned size DOES NOT include the extra byte for the NULL
296  *       termination, just the raw data (i.e. from_size).
297  *
298  * This is mainly to protect agains code that uses str* functions on
299  * a fixed buffer that may not have a terminating NULL.
300  *
301  * @param[in] from Pointer to copy memory from.
302  * @param[in] from_size Size of the data to be copied.
303  * @param[out] to_size Pointer to size var for new block (OPTIONAL)
304  *
305  * @return Pointer to the duplicated memory block, or NULL if memory allocation
306  * failed.
307  */
netsnmp_memdup_nt(const void * from,size_t from_size,size_t * to_size)308 void *netsnmp_memdup_nt(const void *from, size_t from_size, size_t *to_size)
309 {
310     char *to = NULL;
311 
312     if (from) {
313         to = malloc(from_size+1);
314         if (to) {
315             memcpy(to, from, from_size);
316             to[from_size] = 0;
317             if (to_size)
318                *to_size = from_size;
319         }
320     }
321     return to;
322 }                               /* end netsnmp_memdupNT() */
323 
324 #ifndef NETSNMP_FEATURE_REMOVE_NETSNMP_CHECK_DEFINEDNESS
325 /**
326  * When running under Valgrind, check whether all bytes in the range [packet,
327  * packet+length) are defined. Let Valgrind print a backtrace if one or more
328  * bytes with uninitialized values have been found. This function can help to
329  * find the cause of undefined value errors if --track-origins=yes is not
330  * sufficient. Does nothing when not running under Valgrind.
331  *
332  * Note: this requires a fairly recent valgrind.
333  */
334 void
netsnmp_check_definedness(const void * packet,size_t length)335 netsnmp_check_definedness(const void *packet, size_t length)
336 {
337 #if defined(__VALGRIND_MAJOR__) && defined(__VALGRIND_MINOR__)   \
338     && (__VALGRIND_MAJOR__ > 3                                   \
339         || (__VALGRIND_MAJOR__ == 3 && __VALGRIND_MINOR__ >= 6))
340 
341     if (RUNNING_ON_VALGRIND) {
342         int i;
343         char vbits;
344 
345         for (i = 0; i < length; ++i) {
346             if (VALGRIND_GET_VBITS((const char *)packet + i, &vbits, 1) == 1
347                 && vbits)
348                 VALGRIND_PRINTF_BACKTRACE("Undefined: byte %d/%d", i,
349                                           (int)length);
350         }
351     }
352 
353 #endif
354 }
355 #endif /* NETSNMP_FEATURE_REMOVE_NETSNMP_CHECK_DEFINEDNESS */
356 
357 /** copies a (possible) unterminated string of a given length into a
358  *  new buffer and null terminates it as well (new buffer MAY be one
359  *  byte longer to account for this */
360 char           *
netsnmp_strdup_and_null(const u_char * from,size_t from_len)361 netsnmp_strdup_and_null(const u_char * from, size_t from_len)
362 {
363     char         *ret;
364 
365     if (from_len > 0 && from[from_len - 1] == '\0')
366         from_len--;
367     ret = malloc(from_len + 1);
368     if (ret) {
369         memcpy(ret, from, from_len);
370         ret[from_len] = '\0';
371     }
372     return ret;
373 }
374 
375 /** converts binary to hexidecimal
376  *
377  *     @param *input            Binary data.
378  *     @param len               Length of binary data.
379  *     @param **dest            NULL terminated string equivalent in hex.
380  *     @param *dest_len         size of destination buffer
381  *     @param allow_realloc     flag indicating if buffer can be realloc'd
382  *
383  * @return olen	Length of output string not including NULL terminator.
384  */
385 u_int
netsnmp_binary_to_hex(u_char ** dest,size_t * dest_len,int allow_realloc,const u_char * input,size_t len)386 netsnmp_binary_to_hex(u_char ** dest, size_t *dest_len, int allow_realloc,
387                       const u_char * input, size_t len)
388 {
389     u_int           olen = (len * 2) + 1;
390     u_char         *s, *op;
391     const u_char   *ip = input;
392 
393     if (dest == NULL || dest_len == NULL || input == NULL)
394         return 0;
395 
396     if (NULL == *dest) {
397         s = (unsigned char *) calloc(1, olen);
398         *dest_len = olen;
399     }
400     else
401         s = *dest;
402 
403     if (*dest_len < olen) {
404         if (!allow_realloc)
405             return 0;
406         *dest_len = olen;
407         if (snmp_realloc(dest, dest_len))
408             return 0;
409     }
410 
411     op = s;
412     while (ip - input < (int) len) {
413         *op++ = VAL2HEX((*ip >> 4) & 0xf);
414         *op++ = VAL2HEX(*ip & 0xf);
415         ip++;
416     }
417     *op = '\0';
418 
419     if (s != *dest)
420         *dest = s;
421     *dest_len = olen;
422 
423     return olen;
424 
425 }                               /* end netsnmp_binary_to_hex() */
426 
427 /** converts binary to hexidecimal
428  *
429  *	@param *input		Binary data.
430  *	@param len		Length of binary data.
431  *	@param **output	NULL terminated string equivalent in hex.
432  *
433  * @return olen	Length of output string not including NULL terminator.
434  *
435  * FIX	Is there already one of these in the UCD SNMP codebase?
436  *	The old one should be used, or this one should be moved to
437  *	snmplib/snmp_api.c.
438  */
439 u_int
binary_to_hex(const u_char * input,size_t len,char ** output)440 binary_to_hex(const u_char * input, size_t len, char **output)
441 {
442     size_t out_len = 0;
443 
444     *output = NULL; /* will alloc new buffer */
445 
446     return netsnmp_binary_to_hex((u_char**)output, &out_len, 1, input, len);
447 }                               /* end binary_to_hex() */
448 
449 
450 
451 
452 /**
453  * hex_to_binary2
454  *	@param *input		Printable data in base16.
455  *	@param len		Length in bytes of data.
456  *	@param **output	Binary data equivalent to input.
457  *
458  * @return SNMPERR_GENERR on failure, otherwise length of allocated string.
459  *
460  * Input of an odd length is right aligned.
461  *
462  * FIX	Another version of "hex-to-binary" which takes odd length input
463  *	strings.  It also allocates the memory to hold the binary data.
464  *	Should be integrated with the official hex_to_binary() function.
465  */
466 int
hex_to_binary2(const u_char * input,size_t len,char ** output)467 hex_to_binary2(const u_char * input, size_t len, char **output)
468 {
469     u_int           olen = (len / 2) + (len % 2);
470     char           *s = calloc(1, olen ? olen : 1), *op = s;
471     const u_char   *ip = input;
472 
473 
474     *output = NULL;
475     if (!s)
476         goto hex_to_binary2_quit;
477 
478     *op = 0;
479     if (len % 2) {
480         if (!isxdigit(*ip))
481             goto hex_to_binary2_quit;
482         *op++ = HEX2VAL(*ip);
483         ip++;
484     }
485 
486     while (ip < input + len) {
487         if (!isxdigit(*ip))
488             goto hex_to_binary2_quit;
489         *op = HEX2VAL(*ip) << 4;
490         ip++;
491 
492         if (!isxdigit(*ip))
493             goto hex_to_binary2_quit;
494         *op++ += HEX2VAL(*ip);
495         ip++;
496     }
497 
498     *output = s;
499     return olen;
500 
501   hex_to_binary2_quit:
502     free_zero(s, olen);
503     return -1;
504 
505 }                               /* end hex_to_binary2() */
506 
507 int
snmp_decimal_to_binary(u_char ** buf,size_t * buf_len,size_t * out_len,int allow_realloc,const char * decimal)508 snmp_decimal_to_binary(u_char ** buf, size_t * buf_len, size_t * out_len,
509                        int allow_realloc, const char *decimal)
510 {
511     int             subid = 0;
512     const char     *cp = decimal;
513 
514     if (buf == NULL || buf_len == NULL || out_len == NULL
515         || decimal == NULL) {
516         return 0;
517     }
518 
519     while (*cp != '\0') {
520         if (isspace((int) *cp) || *cp == '.') {
521             cp++;
522             continue;
523         }
524         if (!isdigit((int) *cp)) {
525             return 0;
526         }
527         if ((subid = atoi(cp)) > 255) {
528             return 0;
529         }
530         if ((*out_len >= *buf_len) &&
531             !(allow_realloc && snmp_realloc(buf, buf_len))) {
532             return 0;
533         }
534         *(*buf + *out_len) = (u_char) subid;
535         (*out_len)++;
536         while (isdigit((int) *cp)) {
537             cp++;
538         }
539     }
540     return 1;
541 }
542 
543 /**
544  * convert an ASCII hex string (with specified delimiters) to binary
545  *
546  * @param buf     address of a pointer (pointer to pointer) for the output buffer.
547  *                If allow_realloc is set, the buffer may be grown via snmp_realloc
548  *                to accomodate the data.
549  *
550  * @param buf_len pointer to a size_t containing the initial size of buf.
551  *
552  * @param offset On input, a pointer to a size_t indicating an offset into buf.
553  *                The  binary data will be stored at this offset.
554  *                On output, this pointer will have updated the offset to be
555  *                the first byte after the converted data.
556  *
557  * @param allow_realloc If true, the buffer can be reallocated. If false, and
558  *                      the buffer is not large enough to contain the string,
559  *                      an error will be returned.
560  *
561  * @param hex     pointer to hex string to be converted. May be prefixed by
562  *                "0x" or "0X".
563  *
564  * @param delim   point to a string of allowed delimiters between bytes.
565  *                If not specified, any non-hex characters will be an error.
566  *
567  * @retval 1  success
568  * @retval 0  error
569  */
570 int
netsnmp_hex_to_binary(u_char ** buf,size_t * buf_len,size_t * offset,int allow_realloc,const char * hex,const char * delim)571 netsnmp_hex_to_binary(u_char ** buf, size_t * buf_len, size_t * offset,
572                       int allow_realloc, const char *hex, const char *delim)
573 {
574     unsigned int    subid = 0;
575     const char     *cp = hex;
576 
577     if (buf == NULL || buf_len == NULL || offset == NULL || hex == NULL) {
578         return 0;
579     }
580 
581     if ((*cp == '0') && ((*(cp + 1) == 'x') || (*(cp + 1) == 'X'))) {
582         cp += 2;
583     }
584 
585     while (*cp != '\0') {
586         if (!isxdigit((int) *cp) ||
587             !isxdigit((int) *(cp+1))) {
588             if ((NULL != delim) && (NULL != strchr(delim, *cp))) {
589                 cp++;
590                 continue;
591             }
592             return 0;
593         }
594         if (sscanf(cp, "%2x", &subid) == 0) {
595             return 0;
596         }
597         /*
598          * if we dont' have enough space, realloc.
599          * (snmp_realloc will adjust buf_len to new size)
600          */
601         if ((*offset >= *buf_len) &&
602             !(allow_realloc && snmp_realloc(buf, buf_len))) {
603             return 0;
604         }
605         *(*buf + *offset) = (u_char) subid;
606         (*offset)++;
607         if (*++cp == '\0') {
608             /*
609              * Odd number of hex digits is an error.
610              */
611             return 0;
612         } else {
613             cp++;
614         }
615     }
616     return 1;
617 }
618 
619 /**
620  * convert an ASCII hex string to binary
621  *
622  * @note This is a wrapper which calls netsnmp_hex_to_binary with a
623  * delimiter string of " ".
624  *
625  * See netsnmp_hex_to_binary for parameter descriptions.
626  *
627  * @retval 1  success
628  * @retval 0  error
629  */
630 int
snmp_hex_to_binary(u_char ** buf,size_t * buf_len,size_t * offset,int allow_realloc,const char * hex)631 snmp_hex_to_binary(u_char ** buf, size_t * buf_len, size_t * offset,
632                    int allow_realloc, const char *hex)
633 {
634     return netsnmp_hex_to_binary(buf, buf_len, offset, allow_realloc, hex, " ");
635 }
636 
637 /*******************************************************************-o-******
638  * dump_chunk
639  *
640  * Parameters:
641  *	*title	(May be NULL.)
642  *	*buf
643  *	 size
644  */
645 void
dump_chunk(const char * debugtoken,const char * title,const u_char * buf,int size)646 dump_chunk(const char *debugtoken, const char *title, const u_char * buf,
647            int size)
648 {
649     int             printunit = 64;     /* XXX  Make global. */
650     char            chunk[SNMP_MAXBUF], *s, *sp;
651 
652     if (title && (*title != '\0')) {
653         DEBUGMSGTL((debugtoken, "%s\n", title));
654     }
655 
656 
657     memset(chunk, 0, SNMP_MAXBUF);
658     size = binary_to_hex(buf, size, &s);
659     sp = s;
660 
661     while (size > 0) {
662         if (size > printunit) {
663             memcpy(chunk, sp, printunit);
664             chunk[printunit] = '\0';
665             DEBUGMSGTL((debugtoken, "\t%s\n", chunk));
666         } else {
667             DEBUGMSGTL((debugtoken, "\t%s\n", sp));
668         }
669 
670         sp += printunit;
671         size -= printunit;
672     }
673 
674 
675     SNMP_FREE(s);
676 
677 }                               /* end dump_chunk() */
678 
679 
680 
681 
682 /*******************************************************************-o-******
683  * dump_snmpEngineID
684  *
685  * Parameters:
686  *	*estring
687  *	*estring_len
688  *
689  * Returns:
690  *	Allocated memory pointing to a string of buflen char representing
691  *	a printf'able form of the snmpEngineID.
692  *
693  *	-OR- NULL on error.
694  *
695  *
696  * Translates the snmpEngineID TC into a printable string.  From RFC 2271,
697  * Section 5 (pp. 36-37):
698  *
699  * First bit:	0	Bit string structured by means non-SNMPv3.
700  *  		1	Structure described by SNMPv3 SnmpEngineID TC.
701  *
702  * Bytes 1-4:		Enterprise ID.  (High bit of first byte is ignored.)
703  *
704  * Byte 5:	0	(RESERVED by IANA.)
705  *  		1	IPv4 address.		(   4 octets)
706  *  		2	IPv6 address.		(  16 octets)
707  *  		3	MAC address.		(   6 octets)
708  *  		4	Locally defined text.	(0-27 octets)
709  *  		5	Locally defined octets.	(0-27 octets)
710  *  		6-127	(RESERVED for enterprise.)
711  *
712  * Bytes 6-32:		(Determined by byte 5.)
713  *
714  *
715  * Non-printable characters are given in hex.  Text is given in quotes.
716  * IP and MAC addresses are given in standard (UN*X) conventions.  Sections
717  * are comma separated.
718  *
719  * esp, remaining_len and s trace the state of the constructed buffer.
720  * s will be defined if there is something to return, and it will point
721  * to the end of the constructed buffer.
722  *
723  *
724  * ASSUME  "Text" means printable characters.
725  *
726  * XXX	Must the snmpEngineID always have a minimum length of 12?
727  *	(Cf. part 2 of the TC definition.)
728  * XXX	Does not enforce upper-bound of 32 bytes.
729  * XXX	Need a switch to decide whether to use DNS name instead of a simple
730  *	IP address.
731  *
732  * FIX	Use something other than snprint_hexstring which doesn't add
733  *	trailing spaces and (sometimes embedded) newlines...
734  */
735 #ifdef NETSNMP_ENABLE_TESTING_CODE
736 char           *
dump_snmpEngineID(const u_char * estring,size_t * estring_len)737 dump_snmpEngineID(const u_char * estring, size_t * estring_len)
738 {
739 #define eb(b)	( *(esp+b) & 0xff )
740 
741     int             gotviolation = 0, slen = 0;
742     u_int           remaining_len;
743 
744     char            buf[SNMP_MAXBUF], *s = NULL, *t;
745     const u_char   *esp = estring;
746 
747     struct in_addr  iaddr;
748 
749 
750 
751     /*
752      * Sanity check.
753      */
754     if (!estring || (*estring_len <= 0)) {
755         goto dump_snmpEngineID_quit;
756     }
757     remaining_len = *estring_len;
758     memset(buf, 0, SNMP_MAXBUF);
759 
760 
761 
762     /*
763      * Test first bit.  Return immediately with a hex string, or
764      * begin by formatting the enterprise ID.
765      */
766     if (!(*esp & 0x80)) {
767         snprint_hexstring(buf, SNMP_MAXBUF, esp, remaining_len);
768         s = strchr(buf, '\0');
769         s -= 1;
770         goto dump_snmpEngineID_quit;
771     }
772 
773     s = buf;
774     s += sprintf(s, "enterprise %d, ", ((*(esp + 0) & 0x7f) << 24) |
775                  ((*(esp + 1) & 0xff) << 16) |
776                  ((*(esp + 2) & 0xff) << 8) | ((*(esp + 3) & 0xff)));
777     /*
778      * XXX  Ick.
779      */
780 
781     if (remaining_len < 5) {    /* XXX  Violating string. */
782         goto dump_snmpEngineID_quit;
783     }
784 
785     esp += 4;                   /* Incremented one more in the switch below. */
786     remaining_len -= 5;
787 
788 
789 
790     /*
791      * Act on the fifth byte.
792      */
793     switch ((int) *esp++) {
794     case 1:                    /* IPv4 address. */
795 
796         if (remaining_len < 4)
797             goto dump_snmpEngineID_violation;
798         memcpy(&iaddr.s_addr, esp, 4);
799 
800         if (!(t = inet_ntoa(iaddr)))
801             goto dump_snmpEngineID_violation;
802         s += sprintf(s, "%s", t);
803 
804         esp += 4;
805         remaining_len -= 4;
806         break;
807 
808     case 2:                    /* IPv6 address. */
809 
810         if (remaining_len < 16)
811             goto dump_snmpEngineID_violation;
812 
813         s += sprintf(s,
814                      "%02X%02X %02X%02X %02X%02X %02X%02X::"
815                      "%02X%02X %02X%02X %02X%02X %02X%02X",
816                      eb(0), eb(1), eb(2), eb(3),
817                      eb(4), eb(5), eb(6), eb(7),
818                      eb(8), eb(9), eb(10), eb(11),
819                      eb(12), eb(13), eb(14), eb(15));
820 
821         esp += 16;
822         remaining_len -= 16;
823         break;
824 
825     case 3:                    /* MAC address. */
826 
827         if (remaining_len < 6)
828             goto dump_snmpEngineID_violation;
829 
830         s += sprintf(s, "%02X:%02X:%02X:%02X:%02X:%02X",
831                      eb(0), eb(1), eb(2), eb(3), eb(4), eb(5));
832 
833         esp += 6;
834         remaining_len -= 6;
835         break;
836 
837     case 4:                    /* Text. */
838 
839         s += sprintf(s, "\"%.*s\"", (int) (sizeof(buf)-strlen(buf)-3), esp);
840         goto dump_snmpEngineID_quit;
841         break;
842 
843      /*NOTREACHED*/ case 5:    /* Octets. */
844 
845         snprint_hexstring(s, (SNMP_MAXBUF - (s-buf)),
846                           esp, remaining_len);
847         s = strchr(buf, '\0');
848         s -= 1;
849         goto dump_snmpEngineID_quit;
850         break;
851 
852        /*NOTREACHED*/ dump_snmpEngineID_violation:
853     case 0:                    /* Violation of RESERVED,
854                                  * *   -OR- of expected length.
855                                  */
856         gotviolation = 1;
857         s += sprintf(s, "!!! ");
858         /* FALLTHROUGH */
859 
860     default:                   /* Unknown encoding. */
861 
862         if (!gotviolation) {
863             s += sprintf(s, "??? ");
864         }
865         snprint_hexstring(s, (SNMP_MAXBUF - (s-buf)),
866                           esp, remaining_len);
867         s = strchr(buf, '\0');
868         s -= 1;
869 
870         goto dump_snmpEngineID_quit;
871 
872     }                           /* endswitch */
873 
874 
875 
876     /*
877      * Cases 1-3 (IP and MAC addresses) should not have trailing
878      * octets, but perhaps they do.  Throw them in too.  XXX
879      */
880     if (remaining_len > 0) {
881         s += sprintf(s, " (??? ");
882 
883         snprint_hexstring(s, (SNMP_MAXBUF - (s-buf)),
884                           esp, remaining_len);
885         s = strchr(buf, '\0');
886         s -= 1;
887 
888         s += sprintf(s, ")");
889     }
890 
891 
892 
893   dump_snmpEngineID_quit:
894     if (s) {
895         slen = s - buf + 1;
896         s = calloc(1, slen);
897         memcpy(s, buf, (slen) - 1);
898     }
899 
900     memset(buf, 0, SNMP_MAXBUF);        /* XXX -- Overkill? XXX: Yes! */
901 
902     return s;
903 
904 #undef eb
905 }                               /* end dump_snmpEngineID() */
906 #endif                          /* NETSNMP_ENABLE_TESTING_CODE */
907 
908 
909 /**
910  * Create a new real-time marker.
911  *
912  * \deprecated Use netsnmp_set_monotonic_marker() instead.
913  *
914  * @note Caller must free time marker when no longer needed.
915  */
916 marker_t
atime_newMarker(void)917 atime_newMarker(void)
918 {
919     marker_t        pm = (marker_t) calloc(1, sizeof(struct timeval));
920     gettimeofday((struct timeval *) pm, NULL);
921     return pm;
922 }
923 
924 /**
925  * Set a time marker to the current value of the real-time clock.
926  * \deprecated Use netsnmp_set_monotonic_marker() instead.
927  */
928 void
atime_setMarker(marker_t pm)929 atime_setMarker(marker_t pm)
930 {
931     if (!pm)
932         return;
933 
934     gettimeofday((struct timeval *) pm, NULL);
935 }
936 
937 /**
938  * Query the current value of the monotonic clock.
939  *
940  * Returns the current value of a monotonic clock if such a clock is provided by
941  * the operating system or the wall clock time if no such clock is provided by
942  * the operating system. A monotonic clock is a clock that is never adjusted
943  * backwards and that proceeds at the same rate as wall clock time.
944  *
945  * @param[out] tv Pointer to monotonic clock time.
946  */
netsnmp_get_monotonic_clock(struct timeval * tv)947 void netsnmp_get_monotonic_clock(struct timeval* tv)
948 {
949 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
950     struct timespec ts;
951     int res;
952 
953     res = clock_gettime(CLOCK_MONOTONIC, &ts);
954     if (res >= 0) {
955         tv->tv_sec = ts.tv_sec;
956         tv->tv_usec = ts.tv_nsec / 1000;
957     } else {
958         gettimeofday(tv, NULL);
959     }
960 #elif defined(WIN32)
961     /*
962      * Windows: return tick count. Note: the rate at which the tick count
963      * increases is not adjusted by the time synchronization algorithm, so
964      * expect an error of <= 100 ppm for the rate at which this clock
965      * increases.
966      */
967     typedef ULONGLONG (WINAPI * pfGetTickCount64)(void);
968     static int s_initialized;
969     static pfGetTickCount64 s_pfGetTickCount64;
970     uint64_t now64;
971 
972     if (!s_initialized) {
973         HMODULE hKernel32 = GetModuleHandle("kernel32");
974         s_pfGetTickCount64 =
975             (pfGetTickCount64) GetProcAddress(hKernel32, "GetTickCount64");
976         s_initialized = TRUE;
977     }
978 
979     if (s_pfGetTickCount64) {
980         /* Windows Vista, Windows 2008 or any later Windows version */
981         now64 = (*s_pfGetTickCount64)();
982     } else {
983         /* Windows XP, Windows 2003 or any earlier Windows version */
984         static uint32_t s_wraps, s_last;
985         uint32_t now;
986 
987         now = GetTickCount();
988         if (now < s_last)
989             s_wraps++;
990         s_last = now;
991         now64 = ((uint64_t)s_wraps << 32) | now;
992     }
993     tv->tv_sec = now64 / 1000;
994     tv->tv_usec = (now64 % 1000) * 1000;
995 #else
996     /* At least FreeBSD 4 doesn't provide monotonic clock support. */
997 #warning Not sure how to query a monotonically increasing clock on your system. \
998 Timers will not work correctly if the system clock is adjusted by e.g. ntpd.
999     gettimeofday(tv, NULL);
1000 #endif
1001 }
1002 
1003 /**
1004  * Set a time marker to the current value of the monotonic clock.
1005  */
1006 void
netsnmp_set_monotonic_marker(marker_t * pm)1007 netsnmp_set_monotonic_marker(marker_t *pm)
1008 {
1009     if (!*pm)
1010         *pm = malloc(sizeof(struct timeval));
1011     if (*pm)
1012         netsnmp_get_monotonic_clock(*pm);
1013 }
1014 
1015 /**
1016  * Returns the difference (in msec) between the two markers
1017  *
1018  * \deprecated Don't use in new code.
1019  */
1020 long
atime_diff(const_marker_t first,const_marker_t second)1021 atime_diff(const_marker_t first, const_marker_t second)
1022 {
1023     struct timeval diff;
1024 
1025     NETSNMP_TIMERSUB((const struct timeval *) second, (const struct timeval *) first, &diff);
1026 
1027     return (long)(diff.tv_sec * 1000 + diff.tv_usec / 1000);
1028 }
1029 
1030 /**
1031  * Returns the difference (in u_long msec) between the two markers
1032  *
1033  * \deprecated Don't use in new code.
1034  */
1035 u_long
uatime_diff(const_marker_t first,const_marker_t second)1036 uatime_diff(const_marker_t first, const_marker_t second)
1037 {
1038     struct timeval diff;
1039 
1040     NETSNMP_TIMERSUB((const struct timeval *) second, (const struct timeval *) first, &diff);
1041 
1042     return (((u_long) diff.tv_sec) * 1000 + diff.tv_usec / 1000);
1043 }
1044 
1045 /**
1046  * Returns the difference (in u_long 1/100th secs) between the two markers
1047  * (functionally this is what sysUpTime needs)
1048  *
1049  * \deprecated Don't use in new code.
1050  */
1051 u_long
uatime_hdiff(const_marker_t first,const_marker_t second)1052 uatime_hdiff(const_marker_t first, const_marker_t second)
1053 {
1054     struct timeval diff;
1055 
1056     NETSNMP_TIMERSUB((const struct timeval *) second, (const struct timeval *) first, &diff);
1057     return ((u_long) diff.tv_sec) * 100 + diff.tv_usec / 10000;
1058 }
1059 
1060 /**
1061  * Test: Has (marked time plus delta) exceeded current time ?
1062  * Returns 0 if test fails or cannot be tested (no marker).
1063  *
1064  * \deprecated Use netsnmp_ready_monotonic() instead.
1065  */
1066 int
atime_ready(const_marker_t pm,int delta_ms)1067 atime_ready(const_marker_t pm, int delta_ms)
1068 {
1069     marker_t        now;
1070     long            diff;
1071     if (!pm)
1072         return 0;
1073 
1074     now = atime_newMarker();
1075 
1076     diff = atime_diff(pm, now);
1077     free(now);
1078     if (diff < delta_ms)
1079         return 0;
1080 
1081     return 1;
1082 }
1083 
1084 #ifndef NETSNMP_FEATURE_REMOVE_UATIME_READY
1085 /**
1086  * Test: Has (marked time plus delta) exceeded current time ?
1087  * Returns 0 if test fails or cannot be tested (no marker).
1088  *
1089  * \deprecated Use netsnmp_ready_monotonic() instead.
1090  */
1091 int
uatime_ready(const_marker_t pm,unsigned int delta_ms)1092 uatime_ready(const_marker_t pm, unsigned int delta_ms)
1093 {
1094     marker_t        now;
1095     u_long          diff;
1096     if (!pm)
1097         return 0;
1098 
1099     now = atime_newMarker();
1100 
1101     diff = uatime_diff(pm, now);
1102     free(now);
1103     if (diff < delta_ms)
1104         return 0;
1105 
1106     return 1;
1107 }
1108 #endif /* NETSNMP_FEATURE_REMOVE_UATIME_READY */
1109 
1110 /**
1111  * Is the current time past (marked time plus delta) ?
1112  *
1113  * @param[in] pm Pointer to marked time as obtained via
1114  *   netsnmp_set_monotonic_marker().
1115  * @param[in] delta_ms Time delta in milliseconds.
1116  *
1117  * @return pm != NULL && now >= (*pm + delta_ms)
1118  */
1119 int
netsnmp_ready_monotonic(const_marker_t pm,int delta_ms)1120 netsnmp_ready_monotonic(const_marker_t pm, int delta_ms)
1121 {
1122     struct timeval  now, diff, delta;
1123 
1124     netsnmp_assert(delta_ms >= 0);
1125     if (pm) {
1126         netsnmp_get_monotonic_clock(&now);
1127         NETSNMP_TIMERSUB(&now, (const struct timeval *) pm, &diff);
1128         delta.tv_sec = delta_ms / 1000;
1129         delta.tv_usec = (delta_ms % 1000) * 1000UL;
1130         return timercmp(&diff, &delta, >=) ? TRUE : FALSE;
1131     } else {
1132         return FALSE;
1133     }
1134 }
1135 
1136 
1137         /*
1138          * Time-related utility functions
1139          */
1140 
1141 /**
1142  * Return the number of timeTicks since the given marker
1143  *
1144  * \deprecated Don't use in new code.
1145  */
1146 int
marker_tticks(const_marker_t pm)1147 marker_tticks(const_marker_t pm)
1148 {
1149     int             res;
1150     marker_t        now = atime_newMarker();
1151 
1152     res = atime_diff(pm, now);
1153     free(now);
1154     return res / 10;            /* atime_diff works in msec, not csec */
1155 }
1156 
1157 #ifndef NETSNMP_FEATURE_REMOVE_TIMEVAL_TTICKS
1158 /**
1159  * \deprecated Don't use in new code.
1160  */
1161 int
timeval_tticks(const struct timeval * tv)1162 timeval_tticks(const struct timeval *tv)
1163 {
1164     return marker_tticks((const_marker_t) tv);
1165 }
1166 #endif /* NETSNMP_FEATURE_REMOVE_TIMEVAL_TTICKS */
1167 
1168 /**
1169  * Non Windows:  Returns a pointer to the desired environment variable
1170  *               or NULL if the environment variable does not exist.
1171  *
1172  * Windows:      Returns a pointer to the desired environment variable
1173  *               if it exists.  If it does not, the variable is looked up
1174  *               in the registry in HKCU\\Net-SNMP or HKLM\\Net-SNMP
1175  *               (whichever it finds first) and stores the result in the
1176  *               environment variable.  It then returns a pointer to
1177  *               environment variable.
1178  */
1179 
netsnmp_getenv(const char * name)1180 char *netsnmp_getenv(const char *name)
1181 {
1182 #if !defined (WIN32) && !defined (cygwin)
1183   return (getenv(name));
1184 #else
1185   char *temp = NULL;
1186   HKEY hKey;
1187   unsigned char * key_value = NULL;
1188   DWORD key_value_size = 0;
1189   DWORD key_value_type = 0;
1190   DWORD getenv_worked = 0;
1191 
1192   DEBUGMSGTL(("read_config", "netsnmp_getenv called with name: %s\n",name));
1193 
1194   if (!(name))
1195     return NULL;
1196 
1197   /* Try environment variable first */
1198   temp = getenv(name);
1199   if (temp) {
1200     getenv_worked = 1;
1201     DEBUGMSGTL(("read_config", "netsnmp_getenv will return from ENV: %s\n",temp));
1202   }
1203 
1204   /* Next try HKCU */
1205   if (temp == NULL)
1206   {
1207     if (getenv("SNMP_IGNORE_WINDOWS_REGISTRY"))
1208       return NULL;
1209 
1210     if (RegOpenKeyExA(
1211           HKEY_CURRENT_USER,
1212           "SOFTWARE\\Net-SNMP",
1213           0,
1214           KEY_QUERY_VALUE,
1215           &hKey) == ERROR_SUCCESS) {
1216 
1217       if (RegQueryValueExA(
1218             hKey,
1219             name,
1220             NULL,
1221             &key_value_type,
1222             NULL,               /* Just get the size */
1223             &key_value_size) == ERROR_SUCCESS) {
1224 
1225         SNMP_FREE(key_value);
1226 
1227         /* Allocate memory needed +1 to allow RegQueryValueExA to NULL terminate the
1228          * string data in registry is missing one (which is unlikely).
1229          */
1230         key_value = malloc((sizeof(char) * key_value_size)+sizeof(char));
1231 
1232         if (RegQueryValueExA(
1233               hKey,
1234               name,
1235               NULL,
1236               &key_value_type,
1237               key_value,
1238               &key_value_size) == ERROR_SUCCESS) {
1239         }
1240         temp = (char *) key_value;
1241       }
1242       RegCloseKey(hKey);
1243       if (temp)
1244         DEBUGMSGTL(("read_config", "netsnmp_getenv will return from HKCU: %s\n",temp));
1245     }
1246   }
1247 
1248   /* Next try HKLM */
1249   if (temp == NULL)
1250   {
1251     if (RegOpenKeyExA(
1252           HKEY_LOCAL_MACHINE,
1253           "SOFTWARE\\Net-SNMP",
1254           0,
1255           KEY_QUERY_VALUE,
1256           &hKey) == ERROR_SUCCESS) {
1257 
1258       if (RegQueryValueExA(
1259             hKey,
1260             name,
1261             NULL,
1262             &key_value_type,
1263             NULL,               /* Just get the size */
1264             &key_value_size) == ERROR_SUCCESS) {
1265 
1266         SNMP_FREE(key_value);
1267 
1268         /* Allocate memory needed +1 to allow RegQueryValueExA to NULL terminate the
1269          * string data in registry is missing one (which is unlikely).
1270          */
1271         key_value = malloc((sizeof(char) * key_value_size)+sizeof(char));
1272 
1273         if (RegQueryValueExA(
1274               hKey,
1275               name,
1276               NULL,
1277               &key_value_type,
1278               key_value,
1279               &key_value_size) == ERROR_SUCCESS) {
1280         }
1281         temp = (char *) key_value;
1282 
1283       }
1284       RegCloseKey(hKey);
1285       if (temp)
1286         DEBUGMSGTL(("read_config", "netsnmp_getenv will return from HKLM: %s\n",temp));
1287     }
1288   }
1289 
1290   if (temp && !getenv_worked) {
1291     setenv(name, temp, 1);
1292     SNMP_FREE(temp);
1293   }
1294 
1295   DEBUGMSGTL(("read_config", "netsnmp_getenv returning: %s\n",getenv(name)));
1296 
1297   return(getenv(name));
1298 #endif
1299 }
1300 
1301 /**
1302  * Set an environment variable.
1303  *
1304  * This function is only necessary on Windows for the MSVC and MinGW
1305  * environments. If the process that uses the Net-SNMP DLL (e.g. a Perl
1306  * interpreter) and the Net-SNMP have been built with a different compiler
1307  * version then each will have a separate set of environment variables.
1308  * This function allows to set an environment variable such that it gets
1309  * noticed by the Net-SNMP DLL.
1310  */
netsnmp_setenv(const char * envname,const char * envval,int overwrite)1311 int netsnmp_setenv(const char *envname, const char *envval, int overwrite)
1312 {
1313     return setenv(envname, envval, overwrite);
1314 }
1315 
1316 /*
1317  * swap the order of an inet addr string
1318  */
1319 int
netsnmp_addrstr_hton(char * ptr,size_t len)1320 netsnmp_addrstr_hton(char *ptr, size_t len)
1321 {
1322     char tmp[8];
1323 
1324     if (!NETSNMP_BIGENDIAN) {
1325         if (8 == len) {
1326             tmp[0] = ptr[6];
1327             tmp[1] = ptr[7];
1328             tmp[2] = ptr[4];
1329             tmp[3] = ptr[5];
1330             tmp[4] = ptr[2];
1331             tmp[5] = ptr[3];
1332             tmp[6] = ptr[0];
1333             tmp[7] = ptr[1];
1334             memcpy(ptr, &tmp, 8);
1335         }
1336         else if (32 == len) {
1337             netsnmp_addrstr_hton(ptr,      8);
1338             netsnmp_addrstr_hton(ptr + 8,  8);
1339             netsnmp_addrstr_hton(ptr + 16, 8);
1340             netsnmp_addrstr_hton(ptr + 24, 8);
1341         }
1342         else
1343             return -1;
1344     }
1345 
1346     return 0;
1347 }
1348 
1349 #ifndef NETSNMP_FEATURE_REMOVE_STRING_TIME_TO_SECS
1350 /**
1351  * Takes a time string like 4h and converts it to seconds.
1352  * The string time given may end in 's' for seconds (the default
1353  * anyway if no suffix is specified),
1354  * 'm' for minutes, 'h' for hours, 'd' for days, or 'w' for weeks.  The
1355  * upper case versions are also accepted.
1356  *
1357  * @param time_string The time string to convert.
1358  *
1359  * @return seconds converted from the string
1360  * @return -1  : on failure
1361  */
1362 int
netsnmp_string_time_to_secs(const char * time_string)1363 netsnmp_string_time_to_secs(const char *time_string) {
1364     int secs = -1;
1365     if (!time_string || !time_string[0])
1366         return secs;
1367 
1368     secs = atoi(time_string);
1369 
1370     if (isdigit((unsigned char)time_string[strlen(time_string)-1]))
1371         return secs; /* no letter specified, it's already in seconds */
1372 
1373     switch (time_string[strlen(time_string)-1]) {
1374     case 's':
1375     case 'S':
1376         /* already in seconds */
1377         break;
1378 
1379     case 'm':
1380     case 'M':
1381         secs = secs * 60;
1382         break;
1383 
1384     case 'h':
1385     case 'H':
1386         secs = secs * 60 * 60;
1387         break;
1388 
1389     case 'd':
1390     case 'D':
1391         secs = secs * 60 * 60 * 24;
1392         break;
1393 
1394     case 'w':
1395     case 'W':
1396         secs = secs * 60 * 60 * 24 * 7;
1397         break;
1398 
1399     default:
1400         snmp_log(LOG_ERR, "time string %s contains an invalid suffix letter\n",
1401                  time_string);
1402         return -1;
1403     }
1404 
1405     DEBUGMSGTL(("string_time_to_secs", "Converted time string %s to %d\n",
1406                 time_string, secs));
1407     return secs;
1408 }
1409 #endif /* NETSNMP_FEATURE_REMOVE_STRING_TIME_TO_SECS */
1410