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