Coverage Report

Created: 2025-10-10 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/net-snmp/agent/helpers/table.c
Line
Count
Source
1
/*
2
 * table.c 
3
 */
4
5
/* Portions of this file are subject to the following copyright(s).  See
6
 * the Net-SNMP's COPYING file for more details and other copyrights
7
 * that may apply:
8
 */
9
/*
10
 * Portions of this file are copyrighted by:
11
 * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
12
 * Use is subject to license terms specified in the COPYING file
13
 * distributed with the Net-SNMP package.
14
 */
15
/*
16
 * Portions of this file are copyrighted by:
17
 * Copyright (C) 2007 Apple, Inc. All rights reserved.
18
 * Use is subject to license terms specified in the COPYING file
19
 * distributed with the Net-SNMP package.
20
 *
21
 * Portions of this file are copyrighted by:
22
 * Copyright (c) 2016 VMware, Inc. All rights reserved.
23
 * Use is subject to license terms specified in the COPYING file
24
 * distributed with the Net-SNMP package.
25
 */
26
27
#include <net-snmp/net-snmp-config.h>
28
29
#include <net-snmp/net-snmp-features.h>
30
#include <net-snmp/net-snmp-includes.h>
31
#include <net-snmp/agent/net-snmp-agent-includes.h>
32
33
#include <net-snmp/agent/table.h>
34
35
#ifndef NETSNMP_NO_WRITE_SUPPORT
36
netsnmp_feature_require(oid_stash);
37
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
38
39
#ifdef HAVE_STRING_H
40
#include <string.h>
41
#else
42
#include <strings.h>
43
#endif
44
45
#include <net-snmp/library/snmp_assert.h>
46
47
netsnmp_feature_child_of(table_all, mib_helpers);
48
49
netsnmp_feature_child_of(table_build_result, table_all);
50
netsnmp_feature_child_of(table_get_or_create_row_stash, table_all);
51
netsnmp_feature_child_of(registration_owns_table_info, table_all);
52
netsnmp_feature_child_of(table_sparse, table_all);
53
54
static void     table_helper_cleanup(netsnmp_agent_request_info *reqinfo,
55
                                     netsnmp_request_info *request,
56
                                     int status);
57
static void     table_data_free_func(void *data);
58
static int
59
sparse_table_helper_handler(netsnmp_mib_handler *handler,
60
                            netsnmp_handler_registration *reginfo,
61
                            netsnmp_agent_request_info *reqinfo,
62
                            netsnmp_request_info *requests);
63
64
/** @defgroup table table
65
 *  Helps you implement a table.
66
 *  @ingroup handler
67
 *
68
 *  This handler helps you implement a table by doing some of the
69
 *  processing for you.
70
 *  
71
 *  This handler truly shows the power of the new handler mechanism.
72
 *  By creating a table handler and injecting it into your calling
73
 *  chain, or by using the netsnmp_register_table() function to register your
74
 *  table, you get access to some pre-parsed information.
75
 *  Specifically, the table handler pulls out the column number and
76
 *  indexes from the request oid so that you don't have to do the
77
 *  complex work to do that parsing within your own code.
78
 *
79
 *  To do this, the table handler needs to know up front how your
80
 *  table is structured.  To inform it about this, you fill in a
81
 *  table_registration_info structure that is passed to the table
82
 *  handler.  It contains the asn index types for the table as well as
83
 *  the minimum and maximum column that should be used.
84
 *  
85
 *  @{
86
 */
87
88
/** Given a netsnmp_table_registration_info object, creates a table handler.
89
 *  You can use this table handler by injecting it into a calling
90
 *  chain.  When the handler gets called, it'll do processing and
91
 *  store it's information into the request->parent_data structure.
92
 *
93
 *  The table helper handler pulls out the column number and indexes from 
94
 *  the request oid so that you don't have to do the complex work of
95
 *  parsing within your own code.
96
 *
97
 *  @param tabreq is a pointer to a netsnmp_table_registration_info struct.
98
 *  The table handler needs to know up front how your table is structured.
99
 *  A netsnmp_table_registration_info structure that is 
100
 *  passed to the table handler should contain the asn index types for the 
101
 *  table as well as the minimum and maximum column that should be used.
102
 *
103
 *  @return Returns a pointer to a netsnmp_mib_handler struct which contains
104
 *  the handler's name and the access method
105
 *
106
 */
107
netsnmp_mib_handler *
108
netsnmp_get_table_handler(netsnmp_table_registration_info *tabreq)
109
0
{
110
0
    netsnmp_mib_handler *ret = NULL;
111
112
0
    if (!tabreq) {
113
0
        snmp_log(LOG_INFO, "netsnmp_get_table_handler(NULL) called\n");
114
0
        return NULL;
115
0
    }
116
117
0
    ret = netsnmp_create_handler(TABLE_HANDLER_NAME, table_helper_handler);
118
0
    if (ret) {
119
0
        ret->myvoid = (void *) tabreq;
120
0
        tabreq->number_indexes = count_varbinds(tabreq->indexes);
121
0
    }
122
0
    return ret;
123
0
}
124
125
static void *netsnmp_clone_tri(void *tri)
126
0
{
127
0
    return netsnmp_table_registration_info_clone(tri);
128
0
}
129
130
static void netsnmp_free_tri(void *tri)
131
0
{
132
0
    netsnmp_table_registration_info_free(tri);
133
0
}
134
135
/** Configures a handler such that table registration information is freed by
136
 *  netsnmp_handler_free(). Should only be called if handler->myvoid points to
137
 *  an object of type netsnmp_table_registration_info.
138
 */
139
void netsnmp_handler_owns_table_info(netsnmp_mib_handler *handler)
140
0
{
141
0
    netsnmp_assert(handler);
142
0
    netsnmp_assert(handler->myvoid);
143
0
    handler->data_clone = netsnmp_clone_tri;
144
0
    handler->data_free = netsnmp_free_tri;
145
0
}
146
147
/** Configures a handler such that table registration information is freed by
148
 *  netsnmp_handler_free(). Should only be called if reg->handler->myvoid
149
 *  points to an object of type netsnmp_table_registration_info.
150
 */
151
#ifndef NETSNMP_FEATURE_REMOVE_REGISTRATION_OWNS_TABLE_INFO
152
void netsnmp_registration_owns_table_info(netsnmp_handler_registration *reg)
153
0
{
154
0
    if (reg)
155
0
        netsnmp_handler_owns_table_info(reg->handler);
156
0
}
157
#endif /* NETSNMP_FEATURE_REMOVE_REGISTRATION_OWNS_TABLE_INFO */
158
159
/** creates a table handler given the netsnmp_table_registration_info object,
160
 *  inserts it into the request chain and then calls
161
 *  netsnmp_register_handler() to register the table into the agent.
162
 */
163
int
164
netsnmp_register_table(netsnmp_handler_registration *reginfo,
165
                       netsnmp_table_registration_info *tabreq)
166
0
{
167
0
    netsnmp_mib_handler *handler = netsnmp_get_table_handler(tabreq);
168
0
    if (!handler ||
169
0
        (netsnmp_inject_handler(reginfo, handler) != SNMPERR_SUCCESS)) {
170
0
        snmp_log(LOG_ERR, "could not create table handler\n");
171
0
        netsnmp_handler_free(handler);
172
0
        netsnmp_handler_registration_free(reginfo);
173
0
        return MIB_REGISTRATION_FAILED;
174
0
    }
175
176
0
    return netsnmp_register_handler(reginfo);
177
0
}
178
179
int
180
netsnmp_unregister_table(netsnmp_handler_registration *reginfo)
181
0
{
182
    /* Locate "this" reginfo */
183
    /* SNMP_FREE(reginfo->myvoid); */
184
0
    return netsnmp_unregister_handler(reginfo);
185
0
}
186
187
/** Extracts the processed table information from a given request.
188
 *  Call this from subhandlers on a request to extract the processed
189
 *  netsnmp_request_info information.  The resulting information includes the
190
 *  index values and the column number.
191
 *
192
 * @param request populated netsnmp request structure
193
 *
194
 * @return populated netsnmp_table_request_info structure
195
 */
196
NETSNMP_INLINE netsnmp_table_request_info *
197
netsnmp_extract_table_info(netsnmp_request_info *request)
198
0
{
199
0
    return (netsnmp_table_request_info *)
200
0
        netsnmp_request_get_list_data(request, TABLE_HANDLER_NAME);
201
0
}
202
203
/** extracts the registered netsnmp_table_registration_info object from a
204
 *  netsnmp_handler_registration object */
205
netsnmp_table_registration_info *
206
netsnmp_find_table_registration_info(netsnmp_handler_registration *reginfo)
207
0
{
208
0
    return (netsnmp_table_registration_info *)
209
0
        netsnmp_find_handler_data_by_name(reginfo, TABLE_HANDLER_NAME);
210
0
}
211
212
/** implements the table helper handler */
213
int
214
table_helper_handler(netsnmp_mib_handler *handler,
215
                     netsnmp_handler_registration *reginfo,
216
                     netsnmp_agent_request_info *reqinfo,
217
                     netsnmp_request_info *requests)
218
0
{
219
220
0
    netsnmp_request_info *request;
221
0
    netsnmp_table_registration_info *tbl_info;
222
0
    int             oid_index_pos;
223
0
    unsigned int    oid_column_pos;
224
0
    unsigned int    tmp_idx;
225
0
    ssize_t       tmp_len;
226
0
    int             incomplete, out_of_range;
227
0
    int             status = SNMP_ERR_NOERROR, need_processing = 0;
228
0
    oid            *tmp_name;
229
0
    netsnmp_table_request_info *tbl_req_info;
230
0
    netsnmp_variable_list *vb;
231
232
0
    if (!reginfo || !handler)
233
0
        return SNMPERR_GENERR;
234
235
0
    oid_index_pos  = reginfo->rootoid_len + 2;
236
0
    oid_column_pos = reginfo->rootoid_len + 1;
237
0
    tbl_info = (netsnmp_table_registration_info *) handler->myvoid;
238
239
0
    if ((!handler->myvoid) || (!tbl_info->indexes)) {
240
0
        snmp_log(LOG_ERR, "improperly registered table found\n");
241
0
        snmp_log(LOG_ERR, "name: %s, table info: %p, indexes: %p\n",
242
0
                 handler->handler_name, handler->myvoid, tbl_info->indexes);
243
244
        /*
245
         * XXX-rks: unregister table? 
246
         */
247
0
        return SNMP_ERR_GENERR;
248
0
    }
249
250
0
    DEBUGIF("helper:table:req") {
251
0
        DEBUGMSGTL(("helper:table:req",
252
0
                    "Got %s (%d) mode request for handler %s: base oid:",
253
0
                    se_find_label_in_slist("agent_mode", reqinfo->mode),
254
0
                    reqinfo->mode, handler->handler_name));
255
0
        DEBUGMSGOID(("helper:table:req", reginfo->rootoid,
256
0
                     reginfo->rootoid_len));
257
0
        DEBUGMSG(("helper:table:req", "\n"));
258
0
    }
259
    
260
    /*
261
     * if the agent request info has a state reference, then this is a 
262
     * later pass of a set request and we can skip all the lookup stuff.
263
     *
264
     * xxx-rks: this might break for handlers which only handle one varbind
265
     * at a time... those handlers should not save data by their handler_name
266
     * in the netsnmp_agent_request_info. 
267
     */
268
0
    if (netsnmp_agent_get_list_data(reqinfo, handler->next->handler_name)) {
269
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
270
0
        if (MODE_IS_SET(reqinfo->mode)) {
271
0
            return netsnmp_call_next_handler(handler, reginfo, reqinfo,
272
0
                                             requests);
273
0
        } else {
274
0
#endif /* NETSNMP_NO_WRITE_SUPPORT */
275
/** XXX-rks: memory leak. add cleanup handler? */
276
0
            netsnmp_free_agent_data_sets(reqinfo);
277
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
278
0
        }
279
0
#endif /* NETSNMP_NO_WRITE_SUPPORT */
280
0
    }
281
282
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
283
0
    if ( MODE_IS_SET(reqinfo->mode) &&
284
0
         (reqinfo->mode != MODE_SET_RESERVE1)) {
285
        /*
286
         * for later set modes, we can skip all the index parsing,
287
         * and we always need to let child handlers have a chance
288
         * to clean up, if they were called in the first place (i.e. have
289
         * a valid table info pointer).
290
         */
291
0
        if(NULL == netsnmp_extract_table_info(requests)) {
292
0
            DEBUGMSGTL(("helper:table","no table info for set - skipping\n"));
293
0
        }
294
0
        else
295
0
            need_processing = 1;
296
0
    }
297
0
    else {
298
0
#endif /* NETSNMP_NO_WRITE_SUPPORT */
299
        /*
300
         * for GETS, only continue if we have at least one valid request.
301
         * for RESERVE1, only continue if we have indexes for all requests.
302
         */
303
           
304
    /*
305
     * loop through requests
306
     */
307
308
0
    for (request = requests; request; request = request->next) {
309
0
        netsnmp_variable_list *var = request->requestvb;
310
311
0
        DEBUGMSGOID(("verbose:table", var->name, var->name_length));
312
0
        DEBUGMSG(("verbose:table", "\n"));
313
314
0
        if (request->processed) {
315
0
            DEBUGMSG(("verbose:table", "already processed\n"));
316
0
            continue;
317
0
        }
318
0
        netsnmp_assert(request->status == SNMP_ERR_NOERROR);
319
320
        /*
321
         * this should probably be handled further up 
322
         */
323
0
        if ((reqinfo->mode == MODE_GET) && (var->type != ASN_NULL)) {
324
            /*
325
             * valid request if ASN_NULL 
326
             */
327
0
            DEBUGMSGTL(("helper:table",
328
0
                        "  GET var type is not ASN_NULL\n"));
329
0
            netsnmp_set_request_error(reqinfo, request,
330
0
                                      SNMP_ERR_WRONGTYPE);
331
0
            continue;
332
0
        }
333
334
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
335
0
        if (reqinfo->mode == MODE_SET_RESERVE1) {
336
0
            DEBUGIF("helper:table:set") {
337
0
                u_char         *buf = NULL;
338
0
                size_t          buf_len = 0, out_len = 0;
339
0
                DEBUGMSGTL(("helper:table:set", " SET_REQUEST for OID: "));
340
0
                DEBUGMSGOID(("helper:table:set", var->name, var->name_length));
341
0
                out_len = 0;
342
0
                if (sprint_realloc_by_type(&buf, &buf_len, &out_len, 1,
343
0
                                           var, NULL, NULL, NULL)) {
344
0
                    DEBUGMSG(("helper:table:set"," type=%d(%02x), value=%s\n",
345
0
                              var->type, var->type, buf));
346
0
                } else {
347
0
                    if (buf != NULL) {
348
0
                        DEBUGMSG(("helper:table:set",
349
0
                                  " type=%d(%02x), value=%s [TRUNCATED]\n",
350
0
                                  var->type, var->type, buf));
351
0
                    } else {
352
0
                        DEBUGMSG(("helper:table:set",
353
0
                                  " type=%d(%02x), value=[NIL] [TRUNCATED]\n",
354
0
                                  var->type, var->type));
355
0
                    }
356
0
                }
357
0
                if (buf != NULL) {
358
0
                    free(buf);
359
0
                }
360
0
            }
361
0
        }
362
0
#endif /* NETSNMP_NO_WRITE_SUPPORT */
363
364
        /*
365
         * check to make sure its in table range 
366
         */
367
368
0
        out_of_range = 0;
369
        /*
370
         * if our root oid is > var->name and this is not a GETNEXT, 
371
         * then the oid is out of range. (only compare up to shorter 
372
         * length) 
373
         */
374
0
        if (reginfo->rootoid_len > var->name_length)
375
0
            tmp_len = var->name_length;
376
0
        else
377
0
            tmp_len = reginfo->rootoid_len;
378
0
        if (snmp_oid_compare(reginfo->rootoid, reginfo->rootoid_len,
379
0
                             var->name, tmp_len) > 0) {
380
0
            if (reqinfo->mode == MODE_GETNEXT) {
381
0
                if (var->name != var->name_loc)
382
0
                    SNMP_FREE(var->name);
383
0
                snmp_set_var_objid(var, reginfo->rootoid,
384
0
                                   reginfo->rootoid_len);
385
0
            } else {
386
0
                DEBUGMSGTL(("helper:table", "  oid is out of range.\n"));
387
0
                out_of_range = 1;
388
0
            }
389
0
        }
390
        /*
391
         * if var->name is longer than the root, make sure it is 
392
         * table.1 (table.ENTRY).  
393
         */
394
0
        else if ((var->name_length > reginfo->rootoid_len) &&
395
0
                 (var->name[reginfo->rootoid_len] != 1)) {
396
0
            if ((var->name[reginfo->rootoid_len] < 1) &&
397
0
                (reqinfo->mode == MODE_GETNEXT)) {
398
0
                var->name[reginfo->rootoid_len] = 1;
399
0
                var->name_length = reginfo->rootoid_len;
400
0
            } else {
401
0
                out_of_range = 1;
402
0
                DEBUGMSGTL(("helper:table", "  oid is out of range.\n"));
403
0
            }
404
0
        }
405
        /*
406
         * if it is not in range, then mark it in the request list 
407
         * because we can't process it, and set an error so
408
         * nobody else wastes time trying to process it either.  
409
         */
410
0
        if (out_of_range) {
411
0
            DEBUGMSGTL(("helper:table", "  Not processed: "));
412
0
            DEBUGMSGOID(("helper:table", var->name, var->name_length));
413
0
            DEBUGMSG(("helper:table", "\n"));
414
415
            /*
416
             *  Reject requests of the form 'myTable.N'   (N != 1)
417
             */
418
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
419
0
            if (reqinfo->mode == MODE_SET_RESERVE1)
420
0
                table_helper_cleanup(reqinfo, request,
421
0
                                     SNMP_ERR_NOTWRITABLE);
422
0
            else
423
0
#endif /* NETSNMP_NO_WRITE_SUPPORT */
424
0
            if (reqinfo->mode == MODE_GET)
425
0
                table_helper_cleanup(reqinfo, request,
426
0
                                     SNMP_NOSUCHOBJECT);
427
0
            else
428
0
                request->processed = 1; /* skip if next handler called */
429
0
            continue;
430
0
        }
431
432
433
        /*
434
         * Check column ranges; set-up to pull out indexes from OID. 
435
         */
436
437
0
        incomplete = 0;
438
0
        tbl_req_info = netsnmp_extract_table_info(request);
439
0
        if (NULL == tbl_req_info) {
440
0
            tbl_req_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_request_info);
441
0
            if (tbl_req_info == NULL) {
442
0
                table_helper_cleanup(reqinfo, request,
443
0
                                     SNMP_ERR_GENERR);
444
0
                continue;
445
0
            }
446
0
            tbl_req_info->reg_info = tbl_info;
447
0
            tbl_req_info->indexes = snmp_clone_varbind(tbl_info->indexes);
448
0
            tbl_req_info->number_indexes = 0;       /* none yet */
449
0
            netsnmp_request_add_list_data(request,
450
0
                                          netsnmp_create_data_list
451
0
                                          (TABLE_HANDLER_NAME,
452
0
                                           (void *) tbl_req_info,
453
0
                                           table_data_free_func));
454
0
        } else {
455
0
            DEBUGMSGTL(("helper:table", "  using existing tbl_req_info\n "));
456
0
        }
457
458
        /*
459
         * do we have a column?
460
         */
461
0
        if (var->name_length > oid_column_pos) {
462
            /*
463
             * oid is long enough to contain COLUMN info
464
             */
465
0
            DEBUGMSGTL(("helper:table:col",
466
0
                        "  have at least a column (%" NETSNMP_PRIo "d)\n",
467
0
                        var->name[oid_column_pos]));
468
0
            if (var->name[oid_column_pos] < tbl_info->min_column) {
469
0
                DEBUGMSGTL(("helper:table:col",
470
0
                            "    but it's less than min (%d)\n",
471
0
                            tbl_info->min_column));
472
0
                if (reqinfo->mode == MODE_GETNEXT) {
473
                    /*
474
                     * fix column, truncate useless column info 
475
                     */
476
0
                    var->name_length = oid_column_pos;
477
0
                    tbl_req_info->colnum = tbl_info->min_column;
478
0
                } else
479
0
                    out_of_range = 1;
480
0
            } else if (var->name[oid_column_pos] > tbl_info->max_column)
481
0
                out_of_range = 1;
482
0
            else
483
0
                tbl_req_info->colnum = var->name[oid_column_pos];
484
485
0
            if (out_of_range) {
486
                /*
487
                 * this is out of range...  remove from requests, free
488
                 * memory 
489
                 */
490
0
                DEBUGMSGTL(("helper:table",
491
0
                            "    oid is out of range. Not processed: "));
492
0
                DEBUGMSGOID(("helper:table", var->name, var->name_length));
493
0
                DEBUGMSG(("helper:table", "\n"));
494
495
                /*
496
                 *  Reject requests of the form 'myEntry.N'   (invalid N)
497
                 */
498
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
499
0
                if (reqinfo->mode == MODE_SET_RESERVE1)
500
0
                    table_helper_cleanup(reqinfo, request,
501
0
                                         SNMP_ERR_NOTWRITABLE);
502
0
                else
503
0
#endif /* NETSNMP_NO_WRITE_SUPPORT */
504
0
                if (reqinfo->mode == MODE_GET)
505
0
                    table_helper_cleanup(reqinfo, request,
506
0
                                         SNMP_NOSUCHOBJECT);
507
0
                else
508
0
                    request->processed = 1; /* skip if next handler called */
509
0
                continue;
510
0
            }
511
            /*
512
             * use column verification 
513
             */
514
0
            else if (tbl_info->valid_columns) {
515
0
                tbl_req_info->colnum =
516
0
                    netsnmp_closest_column(var->name[oid_column_pos],
517
0
                                           tbl_info->valid_columns);
518
0
                DEBUGMSGTL(("helper:table:col", "    closest column is %d\n",
519
0
                            tbl_req_info->colnum));
520
                /*
521
                 * xxx-rks: document why the continue...
522
                 */
523
0
                if (tbl_req_info->colnum == 0)
524
0
                    continue;
525
0
                if (tbl_req_info->colnum != var->name[oid_column_pos]) {
526
0
                    DEBUGMSGTL(("helper:table:col",
527
0
                                "    which doesn't match req "
528
0
                                "%" NETSNMP_PRIo "d - truncating index info\n",
529
0
                                var->name[oid_column_pos]));
530
                    /*
531
                     * different column! truncate useless index info 
532
                     */
533
0
                    var->name_length = oid_column_pos + 1; /* pos is 0 based */
534
0
                }
535
0
            }
536
            /*
537
             * var->name_length may have changed - check again 
538
             */
539
0
            if ((int)var->name_length <= oid_index_pos) { /* pos is 0 based */
540
0
                DEBUGMSGTL(("helper:table", "    not enough for indexes\n"));
541
0
                tbl_req_info->index_oid_len = 0; /** none available */
542
0
            } else {
543
                /*
544
                 * oid is long enough to contain INDEX info
545
                 */
546
0
                tbl_req_info->index_oid_len =
547
0
                    var->name_length - oid_index_pos;
548
0
                DEBUGMSGTL(("helper:table", "    have %lu bytes of index\n",
549
0
                            (unsigned long)tbl_req_info->index_oid_len));
550
0
                netsnmp_assert(tbl_req_info->index_oid_len < MAX_OID_LEN);
551
0
                memcpy(tbl_req_info->index_oid, &var->name[oid_index_pos],
552
0
                       tbl_req_info->index_oid_len * sizeof(oid));
553
0
                tmp_name = tbl_req_info->index_oid;
554
0
            }
555
0
        } else if (reqinfo->mode == MODE_GETNEXT ||
556
0
                   reqinfo->mode == MODE_GETBULK) {
557
            /*
558
             * oid is NOT long enough to contain column or index info, so start
559
             * at the minimum column. Set index oid len to 0 because we don't
560
             * have any index info in the OID.
561
             */
562
0
            DEBUGMSGTL(("helper:table", "  no column/index in request\n"));
563
0
            tbl_req_info->index_oid_len = 0;
564
0
            tbl_req_info->colnum = tbl_info->min_column;
565
0
        } else {
566
            /*
567
             * oid is NOT long enough to contain index info,
568
             * so we can't do anything with it.
569
             *
570
             * Reject requests of the form 'myTable' or 'myEntry'
571
             */
572
0
            if (reqinfo->mode == MODE_GET ) {
573
0
                table_helper_cleanup(reqinfo, request, SNMP_NOSUCHOBJECT);
574
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
575
0
            } else if (reqinfo->mode == MODE_SET_RESERVE1 ) {
576
0
                table_helper_cleanup(reqinfo, request, SNMP_ERR_NOTWRITABLE);
577
0
#endif /* NETSNMP_NO_WRITE_SUPPORT */
578
0
            }
579
0
            continue;
580
0
        }
581
582
        /*
583
         * set up tmp_len to be the number of OIDs we have beyond the column;
584
         * these should be the index(s) for the table. If the index_oid_len
585
         * is 0, set tmp_len to -1 so that when we try to parse the index below,
586
         * we just zero fill everything.
587
         */
588
0
        if (tbl_req_info->index_oid_len == 0) {
589
0
            incomplete = 1;
590
0
            tmp_len = -1;
591
0
        } else
592
0
            tmp_len = tbl_req_info->index_oid_len;
593
594
595
        /*
596
         * for each index type, try to extract the index from var->name
597
         */
598
0
        DEBUGMSGTL(("helper:table", "  looking for %d indexes\n",
599
0
                    tbl_info->number_indexes));
600
0
        for (tmp_idx = 0, vb = tbl_req_info->indexes;
601
0
             tmp_idx < tbl_info->number_indexes;
602
0
             ++tmp_idx, vb = vb->next_variable) {
603
0
            size_t parsed_oid_len;
604
605
0
            if (incomplete && tmp_len) {
606
                /*
607
                 * incomplete/illegal OID, set up dummy 0 to parse 
608
                 */
609
0
                DEBUGMSGTL(("helper:table",
610
0
                            "  oid indexes not complete: "));
611
0
                DEBUGMSGOID(("helper:table", var->name, var->name_length));
612
0
                DEBUGMSG(("helper:table", "\n"));
613
614
                /*
615
                 * no sense in trying anymore if this is a GET/SET. 
616
                 *
617
                 * Reject requests of the form 'myObject'   (no instance)
618
                 */
619
0
                tmp_len = 0;
620
0
                tmp_name = NULL;
621
0
                break;
622
0
            }
623
            /*
624
             * try and parse current index 
625
             */
626
0
            netsnmp_assert(tmp_len >= 0);
627
0
            parsed_oid_len = tmp_len;
628
0
            if (parse_one_oid_index(&tmp_name, &parsed_oid_len,
629
0
                                    vb, 1) != SNMPERR_SUCCESS) {
630
0
                incomplete = 1;
631
0
                tmp_len = -1;   /* is this necessary? Better safe than
632
                                 * sorry */
633
0
            } else {
634
0
                tmp_len = parsed_oid_len;
635
0
                DEBUGMSGTL(("helper:table", "  got 1 (incomplete=%d)\n",
636
0
                            incomplete));
637
                /*
638
                 * do not count incomplete indexes 
639
                 */
640
0
                if (incomplete)
641
0
                    continue;
642
0
                ++tbl_req_info->number_indexes; /** got one ok */
643
0
                if (tmp_len <= 0) {
644
0
                    incomplete = 1;
645
0
                    tmp_len = -1;       /* is this necessary? Better safe
646
                                         * than sorry */
647
0
                }
648
0
            }
649
0
        }                       /** for loop */
650
651
0
        DEBUGIF("helper:table:results") {
652
0
                unsigned int    count;
653
0
                u_char         *buf = NULL;
654
0
                size_t          buf_len = 0, out_len = 0;
655
0
                DEBUGMSGTL(("helper:table:results", "  found %d indexes\n",
656
0
                            tbl_req_info->number_indexes));
657
0
                DEBUGMSGTL(("helper:table:results",
658
0
                            "  column: %d, indexes: %d",
659
0
                            tbl_req_info->colnum,
660
0
                            tbl_req_info->number_indexes));
661
0
                for (vb = tbl_req_info->indexes, count = 0;
662
0
                     vb && count < tbl_req_info->number_indexes;
663
0
                     count++, vb = vb->next_variable) {
664
0
                    out_len = 0;
665
0
                    if (sprint_realloc_by_type(&buf, &buf_len, &out_len, 1,
666
0
                                               vb, NULL, NULL, NULL)) {
667
0
                        DEBUGMSG(("helper:table:results",
668
0
                                  "   index: type=%d(%02x), value=%s",
669
0
                                  vb->type, vb->type, buf));
670
0
                    } else {
671
0
                        if (buf != NULL) {
672
0
                            DEBUGMSG(("helper:table:results",
673
0
                                      "   index: type=%d(%02x), value=%s [TRUNCATED]",
674
0
                                      vb->type, vb->type, buf));
675
0
                        } else {
676
0
                            DEBUGMSG(("helper:table:results",
677
0
                                      "   index: type=%d(%02x), value=[NIL] [TRUNCATED]",
678
0
                                      vb->type, vb->type));
679
0
                        }
680
0
                    }
681
0
                }
682
0
                if (buf != NULL) {
683
0
                    free(buf);
684
0
                }
685
0
                DEBUGMSG(("helper:table:results", "\n"));
686
0
        }
687
688
689
        /*
690
         * do we have sufficient index info to continue?
691
         */
692
693
0
        if ((reqinfo->mode != MODE_GETNEXT) &&
694
0
            ((tbl_req_info->number_indexes != tbl_info->number_indexes) ||
695
0
             (tmp_len != -1))) {
696
697
0
            DEBUGMSGTL(("helper:table",
698
0
                        "invalid index(es) for table - skipping\n"));
699
700
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
701
0
            if ( MODE_IS_SET(reqinfo->mode) ) {
702
                /*
703
                 * no point in continuing without indexes for set.
704
                 */
705
0
                netsnmp_assert(reqinfo->mode == MODE_SET_RESERVE1);
706
                /** clear first request so we wont try to run FREE mode */
707
0
                netsnmp_free_request_data_sets(requests);
708
                /** set actual error */
709
0
                table_helper_cleanup(reqinfo, request, SNMP_ERR_NOCREATION);
710
0
                need_processing = 0; /* don't call next handler */
711
0
                break;
712
0
            }
713
0
#endif /* NETSNMP_NO_WRITE_SUPPORT */
714
0
            table_helper_cleanup(reqinfo, request, SNMP_NOSUCHINSTANCE);
715
0
            continue;
716
0
        }
717
0
        netsnmp_assert(request->status == SNMP_ERR_NOERROR);
718
        
719
0
        ++need_processing;
720
721
0
    }                           /* for each request */
722
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
723
0
    }
724
0
#endif /* NETSNMP_NO_WRITE_SUPPORT */
725
726
    /*
727
     * bail if there is nothing for our child handlers
728
     */
729
0
    if (0 == need_processing)
730
0
        return status;
731
732
    /*
733
     * call our child access function 
734
     */
735
0
    status =
736
0
        netsnmp_call_next_handler(handler, reginfo, reqinfo, requests);
737
738
    /*
739
     * check for sparse tables
740
     */
741
0
    if (reqinfo->mode == MODE_GETNEXT)
742
0
        sparse_table_helper_handler( handler, reginfo, reqinfo, requests );
743
744
0
    return status;
745
0
}
746
747
0
#define SPARSE_TABLE_HANDLER_NAME "sparse_table"
748
749
/** implements the sparse table helper handler
750
 * @internal
751
 *
752
 * @note
753
 * This function is static to prevent others from calling it
754
 * directly. It it automatically called by the table helper,
755
 * 
756
 */
757
static int
758
sparse_table_helper_handler(netsnmp_mib_handler *handler,
759
                     netsnmp_handler_registration *reginfo,
760
                     netsnmp_agent_request_info *reqinfo,
761
                     netsnmp_request_info *requests)
762
0
{
763
0
    int             status = SNMP_ERR_NOERROR;
764
0
    netsnmp_request_info *request;
765
0
    oid             coloid[MAX_OID_LEN];
766
0
    netsnmp_table_request_info *table_info;
767
768
    /*
769
     * since we don't call child handlers, warn if one was registered
770
     * beneath us. A special exception for the table helper, which calls
771
     * the handler directly. Use handle custom flag to only log once.
772
     */
773
0
    if((table_helper_handler != handler->access_method) &&
774
0
       (NULL != handler->next)) {
775
        /*
776
         * always warn if called without our own handler. If we
777
         * have our own handler, use custom bit 1 to only log once.
778
         */
779
0
        if((sparse_table_helper_handler != handler->access_method) ||
780
0
           !(handler->flags & MIB_HANDLER_CUSTOM1)) {
781
0
            snmp_log(LOG_WARNING, "handler (%s) registered after sparse table "
782
0
                     "handler will not be called\n",
783
0
                     handler->next->handler_name ?
784
0
                     handler->next->handler_name : "" );
785
0
            if(sparse_table_helper_handler == handler->access_method)
786
0
                handler->flags |= MIB_HANDLER_CUSTOM1;
787
0
        }
788
0
    }
789
790
0
    if (reqinfo->mode == MODE_GETNEXT) {
791
0
        for(request = requests ; request; request = request->next) {
792
0
            if ((request->requestvb->type == ASN_NULL && request->processed) ||
793
0
                request->delegated)
794
0
                continue;
795
0
            if (request->requestvb->type == SNMP_NOSUCHINSTANCE) {
796
                /*
797
                 * get next skipped this value for this column, we
798
                 * need to keep searching forward 
799
                 */
800
0
                DEBUGMSGT(("sparse", "retry for NOSUCHINSTANCE\n"));
801
0
                request->requestvb->type = ASN_PRIV_RETRY;
802
0
            }
803
0
            if (request->requestvb->type == SNMP_NOSUCHOBJECT ||
804
0
                request->requestvb->type == SNMP_ENDOFMIBVIEW) {
805
                /*
806
                 * get next has completely finished with this column,
807
                 * so we need to try with the next column (if any)
808
                 */
809
0
                DEBUGMSGT(("sparse", "retry for NOSUCHOBJECT\n"));
810
0
                table_info = netsnmp_extract_table_info(request);
811
0
                table_info->colnum = netsnmp_table_next_column(table_info);
812
0
                if (0 != table_info->colnum) {
813
0
                    memcpy(coloid, reginfo->rootoid,
814
0
                           reginfo->rootoid_len * sizeof(oid));
815
0
                    coloid[reginfo->rootoid_len]   = 1;   /* table.entry node */
816
0
                    coloid[reginfo->rootoid_len+1] = table_info->colnum;
817
0
                    snmp_set_var_objid(request->requestvb,
818
0
                                       coloid, reginfo->rootoid_len + 2);
819
                    
820
0
                    request->requestvb->type = ASN_PRIV_RETRY;
821
0
                }
822
0
                else {
823
                    /*
824
                     * If we don't have column info, reset to null so
825
                     * the agent will move on to the next table.
826
                     */
827
0
                    request->requestvb->type = ASN_NULL;
828
0
                }
829
0
            }
830
0
        }
831
0
    }
832
0
    return status;
833
0
}
834
835
/** create sparse table handler
836
 */
837
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_SPARSE
838
netsnmp_mib_handler *
839
netsnmp_sparse_table_handler_get(void)
840
0
{
841
0
    return netsnmp_create_handler(SPARSE_TABLE_HANDLER_NAME,
842
0
                                  sparse_table_helper_handler);
843
0
}
844
845
/** creates a table handler given the netsnmp_table_registration_info object,
846
 *  inserts it into the request chain and then calls
847
 *  netsnmp_register_handler() to register the table into the agent.
848
 */
849
int
850
netsnmp_sparse_table_register(netsnmp_handler_registration *reginfo,
851
                       netsnmp_table_registration_info *tabreq)
852
0
{
853
0
    netsnmp_mib_handler *handler1, *handler2;
854
855
0
    handler1 = netsnmp_create_handler(SPARSE_TABLE_HANDLER_NAME,
856
0
                                     sparse_table_helper_handler);
857
0
    if (NULL == handler1)
858
0
        return MIB_REGISTRATION_FAILED;
859
860
0
    handler2 = netsnmp_get_table_handler(tabreq);
861
0
    if (NULL == handler2 ) {
862
0
        netsnmp_handler_free(handler1);
863
0
        return SNMP_ERR_GENERR;
864
0
    }
865
866
0
    if (SNMPERR_SUCCESS != netsnmp_inject_handler(reginfo, handler1)) {
867
0
        netsnmp_handler_free(handler1);
868
0
        netsnmp_handler_free(handler2);
869
0
        return MIB_REGISTRATION_FAILED;
870
0
    }
871
872
0
    if (SNMPERR_SUCCESS != netsnmp_inject_handler(reginfo, handler2)) {
873
        /** handler1 is in reginfo... remove and free?? */
874
0
        netsnmp_handler_free(handler2);
875
0
        return MIB_REGISTRATION_FAILED;
876
0
    }
877
878
    /** both handlers now in reginfo, so nothing to do on error */
879
0
    return netsnmp_register_handler(reginfo);
880
0
}
881
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_SPARSE */
882
883
884
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_BUILD_RESULT
885
/** Builds the result to be returned to the agent given the table information.
886
 *  Use this function to return results from lower level handlers to
887
 *  the agent.  It takes care of building the proper resulting oid
888
 *  (containing proper indexing) and inserts the result value into the
889
 *  returning varbind.
890
 */
891
int
892
netsnmp_table_build_result(netsnmp_handler_registration *reginfo,
893
                           netsnmp_request_info *reqinfo,
894
                           netsnmp_table_request_info *table_info,
895
                           u_char type, u_char * result, size_t result_len)
896
0
{
897
898
0
    netsnmp_variable_list *var;
899
900
0
    if (!reqinfo || !table_info)
901
0
        return SNMPERR_GENERR;
902
903
0
    var = reqinfo->requestvb;
904
905
0
    if (var->name != var->name_loc)
906
0
        free(var->name);
907
0
    var->name = NULL;
908
909
0
    if (netsnmp_table_build_oid(reginfo, reqinfo, table_info) !=
910
0
        SNMPERR_SUCCESS)
911
0
        return SNMPERR_GENERR;
912
913
0
    snmp_set_var_typed_value(var, type, result, result_len);
914
915
0
    return SNMPERR_SUCCESS;
916
0
}
917
918
/** given a registration info object, a request object and the table
919
 *  info object it builds the request->requestvb->name oid from the
920
 *  index values and column information found in the table_info
921
 *  object. Index values are extracted from the table_info varbinds.
922
 */
923
int
924
netsnmp_table_build_oid(netsnmp_handler_registration *reginfo,
925
                        netsnmp_request_info *reqinfo,
926
                        netsnmp_table_request_info *table_info)
927
0
{
928
0
    oid             tmpoid[MAX_OID_LEN];
929
0
    netsnmp_variable_list *var;
930
931
0
    if (!reginfo || !reqinfo || !table_info)
932
0
        return SNMPERR_GENERR;
933
934
    /*
935
     * xxx-rks: inefficient. we do a copy here, then build_oid does it
936
     *          again. either come up with a new utility routine, or
937
     *          do some hijinks here to eliminate extra copy.
938
     *          Probably could make sure all callers have the
939
     *          index & variable list updated, and use
940
     *          netsnmp_table_build_oid_from_index() instead of all this.
941
     */
942
0
    memcpy(tmpoid, reginfo->rootoid, reginfo->rootoid_len * sizeof(oid));
943
0
    tmpoid[reginfo->rootoid_len] = 1;   /** .Entry */
944
0
    tmpoid[reginfo->rootoid_len + 1] = table_info->colnum; /** .column */
945
946
0
    var = reqinfo->requestvb;
947
0
    if (build_oid(&var->name, &var->name_length,
948
0
                  tmpoid, reginfo->rootoid_len + 2, table_info->indexes)
949
0
        != SNMPERR_SUCCESS)
950
0
        return SNMPERR_GENERR;
951
952
0
    return SNMPERR_SUCCESS;
953
0
}
954
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_BUILD_RESULT */
955
956
/** given a registration info object, a request object and the table
957
 *  info object it builds the request->requestvb->name oid from the
958
 *  index values and column information found in the table_info
959
 *  object.  Index values are extracted from the table_info index oid.
960
 */
961
int
962
netsnmp_table_build_oid_from_index(netsnmp_handler_registration *reginfo,
963
                                   netsnmp_request_info *reqinfo,
964
                                   netsnmp_table_request_info *table_info)
965
0
{
966
0
    oid             tmpoid[MAX_OID_LEN];
967
0
    netsnmp_variable_list *var;
968
0
    int             len;
969
970
0
    if (!reginfo || !reqinfo || !table_info)
971
0
        return SNMPERR_GENERR;
972
973
0
    var = reqinfo->requestvb;
974
0
    len = reginfo->rootoid_len;
975
0
    memcpy(tmpoid, reginfo->rootoid, len * sizeof(oid));
976
0
    tmpoid[len++] = 1;          /* .Entry */
977
0
    tmpoid[len++] = table_info->colnum; /* .column */
978
0
    memcpy(&tmpoid[len], table_info->index_oid,
979
0
           table_info->index_oid_len * sizeof(oid));
980
0
    len += table_info->index_oid_len;
981
0
    snmp_set_var_objid( var, tmpoid, len );
982
983
0
    return SNMPERR_SUCCESS;
984
0
}
985
986
/** parses an OID into table indexes */
987
int
988
netsnmp_update_variable_list_from_index(netsnmp_table_request_info *tri)
989
0
{
990
0
    if (!tri)
991
0
        return SNMPERR_GENERR;
992
993
    /*
994
     * free any existing allocated memory, then parse oid into varbinds
995
     */
996
0
    snmp_reset_var_buffers( tri->indexes);
997
998
0
    return parse_oid_indexes(tri->index_oid, tri->index_oid_len,
999
0
                             tri->indexes);
1000
0
}
1001
1002
/** builds an oid given a set of indexes. */
1003
int
1004
netsnmp_update_indexes_from_variable_list(netsnmp_table_request_info *tri)
1005
0
{
1006
0
    if (!tri)
1007
0
        return SNMPERR_GENERR;
1008
1009
0
    return build_oid_noalloc(tri->index_oid,
1010
0
                             sizeof(tri->index_oid) / sizeof(tri->index_oid[0]),
1011
0
                             &tri->index_oid_len, NULL, 0, tri->indexes);
1012
0
}
1013
1014
/**
1015
 * checks the original request against the current data being passed in if 
1016
 * its greater than the request oid but less than the current valid
1017
 * return, set the current valid return to the new value.
1018
 * 
1019
 * returns 1 if outvar was replaced with the oid from newvar (success).
1020
 * returns 0 if not. 
1021
 */
1022
int
1023
netsnmp_check_getnext_reply(netsnmp_request_info *request,
1024
                            oid * prefix,
1025
                            size_t prefix_len,
1026
                            netsnmp_variable_list * newvar,
1027
                            netsnmp_variable_list ** outvar)
1028
0
{
1029
0
    oid      myname[MAX_OID_LEN];
1030
0
    size_t   myname_len;
1031
1032
0
    build_oid_noalloc(myname, MAX_OID_LEN, &myname_len,
1033
0
                      prefix, prefix_len, newvar);
1034
    /*
1035
     * is the build of the new indexes less than our current result 
1036
     */
1037
0
    if ((!(*outvar) || snmp_oid_compare(myname + prefix_len,
1038
0
                                        myname_len - prefix_len,
1039
0
                                        (*outvar)->name + prefix_len,
1040
0
                                        (*outvar)->name_length -
1041
0
                                        prefix_len) < 0)) {
1042
        /*
1043
         * and greater than the requested oid 
1044
         */
1045
0
        if (snmp_oid_compare(myname, myname_len,
1046
0
                             request->requestvb->name,
1047
0
                             request->requestvb->name_length) > 0) {
1048
            /*
1049
             * the new result must be better than the old 
1050
             */
1051
#ifdef ONLY_WORKS_WITH_ONE_VARBIND
1052
            if (!*outvar)
1053
                *outvar = snmp_clone_varbind(newvar);
1054
      else
1055
                /* 
1056
                 * TODO: walk the full varbind list, setting
1057
                 *       *all* the values - not just the first.
1058
                 */
1059
                snmp_set_var_typed_value(*outvar, newvar->type,
1060
        newvar->val.string, newvar->val_len);
1061
#else  /* Interim replacement approach - less efficient, but it works! */
1062
0
            if (*outvar)
1063
0
                snmp_free_varbind(*outvar);
1064
0
            *outvar = snmp_clone_varbind(newvar);
1065
0
#endif
1066
0
            snmp_set_var_objid(*outvar, myname, myname_len);
1067
1068
0
            return 1;
1069
0
        }
1070
0
    }
1071
0
    return 0;
1072
0
}
1073
1074
netsnmp_table_registration_info *
1075
netsnmp_table_registration_info_clone(netsnmp_table_registration_info *tri)
1076
0
{
1077
0
    netsnmp_table_registration_info *copy;
1078
0
    copy = malloc(sizeof(*copy));
1079
0
    if (copy) {
1080
0
        *copy = *tri;
1081
0
        copy->indexes = snmp_clone_varbind(tri->indexes);
1082
0
        if (!copy->indexes) {
1083
0
            free(copy);
1084
0
            copy = NULL;
1085
0
        }
1086
0
    }
1087
0
    return copy;
1088
0
}
1089
1090
void
1091
netsnmp_table_registration_info_free(netsnmp_table_registration_info *tri)
1092
0
{
1093
0
    if (NULL == tri)
1094
0
        return;
1095
1096
0
    if (NULL != tri->indexes)
1097
0
        snmp_free_varbind(tri->indexes);
1098
1099
#if 0
1100
    /*
1101
     * sigh... example use of valid_columns points to static memory,
1102
     * so freeing it would be bad... we'll just have to live with any
1103
     * leaks, for now...
1104
     */
1105
#endif
1106
1107
0
    free(tri);
1108
0
}
1109
1110
/** @} */
1111
1112
/*
1113
 * internal routines 
1114
 */
1115
void
1116
table_data_free_func(void *data)
1117
0
{
1118
0
    netsnmp_table_request_info *info = (netsnmp_table_request_info *) data;
1119
0
    if (!info)
1120
0
        return;
1121
0
    snmp_free_varbind(info->indexes);
1122
0
    free(info);
1123
0
}
1124
1125
1126
1127
static void
1128
table_helper_cleanup(netsnmp_agent_request_info *reqinfo,
1129
                     netsnmp_request_info *request, int status)
1130
0
{
1131
0
    netsnmp_set_request_error(reqinfo, request, status);
1132
0
    netsnmp_free_request_data_sets(request);
1133
0
    if (!request)
1134
0
        return;
1135
0
    request->parent_data = NULL;
1136
0
}
1137
1138
1139
/*
1140
 * find the closest column to current (which may be current).
1141
 *
1142
 * called when a table runs out of rows for column X. This
1143
 * function is called with current = X + 1, to verify that
1144
 * X + 1 is a valid column, or find the next closest column if not.
1145
 *
1146
 * All list types should be sorted, lowest to highest.
1147
 */
1148
unsigned int
1149
netsnmp_closest_column(unsigned int current,
1150
                       netsnmp_column_info *valid_columns)
1151
0
{
1152
0
    unsigned int    closest = 0;
1153
0
    int             idx;
1154
1155
0
    if (valid_columns == NULL)
1156
0
        return 0;
1157
1158
0
    for( ; valid_columns; valid_columns = valid_columns->next) {
1159
1160
0
        if (valid_columns->isRange) {
1161
            /*
1162
             * if current < low range, it might be closest.
1163
             * otherwise, if it's < high range, current is in
1164
             * the range, and thus is an exact match.
1165
             */
1166
0
            if (current < valid_columns->details.range[0]) {
1167
0
                if ( (valid_columns->details.range[0] < closest) ||
1168
0
                     (0 == closest)) {
1169
0
                    closest = valid_columns->details.range[0];
1170
0
                }
1171
0
            } else if (current <= valid_columns->details.range[1]) {
1172
0
                closest = current;
1173
0
                break;       /* can not get any closer! */
1174
0
            }
1175
1176
0
        } /* range */
1177
0
        else {                  /* list */
1178
            /*
1179
             * if current < first item, no need to iterate over list.
1180
             * that item is either closest, or not.
1181
             */
1182
0
            if (current < valid_columns->details.list[0]) {
1183
0
                if ((valid_columns->details.list[0] < closest) ||
1184
0
                    (0 == closest))
1185
0
                    closest = valid_columns->details.list[0];
1186
0
                continue;
1187
0
            }
1188
1189
            /** if current > last item in list, no need to iterate */
1190
0
            if (current >
1191
0
                valid_columns->details.list[(int)valid_columns->list_count - 1])
1192
0
                continue;       /* not in list range. */
1193
1194
            /** skip anything less than current*/
1195
0
            for (idx = 0; valid_columns->details.list[idx] < current; ++idx)
1196
0
                ;
1197
            
1198
            /** check for exact match */
1199
0
            if (current == valid_columns->details.list[idx]) {
1200
0
                closest = current;
1201
0
                break;      /* can not get any closer! */
1202
0
            }
1203
            
1204
            /** list[idx] > current; is it < closest? */
1205
0
            if ((valid_columns->details.list[idx] < closest) ||
1206
0
                (0 == closest))
1207
0
                closest = valid_columns->details.list[idx];
1208
1209
0
        }                       /* list */
1210
0
    }                           /* for */
1211
1212
0
    return closest;
1213
0
}
1214
1215
/**
1216
 * This function can be used to setup the table's definition within
1217
 * your module's initialize function, it takes a variable index parameter list
1218
 * for example: the table_info structure is followed by two integer index types
1219
 * netsnmp_table_helper_add_indexes(
1220
 *                  table_info,   
1221
 *              ASN_INTEGER,  
1222
 *        ASN_INTEGER,  
1223
 *        0);
1224
 *
1225
 * @param tinfo is a pointer to a netsnmp_table_registration_info struct.
1226
 *  The table handler needs to know up front how your table is structured.
1227
 *  A netsnmp_table_registration_info structure that is 
1228
 *  passed to the table handler should contain the asn index types for the 
1229
 *  table as well as the minimum and maximum column that should be used.
1230
 *
1231
 * @return void
1232
 *
1233
 */
1234
void
1235
netsnmp_table_helper_add_indexes(netsnmp_table_registration_info *tinfo,
1236
                                 ...)
1237
0
{
1238
0
    va_list         debugargs;
1239
0
    int             type;
1240
1241
0
    va_start(debugargs, tinfo);
1242
0
    while ((type = va_arg(debugargs, int)) != 0) {
1243
0
        netsnmp_table_helper_add_index(tinfo, type);
1244
0
    }
1245
0
    va_end(debugargs);
1246
0
}
1247
1248
#ifndef NETSNMP_NO_WRITE_SUPPORT
1249
#ifndef NETSNMP_FEATURE_REMOVE_TABLE_GET_OR_CREATE_ROW_STASH
1250
static void
1251
0
_row_stash_data_list_free(void *ptr) {
1252
0
    netsnmp_oid_stash_node **tmp = (netsnmp_oid_stash_node **)ptr;
1253
0
    netsnmp_oid_stash_free(tmp, NULL);
1254
0
    free(ptr);
1255
0
}
1256
1257
/** returns a row-wide place to store data in.
1258
    @todo This function will likely change to add free pointer functions. */
1259
netsnmp_oid_stash_node **
1260
netsnmp_table_get_or_create_row_stash(netsnmp_agent_request_info *reqinfo,
1261
                                      const u_char * storage_name)
1262
0
{
1263
0
    netsnmp_oid_stash_node **stashp = NULL;
1264
0
    stashp = (netsnmp_oid_stash_node **)
1265
0
        netsnmp_agent_get_list_data(reqinfo, (const char *) storage_name);
1266
1267
0
    if (!stashp) {
1268
        /*
1269
         * hasn't be created yet.  we create it here. 
1270
         */
1271
0
        stashp = SNMP_MALLOC_TYPEDEF(netsnmp_oid_stash_node *);
1272
1273
0
        if (!stashp)
1274
0
            return NULL;        /* ack. out of mem */
1275
1276
0
        netsnmp_agent_add_list_data(reqinfo,
1277
0
                                    netsnmp_create_data_list((const char *) storage_name,
1278
0
                                                             stashp,
1279
0
                                                             _row_stash_data_list_free));
1280
0
    }
1281
0
    return stashp;
1282
0
}
1283
#endif /* NETSNMP_FEATURE_REMOVE_TABLE_GET_OR_CREATE_ROW_STASH */
1284
#endif /* NETSNMP_NO_WRITE_SUPPORT */
1285
1286
/*
1287
 * advance the table info colnum to the next column, or 0 if there are no more
1288
 *
1289
 * @return new column, or 0 if there are no more
1290
 */
1291
unsigned int
1292
netsnmp_table_next_column(netsnmp_table_request_info *table_info)
1293
0
{
1294
0
    if (NULL == table_info)
1295
0
        return 0;
1296
1297
    /*
1298
     * try and validate next column
1299
     */
1300
0
    if (table_info->reg_info->valid_columns)
1301
0
        return netsnmp_closest_column(table_info->colnum + 1,
1302
0
                                      table_info->reg_info->valid_columns);
1303
    
1304
    /*
1305
     * can't validate. assume 1..max_column are valid
1306
     */
1307
0
    if (table_info->colnum < table_info->reg_info->max_column)
1308
0
        return table_info->colnum + 1;
1309
    
1310
0
    return 0; /* out of range */
1311
0
}