SNMP integration and GUI
[tomato.git] / release / src / router / snmp / snmplib / tools.c
blobced45287aced9a6d63c61fba1aac6cc9c3a40b11
1 /*
2 * tools.c
3 */
5 #include <net-snmp/net-snmp-config.h>
7 #include <ctype.h>
8 #include <stdio.h>
9 #include <sys/types.h>
10 #if TIME_WITH_SYS_TIME
11 # ifdef WIN32
12 # include <sys/timeb.h>
13 # else
14 # include <sys/time.h>
15 # endif
16 # include <time.h>
17 #else
18 # if HAVE_SYS_TIME_H
19 # include <sys/time.h>
20 # else
21 # include <time.h>
22 # endif
23 #endif
24 #ifdef HAVE_SYS_SOCKET_H
25 #include <sys/socket.h>
26 #endif
27 #if HAVE_WINSOCK_H
28 #include <winsock.h>
29 #endif
30 #ifdef HAVE_STDLIB_H
31 #include <stdlib.h>
32 #endif
33 #if HAVE_STRING_H
34 #include <string.h>
35 #else
36 #include <strings.h>
37 #endif
38 #ifdef HAVE_NETINET_IN_H
39 #include <netinet/in.h>
40 #endif
41 #ifdef HAVE_ARPA_INET_H
42 #include <arpa/inet.h>
43 #endif
45 #if HAVE_DMALLOC_H
46 #include <dmalloc.h>
47 #endif
49 #include <net-snmp/types.h>
50 #include <net-snmp/output_api.h>
51 #include <net-snmp/utilities.h>
52 #include <net-snmp/library/tools.h> /* for "internal" definitions */
54 #include <net-snmp/library/snmp_api.h>
55 #include <net-snmp/library/mib.h>
56 #include <net-snmp/library/scapi.h>
60 * snmp_realloc:
62 * Parameters:
64 * buf pointer to a buffer pointer
65 * buf_len pointer to current size of buffer in bytes
67 * This function increase the size of the buffer pointed at by *buf, which is
68 * initially of size *buf_len. Contents are preserved **AT THE BOTTOM END OF
69 * THE BUFFER**. If memory can be (re-)allocated then it returns 1, else it
70 * returns 0.
74 int
75 snmp_realloc(u_char ** buf, size_t * buf_len)
77 u_char *new_buf = NULL;
78 size_t new_buf_len = 0;
80 if (buf == NULL) {
81 return 0;
85 * The current re-allocation algorithm is to increase the buffer size by
86 * whichever is the greater of 256 bytes or the current buffer size, up to
87 * a maximum increase of 8192 bytes.
90 if (*buf_len <= 255) {
91 new_buf_len = *buf_len + 256;
92 } else if (*buf_len > 255 && *buf_len <= 8191) {
93 new_buf_len = *buf_len * 2;
94 } else if (*buf_len > 8191) {
95 new_buf_len = *buf_len + 8192;
98 if (*buf == NULL) {
99 new_buf = (u_char *) malloc(new_buf_len);
100 } else {
101 new_buf = (u_char *) realloc(*buf, new_buf_len);
104 if (new_buf != NULL) {
105 *buf = new_buf;
106 *buf_len = new_buf_len;
107 return 1;
108 } else {
109 return 0;
114 snmp_strcat(u_char ** buf, size_t * buf_len, size_t * out_len,
115 int allow_realloc, const u_char * s)
117 if (buf == NULL || buf_len == NULL || out_len == NULL) {
118 return 0;
121 if (s == NULL) {
123 * Appending a NULL string always succeeds since it is a NOP.
125 return 1;
128 while ((*out_len + strlen((const char *) s) + 1) >= *buf_len) {
129 if (!(allow_realloc && snmp_realloc(buf, buf_len))) {
130 return 0;
134 strcpy((char *) (*buf + *out_len), (const char *) s);
135 *out_len += strlen((char *) (*buf + *out_len));
136 return 1;
139 /*******************************************************************-o-******
140 * free_zero
142 * Parameters:
143 * *buf Pointer at bytes to free.
144 * size Number of bytes in buf.
146 void
147 free_zero(void *buf, size_t size)
149 if (buf) {
150 memset(buf, 0, size);
151 free(buf);
154 } /* end free_zero() */
159 /*******************************************************************-o-******
160 * malloc_random
162 * Parameters:
163 * size Number of bytes to malloc() and fill with random bytes.
165 * Returns pointer to allocaed & set buffer on success, size contains
166 * number of random bytes filled.
168 * buf is NULL and *size set to KMT error value upon failure.
171 u_char *
172 malloc_random(size_t * size)
174 int rval = SNMPERR_SUCCESS;
175 u_char *buf = (u_char *) calloc(1, *size);
177 if (buf) {
178 rval = sc_random(buf, size);
180 if (rval < 0) {
181 free_zero(buf, *size);
182 buf = NULL;
183 } else {
184 *size = rval;
188 return buf;
190 } /* end malloc_random() */
195 /*******************************************************************-o-******
196 * memdup
198 * Parameters:
199 * to Pointer to allocate and copy memory to.
200 * from Pointer to copy memory from.
201 * size Size of the data to be copied.
203 * Returns
204 * SNMPERR_SUCCESS On success.
205 * SNMPERR_GENERR On failure.
208 memdup(u_char ** to, const u_char * from, size_t size)
210 if (to == NULL)
211 return SNMPERR_GENERR;
212 if (from == NULL) {
213 *to = NULL;
214 return SNMPERR_SUCCESS;
216 if ((*to = (u_char *) malloc(size)) == NULL)
217 return SNMPERR_GENERR;
218 memcpy(*to, from, size);
219 return SNMPERR_SUCCESS;
221 } /* end memdup() */
223 /** copies a (possible) unterminated string of a given length into a
224 * new buffer and null terminates it as well (new buffer MAY be one
225 * byte longer to account for this */
226 char *
227 netsnmp_strdup_and_null(const u_char * from, size_t from_len)
229 u_char *ret;
231 if (from_len == 0 || from[from_len - 1] != '\0') {
232 ret = malloc(from_len + 1);
233 if (!ret)
234 return NULL;
235 ret[from_len] = '\0';
236 } else {
237 ret = malloc(from_len);
238 if (!ret)
239 return NULL;
240 ret[from_len - 1] = '\0';
242 memcpy(ret, from, from_len);
243 return ret;
246 /*******************************************************************-o-******
247 * binary_to_hex
249 * Parameters:
250 * *input Binary data.
251 * len Length of binary data.
252 * **output NULL terminated string equivalent in hex.
254 * Returns:
255 * olen Length of output string not including NULL terminator.
257 * FIX Is there already one of these in the UCD SNMP codebase?
258 * The old one should be used, or this one should be moved to
259 * snmplib/snmp_api.c.
261 u_int
262 binary_to_hex(const u_char * input, size_t len, char **output)
264 u_int olen = (len * 2) + 1;
265 char *s = (char *) calloc(1, olen), *op = s;
266 const u_char *ip = input;
269 while (ip - input < (int) len) {
270 *op++ = VAL2HEX((*ip >> 4) & 0xf);
271 *op++ = VAL2HEX(*ip & 0xf);
272 ip++;
274 *op = '\0';
276 *output = s;
277 return olen;
279 } /* end binary_to_hex() */
284 /*******************************************************************-o-******
285 * hex_to_binary2
287 * Parameters:
288 * *input Printable data in base16.
289 * len Length in bytes of data.
290 * **output Binary data equivalent to input.
292 * Returns:
293 * SNMPERR_GENERR Failure.
294 * <len> Otherwise, Length of allocated string.
297 * Input of an odd length is right aligned.
299 * FIX Another version of "hex-to-binary" which takes odd length input
300 * strings. It also allocates the memory to hold the binary data.
301 * Should be integrated with the official hex_to_binary() function.
304 hex_to_binary2(const u_char * input, size_t len, char **output)
306 u_int olen = (len / 2) + (len % 2);
307 char *s = (char *) calloc(1, (olen) ? olen : 1), *op = s;
308 const u_char *ip = input;
311 *output = NULL;
312 *op = 0;
313 if (len % 2) {
314 if (!isxdigit(*ip))
315 goto hex_to_binary2_quit;
316 *op++ = HEX2VAL(*ip);
317 ip++;
320 while (ip - input < (int) len) {
321 if (!isxdigit(*ip))
322 goto hex_to_binary2_quit;
323 *op = HEX2VAL(*ip) << 4;
324 ip++;
326 if (!isxdigit(*ip))
327 goto hex_to_binary2_quit;
328 *op++ += HEX2VAL(*ip);
329 ip++;
332 *output = s;
333 return olen;
335 hex_to_binary2_quit:
336 free_zero(s, olen);
337 return -1;
339 } /* end hex_to_binary2() */
342 snmp_decimal_to_binary(u_char ** buf, size_t * buf_len, size_t * out_len,
343 int allow_realloc, const char *decimal)
345 int subid = 0;
346 const char *cp = decimal;
348 if (buf == NULL || buf_len == NULL || out_len == NULL
349 || decimal == NULL) {
350 return 0;
353 while (*cp != '\0') {
354 if (isspace((int) *cp) || *cp == '.') {
355 cp++;
356 continue;
358 if (!isdigit((int) *cp)) {
359 return 0;
361 if ((subid = atoi(cp)) > 255) {
362 return 0;
364 if ((*out_len >= *buf_len) &&
365 !(allow_realloc && snmp_realloc(buf, buf_len))) {
366 return 0;
368 *(*buf + *out_len) = (u_char) subid;
369 (*out_len)++;
370 while (isdigit((int) *cp)) {
371 cp++;
374 return 1;
378 snmp_hex_to_binary(u_char ** buf, size_t * buf_len, size_t * out_len,
379 int allow_realloc, const char *hex)
381 int subid = 0;
382 const char *cp = hex;
384 if (buf == NULL || buf_len == NULL || out_len == NULL || hex == NULL) {
385 return 0;
388 if ((*cp == '0') && ((*(cp + 1) == 'x') || (*(cp + 1) == 'X'))) {
389 cp += 2;
392 while (*cp != '\0') {
393 if (isspace((int) *cp)) {
394 cp++;
395 continue;
397 if (!isxdigit((int) *cp)) {
398 return 0;
400 if (sscanf(cp, "%2x", &subid) == 0) {
401 return 0;
403 if ((*out_len >= *buf_len) &&
404 !(allow_realloc && snmp_realloc(buf, buf_len))) {
405 return 0;
407 *(*buf + *out_len) = (u_char) subid;
408 (*out_len)++;
409 if (*++cp == '\0') {
411 * Odd number of hex digits is an error.
413 return 0;
414 } else {
415 cp++;
418 return 1;
421 /*******************************************************************-o-******
422 * dump_chunk
424 * Parameters:
425 * *title (May be NULL.)
426 * *buf
427 * size
429 void
430 dump_chunk(const char *debugtoken, const char *title, const u_char * buf,
431 int size)
433 u_int printunit = 64; /* XXX Make global. */
434 char chunk[SNMP_MAXBUF], *s, *sp;
436 if (title && (*title != '\0')) {
437 DEBUGMSGTL((debugtoken, "%s\n", title));
441 memset(chunk, 0, SNMP_MAXBUF);
442 size = binary_to_hex(buf, size, &s);
443 sp = s;
445 while (size > 0) {
446 if (size > (int) printunit) {
447 strncpy(chunk, sp, printunit);
448 chunk[printunit] = '\0';
449 DEBUGMSGTL((debugtoken, "\t%s\n", chunk));
450 } else {
451 DEBUGMSGTL((debugtoken, "\t%s\n", sp));
454 sp += printunit;
455 size -= printunit;
459 SNMP_FREE(s);
461 } /* end dump_chunk() */
466 /*******************************************************************-o-******
467 * dump_snmpEngineID
469 * Parameters:
470 * *estring
471 * *estring_len
473 * Returns:
474 * Allocated memory pointing to a string of buflen char representing
475 * a printf'able form of the snmpEngineID.
477 * -OR- NULL on error.
480 * Translates the snmpEngineID TC into a printable string. From RFC 2271,
481 * Section 5 (pp. 36-37):
483 * First bit: 0 Bit string structured by means non-SNMPv3.
484 * 1 Structure described by SNMPv3 SnmpEngineID TC.
486 * Bytes 1-4: Enterprise ID. (High bit of first byte is ignored.)
488 * Byte 5: 0 (RESERVED by IANA.)
489 * 1 IPv4 address. ( 4 octets)
490 * 2 IPv6 address. ( 16 octets)
491 * 3 MAC address. ( 6 octets)
492 * 4 Locally defined text. (0-27 octets)
493 * 5 Locally defined octets. (0-27 octets)
494 * 6-127 (RESERVED for enterprise.)
496 * Bytes 6-32: (Determined by byte 5.)
499 * Non-printable characters are given in hex. Text is given in quotes.
500 * IP and MAC addresses are given in standard (UN*X) conventions. Sections
501 * are comma separated.
503 * esp, remaining_len and s trace the state of the constructed buffer.
504 * s will be defined if there is something to return, and it will point
505 * to the end of the constructed buffer.
508 * ASSUME "Text" means printable characters.
510 * XXX Must the snmpEngineID always have a minimum length of 12?
511 * (Cf. part 2 of the TC definition.)
512 * XXX Does not enforce upper-bound of 32 bytes.
513 * XXX Need a switch to decide whether to use DNS name instead of a simple
514 * IP address.
516 * FIX Use something other than sprint_hexstring which doesn't add
517 * trailing spaces and (sometimes embedded) newlines...
519 #ifdef SNMP_TESTING_CODE
520 char *
521 dump_snmpEngineID(const u_char * estring, size_t * estring_len)
523 #define eb(b) ( *(esp+b) & 0xff )
525 int rval = SNMPERR_SUCCESS, gotviolation = 0, slen = 0;
526 u_int remaining_len;
528 char buf[SNMP_MAXBUF], *s = NULL, *t;
529 const u_char *esp = estring;
531 struct in_addr iaddr;
536 * Sanity check.
538 if (!estring || (*estring_len <= 0)) {
539 QUITFUN(SNMPERR_GENERR, dump_snmpEngineID_quit);
541 remaining_len = *estring_len;
542 memset(buf, 0, SNMP_MAXBUF);
547 * Test first bit. Return immediately with a hex string, or
548 * begin by formatting the enterprise ID.
550 if (!(*esp & 0x80)) {
551 sprint_hexstring(buf, esp, remaining_len);
552 s = strchr(buf, '\0');
553 s -= 1;
554 goto dump_snmpEngineID_quit;
557 s = buf;
558 s += sprintf(s, "enterprise %d, ", ((*(esp + 0) & 0x7f) << 24) |
559 ((*(esp + 1) & 0xff) << 16) |
560 ((*(esp + 2) & 0xff) << 8) | ((*(esp + 3) & 0xff)));
562 * XXX Ick.
565 if (remaining_len < 5) { /* XXX Violating string. */
566 goto dump_snmpEngineID_quit;
569 esp += 4; /* Incremented one more in the switch below. */
570 remaining_len -= 5;
575 * Act on the fifth byte.
577 switch ((int) *esp++) {
578 case 1: /* IPv4 address. */
580 if (remaining_len < 4)
581 goto dump_snmpEngineID_violation;
582 memcpy(&iaddr.s_addr, esp, 4);
584 if (!(t = inet_ntoa(iaddr)))
585 goto dump_snmpEngineID_violation;
586 s += sprintf(s, "%s", t);
588 esp += 4;
589 remaining_len -= 4;
590 break;
592 case 2: /* IPv6 address. */
594 if (remaining_len < 16)
595 goto dump_snmpEngineID_violation;
597 s += sprintf(s,
598 "%02X%02X %02X%02X %02X%02X %02X%02X::"
599 "%02X%02X %02X%02X %02X%02X %02X%02X",
600 eb(0), eb(1), eb(2), eb(3),
601 eb(4), eb(5), eb(6), eb(7),
602 eb(8), eb(9), eb(10), eb(11),
603 eb(12), eb(13), eb(14), eb(15));
605 esp += 16;
606 remaining_len -= 16;
607 break;
609 case 3: /* MAC address. */
611 if (remaining_len < 6)
612 goto dump_snmpEngineID_violation;
614 s += sprintf(s, "%02X:%02X:%02X:%02X:%02X:%02X",
615 eb(0), eb(1), eb(2), eb(3), eb(4), eb(5));
617 esp += 6;
618 remaining_len -= 6;
619 break;
621 case 4: /* Text. */
624 * Doesn't exist on all (many) architectures
627 * s += snprintf(s, remaining_len+3, "\"%s\"", esp);
629 s += sprintf(s, "\"%s\"", esp);
630 goto dump_snmpEngineID_quit;
631 break;
632 /*NOTREACHED*/ case 5: /* Octets. */
634 sprint_hexstring(s, esp, remaining_len);
635 s = strchr(buf, '\0');
636 s -= 1;
637 goto dump_snmpEngineID_quit;
638 break;
639 /*NOTREACHED*/ dump_snmpEngineID_violation:
640 case 0: /* Violation of RESERVED,
641 * * -OR- of expected length.
643 gotviolation = 1;
644 s += sprintf(s, "!!! ");
646 default: /* Unknown encoding. */
648 if (!gotviolation) {
649 s += sprintf(s, "??? ");
651 sprint_hexstring(s, esp, remaining_len);
652 s = strchr(buf, '\0');
653 s -= 1;
655 goto dump_snmpEngineID_quit;
657 } /* endswitch */
662 * Cases 1-3 (IP and MAC addresses) should not have trailing
663 * octets, but perhaps they do. Throw them in too. XXX
665 if (remaining_len > 0) {
666 s += sprintf(s, " (??? ");
668 sprint_hexstring(s, esp, remaining_len);
669 s = strchr(buf, '\0');
670 s -= 1;
672 s += sprintf(s, ")");
677 dump_snmpEngineID_quit:
678 if (s) {
679 slen = s - buf + 1;
680 s = calloc(1, slen);
681 memcpy(s, buf, (slen) - 1);
684 memset(buf, 0, SNMP_MAXBUF); /* XXX -- Overkill? XXX: Yes! */
686 return s;
688 #undef eb
689 } /* end dump_snmpEngineID() */
690 #endif /* SNMP_TESTING_CODE */
694 * create a new time marker.
695 * NOTE: Caller must free time marker when no longer needed.
697 marker_t
698 atime_newMarker(void)
700 marker_t pm = (marker_t) calloc(1, sizeof(struct timeval));
701 gettimeofday((struct timeval *) pm, 0);
702 return pm;
706 * set a time marker.
708 void
709 atime_setMarker(marker_t pm)
711 if (!pm)
712 return;
714 gettimeofday((struct timeval *) pm, 0);
719 * Returns the difference (in msec) between the two markers
721 long
722 atime_diff(marker_t first, marker_t second)
724 struct timeval *tv1, *tv2, diff;
726 tv1 = (struct timeval *) first;
727 tv2 = (struct timeval *) second;
729 diff.tv_sec = tv2->tv_sec - tv1->tv_sec - 1;
730 diff.tv_usec = tv2->tv_usec - tv1->tv_usec + 1000000;
732 return (diff.tv_sec * 1000 + diff.tv_usec / 1000);
736 * Returns the difference (in u_long msec) between the two markers
738 u_long
739 uatime_diff(marker_t first, marker_t second)
741 struct timeval *tv1, *tv2, diff;
743 tv1 = (struct timeval *) first;
744 tv2 = (struct timeval *) second;
746 diff.tv_sec = tv2->tv_sec - tv1->tv_sec - 1;
747 diff.tv_usec = tv2->tv_usec - tv1->tv_usec + 1000000;
749 return (((u_long) diff.tv_sec) * 1000 + diff.tv_usec / 1000);
753 * Returns the difference (in u_long 1/100th secs) between the two markers
754 * (functionally this is what sysUpTime needs)
756 u_long
757 uatime_hdiff(marker_t first, marker_t second)
759 struct timeval *tv1, *tv2, diff;
760 u_long res;
762 tv1 = (struct timeval *) first;
763 tv2 = (struct timeval *) second;
765 diff.tv_sec = tv2->tv_sec - tv1->tv_sec - 1;
766 diff.tv_usec = tv2->tv_usec - tv1->tv_usec + 1000000;
768 res = ((u_long) diff.tv_sec) * 100 + diff.tv_usec / 10000;
769 return res;
773 * Test: Has (marked time plus delta) exceeded current time (in msec) ?
774 * Returns 0 if test fails or cannot be tested (no marker).
777 atime_ready(marker_t pm, int deltaT)
779 marker_t now;
780 long diff;
781 if (!pm)
782 return 0;
784 now = atime_newMarker();
786 diff = atime_diff(pm, now);
787 free(now);
788 if (diff < deltaT)
789 return 0;
791 return 1;
795 * Test: Has (marked time plus delta) exceeded current time (in msec) ?
796 * Returns 0 if test fails or cannot be tested (no marker).
799 uatime_ready(marker_t pm, unsigned int deltaT)
801 marker_t now;
802 u_long diff;
803 if (!pm)
804 return 0;
806 now = atime_newMarker();
808 diff = uatime_diff(pm, now);
809 free(now);
810 if (diff < deltaT)
811 return 0;
813 return 1;
818 * Time-related utility functions
822 * Return the number of timeTicks since the given marker
825 marker_tticks(marker_t pm)
827 int res;
828 marker_t now = atime_newMarker();
830 res = atime_diff(pm, now);
831 free(now);
832 return res / 10; /* atime_diff works in msec, not csec */
836 timeval_tticks(struct timeval *tv)
838 return marker_tticks((marker_t) tv);