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