Coverage Report

Created: 2026-01-10 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/net-snmp/snmplib/oid_stash.c
Line
Count
Source
1
#include <net-snmp/net-snmp-config.h>
2
3
#include <string.h>
4
5
#include <stdlib.h>
6
#include <sys/types.h>
7
8
#include <net-snmp/net-snmp-features.h>
9
#include <net-snmp/net-snmp-includes.h>
10
11
netsnmp_feature_child_of(oid_stash_all, libnetsnmp);
12
netsnmp_feature_child_of(oid_stash, oid_stash_all);
13
netsnmp_feature_child_of(oid_stash_no_free, oid_stash_all);
14
15
#ifndef NETSNMP_FEATURE_REMOVE_OID_STASH
16
17
/** @defgroup oid_stash Store and retrieve data referenced by an OID.
18
    This is essentially a way of storing data associated with a given
19
    OID.  It stores a bunch of data pointers within a memory tree that
20
    allows fairly efficient lookups with a heavily populated tree.
21
    @ingroup library
22
    @{
23
*/
24
25
/*
26
 * xxx-rks: when you have some spare time:
27
 *
28
 * b) basically, everything currently creates one node per sub-oid,
29
 *    which is less than optimal. add code to create nodes with the
30
 *    longest possible OID per node, and split nodes when necessary
31
 *    during adds.
32
 *
33
 * c) If you are feeling really ambitious, also merge split nodes if
34
 *    possible on a delete.
35
 *
36
 * xxx-wes: uh, right, like I *ever* have that much time.
37
 *
38
 */
39
40
/***************************************************************************
41
 *
42
 *
43
 ***************************************************************************/
44
45
/**
46
 * Create an netsnmp_oid_stash node
47
 *
48
 * @param mysize  the size of the child pointer array
49
 *
50
 * @return NULL on error, otherwise the newly allocated node
51
 */
52
netsnmp_oid_stash_node *
53
netsnmp_oid_stash_create_sized_node(size_t mysize)
54
0
{
55
0
    netsnmp_oid_stash_node *ret;
56
0
    ret = SNMP_MALLOC_TYPEDEF(netsnmp_oid_stash_node);
57
0
    if (!ret)
58
0
        return NULL;
59
0
    ret->children = calloc(mysize, sizeof(netsnmp_oid_stash_node *));
60
0
    if (!ret->children) {
61
0
        free(ret);
62
0
        return NULL;
63
0
    }
64
0
    ret->children_size = mysize;
65
0
    return ret;
66
0
}
67
68
/** Creates a netsnmp_oid_stash_node.
69
 * Assumes you want the default OID_STASH_CHILDREN_SIZE hash size for the node.
70
 * @return NULL on error, otherwise the newly allocated node
71
 */
72
NETSNMP_INLINE netsnmp_oid_stash_node *
73
netsnmp_oid_stash_create_node(void)
74
0
{
75
0
    return netsnmp_oid_stash_create_sized_node(OID_STASH_CHILDREN_SIZE);
76
0
}
77
78
netsnmp_feature_child_of(oid_stash_add_data, oid_stash_all);
79
#ifndef NETSNMP_FEATURE_REMOVE_OID_STASH_ADD_DATA
80
/** adds data to the stash at a given oid.
81
82
 * @param root the top of the stash tree
83
 * @param lookup the oid index to store the data at.
84
 * @param lookup_len the length of the lookup oid.
85
 * @param mydata the data to store
86
87
 * @return SNMPERR_SUCCESS on success, SNMPERR_GENERR if data is
88
   already there, SNMPERR_MALLOC on malloc failures or if arguments
89
   passed in with NULL values.
90
 */
91
int
92
netsnmp_oid_stash_add_data(netsnmp_oid_stash_node **root,
93
                           const oid * lookup, size_t lookup_len, void *mydata)
94
0
{
95
0
    netsnmp_oid_stash_node *curnode, *tmpp, *loopp;
96
0
    unsigned int    i;
97
98
0
    if (!root || !lookup || lookup_len == 0)
99
0
        return SNMPERR_GENERR;
100
101
0
    if (!*root) {
102
0
        *root = netsnmp_oid_stash_create_node();
103
0
        if (!*root)
104
0
            return SNMPERR_MALLOC;
105
0
    }
106
0
    DEBUGMSGTL(( "oid_stash", "stash_add_data "));
107
0
    DEBUGMSGOID(("oid_stash", lookup, lookup_len));
108
0
    DEBUGMSG((   "oid_stash", "\n"));
109
0
    tmpp = NULL;
110
0
    for (curnode = *root, i = 0; i < lookup_len; i++) {
111
0
        tmpp = curnode->children[lookup[i] % curnode->children_size];
112
0
        if (!tmpp) {
113
            /*
114
             * no child in array at all 
115
             */
116
0
            tmpp = curnode->children[lookup[i] % curnode->children_size] =
117
0
                netsnmp_oid_stash_create_node();
118
0
            tmpp->value = lookup[i];
119
0
            tmpp->parent = curnode;
120
0
        } else {
121
0
            for (loopp = tmpp; loopp; loopp = loopp->next_sibling) {
122
0
                if (loopp->value == lookup[i])
123
0
                    break;
124
0
            }
125
0
            if (loopp) {
126
0
                tmpp = loopp;
127
0
            } else {
128
                /*
129
                 * none exists.  Create it 
130
                 */
131
0
                loopp = netsnmp_oid_stash_create_node();
132
0
                loopp->value = lookup[i];
133
0
                loopp->next_sibling = tmpp;
134
0
                loopp->parent = curnode;
135
0
                tmpp->prev_sibling = loopp;
136
0
                curnode->children[lookup[i] % curnode->children_size] =
137
0
                    loopp;
138
0
                tmpp = loopp;
139
0
            }
140
            /*
141
             * tmpp now points to the proper node 
142
             */
143
0
        }
144
0
        curnode = tmpp;
145
0
    }
146
    /*
147
     * tmpp now points to the exact match 
148
     */
149
0
    if (curnode->thedata)
150
0
        return SNMPERR_GENERR;
151
0
    if (NULL == tmpp)
152
0
        return SNMPERR_GENERR;
153
0
    tmpp->thedata = mydata;
154
0
    return SNMPERR_SUCCESS;
155
0
}
156
#endif /* NETSNMP_FEATURE_REMOVE_OID_STASH_ADD_DATA */
157
158
/** returns a node associated with a given OID.
159
 * @param root the top of the stash tree
160
 * @param lookup the oid to look up a node for.
161
 * @param lookup_len the length of the lookup oid
162
 */
163
netsnmp_oid_stash_node *
164
netsnmp_oid_stash_get_node(netsnmp_oid_stash_node *root,
165
                           const oid * lookup, size_t lookup_len)
166
0
{
167
0
    netsnmp_oid_stash_node *curnode, *tmpp, *loopp;
168
0
    unsigned int    i;
169
170
0
    if (!root)
171
0
        return NULL;
172
0
    tmpp = NULL;
173
0
    for (curnode = root, i = 0; i < lookup_len; i++) {
174
0
        tmpp = curnode->children[lookup[i] % curnode->children_size];
175
0
        if (!tmpp) {
176
0
            return NULL;
177
0
        } else {
178
0
            for (loopp = tmpp; loopp; loopp = loopp->next_sibling) {
179
0
                if (loopp->value == lookup[i])
180
0
                    break;
181
0
            }
182
0
            if (loopp) {
183
0
                tmpp = loopp;
184
0
            } else {
185
0
                return NULL;
186
0
            }
187
0
        }
188
0
        curnode = tmpp;
189
0
    }
190
0
    return tmpp;
191
0
}
192
193
/** returns the next node associated with a given OID. INCOMPLETE.
194
    This is equivalent to a GETNEXT operation.
195
 * @internal
196
 * @param root the top of the stash tree
197
 * @param lookup the oid to look up a node for.
198
 * @param lookup_len the length of the lookup oid
199
 */
200
netsnmp_feature_child_of(oid_stash_iterate, oid_stash_all);
201
#ifndef NETSNMP_FEATURE_REMOVE_OID_STASH_ITERATE
202
netsnmp_oid_stash_node *
203
netsnmp_oid_stash_getnext_node(netsnmp_oid_stash_node *root,
204
                               oid * lookup, size_t lookup_len)
205
0
{
206
0
    netsnmp_oid_stash_node *curnode, *tmpp, *loopp;
207
0
    unsigned int    i, j, bigger_than = 0, do_bigger = 0;
208
209
0
    if (!root)
210
0
        return NULL;
211
0
    tmpp = NULL;
212
213
    /* get closest matching node */
214
0
    for (curnode = root, i = 0; i < lookup_len; i++) {
215
0
        tmpp = curnode->children[lookup[i] % curnode->children_size];
216
0
        if (!tmpp) {
217
0
            break;
218
0
        } else {
219
0
            for (loopp = tmpp; loopp; loopp = loopp->next_sibling) {
220
0
                if (loopp->value == lookup[i])
221
0
                    break;
222
0
            }
223
0
            if (loopp) {
224
0
                tmpp = loopp;
225
0
            } else {
226
0
                break;
227
0
            }
228
0
        }
229
0
        curnode = tmpp;
230
0
    }
231
232
    /* find the *next* node lexographically greater */
233
0
    if (!curnode)
234
0
        return NULL; /* ack! */
235
236
0
    if (i+1 < lookup_len) {
237
0
        bigger_than = lookup[i+1];
238
0
        do_bigger = 1;
239
0
    }
240
241
0
    do {
242
        /* check the children first */
243
0
        tmpp = NULL;
244
        /* next child must be (next) greater than our next search node */
245
        /* XXX: should start this loop at best_nums[i]%... and wrap */
246
0
        for(j = 0; j < curnode->children_size; j++) {
247
0
            for (loopp = curnode->children[j];
248
0
                 loopp; loopp = loopp->next_sibling) {
249
0
                if ((!do_bigger || loopp->value > bigger_than) &&
250
0
                    (!tmpp || tmpp->value > loopp->value)) {
251
0
                    tmpp = loopp;
252
                    /* XXX: can do better and include min_nums[i] */
253
0
                    if (tmpp->value <= curnode->children_size-1) {
254
                        /* best we can do. */
255
0
                        goto done_this_loop;
256
0
                    }
257
0
                }
258
0
            }
259
0
        }
260
261
0
      done_this_loop:
262
0
        if (tmpp && tmpp->thedata)
263
            /* found a node with data.  Go with it. */
264
0
            return tmpp;
265
266
0
        if (tmpp) {
267
            /* found a child node without data, maybe find a grandchild? */
268
0
            do_bigger = 0;
269
0
            curnode = tmpp;
270
0
        } else {
271
            /* no respectable children (the bums), we'll have to go up.
272
               But to do so, they must be better than our current best_num + 1.
273
            */
274
0
            do_bigger = 1;
275
0
            bigger_than = curnode->value;
276
0
            curnode = curnode->parent;
277
0
        }
278
0
    } while (curnode);
279
280
    /* fell off the top */
281
0
    return NULL;
282
0
}
283
#endif /* NETSNMP_FEATURE_REMOVE_OID_STASH_ITERATE */
284
285
netsnmp_feature_child_of(oid_stash_get_data, oid_stash_all);
286
#ifndef NETSNMP_FEATURE_REMOVE_OID_STASH_GET_DATA
287
/** returns a data pointer associated with a given OID.
288
289
    This is equivalent to netsnmp_oid_stash_get_node, but returns only
290
    the data not the entire node.
291
292
 * @param root the top of the stash
293
 * @param lookup the oid to search for
294
 * @param lookup_len the length of the search oid.
295
 */
296
void           *
297
netsnmp_oid_stash_get_data(netsnmp_oid_stash_node *root,
298
                           const oid * lookup, size_t lookup_len)
299
0
{
300
0
    netsnmp_oid_stash_node *ret;
301
0
    ret = netsnmp_oid_stash_get_node(root, lookup, lookup_len);
302
0
    if (ret)
303
0
        return ret->thedata;
304
0
    return NULL;
305
0
}
306
#endif /* NETSNMP_FEATURE_REMOVE_OID_STASH_GET_DATA */
307
308
netsnmp_feature_child_of(oid_stash_store_all, oid_stash_all);
309
#ifndef NETSNMP_FEATURE_REMOVE_OID_STASH_STORE_ALL
310
/** a wrapper around netsnmp_oid_stash_store for use with a snmp_alarm.
311
 * when calling snmp_alarm, you can list this as a callback.  The
312
 * clientarg should be a pointer to a netsnmp_oid_stash_save_info
313
 * pointer.  It can also be called directly, of course.  The last
314
 * argument (clientarg) is the only one that is used.  The rest are
315
 * ignored by the function.
316
 * @param majorID
317
 * @param minorID
318
 * @param serverarg
319
 * @param clientarg A pointer to a netsnmp_oid_stash_save_info structure.
320
 */
321
int
322
netsnmp_oid_stash_store_all(int majorID, int minorID,
323
0
                            void *serverarg, void *clientarg) {
324
0
    oid oidbase[MAX_OID_LEN];
325
0
    netsnmp_oid_stash_save_info *sinfo;
326
    
327
0
    if (!clientarg)
328
0
        return SNMP_ERR_NOERROR;
329
    
330
0
    sinfo = (netsnmp_oid_stash_save_info *) clientarg;
331
0
    netsnmp_oid_stash_store(*(sinfo->root), sinfo->token, sinfo->dumpfn,
332
0
                            oidbase,0);
333
0
    return SNMP_ERR_NOERROR;
334
0
}
335
#endif /* NETSNMP_FEATURE_REMOVE_OID_STASH_STORE_ALL */
336
337
/** stores data in a stash tree to persistent storage.
338
339
    This function can be called to save all data in a stash tree to
340
    Net-SNMP's percent storage.  Make sure you register a parsing
341
    function with the read_config system to re-incorporate your saved
342
    data into future trees.
343
344
    @param root the top of the stash to store.
345
    @param tokenname the file token name to save in (passing "snmpd" will
346
    save things into snmpd.conf).
347
    @param dumpfn A function which can dump the data stored at a particular
348
    node into a char buffer.
349
    @param curoid must be a pointer to a OID array of length MAX_OID_LEN.
350
    @param curoid_len must be 0 for the top level call.
351
*/
352
void
353
netsnmp_oid_stash_store(netsnmp_oid_stash_node *root,
354
                        const char *tokenname, NetSNMPStashDump *dumpfn,
355
0
                        oid *curoid, size_t curoid_len) {
356
357
0
    char buf[SNMP_MAXBUF];
358
0
    netsnmp_oid_stash_node *tmpp;
359
0
    char *cp;
360
0
    char *appname = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
361
0
                                          NETSNMP_DS_LIB_APPTYPE);
362
0
    int i;
363
    
364
0
    if (!tokenname || !root || !curoid || !dumpfn)
365
0
        return;
366
367
0
    for (i = 0; i < (int)root->children_size; i++) {
368
0
        if (root->children[i]) {
369
0
            for (tmpp = root->children[i]; tmpp; tmpp = tmpp->next_sibling) {
370
0
                curoid[curoid_len] = tmpp->value;
371
0
                if (tmpp->thedata) {
372
0
                    snprintf(buf, sizeof(buf), "%s ", tokenname);
373
0
                    cp = read_config_save_objid(buf+strlen(buf), curoid,
374
0
                                                curoid_len+1);
375
0
                    *cp++ = ' ';
376
0
                    *cp = '\0';
377
0
                    if ((*dumpfn)(cp, sizeof(buf) - strlen(buf),
378
0
                                  tmpp->thedata, tmpp))
379
0
                        read_config_store(appname, buf);
380
0
                }
381
0
                netsnmp_oid_stash_store(tmpp, tokenname, dumpfn,
382
0
                                        curoid, curoid_len+1);
383
0
            }
384
0
        }
385
0
    }
386
0
}
387
388
/** For debugging: dump the netsnmp_oid_stash tree to stdout
389
    @param root The top of the tree
390
    @param prefix a character string prefix printed to the beginning of each line.
391
*/
392
void 
393
oid_stash_dump(netsnmp_oid_stash_node *root, char *prefix)
394
0
{
395
0
    char            myprefix[MAX_OID_LEN * 4];
396
0
    netsnmp_oid_stash_node *tmpp;
397
0
    int             prefix_len = strlen(prefix) + 1;    /* actually it's +2 */
398
0
    unsigned int    i;
399
400
0
    memset(myprefix, ' ', MAX_OID_LEN * 4);
401
0
    myprefix[prefix_len] = '\0';
402
403
0
    for (i = 0; i < root->children_size; i++) {
404
0
        if (root->children[i]) {
405
0
            for (tmpp = root->children[i]; tmpp; tmpp = tmpp->next_sibling) {
406
0
                printf("%s%" NETSNMP_PRIo "d@%d: %s\n", prefix, tmpp->value, i,
407
0
                       (tmpp->thedata) ? "DATA" : "");
408
0
                oid_stash_dump(tmpp, myprefix);
409
0
            }
410
0
        }
411
0
    }
412
0
}
413
414
/** Frees the contents of a netsnmp_oid_stash tree.
415
    @param root the top of the tree (or branch to be freed)
416
    @param freefn The function to be called on each data (void *)
417
    pointer.  If left NULL the system free() function will be called
418
*/
419
void
420
netsnmp_oid_stash_free(netsnmp_oid_stash_node **root,
421
0
                       NetSNMPStashFreeNode *freefn) {
422
423
0
    netsnmp_oid_stash_node *curnode, *tmpp;
424
0
    unsigned int    i;
425
426
0
    if (!root || !*root)
427
0
        return;
428
429
    /* loop through all our children and free each node */
430
0
    for (i = 0; i < (*root)->children_size; i++) {
431
0
        if ((*root)->children[i]) {
432
0
            for(tmpp = (*root)->children[i]; tmpp; tmpp = curnode) {
433
0
                if (tmpp->thedata) {
434
0
                    if (freefn)
435
0
                        (*freefn)(tmpp->thedata);
436
0
                    else
437
0
                        free(tmpp->thedata);
438
0
                }
439
0
                curnode = tmpp->next_sibling;
440
0
                netsnmp_oid_stash_free(&tmpp, freefn);
441
0
            }
442
0
        }
443
0
    }
444
0
    free((*root)->children);
445
0
    free (*root);
446
0
    *root = NULL;
447
0
}
448
449
#else /* NETSNMP_FEATURE_REMOVE_OID_STASH */
450
netsnmp_feature_unused(oid_stash);
451
#endif/* NETSNMP_FEATURE_REMOVE_OID_STASH */
452
453
#ifndef NETSNMP_FEATURE_REMOVE_OID_STASH_NO_FREE
454
void
455
netsnmp_oid_stash_no_free(void *bogus)
456
0
{
457
    /* noop */
458
0
}
459
#endif /* NETSNMP_FEATURE_REMOVE_OID_STASH_NO_FREE */
460
461
/** @} */