1 /*
2  * Portions of this file are subject to the following copyright(s).  See
3  * the Net-SNMP's COPYING file for more details and other copyrights
4  * that may apply:
5  *
6  * Portions of this file are copyrighted by:
7  * Copyright (c) 2016 VMware, Inc. All rights reserved.
8  * Use is subject to license terms specified in the COPYING file
9  * distributed with the Net-SNMP package.
10  */
11 
12 #include <net-snmp/net-snmp-config.h>
13 #include <net-snmp/net-snmp-features.h>
14 
15 #include <net-snmp/net-snmp-features.h>
16 #include <net-snmp/net-snmp-includes.h>
17 #include <net-snmp/agent/net-snmp-agent-includes.h>
18 
19 #include <net-snmp/agent/table_dataset.h>
20 
21 #if HAVE_STRING_H
22 #include <string.h>
23 #else
24 #include <strings.h>
25 #endif
26 
27 netsnmp_feature_child_of(table_dataset_all, mib_helpers);
28 netsnmp_feature_child_of(table_dataset, table_dataset_all);
29 netsnmp_feature_child_of(table_dataset_remove_row, table_dataset_all);
30 netsnmp_feature_child_of(table_data_set_column, table_dataset_all);
31 netsnmp_feature_child_of(table_dataset_get_newrow, table_dataset_all);
32 netsnmp_feature_child_of(table_set_add_indexes, table_dataset_all);
33 netsnmp_feature_child_of(delete_table_data_set, table_dataset_all);
34 netsnmp_feature_child_of(table_set_multi_add_default_row, table_dataset_all);
35 netsnmp_feature_child_of(table_dataset_unregister_auto_data_table, table_dataset_all);
36 
37 #ifdef NETSNMP_FEATURE_REQUIRE_TABLE_DATASET
38 netsnmp_feature_require(table_get_or_create_row_stash);
39 netsnmp_feature_require(table_data_delete_table);
40 netsnmp_feature_require(table_data);
41 netsnmp_feature_require(oid_stash_get_data);
42 netsnmp_feature_require(oid_stash_add_data);
43 #endif /* NETSNMP_FEATURE_REQUIRE_TABLE_DATASET */
44 
45 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATASET
46 
47 #ifndef NETSNMP_NO_WRITE_SUPPORT
48 netsnmp_feature_require(oid_stash);
49 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
50 
51 #ifndef NETSNMP_DISABLE_MIB_LOADING
52 netsnmp_feature_require(mib_to_asn_type);
53 #endif /* NETSNMP_DISABLE_MIB_LOADING */
54 
55 static netsnmp_data_list *auto_tables;
56 
57 typedef struct data_set_tables_s {
58     netsnmp_table_data_set *table_set;
59 } data_set_tables;
60 
61 typedef struct data_set_cache_s {
62     void           *data;
63     size_t          data_len;
64 } data_set_cache;
65 
66 #define STATE_ACTION   1
67 #define STATE_COMMIT   2
68 #define STATE_UNDO     3
69 #define STATE_FREE     4
70 
71 typedef struct newrow_stash_s {
72     netsnmp_table_row *newrow;
73     int             state;
74     int             created;
75     int             deleted;
76 } newrow_stash;
77 
78 /** @defgroup table_dataset table_dataset
79  *  Helps you implement a table with automatted storage.
80  *  @ingroup table_data
81  *
82  *  This handler helps you implement a table where all the data is
83  *  expected to be stored within the agent itself and not in some
84  *  external storage location.  It handles all MIB requests including
85  *  GETs, GETNEXTs and SETs.  It's possible to simply create a table
86  *  without actually ever defining a handler to be called when SNMP
87  *  requests come in.  To use the data, you can either attach a
88  *  sub-handler that merely uses/manipulates the data further when
89  *  requests come in, or you can loop through it externally when it's
90  *  actually needed.  This handler is most useful in cases where a
91  *  table is holding configuration data for something which gets
92  *  triggered via another event.
93  *
94  *  NOTE NOTE NOTE: This helper isn't complete and is likely to change
95  *  somewhat over time.  Specifically, the way it stores data
96  *  internally may change drastically.
97  *
98  *  @{
99  */
100 
101 void
netsnmp_init_table_dataset(void)102 netsnmp_init_table_dataset(void) {
103 #ifndef NETSNMP_DISABLE_MIB_LOADING
104     register_app_config_handler("table",
105                                 netsnmp_config_parse_table_set, NULL,
106                                 "tableoid");
107 #endif /* NETSNMP_DISABLE_MIB_LOADING */
108     register_app_config_handler("add_row", netsnmp_config_parse_add_row,
109                                 NULL, "table_name indexes... values...");
110 }
111 
112 /* ==================================
113  *
114  * Data Set API: Table maintenance
115  *
116  * ================================== */
117 
118 /** deletes a single dataset table data.
119  *  returns the (possibly still good) next pointer of the deleted data object.
120  */
121 NETSNMP_STATIC_INLINE netsnmp_table_data_set_storage *
netsnmp_table_dataset_delete_data(netsnmp_table_data_set_storage * data)122 netsnmp_table_dataset_delete_data(netsnmp_table_data_set_storage *data)
123 {
124     netsnmp_table_data_set_storage *nextPtr = NULL;
125     if (data) {
126         nextPtr = data->next;
127         SNMP_FREE(data->data.voidp);
128     }
129     SNMP_FREE(data);
130     return nextPtr;
131 }
132 
133 /** deletes all the data from this node and beyond in the linked list */
134 NETSNMP_INLINE void
netsnmp_table_dataset_delete_all_data(netsnmp_table_data_set_storage * data)135 netsnmp_table_dataset_delete_all_data(netsnmp_table_data_set_storage *data)
136 {
137 
138     while (data) {
139         data = netsnmp_table_dataset_delete_data(data);
140     }
141 }
142 
143 /** deletes all the data from this node and beyond in the linked list */
144 NETSNMP_INLINE void
netsnmp_table_dataset_delete_row(netsnmp_table_row * row)145 netsnmp_table_dataset_delete_row(netsnmp_table_row *row)
146 {
147     netsnmp_table_data_set_storage *data;
148 
149     if (!row)
150         return;
151 
152     data = (netsnmp_table_data_set_storage*)netsnmp_table_data_delete_row(row);
153     netsnmp_table_dataset_delete_all_data(data);
154 }
155 
156 /** adds a new row to a dataset table */
157 NETSNMP_INLINE void
netsnmp_table_dataset_add_row(netsnmp_table_data_set * table,netsnmp_table_row * row)158 netsnmp_table_dataset_add_row(netsnmp_table_data_set *table,
159                               netsnmp_table_row *row)
160 {
161     if (!table)
162         return;
163     netsnmp_table_data_add_row(table->table, row);
164 }
165 
166 /** adds a new row to a dataset table */
167 NETSNMP_INLINE void
netsnmp_table_dataset_replace_row(netsnmp_table_data_set * table,netsnmp_table_row * origrow,netsnmp_table_row * newrow)168 netsnmp_table_dataset_replace_row(netsnmp_table_data_set *table,
169                                   netsnmp_table_row *origrow,
170                                   netsnmp_table_row *newrow)
171 {
172     if (!table)
173         return;
174     netsnmp_table_data_replace_row(table->table, origrow, newrow);
175 }
176 
177 /** removes a row from the table, but doesn't delete/free the column values */
178 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATASET_REMOVE_ROW
179 NETSNMP_INLINE void
netsnmp_table_dataset_remove_row(netsnmp_table_data_set * table,netsnmp_table_row * row)180 netsnmp_table_dataset_remove_row(netsnmp_table_data_set *table,
181                                  netsnmp_table_row *row)
182 {
183     if (!table)
184         return;
185 
186     netsnmp_table_data_remove_and_delete_row(table->table, row);
187 }
188 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATASET_REMOVE_ROW */
189 
190 /** removes a row from the table and then deletes it (and all its data) */
191 NETSNMP_INLINE void
netsnmp_table_dataset_remove_and_delete_row(netsnmp_table_data_set * table,netsnmp_table_row * row)192 netsnmp_table_dataset_remove_and_delete_row(netsnmp_table_data_set *table,
193                                             netsnmp_table_row *row)
194 {
195     netsnmp_table_data_set_storage *data;
196 
197     if (!table)
198         return;
199 
200     data = (netsnmp_table_data_set_storage *)
201         netsnmp_table_data_remove_and_delete_row(table->table, row);
202 
203     netsnmp_table_dataset_delete_all_data(data);
204 }
205 
206 /** Create a netsnmp_table_data_set structure given a table_data definition */
207 netsnmp_table_data_set *
netsnmp_create_table_data_set(const char * table_name)208 netsnmp_create_table_data_set(const char *table_name)
209 {
210     netsnmp_table_data_set *table_set =
211         SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set);
212     if (!table_set)
213         return NULL;
214     table_set->table = netsnmp_create_table_data(table_name);
215     return table_set;
216 }
217 
218 #ifndef NETSNMP_FEATURE_REMOVE_DELETE_TABLE_DATA_SET
netsnmp_delete_table_data_set(netsnmp_table_data_set * table_set)219 void netsnmp_delete_table_data_set(netsnmp_table_data_set *table_set)
220 {
221     netsnmp_table_data_set_storage *ptr, *next;
222     netsnmp_table_row *prow, *pnextrow;
223 
224     for (ptr = table_set->default_row; ptr; ptr = next) {
225         next = ptr->next;
226         free(ptr);
227     }
228     table_set->default_row = NULL;
229     for (prow = table_set->table->first_row; prow; prow = pnextrow) {
230         pnextrow = prow->next;
231         netsnmp_table_dataset_remove_and_delete_row(table_set, prow);
232     }
233     table_set->table->first_row = NULL;
234     netsnmp_table_data_delete_table(table_set->table);
235     free(table_set);
236 }
237 #endif /* NETSNMP_FEATURE_REMOVE_DELETE_TABLE_DATA_SET */
238 
239 /** clones a dataset row, including all data. */
240 netsnmp_table_row *
netsnmp_table_data_set_clone_row(netsnmp_table_row * row)241 netsnmp_table_data_set_clone_row(netsnmp_table_row *row)
242 {
243     netsnmp_table_data_set_storage *data, **newrowdata;
244     netsnmp_table_row *newrow;
245 
246     if (!row)
247         return NULL;
248 
249     newrow = netsnmp_table_data_clone_row(row);
250     if (!newrow)
251         return NULL;
252 
253     data = (netsnmp_table_data_set_storage *) row->data;
254 
255     if (data) {
256         for (newrowdata =
257              (netsnmp_table_data_set_storage **) &(newrow->data); data;
258              newrowdata = &((*newrowdata)->next), data = data->next) {
259 
260             *newrowdata = netsnmp_memdup(data,
261                 sizeof(netsnmp_table_data_set_storage));
262             if (!*newrowdata) {
263                 netsnmp_table_dataset_delete_row(newrow);
264                 return NULL;
265             }
266 
267             if (data->data.voidp) {
268                 (*newrowdata)->data.voidp =
269                     netsnmp_memdup(data->data.voidp, data->data_len);
270                 if (!(*newrowdata)->data.voidp) {
271                     netsnmp_table_dataset_delete_row(newrow);
272                     return NULL;
273                 }
274             }
275         }
276     }
277     return newrow;
278 }
279 
280 /* ==================================
281  *
282  * Data Set API: Default row operations
283  *
284  * ================================== */
285 
286 /** creates a new row from an existing defined default set */
287 netsnmp_table_row *
netsnmp_table_data_set_create_row_from_defaults(netsnmp_table_data_set_storage * defrow)288 netsnmp_table_data_set_create_row_from_defaults
289     (netsnmp_table_data_set_storage *defrow)
290 {
291     netsnmp_table_row *row;
292     row = netsnmp_create_table_data_row();
293     if (!row)
294         return NULL;
295     for (; defrow; defrow = defrow->next) {
296         netsnmp_set_row_column(row, defrow->column, defrow->type,
297                                defrow->data.voidp, defrow->data_len);
298 #ifndef NETSNMP_NO_WRITE_SUPPORT
299         if (defrow->writable)
300             netsnmp_mark_row_column_writable(row, defrow->column, 1);
301 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
302     }
303     return row;
304 }
305 
306 /** adds a new default row to a table_set.
307  * Arguments should be the table_set, column number, variable type and
308  * finally a 1 if it is allowed to be writable, or a 0 if not.  If the
309  * default_value field is not NULL, it will be used to populate new
310  * valuse in that column fro newly created rows. It is copied into the
311  * storage template (free your calling argument).
312  *
313  * returns SNMPERR_SUCCESS or SNMPERR_FAILURE
314  */
315 int
netsnmp_table_set_add_default_row(netsnmp_table_data_set * table_set,unsigned int column,int type,int writable,void * default_value,size_t default_value_len)316 netsnmp_table_set_add_default_row(netsnmp_table_data_set *table_set,
317                                   unsigned int column,
318                                   int type, int writable,
319                                   void *default_value,
320                                   size_t default_value_len)
321 {
322     netsnmp_table_data_set_storage *new_col, *ptr, *pptr;
323 
324     if (!table_set)
325         return SNMPERR_GENERR;
326 
327     /*
328      * double check
329      */
330     new_col =
331         netsnmp_table_data_set_find_column(table_set->default_row, column);
332     if (new_col != NULL) {
333         if (new_col->type == type && new_col->writable == writable)
334             return SNMPERR_SUCCESS;
335         return SNMPERR_GENERR;
336     }
337 
338     new_col = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage);
339     if (new_col == NULL)
340         return SNMPERR_GENERR;
341     new_col->type = type;
342     new_col->writable = writable;
343     new_col->column = column;
344     if (default_value) {
345         new_col->data.voidp = netsnmp_memdup(default_value, default_value_len);
346         new_col->data_len = default_value_len;
347     }
348     if (table_set->default_row == NULL)
349         table_set->default_row = new_col;
350     else {
351         /* sort in order just because (needed for add_row support) */
352         for (ptr = table_set->default_row, pptr = NULL;
353              ptr;
354              pptr = ptr, ptr = ptr->next) {
355             if (ptr->column > column) {
356                 new_col->next = ptr;
357                 if (pptr)
358                     pptr->next = new_col;
359                 else
360                     table_set->default_row = new_col;
361                 return SNMPERR_SUCCESS;
362             }
363         }
364         if (pptr)
365             pptr->next = new_col;
366         else
367             snmp_log(LOG_ERR,"Shouldn't have gotten here: table_dataset/add_row");
368     }
369     return SNMPERR_SUCCESS;
370 }
371 
372 /** adds multiple data column definitions to each row.  Functionally,
373  *  this is a wrapper around calling netsnmp_table_set_add_default_row
374  *  repeatedly for you.
375  */
376 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_SET_MULTI_ADD_DEFAULT_ROW
377 void
netsnmp_table_set_multi_add_default_row(netsnmp_table_data_set * tset,...)378 netsnmp_table_set_multi_add_default_row(netsnmp_table_data_set *tset, ...)
379 {
380     va_list         debugargs;
381     unsigned int    column;
382     int             type, writable;
383     void           *data;
384     size_t          data_len;
385 
386     va_start(debugargs, tset);
387 
388     while ((column = va_arg(debugargs, unsigned int)) != 0) {
389         type = va_arg(debugargs, int);
390         writable = va_arg(debugargs, int);
391         data = va_arg(debugargs, void *);
392         data_len = va_arg(debugargs, size_t);
393         netsnmp_table_set_add_default_row(tset, column, type, writable,
394                                           data, data_len);
395     }
396 
397     va_end(debugargs);
398 }
399 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_SET_MULTI_ADD_DEFAULT_ROW */
400 
401 /* ==================================
402  *
403  * Data Set API: MIB maintenance
404  *
405  * ================================== */
406 
407 /** Given a netsnmp_table_data_set definition, create a handler for it */
408 netsnmp_mib_handler *
netsnmp_get_table_data_set_handler(netsnmp_table_data_set * data_set)409 netsnmp_get_table_data_set_handler(netsnmp_table_data_set *data_set)
410 {
411     netsnmp_mib_handler *ret = NULL;
412 
413     if (!data_set) {
414         snmp_log(LOG_INFO,
415                  "netsnmp_get_table_data_set_handler(NULL) called\n");
416         return NULL;
417     }
418 
419     ret =
420         netsnmp_create_handler(TABLE_DATA_SET_NAME,
421                                netsnmp_table_data_set_helper_handler);
422     if (ret) {
423         ret->flags |= MIB_HANDLER_AUTO_NEXT;
424         ret->myvoid = (void *) data_set;
425     }
426     return ret;
427 }
428 
429 /** register a given data_set at a given oid (specified in the
430     netsnmp_handler_registration pointer).  The
431     reginfo->handler->access_method *may* be null if the call doesn't
432     ever want to be called for SNMP operations.
433 */
434 int
netsnmp_register_table_data_set(netsnmp_handler_registration * reginfo,netsnmp_table_data_set * data_set,netsnmp_table_registration_info * table_info)435 netsnmp_register_table_data_set(netsnmp_handler_registration *reginfo,
436                                 netsnmp_table_data_set *data_set,
437                                 netsnmp_table_registration_info *table_info)
438 {
439     netsnmp_mib_handler *handler;
440     int ret;
441 
442     if (NULL == table_info) {
443         /*
444          * allocate the table if one wasn't allocated
445          */
446         table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info);
447         if (table_info == NULL)
448             return SNMP_ERR_GENERR;
449     }
450 
451     if (NULL == table_info->indexes && data_set->table->indexes_template) {
452         /*
453          * copy the indexes in
454          */
455         table_info->indexes =
456             snmp_clone_varbind(data_set->table->indexes_template);
457     }
458 
459     if ((!table_info->min_column || !table_info->max_column) &&
460         (data_set->default_row)) {
461         /*
462          * determine min/max columns
463          */
464         unsigned int    mincol = 0xffffffff, maxcol = 0;
465         netsnmp_table_data_set_storage *row;
466 
467         for (row = data_set->default_row; row; row = row->next) {
468             mincol = SNMP_MIN(mincol, row->column);
469             maxcol = SNMP_MAX(maxcol, row->column);
470         }
471         if (!table_info->min_column)
472             table_info->min_column = mincol;
473         if (!table_info->max_column)
474             table_info->max_column = maxcol;
475     }
476 
477     handler = netsnmp_get_table_data_set_handler(data_set);
478     if (!handler ||
479         (netsnmp_inject_handler(reginfo, handler) != SNMPERR_SUCCESS)) {
480         snmp_log(LOG_ERR, "could not create table data set handler\n");
481         netsnmp_handler_free(handler);
482         netsnmp_handler_registration_free(reginfo);
483         return MIB_REGISTRATION_FAILED;
484     }
485 
486     ret = netsnmp_register_table_data(reginfo, data_set->table,
487                                        table_info);
488     if (ret == SNMPERR_SUCCESS && reginfo->handler)
489         netsnmp_handler_owns_table_info(reginfo->handler->next);
490     return ret;
491 }
492 
493 newrow_stash   *
netsnmp_table_data_set_create_newrowstash(netsnmp_table_data_set * datatable,netsnmp_table_request_info * table_info)494 netsnmp_table_data_set_create_newrowstash
495     (netsnmp_table_data_set     *datatable,
496      netsnmp_table_request_info *table_info)
497 {
498     newrow_stash   *newrowstash = NULL;
499     netsnmp_table_row *newrow   = NULL;
500 
501     newrowstash = SNMP_MALLOC_TYPEDEF(newrow_stash);
502 
503     if (newrowstash != NULL) {
504         newrowstash->created = 1;
505         newrow = netsnmp_table_data_set_create_row_from_defaults
506             (datatable->default_row);
507         newrow->indexes = snmp_clone_varbind(table_info->indexes);
508         newrowstash->newrow = newrow;
509     }
510 
511     return newrowstash;
512 }
513 
514 /* implements the table data helper.  This is the routine that takes
515  *  care of all SNMP requests coming into the table. */
516 int
netsnmp_table_data_set_helper_handler(netsnmp_mib_handler * handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)517 netsnmp_table_data_set_helper_handler(netsnmp_mib_handler *handler,
518                                       netsnmp_handler_registration
519                                       *reginfo,
520                                       netsnmp_agent_request_info *reqinfo,
521                                       netsnmp_request_info *requests)
522 {
523     netsnmp_table_data_set_storage *data = NULL;
524     netsnmp_table_request_info *table_info;
525     netsnmp_request_info *request;
526     netsnmp_table_row *row = NULL;
527 #ifndef NETSNMP_NO_WRITE_SUPPORT
528     netsnmp_oid_stash_node **stashp = NULL;
529     netsnmp_table_row *newrow = NULL;
530     newrow_stash   *newrowstash = NULL;
531 #endif /* NETSNMP_NO_WRITE_SUPPORT */
532 
533     if (!handler)
534         return SNMPERR_GENERR;
535 
536     DEBUGMSGTL(("netsnmp_table_data_set", "handler starting\n"));
537     for (request = requests; request; request = request->next) {
538 #ifndef NETSNMP_NO_WRITE_SUPPORT
539         netsnmp_table_data_set *datatable =
540             (netsnmp_table_data_set *) handler->myvoid;
541         const oid * const suffix =
542             requests->requestvb->name + reginfo->rootoid_len + 2;
543         const size_t suffix_len =
544             requests->requestvb->name_length - (reginfo->rootoid_len + 2);
545 #endif /* NETSNMP_NO_WRITE_SUPPORT */
546 
547         if (request->processed)
548             continue;
549 
550         /*
551          * extract our stored data and table info
552          */
553         row = netsnmp_extract_table_row(request);
554         table_info = netsnmp_extract_table_info(request);
555 
556 #ifndef NETSNMP_NO_WRITE_SUPPORT
557         if (MODE_IS_SET(reqinfo->mode)) {
558 
559             /*
560              * use a cached copy of the row for modification
561              */
562 
563             /*
564              * cache location: may have been created already by other
565              * SET requests in the same master request.
566              */
567             stashp = netsnmp_table_dataset_get_or_create_stash(reqinfo,
568                                                                datatable,
569                                                                table_info);
570             if (NULL == stashp) {
571                 netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR);
572                 continue;
573             }
574 
575             newrowstash
576                 = (newrow_stash*)netsnmp_oid_stash_get_data(*stashp, suffix, suffix_len);
577 
578             if (!newrowstash) {
579                 if (!row) {
580                     if (datatable->allow_creation) {
581                         /*
582                          * entirely new row.  Create the row from the template
583                          */
584                         newrowstash =
585                              netsnmp_table_data_set_create_newrowstash(
586                                                  datatable, table_info);
587                         newrow = newrowstash->newrow;
588                     } else if (datatable->rowstatus_column == 0) {
589                         /*
590                          * A RowStatus object may be used to control the
591                          *  creation of a new row.  But if this object
592                          *  isn't declared (and the table isn't marked as
593                          *  'auto-create'), then we can't create a new row.
594                          */
595                         netsnmp_set_request_error(reqinfo, request,
596                                                   SNMP_ERR_NOCREATION);
597                         continue;
598                     }
599                 } else {
600                     /*
601                      * existing row that needs to be modified
602                      */
603                     newrowstash = SNMP_MALLOC_TYPEDEF(newrow_stash);
604                     if (newrowstash == NULL) {
605                         netsnmp_set_request_error(reqinfo, request,
606                                                   SNMP_ERR_GENERR);
607                         continue;
608                     }
609                     newrow = netsnmp_table_data_set_clone_row(row);
610                     newrowstash->newrow = newrow;
611                 }
612                 netsnmp_oid_stash_add_data(stashp, suffix, suffix_len,
613                                            newrowstash);
614             } else {
615                 newrow = newrowstash->newrow;
616             }
617             /*
618              * all future SET data modification operations use this
619              * temp pointer
620              */
621             if (reqinfo->mode == MODE_SET_RESERVE1 ||
622                 reqinfo->mode == MODE_SET_RESERVE2)
623                 row = newrow;
624         }
625 #endif /* NETSNMP_NO_WRITE_SUPPORT */
626 
627         if (row)
628             data = (netsnmp_table_data_set_storage *) row->data;
629 
630         if (!row || !table_info || !data) {
631             if (!table_info
632 #ifndef NETSNMP_NO_WRITE_SUPPORT
633                 || !MODE_IS_SET(reqinfo->mode)
634 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
635                 ) {
636                 netsnmp_set_request_error(reqinfo, request,
637                                           SNMP_NOSUCHINSTANCE);
638                 continue;
639             }
640         }
641 
642         data =
643             netsnmp_table_data_set_find_column(data, table_info->colnum);
644 
645         switch (reqinfo->mode) {
646         case MODE_GET:
647         case MODE_GETNEXT:
648         case MODE_GETBULK:     /* XXXWWW */
649             if (!data || data->type == SNMP_NOSUCHINSTANCE) {
650                 netsnmp_set_request_error(reqinfo, request,
651                                           SNMP_NOSUCHINSTANCE);
652             } else {
653                 /*
654                  * Note: data->data.voidp can be NULL, e.g. when a zero-length
655                  * octet string has been stored in the table cache.
656                  */
657                 netsnmp_table_data_build_result(reginfo, reqinfo, request,
658                                                 row,
659                                                 table_info->colnum,
660                                                 data->type,
661                                        (u_char*)data->data.voidp,
662                                                 data->data_len);
663             }
664             break;
665 
666 #ifndef NETSNMP_NO_WRITE_SUPPORT
667         case MODE_SET_RESERVE1:
668             if (data) {
669                 /*
670                  * Can we modify the existing row?
671                  */
672                 if (!data->writable) {
673                     netsnmp_set_request_error(reqinfo, request,
674                                               SNMP_ERR_NOTWRITABLE);
675                 } else if (request->requestvb->type != data->type) {
676                     netsnmp_set_request_error(reqinfo, request,
677                                               SNMP_ERR_WRONGTYPE);
678                 }
679             } else if (datatable->rowstatus_column == table_info->colnum) {
680                 /*
681                  * Otherwise, this is where we create a new row using
682                  * the RowStatus object (essentially duplicating the
683                  * steps followed earlier in the 'allow_creation' case)
684                  */
685                 switch (*(request->requestvb->val.integer)) {
686                 case RS_CREATEANDGO:
687                 case RS_CREATEANDWAIT:
688                     newrowstash =
689                              netsnmp_table_data_set_create_newrowstash(
690                                                  datatable, table_info);
691                     newrow = newrowstash->newrow;
692                     row    = newrow;
693                     netsnmp_oid_stash_add_data(stashp, suffix, suffix_len,
694                                                newrowstash);
695                 }
696             }
697             break;
698 
699         case MODE_SET_RESERVE2:
700             /*
701              * If the agent receives a SET request for an object in a non-existant
702              *  row, then the RESERVE1 pass will create the row automatically.
703              *
704              * But since the row doesn't exist at that point, the test for whether
705              *  the object is writable or not will be skipped.  So we need to check
706              *  for this possibility again here.
707              *
708              * Similarly, if row creation is under the control of the RowStatus
709              *  object (i.e. allow_creation == 0), but this particular request
710              *  doesn't include such an object, then the row won't have been created,
711              *  and the writable check will also have been skipped.  Again - check here.
712              */
713             if (data && data->writable == 0) {
714                 netsnmp_set_request_error(reqinfo, request,
715                                           SNMP_ERR_NOTWRITABLE);
716                 continue;
717             }
718             if (datatable->rowstatus_column == table_info->colnum) {
719                 switch (*(request->requestvb->val.integer)) {
720                 case RS_ACTIVE:
721                 case RS_NOTINSERVICE:
722                     /*
723                      * Can only operate on pre-existing rows.
724                      */
725                     if (!newrowstash || newrowstash->created) {
726                         netsnmp_set_request_error(reqinfo, request,
727                                                   SNMP_ERR_INCONSISTENTVALUE);
728                         continue;
729                     }
730                     break;
731 
732                 case RS_CREATEANDGO:
733                 case RS_CREATEANDWAIT:
734                     /*
735                      * Can only operate on newly created rows.
736                      */
737                     if (!(newrowstash && newrowstash->created)) {
738                         netsnmp_set_request_error(reqinfo, request,
739                                                   SNMP_ERR_INCONSISTENTVALUE);
740                         continue;
741                     }
742                     break;
743 
744                 case RS_DESTROY:
745                     /*
746                      * Can operate on new or pre-existing rows.
747                      */
748                     break;
749 
750                 case RS_NOTREADY:
751                 default:
752                     /*
753                      * Not a valid value to Set
754                      */
755                     netsnmp_set_request_error(reqinfo, request,
756                                               SNMP_ERR_WRONGVALUE);
757                     continue;
758                 }
759             }
760             if (!data ) {
761                 netsnmp_set_request_error(reqinfo, request,
762                                           SNMP_ERR_NOCREATION);
763                 continue;
764             }
765 
766             /*
767              * modify row and set new value
768              */
769             SNMP_FREE(data->data.string);
770             data->data.string = (u_char *)
771                 netsnmp_strdup_and_null(request->requestvb->val.string,
772                                         request->requestvb->val_len);
773             if (!data->data.string) {
774                 netsnmp_set_request_error(reqinfo, requests,
775                                           SNMP_ERR_RESOURCEUNAVAILABLE);
776             }
777             data->data_len = request->requestvb->val_len;
778 
779             if (datatable->rowstatus_column == table_info->colnum) {
780                 switch (*(request->requestvb->val.integer)) {
781                 case RS_CREATEANDGO:
782                     /*
783                      * XXX: check legality
784                      */
785                     *(data->data.integer) = RS_ACTIVE;
786                     break;
787 
788                 case RS_CREATEANDWAIT:
789                     /*
790                      * XXX: check legality
791                      */
792                     *(data->data.integer) = RS_NOTINSERVICE;
793                     break;
794 
795                 case RS_DESTROY:
796                     newrowstash->deleted = 1;
797                     break;
798                 }
799             }
800             break;
801 
802         case MODE_SET_ACTION:
803 
804             /*
805              * Install the new row into the stored table.
806 	     * Do this only *once* per row ....
807              */
808             if (newrowstash->state != STATE_ACTION) {
809                 newrowstash->state = STATE_ACTION;
810 		if (newrowstash->created) {
811                     netsnmp_table_dataset_add_row(datatable, newrow);
812                 } else {
813                     netsnmp_table_dataset_replace_row(datatable,
814                                                       row, newrow);
815                 }
816             }
817             /*
818              * ... but every (relevant) varbind in the request will
819 	     * need to know about this new row, so update the
820 	     * per-request row information regardless
821              */
822             if (newrowstash->created) {
823 		netsnmp_request_add_list_data(request,
824 			netsnmp_create_data_list(TABLE_DATA_NAME,
825 						 newrow, NULL));
826             }
827             break;
828 
829         case MODE_SET_UNDO:
830             /*
831              * extract the new row, replace with the old or delete
832              */
833             if (newrowstash->state != STATE_UNDO) {
834                 newrowstash->state = STATE_UNDO;
835                 if (newrowstash->created) {
836                     netsnmp_table_dataset_remove_and_delete_row(datatable, newrow);
837                 } else {
838                     netsnmp_table_dataset_replace_row(datatable,
839                                                       newrow, row);
840                     netsnmp_table_dataset_delete_row(newrow);
841                 }
842                 newrow = NULL;
843             }
844             break;
845 
846         case MODE_SET_COMMIT:
847             if (newrowstash->state != STATE_COMMIT) {
848                 newrowstash->state = STATE_COMMIT;
849                 if (!newrowstash->created) {
850 		    netsnmp_request_info       *req;
851                     netsnmp_table_dataset_delete_row(row);
852 
853 		    /* Walk the request list to update the reference to the old row w/ th new one */
854     		    for (req = requests; req; req=req->next) {
855 
856 		    	/*
857                          * For requests that have the old row values,
858                          * so add the newly-created row information.
859                          */
860         	    	if ((netsnmp_table_row *) netsnmp_extract_table_row(req) == row) {
861 	    			netsnmp_request_remove_list_data(req, TABLE_DATA_ROW);
862             			netsnmp_request_add_list_data(req,
863                 		    netsnmp_create_data_list(TABLE_DATA_ROW, newrow, NULL));
864         	    	}
865     		    }
866 
867 		    row = NULL;
868                 }
869                 if (newrowstash->deleted) {
870                     netsnmp_table_dataset_remove_and_delete_row(datatable, newrow);
871                     newrow = NULL;
872                 }
873             }
874             break;
875 
876         case MODE_SET_FREE:
877             if (newrowstash && newrowstash->state != STATE_FREE) {
878                 newrowstash->state = STATE_FREE;
879                 netsnmp_table_dataset_delete_row(newrow);
880 		newrow = NULL;
881             }
882             break;
883 #endif /* NETSNMP_NO_WRITE_SUPPORT */
884 
885         default:
886             snmp_log(LOG_ERR,
887                      "table_dataset: unknown mode passed into the handler\n");
888             return SNMP_ERR_GENERR;
889         }
890     }
891 
892     /* next handler called automatically - 'AUTO_NEXT' */
893     return SNMP_ERR_NOERROR;
894 }
895 
896 /**
897  * extracts a netsnmp_table_data_set pointer from a given request
898  */
899 NETSNMP_INLINE netsnmp_table_data_set *
netsnmp_extract_table_data_set(netsnmp_request_info * request)900 netsnmp_extract_table_data_set(netsnmp_request_info *request)
901 {
902     return (netsnmp_table_data_set *)
903         netsnmp_request_get_list_data(request, TABLE_DATA_SET_NAME);
904 }
905 
906 /**
907  * extracts a netsnmp_table_data_set pointer from a given request
908  */
909 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_SET_COLUMN
910 netsnmp_table_data_set_storage *
netsnmp_extract_table_data_set_column(netsnmp_request_info * request,unsigned int column)911 netsnmp_extract_table_data_set_column(netsnmp_request_info *request,
912                                      unsigned int column)
913 {
914     netsnmp_table_data_set_storage *data =
915         (netsnmp_table_data_set_storage*)netsnmp_extract_table_row_data( request );
916     if (data) {
917         data = netsnmp_table_data_set_find_column(data, column);
918     }
919     return data;
920 }
921 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_SET_COLUMN */
922 
923 /* ==================================
924  *
925  * Data Set API: Config-based operation
926  *
927  * ================================== */
928 
929 /** registers a table_dataset so that the "add_row" snmpd.conf token
930   * can be used to add data to this table.  If registration_name is
931   * NULL then the name used when the table was created will be used
932   * instead.
933   *
934   * @todo create a properly free'ing registeration pointer for the
935   * datalist, and get the datalist freed at shutdown.
936   */
937 void
netsnmp_register_auto_data_table(netsnmp_table_data_set * table_set,char * registration_name)938 netsnmp_register_auto_data_table(netsnmp_table_data_set *table_set,
939                                  char *registration_name)
940 {
941     data_set_tables *tables;
942     tables = SNMP_MALLOC_TYPEDEF(data_set_tables);
943     if (!tables)
944         return;
945     tables->table_set = table_set;
946     if (!registration_name) {
947         registration_name = table_set->table->name;
948     }
949     netsnmp_add_list_data(&auto_tables,
950                           netsnmp_create_data_list(registration_name,
951                                                    tables, free));     /* XXX */
952 }
953 
954 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATASET_UNREGISTER_AUTO_DATA_TABLE
955 /** Undo the effect of netsnmp_register_auto_data_table().
956  */
957 void
netsnmp_unregister_auto_data_table(netsnmp_table_data_set * table_set,char * registration_name)958 netsnmp_unregister_auto_data_table(netsnmp_table_data_set *table_set,
959 				   char *registration_name)
960 {
961     netsnmp_remove_list_node(&auto_tables, registration_name
962 			     ? registration_name : table_set->table->name);
963 }
964 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATASET_UNREGISTER_AUTO_DATA_TABLE */
965 
966 #ifndef NETSNMP_DISABLE_MIB_LOADING
967 static void
_table_set_add_indexes(netsnmp_table_data_set * table_set,struct tree * tp)968 _table_set_add_indexes(netsnmp_table_data_set *table_set, struct tree *tp)
969 {
970     oid             name[MAX_OID_LEN];
971     size_t          name_length = MAX_OID_LEN;
972     struct index_list *index;
973     struct tree     *indexnode;
974     u_char          type;
975     int             fixed_len = 0;
976 
977     /*
978      * loop through indexes and add types
979      */
980     for (index = tp->indexes; index; index = index->next) {
981         if (!snmp_parse_oid(index->ilabel, name, &name_length) ||
982             (NULL ==
983              (indexnode = get_tree(name, name_length, get_tree_head())))) {
984             config_pwarn("can't instatiate table since "
985                          "I don't know anything about one index");
986             snmp_log(LOG_WARNING, "  index %s not found in tree\n",
987                      index->ilabel);
988             return;             /* xxx mem leak */
989         }
990 
991         type = mib_to_asn_type(indexnode->type);
992         if (type == (u_char) - 1) {
993             config_pwarn("unknown index type");
994             return;             /* xxx mem leak */
995         }
996         /*
997          * if implied, mark it as such. also mark fixed length
998          * octet strings as implied (ie no length prefix) as well.
999          * */
1000         if ((TYPE_OCTETSTR == indexnode->type) &&  /* octet str */
1001             (NULL != indexnode->ranges) &&         /* & has range */
1002             (NULL == indexnode->ranges->next) &&   /*   but only one */
1003             (indexnode->ranges->high ==            /*   & high==low */
1004              indexnode->ranges->low)) {
1005             type |= ASN_PRIVATE;
1006             fixed_len = indexnode->ranges->high;
1007         }
1008         else if (index->isimplied)
1009             type |= ASN_PRIVATE;
1010 
1011         DEBUGMSGTL(("table_set_add_table",
1012                     "adding default index of type %d\n", type));
1013         netsnmp_table_dataset_add_index(table_set, type);
1014 
1015         /*
1016          * hack alert: for fixed lenght strings, save the
1017          * lenght for use during oid parsing.
1018          */
1019         if (fixed_len) {
1020             /*
1021              * find last (just added) index
1022              */
1023             netsnmp_variable_list *var =  table_set->table->indexes_template;
1024             while (NULL != var->next_variable)
1025                 var = var->next_variable;
1026             var->val_len = fixed_len;
1027         }
1028     }
1029 }
1030 /** @internal */
1031 void
netsnmp_config_parse_table_set(const char * token,char * line)1032 netsnmp_config_parse_table_set(const char *token, char *line)
1033 {
1034     oid             table_name[MAX_OID_LEN];
1035     size_t          table_name_length = MAX_OID_LEN;
1036     struct tree    *tp;
1037     netsnmp_table_data_set *table_set;
1038     data_set_tables *tables;
1039     unsigned int    mincol = 0xffffff, maxcol = 0;
1040     char           *pos;
1041 
1042     /*
1043      * instatiate a fake table based on MIB information
1044      */
1045     DEBUGMSGTL(("9:table_set_add_table", "processing '%s'\n", line));
1046     if (NULL != (pos = strchr(line,' '))) {
1047         config_pwarn("ignoring extra tokens on line");
1048         snmp_log(LOG_WARNING,"  ignoring '%s'\n", pos);
1049         *pos = '\0';
1050     }
1051 
1052     /*
1053      * check for duplicate table
1054      */
1055     tables = (data_set_tables *) netsnmp_get_list_data(auto_tables, line);
1056     if (NULL != tables) {
1057         config_pwarn("duplicate table definition");
1058         return;
1059     }
1060 
1061     /*
1062      * parse oid and find tree structure
1063      */
1064     if (!snmp_parse_oid(line, table_name, &table_name_length)) {
1065         config_pwarn
1066             ("can't instatiate table since I can't parse the table name");
1067         return;
1068     }
1069     if(NULL == (tp = get_tree(table_name, table_name_length,
1070                               get_tree_head()))) {
1071         config_pwarn("can't instatiate table since "
1072                      "I can't find mib information about it");
1073         return;
1074     }
1075 
1076     if (NULL == (tp = tp->child_list) || NULL == tp->child_list) {
1077         config_pwarn("can't instatiate table since it doesn't appear to be "
1078                      "a proper table (no children)");
1079         return;
1080     }
1081 
1082     table_set = netsnmp_create_table_data_set(line);
1083 
1084     /*
1085      * check for augments indexes
1086      */
1087     if (NULL != tp->augments) {
1088         oid             name[MAX_OID_LEN];
1089         size_t          name_length = MAX_OID_LEN;
1090         struct tree    *tp2;
1091 
1092         if (!snmp_parse_oid(tp->augments, name, &name_length)) {
1093             config_pwarn("I can't parse the augment table name");
1094             snmp_log(LOG_WARNING, "  can't parse %s\n", tp->augments);
1095             SNMP_FREE (table_set);
1096             return;
1097         }
1098         if(NULL == (tp2 = get_tree(name, name_length, get_tree_head()))) {
1099             config_pwarn("can't instatiate table since "
1100                          "I can't find mib information about augment table");
1101             snmp_log(LOG_WARNING, "  table %s not found in tree\n",
1102                      tp->augments);
1103             SNMP_FREE (table_set);
1104             return;
1105         }
1106         _table_set_add_indexes(table_set, tp2);
1107     }
1108 
1109     _table_set_add_indexes(table_set, tp);
1110 
1111     /*
1112      * loop through children and add each column info
1113      */
1114     for (tp = tp->child_list; tp; tp = tp->next_peer) {
1115         int             canwrite = 0;
1116         u_char          type;
1117         type = mib_to_asn_type(tp->type);
1118         if (type == (u_char) - 1) {
1119             config_pwarn("unknown column type");
1120 	    SNMP_FREE (table_set);
1121             return;             /* xxx mem leak */
1122         }
1123 
1124         DEBUGMSGTL(("table_set_add_table",
1125                     "adding column %s(%ld) of type %d (access %d)\n",
1126                     tp->label, tp->subid, type, tp->access));
1127 
1128         switch (tp->access) {
1129         case MIB_ACCESS_CREATE:
1130             table_set->allow_creation = 1;
1131             /* fallthrough */
1132         case MIB_ACCESS_READWRITE:
1133         case MIB_ACCESS_WRITEONLY:
1134             canwrite = 1;
1135             /* fallthrough */
1136         case MIB_ACCESS_READONLY:
1137             DEBUGMSGTL(("table_set_add_table",
1138                         "adding column %ld of type %d\n", tp->subid, type));
1139             netsnmp_table_set_add_default_row(table_set, tp->subid, type,
1140                                               canwrite, NULL, 0);
1141             mincol = SNMP_MIN(mincol, tp->subid);
1142             maxcol = SNMP_MAX(maxcol, tp->subid);
1143             break;
1144 
1145         case MIB_ACCESS_NOACCESS:
1146         case MIB_ACCESS_NOTIFY:
1147             break;
1148 
1149         default:
1150             config_pwarn("unknown column access type");
1151             break;
1152         }
1153     }
1154 
1155     /*
1156      * register the table
1157      */
1158     netsnmp_register_table_data_set(netsnmp_create_handler_registration
1159                                     (line, NULL, table_name,
1160                                      table_name_length,
1161                                      HANDLER_CAN_RWRITE), table_set, NULL);
1162 
1163     netsnmp_register_auto_data_table(table_set, NULL);
1164 }
1165 #endif /* NETSNMP_DISABLE_MIB_LOADING */
1166 
1167 /** @internal */
1168 void
netsnmp_config_parse_add_row(const char * token,char * line)1169 netsnmp_config_parse_add_row(const char *token, char *line)
1170 {
1171     char            buf[SNMP_MAXBUF_MEDIUM];
1172     char            tname[SNMP_MAXBUF_MEDIUM];
1173     size_t          buf_size;
1174     int             rc;
1175 
1176     data_set_tables *tables;
1177     netsnmp_variable_list *vb;  /* containing only types */
1178     netsnmp_table_row *row;
1179     netsnmp_table_data_set_storage *dr;
1180 
1181     line = copy_nword(line, tname, sizeof(tname));
1182 
1183     tables = (data_set_tables *) netsnmp_get_list_data(auto_tables, tname);
1184     if (!tables) {
1185         config_pwarn("Unknown table trying to add a row");
1186         return;
1187     }
1188 
1189     /*
1190      * do the indexes first
1191      */
1192     row = netsnmp_create_table_data_row();
1193 
1194     for (vb = tables->table_set->table->indexes_template; vb;
1195          vb = vb->next_variable) {
1196         if (!line) {
1197             config_pwarn("missing an index value");
1198             SNMP_FREE (row);
1199             return;
1200         }
1201 
1202         DEBUGMSGTL(("table_set_add_row", "adding index of type %d\n",
1203                     vb->type));
1204         buf_size = sizeof(buf);
1205         line = read_config_read_memory(vb->type, line, buf, &buf_size);
1206         netsnmp_table_row_add_index(row, vb->type, buf, buf_size);
1207     }
1208 
1209     /*
1210      * then do the data
1211      */
1212     for (dr = tables->table_set->default_row; dr; dr = dr->next) {
1213         if (!line) {
1214             config_pwarn("missing a data value. "
1215                          "All columns must be specified.");
1216             snmp_log(LOG_WARNING,"  can't find value for column %d\n",
1217                      dr->column - 1);
1218             SNMP_FREE (row);
1219             return;
1220         }
1221 
1222         buf_size = sizeof(buf);
1223         line = read_config_read_memory(dr->type, line, buf, &buf_size);
1224         DEBUGMSGTL(("table_set_add_row",
1225                     "adding data at column %d of type %d\n", dr->column,
1226                     dr->type));
1227         netsnmp_set_row_column(row, dr->column, dr->type, buf, buf_size);
1228 #ifndef NETSNMP_NO_WRITE_SUPPORT
1229         if (dr->writable)
1230             netsnmp_mark_row_column_writable(row, dr->column, 1);       /* make writable */
1231 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
1232     }
1233     rc = netsnmp_table_data_add_row(tables->table_set->table, row);
1234     if (SNMPERR_SUCCESS != rc) {
1235         config_pwarn("error adding table row");
1236     }
1237     if (NULL != line) {
1238         config_pwarn("extra data value. Too many columns specified.");
1239         snmp_log(LOG_WARNING,"  extra data '%s'\n", line);
1240     }
1241 }
1242 
1243 
1244 #ifndef NETSNMP_NO_WRITE_SUPPORT
1245 netsnmp_oid_stash_node **
netsnmp_table_dataset_get_or_create_stash(netsnmp_agent_request_info * reqinfo,netsnmp_table_data_set * datatable,netsnmp_table_request_info * table_info)1246 netsnmp_table_dataset_get_or_create_stash(netsnmp_agent_request_info *reqinfo,
1247                                           netsnmp_table_data_set *datatable,
1248                                           netsnmp_table_request_info *table_info)
1249 {
1250     netsnmp_oid_stash_node **stashp = NULL;
1251     char                     buf[256]; /* is this reasonable size?? */
1252     size_t                   len;
1253     int                      rc;
1254 
1255     rc = snprintf(buf, sizeof(buf), "dataset_row_stash:%s:",
1256                   datatable->table->name);
1257     if ((-1 == rc) || ((size_t)rc >= sizeof(buf))) {
1258         snmp_log(LOG_ERR,"%s handler name too long\n", datatable->table->name);
1259         return NULL;
1260     }
1261 
1262     len = sizeof(buf) - rc;
1263     rc = snprint_objid(&buf[rc], len, table_info->index_oid,
1264                        table_info->index_oid_len);
1265     if (-1 == rc) {
1266         snmp_log(LOG_ERR,"%s oid or name too long\n", datatable->table->name);
1267         return NULL;
1268     }
1269 
1270     stashp = (netsnmp_oid_stash_node **)
1271         netsnmp_table_get_or_create_row_stash(reqinfo, (u_char *) buf);
1272     return stashp;
1273 }
1274 
1275 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATASET_GET_NEWROW
1276 netsnmp_table_row *
netsnmp_table_dataset_get_newrow(netsnmp_request_info * request,netsnmp_agent_request_info * reqinfo,int rootoid_len,netsnmp_table_data_set * datatable,netsnmp_table_request_info * table_info)1277 netsnmp_table_dataset_get_newrow(netsnmp_request_info *request,
1278                                  netsnmp_agent_request_info *reqinfo,
1279                                  int rootoid_len,
1280                                  netsnmp_table_data_set *datatable,
1281                                  netsnmp_table_request_info *table_info)
1282 {
1283     oid * const suffix = request->requestvb->name + rootoid_len + 2;
1284     size_t suffix_len = request->requestvb->name_length - (rootoid_len + 2);
1285     netsnmp_oid_stash_node **stashp;
1286     newrow_stash   *newrowstash;
1287 
1288     stashp = netsnmp_table_dataset_get_or_create_stash(reqinfo, datatable,
1289                                                        table_info);
1290     if (NULL == stashp)
1291         return NULL;
1292 
1293     newrowstash = (newrow_stash*)netsnmp_oid_stash_get_data(*stashp, suffix, suffix_len);
1294     if (NULL == newrowstash)
1295         return NULL;
1296 
1297     return newrowstash->newrow;
1298 }
1299 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATASET_GET_NEWROW */
1300 #endif /* NETSNMP_NO_WRITE_SUPPORT */
1301 
1302 /* ==================================
1303  *
1304  * Data Set API: Row operations
1305  *
1306  * ================================== */
1307 
1308 /** returns the first row in the table */
1309 netsnmp_table_row *
netsnmp_table_data_set_get_first_row(netsnmp_table_data_set * table)1310 netsnmp_table_data_set_get_first_row(netsnmp_table_data_set *table)
1311 {
1312     return netsnmp_table_data_get_first_row(table->table);
1313 }
1314 
1315 /** returns the next row in the table */
1316 netsnmp_table_row *
netsnmp_table_data_set_get_next_row(netsnmp_table_data_set * table,netsnmp_table_row * row)1317 netsnmp_table_data_set_get_next_row(netsnmp_table_data_set *table,
1318                                     netsnmp_table_row      *row)
1319 {
1320     return netsnmp_table_data_get_next_row(table->table, row);
1321 }
1322 
1323 int
netsnmp_table_set_num_rows(netsnmp_table_data_set * table)1324 netsnmp_table_set_num_rows(netsnmp_table_data_set *table)
1325 {
1326     if (!table)
1327         return 0;
1328     return netsnmp_table_data_num_rows(table->table);
1329 }
1330 
1331 /* ==================================
1332  *
1333  * Data Set API: Column operations
1334  *
1335  * ================================== */
1336 
1337 /** Finds a column within a given storage set, given the pointer to
1338    the start of the storage set list.
1339 */
1340 netsnmp_table_data_set_storage *
netsnmp_table_data_set_find_column(netsnmp_table_data_set_storage * start,unsigned int column)1341 netsnmp_table_data_set_find_column(netsnmp_table_data_set_storage *start,
1342                                    unsigned int column)
1343 {
1344     while (start && start->column != column)
1345         start = start->next;
1346     return start;
1347 }
1348 
1349 #ifndef NETSNMP_NO_WRITE_SUPPORT
1350 /**
1351  * marks a given column in a row as writable or not.
1352  */
1353 int
netsnmp_mark_row_column_writable(netsnmp_table_row * row,int column,int writable)1354 netsnmp_mark_row_column_writable(netsnmp_table_row *row, int column,
1355                                  int writable)
1356 {
1357     netsnmp_table_data_set_storage *data;
1358 
1359     if (!row)
1360         return SNMPERR_GENERR;
1361 
1362     data = (netsnmp_table_data_set_storage *) row->data;
1363     data = netsnmp_table_data_set_find_column(data, column);
1364 
1365     if (!data) {
1366         /*
1367          * create it
1368          */
1369         data = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage);
1370         if (!data) {
1371             snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column");
1372             return SNMPERR_MALLOC;
1373         }
1374         data->column = column;
1375         data->writable = writable;
1376         data->next = (struct netsnmp_table_data_set_storage_s*)row->data;
1377         row->data = data;
1378     } else {
1379         data->writable = writable;
1380     }
1381     return SNMPERR_SUCCESS;
1382 }
1383 #endif /* NETSNMP_NO_WRITE_SUPPORT */
1384 
1385 /**
1386  * Sets a given column in a row with data given a type, value,
1387  * and length. Data is memdup'ed by the function, at least if
1388  * type != SNMP_NOSUCHINSTANCE and if value_len > 0.
1389  *
1390  * @param[in] row       Pointer to the row to be modified.
1391  * @param[in] column    Index of the column to be modified.
1392  * @param[in] type      Either the ASN type of the value to be set or
1393  *   SNMP_NOSUCHINSTANCE.
1394  * @param[in] value     If type != SNMP_NOSUCHINSTANCE, pointer to the
1395  *   new value. May be NULL if value_len == 0, e.g. when storing a
1396  *   zero-length octet string. Ignored when type == SNMP_NOSUCHINSTANCE.
1397  * @param[in] value_len If type != SNMP_NOSUCHINSTANCE, number of bytes
1398  *   occupied by *value. Ignored when type == SNMP_NOSUCHINSTANCE.
1399  *
1400  * @return SNMPERR_SUCCESS upon success; SNMPERR_MALLOC when out of memory;
1401  *   or SNMPERR_GENERR when row == 0 or when type does not match the datatype
1402  *   of the data stored in *row.
1403  *
1404  */
1405 int
netsnmp_set_row_column(netsnmp_table_row * row,unsigned int column,int type,const void * value,size_t value_len)1406 netsnmp_set_row_column(netsnmp_table_row *row, unsigned int column,
1407                        int type, const void *value, size_t value_len)
1408 {
1409     netsnmp_table_data_set_storage *data;
1410 
1411     if (!row)
1412         return SNMPERR_GENERR;
1413 
1414     data = (netsnmp_table_data_set_storage *) row->data;
1415     data = netsnmp_table_data_set_find_column(data, column);
1416 
1417     if (!data) {
1418         /*
1419          * create it
1420          */
1421         data = SNMP_MALLOC_TYPEDEF(netsnmp_table_data_set_storage);
1422         if (!data) {
1423             snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column");
1424             return SNMPERR_MALLOC;
1425         }
1426 
1427         data->column = column;
1428         data->type = type;
1429         data->next = (struct netsnmp_table_data_set_storage_s*)row->data;
1430         row->data = data;
1431     }
1432 
1433     /* Transitions from / to SNMP_NOSUCHINSTANCE are allowed, but no other transitions. */
1434     if (data->type != type && data->type != SNMP_NOSUCHINSTANCE
1435         && type != SNMP_NOSUCHINSTANCE)
1436         return SNMPERR_GENERR;
1437 
1438     /* Return now if neither the type nor the data itself has been modified. */
1439     if (data->type == type && data->data_len == value_len
1440         && (value == NULL || memcmp(&data->data.string, value, value_len) == 0))
1441             return SNMPERR_SUCCESS;
1442 
1443     /* Reallocate memory and store the new value. */
1444     data->data.voidp = realloc(data->data.voidp, value ? value_len : 0);
1445     if (value && value_len && !data->data.voidp) {
1446         data->data_len = 0;
1447         data->type = SNMP_NOSUCHINSTANCE;
1448         snmp_log(LOG_CRIT, "no memory in netsnmp_set_row_column");
1449         return SNMPERR_MALLOC;
1450     }
1451     if (value && value_len)
1452         memcpy(data->data.string, value, value_len);
1453     data->type = type;
1454     data->data_len = value_len;
1455     return SNMPERR_SUCCESS;
1456 }
1457 
1458 
1459 /* ==================================
1460  *
1461  * Data Set API: Index operations
1462  *
1463  * ================================== */
1464 
1465 /** adds an index to the table.  Call this repeatly for each index. */
1466 void
netsnmp_table_dataset_add_index(netsnmp_table_data_set * table,u_char type)1467 netsnmp_table_dataset_add_index(netsnmp_table_data_set *table, u_char type)
1468 {
1469     if (!table)
1470         return;
1471     netsnmp_table_data_add_index(table->table, type);
1472 }
1473 
1474 /** adds multiple indexes to a table_dataset helper object.
1475  *  To end the list, use a 0 after the list of ASN index types. */
1476 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_SET_ADD_INDEXES
1477 void
netsnmp_table_set_add_indexes(netsnmp_table_data_set * tset,...)1478 netsnmp_table_set_add_indexes(netsnmp_table_data_set *tset,
1479                               ...)
1480 {
1481     va_list         debugargs;
1482     int             type;
1483 
1484     va_start(debugargs, tset);
1485 
1486     if (tset)
1487         while ((type = va_arg(debugargs, int)) != 0)
1488             netsnmp_table_data_add_index(tset->table, (u_char)type);
1489 
1490     va_end(debugargs);
1491 }
1492 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_SET_ADD_INDEXES */
1493 
1494 #else /* NETSNMP_FEATURE_REMOVE_TABLE_DATASET */
1495 netsnmp_feature_unused(table_dataset);
1496 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATASET */
1497 /** @}
1498  */
1499