Coverage Report

Created: 2023-09-25 06:06

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