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-includes.h>
16 #include <net-snmp/agent/net-snmp-agent-includes.h>
17 
18 #include <net-snmp/agent/table_data.h>
19 
20 #if HAVE_STRING_H
21 #include <string.h>
22 #else
23 #include <strings.h>
24 #endif
25 
26 #include <net-snmp/agent/table.h>
27 #include <net-snmp/agent/read_only.h>
28 
29 netsnmp_feature_child_of(table_data_all, mib_helpers);
30 
31 netsnmp_feature_child_of(table_data, table_data_all);
32 netsnmp_feature_child_of(register_read_only_table_data, table_data_all);
33 netsnmp_feature_child_of(extract_table_row_data, table_data_all);
34 netsnmp_feature_child_of(insert_table_row, table_data_all);
35 netsnmp_feature_child_of(table_data_delete_table, table_data_all);
36 
37 netsnmp_feature_child_of(table_data_extras, table_data_all);
38 
39 netsnmp_feature_child_of(table_data_create_table, table_data_extras);
40 netsnmp_feature_child_of(table_data_create_row, table_data_extras);
41 netsnmp_feature_child_of(table_data_copy_row, table_data_extras);
42 netsnmp_feature_child_of(table_data_remove_delete_row, table_data_extras);
43 netsnmp_feature_child_of(table_data_unregister, table_data_extras);
44 netsnmp_feature_child_of(table_data_row_count, table_data_extras);
45 netsnmp_feature_child_of(table_data_row_operations, table_data_extras);
46 netsnmp_feature_child_of(table_data_row_first, table_data_extras);
47 
48 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA
49 
50 /** @defgroup table_data table_data
51  *  Helps you implement a table with datamatted storage.
52  *  @ingroup table
53  *
54  *  This helper is obsolete.  If you are writing a new module, please
55  *  consider using the table_tdata helper instead.
56  *
57  *  This helper helps you implement a table where all the indexes are
58  *  expected to be stored within the agent itself and not in some
59  *  external storage location.  It can be used to store a list of
60  *  rows, where a row consists of the indexes to the table and a
61  *  generic data pointer.  You can then implement a subhandler which
62  *  is passed the exact row definition and data it must return data
63  *  for or accept data for.  Complex GETNEXT handling is greatly
64  *  simplified in this case.
65  *
66  *  @{
67  */
68 
69 /* ==================================
70  *
71  * Table Data API: Table maintenance
72  *
73  * ================================== */
74 
75 /*
76  * generates the index portion of an table oid from a varlist.
77  */
78 void
netsnmp_table_data_generate_index_oid(netsnmp_table_row * row)79 netsnmp_table_data_generate_index_oid(netsnmp_table_row *row)
80 {
81     build_oid(&row->index_oid, &row->index_oid_len, NULL, 0, row->indexes);
82 }
83 
84 /** creates and returns a pointer to table data set */
85 netsnmp_table_data *
netsnmp_create_table_data(const char * name)86 netsnmp_create_table_data(const char *name)
87 {
88     netsnmp_table_data *table = SNMP_MALLOC_TYPEDEF(netsnmp_table_data);
89     if (name && table)
90         table->name = strdup(name);
91     return table;
92 }
93 
94 /** creates and returns a pointer to table data set */
95 netsnmp_table_row *
netsnmp_create_table_data_row(void)96 netsnmp_create_table_data_row(void)
97 {
98     netsnmp_table_row *row = SNMP_MALLOC_TYPEDEF(netsnmp_table_row);
99     return row;
100 }
101 
102 /** clones a data row. DOES NOT CLONE THE CONTAINED DATA. */
103 netsnmp_table_row *
netsnmp_table_data_clone_row(netsnmp_table_row * row)104 netsnmp_table_data_clone_row(netsnmp_table_row *row)
105 {
106     netsnmp_table_row *newrow = NULL;
107     if (!row)
108         return NULL;
109 
110     newrow = netsnmp_memdup(row, sizeof(netsnmp_table_row));
111     if (!newrow)
112         return NULL;
113 
114     if (row->indexes) {
115         newrow->indexes = snmp_clone_varbind(newrow->indexes);
116         if (!newrow->indexes) {
117             free(newrow);
118             return NULL;
119         }
120     }
121 
122     if (row->index_oid) {
123         newrow->index_oid =
124             snmp_duplicate_objid(row->index_oid, row->index_oid_len);
125         if (!newrow->index_oid) {
126             free(newrow->indexes);
127             free(newrow);
128             return NULL;
129         }
130     }
131 
132     return newrow;
133 }
134 
135 /** deletes a row's memory.
136  *  returns the void data that it doesn't know how to delete. */
137 void           *
netsnmp_table_data_delete_row(netsnmp_table_row * row)138 netsnmp_table_data_delete_row(netsnmp_table_row *row)
139 {
140     void           *data;
141 
142     if (!row)
143         return NULL;
144 
145     /*
146      * free the memory we can
147      */
148     if (row->indexes)
149         snmp_free_varbind(row->indexes);
150     SNMP_FREE(row->index_oid);
151     data = row->data;
152     free(row);
153 
154     /*
155      * return the void * pointer
156      */
157     return data;
158 }
159 
160 /**
161  * Adds a row of data to a given table (stored in proper lexographical order).
162  *
163  * returns SNMPERR_SUCCESS on successful addition.
164  *      or SNMPERR_GENERR  on failure (E.G., indexes already existed)
165  */
166 int
netsnmp_table_data_add_row(netsnmp_table_data * table,netsnmp_table_row * row)167 netsnmp_table_data_add_row(netsnmp_table_data *table,
168                            netsnmp_table_row *row)
169 {
170     int rc, dup = 0;
171     netsnmp_table_row *nextrow = NULL, *prevrow;
172 
173     if (!row || !table)
174         return SNMPERR_GENERR;
175 
176     if (row->indexes)
177         netsnmp_table_data_generate_index_oid(row);
178 
179     /*
180      * we don't store the index info as it
181      * takes up memory.
182      */
183     if (!table->store_indexes) {
184         snmp_free_varbind(row->indexes);
185         row->indexes = NULL;
186     }
187 
188     if (!row->index_oid) {
189         snmp_log(LOG_ERR,
190                  "illegal data attempted to be added to table %s (no index)\n",
191                  table->name);
192         return SNMPERR_GENERR;
193     }
194 
195     /*
196      * check for simple append
197      */
198     if ((prevrow = table->last_row) != NULL) {
199         rc = snmp_oid_compare(prevrow->index_oid, prevrow->index_oid_len,
200                               row->index_oid, row->index_oid_len);
201         if (0 == rc)
202             dup = 1;
203     }
204     else
205         rc = 1;
206 
207     /*
208      * if no last row, or newrow < last row, search the table and
209      * insert it into the table in the proper oid-lexographical order
210      */
211     if (rc > 0) {
212         for (nextrow = table->first_row, prevrow = NULL;
213              nextrow != NULL; prevrow = nextrow, nextrow = nextrow->next) {
214             if (NULL == nextrow->index_oid) {
215                 DEBUGMSGT(("table_data_add_data", "row doesn't have index!\n"));
216                 /** xxx-rks: remove invalid row? */
217                 continue;
218             }
219             rc = snmp_oid_compare(nextrow->index_oid, nextrow->index_oid_len,
220                                   row->index_oid, row->index_oid_len);
221             if(rc > 0)
222                 break;
223             if (0 == rc) {
224                 dup = 1;
225                 break;
226             }
227         }
228     }
229 
230     if (dup) {
231         /*
232          * exact match.  Duplicate entries illegal
233          */
234         snmp_log(LOG_WARNING,
235                  "duplicate table data attempted to be entered. row exists\n");
236         return SNMPERR_GENERR;
237     }
238 
239     /*
240      * ok, we have the location of where it should go
241      */
242     /*
243      * (after prevrow, and before nextrow)
244      */
245     row->next = nextrow;
246     row->prev = prevrow;
247 
248     if (row->next)
249         row->next->prev = row;
250 
251     if (row->prev)
252         row->prev->next = row;
253 
254     if (NULL == row->prev)      /* it's the (new) first row */
255         table->first_row = row;
256     if (NULL == row->next)      /* it's the last row */
257         table->last_row = row;
258 
259     DEBUGMSGTL(("table_data_add_data", "added something...\n"));
260 
261     return SNMPERR_SUCCESS;
262 }
263 
264 /** swaps out origrow with newrow.  This does *not* delete/free anything! */
265 void
netsnmp_table_data_replace_row(netsnmp_table_data * table,netsnmp_table_row * origrow,netsnmp_table_row * newrow)266 netsnmp_table_data_replace_row(netsnmp_table_data *table,
267                                netsnmp_table_row *origrow,
268                                netsnmp_table_row *newrow)
269 {
270     netsnmp_table_data_remove_row(table, origrow);
271     netsnmp_table_data_add_row(table, newrow);
272 }
273 
274 /**
275  * removes a row of data to a given table and returns it (no free's called)
276  *
277  * returns the row pointer itself on successful removing.
278  *      or NULL on failure (bad arguments)
279  */
280 netsnmp_table_row *
netsnmp_table_data_remove_row(netsnmp_table_data * table,netsnmp_table_row * row)281 netsnmp_table_data_remove_row(netsnmp_table_data *table,
282                               netsnmp_table_row *row)
283 {
284     if (!row || !table)
285         return NULL;
286 
287     if (row->prev)
288         row->prev->next = row->next;
289     else
290         table->first_row = row->next;
291 
292     if (row->next)
293         row->next->prev = row->prev;
294     else
295         table->last_row = row->prev;
296 
297     return row;
298 }
299 
300 /**
301  * removes and frees a row of data to a given table and returns the void *
302  *
303  * returns the void * data on successful deletion.
304  *      or NULL on failure (bad arguments)
305  */
306 void           *
netsnmp_table_data_remove_and_delete_row(netsnmp_table_data * table,netsnmp_table_row * row)307 netsnmp_table_data_remove_and_delete_row(netsnmp_table_data *table,
308                                          netsnmp_table_row *row)
309 {
310     if (!row || !table)
311         return NULL;
312 
313     /*
314      * remove it from the list
315      */
316     netsnmp_table_data_remove_row(table, row);
317     return netsnmp_table_data_delete_row(row);
318 }
319 
320     /* =====================================
321      * Generic API - mostly renamed wrappers
322      * ===================================== */
323 
324 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_CREATE_TABLE
325 netsnmp_table_data *
netsnmp_table_data_create_table(const char * name,long flags)326 netsnmp_table_data_create_table(const char *name, long flags)
327 {
328     return netsnmp_create_table_data( name );
329 }
330 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_CREATE_TABLE */
331 
332 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_DELETE_TABLE
333 void
netsnmp_table_data_delete_table(netsnmp_table_data * table)334 netsnmp_table_data_delete_table( netsnmp_table_data *table )
335 {
336     netsnmp_table_row *row, *nextrow;
337 
338     if (!table)
339         return;
340 
341     snmp_free_varbind(table->indexes_template);
342     table->indexes_template = NULL;
343 
344     for (row = table->first_row; row; row=nextrow) {
345         nextrow   = row->next;
346         row->next = NULL;
347         netsnmp_table_data_delete_row(row);
348         /* Can't delete table-specific entry memory */
349     }
350     table->first_row = NULL;
351 
352     SNMP_FREE(table->name);
353     SNMP_FREE(table);
354     return;
355 }
356 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_DELETE_TABLE */
357 
358 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_CREATE_ROW
359 netsnmp_table_row *
netsnmp_table_data_create_row(void * entry)360 netsnmp_table_data_create_row( void* entry )
361 {
362     netsnmp_table_row *row = SNMP_MALLOC_TYPEDEF(netsnmp_table_row);
363     if (row)
364         row->data = entry;
365     return row;
366 }
367 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_CREATE_ROW */
368 
369     /* netsnmp_table_data_clone_row() defined above */
370 
371 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_COPY_ROW
372 int
netsnmp_table_data_copy_row(netsnmp_table_row * old_row,netsnmp_table_row * new_row)373 netsnmp_table_data_copy_row( netsnmp_table_row  *old_row,
374                              netsnmp_table_row  *new_row )
375 {
376     if (!old_row || !new_row)
377         return -1;
378 
379     memcpy(new_row, old_row, sizeof(netsnmp_table_row));
380 
381     if (old_row->indexes)
382         new_row->indexes = snmp_clone_varbind(old_row->indexes);
383     if (old_row->index_oid)
384         new_row->index_oid =
385             snmp_duplicate_objid(old_row->index_oid, old_row->index_oid_len);
386     /* XXX - Doesn't copy table-specific row structure */
387     return 0;
388 }
389 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_COPY_ROW */
390 
391     /*
392      * netsnmp_table_data_delete_row()
393      * netsnmp_table_data_add_row()
394      * netsnmp_table_data_replace_row()
395      * netsnmp_table_data_remove_row()
396      *     all defined above
397      */
398 
399 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_REMOVE_DELETE_ROW
400 void *
netsnmp_table_data_remove_delete_row(netsnmp_table_data * table,netsnmp_table_row * row)401 netsnmp_table_data_remove_delete_row(netsnmp_table_data *table,
402                                      netsnmp_table_row *row)
403 {
404     return netsnmp_table_data_remove_and_delete_row(table, row);
405 }
406 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_REMOVE_DELETE_ROW */
407 
408 
409 /* ==================================
410  *
411  * Table Data API: MIB maintenance
412  *
413  * ================================== */
414 
415 /** Creates a table_data handler and returns it */
416 netsnmp_mib_handler *
netsnmp_get_table_data_handler(netsnmp_table_data * table)417 netsnmp_get_table_data_handler(netsnmp_table_data *table)
418 {
419     netsnmp_mib_handler *ret = NULL;
420 
421     if (!table) {
422         snmp_log(LOG_INFO,
423                  "netsnmp_get_table_data_handler(NULL) called\n");
424         return NULL;
425     }
426 
427     ret =
428         netsnmp_create_handler(TABLE_DATA_NAME,
429                                netsnmp_table_data_helper_handler);
430     if (ret) {
431         ret->flags |= MIB_HANDLER_AUTO_NEXT;
432         ret->myvoid = (void *) table;
433     }
434     return ret;
435 }
436 
437 /** registers a handler as a data table.
438  *  If table_info != NULL, it registers it as a normal table too. */
439 int
netsnmp_register_table_data(netsnmp_handler_registration * reginfo,netsnmp_table_data * table,netsnmp_table_registration_info * table_info)440 netsnmp_register_table_data(netsnmp_handler_registration *reginfo,
441                             netsnmp_table_data *table,
442                             netsnmp_table_registration_info *table_info)
443 {
444     netsnmp_mib_handler *handler = netsnmp_get_table_data_handler(table);
445     if (!table || !handler ||
446         (netsnmp_inject_handler(reginfo, handler) != SNMPERR_SUCCESS)) {
447         snmp_log(LOG_ERR, "could not create table data handler\n");
448         netsnmp_handler_free(handler);
449         netsnmp_handler_registration_free(reginfo);
450         return MIB_REGISTRATION_FAILED;
451     }
452 
453     return netsnmp_register_table(reginfo, table_info);
454 }
455 
456 
457 #ifndef NETSNMP_FEATURE_REMOVE_REGISTER_READ_ONLY_TABLE_DATA
458 /** registers a handler as a read-only data table
459  *  If table_info != NULL, it registers it as a normal table too. */
460 int
netsnmp_register_read_only_table_data(netsnmp_handler_registration * reginfo,netsnmp_table_data * table,netsnmp_table_registration_info * table_info)461 netsnmp_register_read_only_table_data(netsnmp_handler_registration *reginfo,
462                                       netsnmp_table_data *table,
463                                       netsnmp_table_registration_info *table_info)
464 {
465     netsnmp_mib_handler *handler = netsnmp_get_read_only_handler();
466     if (!handler ||
467         (netsnmp_inject_handler(reginfo, handler) != SNMPERR_SUCCESS)) {
468         snmp_log(LOG_ERR, "could not create read only table data handler\n");
469         netsnmp_handler_free(handler);
470         netsnmp_handler_registration_free(reginfo);
471         return MIB_REGISTRATION_FAILED;
472     }
473 
474     return netsnmp_register_table_data(reginfo, table, table_info);
475 }
476 #endif /* NETSNMP_FEATURE_REMOVE_REGISTER_READ_ONLY_TABLE_DATA */
477 
478 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_UNREGISTER
479 int
netsnmp_unregister_table_data(netsnmp_handler_registration * reginfo)480 netsnmp_unregister_table_data(netsnmp_handler_registration *reginfo)
481 {
482     /* free table; */
483     return netsnmp_unregister_table(reginfo);
484 }
485 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_UNREGISTER */
486 
487 /*
488  * The helper handler that takes care of passing a specific row of
489  * data down to the lower handler(s).  It sets request->processed if
490  * the request should not be handled.
491  */
492 int
netsnmp_table_data_helper_handler(netsnmp_mib_handler * handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)493 netsnmp_table_data_helper_handler(netsnmp_mib_handler *handler,
494                                   netsnmp_handler_registration *reginfo,
495                                   netsnmp_agent_request_info *reqinfo,
496                                   netsnmp_request_info *requests)
497 {
498     netsnmp_table_data *table = (netsnmp_table_data *) handler->myvoid;
499     netsnmp_request_info *request;
500     int             valid_request = 0;
501     netsnmp_table_row *row;
502     netsnmp_table_request_info *table_info;
503     netsnmp_table_registration_info *table_reg_info =
504         netsnmp_find_table_registration_info(reginfo);
505     int             result, regresult;
506     int             oldmode;
507 
508     for (request = requests; request; request = request->next) {
509         if (request->processed)
510             continue;
511 
512         table_info = netsnmp_extract_table_info(request);
513         if (!table_info)
514             continue;           /* ack */
515         switch (reqinfo->mode) {
516         case MODE_GET:
517         case MODE_GETNEXT:
518 #ifndef NETSNMP_NO_WRITE_SUPPORT
519         case MODE_SET_RESERVE1:
520 #endif /* NETSNMP_NO_WRITE_SUPPORT */
521             netsnmp_request_add_list_data(request,
522                                       netsnmp_create_data_list(
523                                           TABLE_DATA_TABLE, table, NULL));
524         }
525 
526         /*
527          * find the row in question
528          */
529         switch (reqinfo->mode) {
530         case MODE_GETNEXT:
531         case MODE_GETBULK:     /* XXXWWW */
532             if (request->requestvb->type != ASN_NULL)
533                 continue;
534             /*
535              * loop through data till we find the next row
536              */
537             result = snmp_oid_compare(request->requestvb->name,
538                                       request->requestvb->name_length,
539                                       reginfo->rootoid,
540                                       reginfo->rootoid_len);
541             regresult = snmp_oid_compare(request->requestvb->name,
542                                          SNMP_MIN(request->requestvb->
543                                                   name_length,
544                                                   reginfo->rootoid_len),
545                                          reginfo->rootoid,
546                                          reginfo->rootoid_len);
547             if (regresult == 0
548                 && request->requestvb->name_length < reginfo->rootoid_len)
549                 regresult = -1;
550 
551             if (result < 0 || 0 == result) {
552                 /*
553                  * before us entirely, return the first
554                  */
555                 row = table->first_row;
556                 table_info->colnum = table_reg_info->min_column;
557             } else if (regresult == 0 && request->requestvb->name_length ==
558                        reginfo->rootoid_len + 1 &&
559                        /* entry node must be 1, but any column is ok */
560                        request->requestvb->name[reginfo->rootoid_len] == 1) {
561                 /*
562                  * exactly to the entry
563                  */
564                 row = table->first_row;
565                 table_info->colnum = table_reg_info->min_column;
566             } else if (regresult == 0 && request->requestvb->name_length ==
567                        reginfo->rootoid_len + 2 &&
568                        /* entry node must be 1, but any column is ok */
569                        request->requestvb->name[reginfo->rootoid_len] == 1) {
570                 /*
571                  * exactly to the column
572                  */
573                 row = table->first_row;
574             } else {
575                 /*
576                  * loop through all rows looking for the first one
577                  * that is equal to the request or greater than it
578                  */
579                 for (row = table->first_row; row; row = row->next) {
580                     /*
581                      * compare the index of the request to the row
582                      */
583                     result =
584                         snmp_oid_compare(row->index_oid,
585                                          row->index_oid_len,
586                                          request->requestvb->name + 2 +
587                                          reginfo->rootoid_len,
588                                          request->requestvb->name_length -
589                                          2 - reginfo->rootoid_len);
590                     if (result == 0) {
591                         /*
592                          * equal match, return the next row
593                          */
594                         row = row->next;
595                         break;
596                     } else if (result > 0) {
597                         /*
598                          * the current row is greater than the
599                          * request, use it
600                          */
601                         break;
602                     }
603                 }
604             }
605             if (!row) {
606                 table_info->colnum++;
607                 if (table_info->colnum <= table_reg_info->max_column) {
608                     row = table->first_row;
609                 }
610             }
611             if (row) {
612                 valid_request = 1;
613                 netsnmp_request_add_list_data(request,
614                                               netsnmp_create_data_list
615                                               (TABLE_DATA_ROW, row,
616                                                NULL));
617                 /*
618                  * Set the name appropriately, so we can pass this
619                  *  request on as a simple GET request
620                  */
621                 netsnmp_table_data_build_result(reginfo, reqinfo, request,
622                                                 row,
623                                                 table_info->colnum,
624                                                 ASN_NULL, NULL, 0);
625             } else {            /* no decent result found.  Give up. It's beyond us. */
626                 request->processed = 1;
627             }
628             break;
629 
630         case MODE_GET:
631             if (request->requestvb->type != ASN_NULL)
632                 continue;
633             /*
634              * find the row in question
635              */
636             if (request->requestvb->name_length < (reginfo->rootoid_len + 3)) { /* table.entry.column... */
637                 /*
638                  * request too short
639                  */
640                 netsnmp_set_request_error(reqinfo, request,
641                                           SNMP_NOSUCHINSTANCE);
642                 break;
643             } else if (NULL ==
644                        (row =
645                         netsnmp_table_data_get_from_oid(table,
646                                                         request->
647                                                         requestvb->name +
648                                                         reginfo->
649                                                         rootoid_len + 2,
650                                                         request->
651                                                         requestvb->
652                                                         name_length -
653                                                         reginfo->
654                                                         rootoid_len -
655                                                         2))) {
656                 /*
657                  * no such row
658                  */
659                 netsnmp_set_request_error(reqinfo, request,
660                                           SNMP_NOSUCHINSTANCE);
661                 break;
662             } else {
663                 valid_request = 1;
664                 netsnmp_request_add_list_data(request,
665                                               netsnmp_create_data_list
666                                               (TABLE_DATA_ROW, row,
667                                                NULL));
668             }
669             break;
670 
671 #ifndef NETSNMP_NO_WRITE_SUPPORT
672         case MODE_SET_RESERVE1:
673             valid_request = 1;
674             if (NULL !=
675                 (row =
676                  netsnmp_table_data_get_from_oid(table,
677                                                  request->requestvb->name +
678                                                  reginfo->rootoid_len + 2,
679                                                  request->requestvb->
680                                                  name_length -
681                                                  reginfo->rootoid_len -
682                                                  2))) {
683                 netsnmp_request_add_list_data(request,
684                                               netsnmp_create_data_list
685                                               (TABLE_DATA_ROW, row,
686                                                NULL));
687             }
688             break;
689 
690         case MODE_SET_RESERVE2:
691         case MODE_SET_ACTION:
692         case MODE_SET_COMMIT:
693         case MODE_SET_FREE:
694         case MODE_SET_UNDO:
695             valid_request = 1;
696 #endif /* NETSNMP_NO_WRITE_SUPPORT */
697 
698         }
699     }
700 
701     if (valid_request &&
702        (reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK)) {
703         /*
704          * If this is a GetNext or GetBulk request, then we've identified
705          *  the row that ought to include the appropriate next instance.
706          *  Convert the request into a Get request, so that the lower-level
707          *  handlers don't need to worry about skipping on, and call these
708          *  handlers ourselves (so we can undo this again afterwards).
709          */
710         oldmode = reqinfo->mode;
711         reqinfo->mode = MODE_GET;
712         result = netsnmp_call_next_handler(handler, reginfo, reqinfo,
713                                          requests);
714         reqinfo->mode = oldmode;
715         handler->flags |= MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE;
716         return result;
717     }
718     else
719         /* next handler called automatically - 'AUTO_NEXT' */
720         return SNMP_ERR_NOERROR;
721 }
722 
723 /** extracts the table being accessed passed from the table_data helper */
724 netsnmp_table_data *
netsnmp_extract_table(netsnmp_request_info * request)725 netsnmp_extract_table(netsnmp_request_info *request)
726 {
727     return (netsnmp_table_data *)
728                 netsnmp_request_get_list_data(request, TABLE_DATA_TABLE);
729 }
730 
731 /** extracts the row being accessed passed from the table_data helper */
732 netsnmp_table_row *
netsnmp_extract_table_row(netsnmp_request_info * request)733 netsnmp_extract_table_row(netsnmp_request_info *request)
734 {
735     return (netsnmp_table_row *) netsnmp_request_get_list_data(request,
736                                                                TABLE_DATA_ROW);
737 }
738 
739 #ifndef NETSNMP_FEATURE_REMOVE_EXTRACT_TABLE_ROW_DATA
740 /** extracts the data from the row being accessed passed from the
741  * table_data helper */
742 void           *
netsnmp_extract_table_row_data(netsnmp_request_info * request)743 netsnmp_extract_table_row_data(netsnmp_request_info *request)
744 {
745     netsnmp_table_row *row;
746     row = (netsnmp_table_row *) netsnmp_extract_table_row(request);
747     if (row)
748         return row->data;
749     else
750         return NULL;
751 }
752 #endif /* NETSNMP_FEATURE_REMOVE_EXTRACT_TABLE_ROW_DATA */
753 
754 #ifndef NETSNMP_FEATURE_REMOVE_INSERT_TABLE_ROW
755 /** inserts a newly created table_data row into a request */
756 void
netsnmp_insert_table_row(netsnmp_request_info * request,netsnmp_table_row * row)757 netsnmp_insert_table_row(netsnmp_request_info *request,
758                          netsnmp_table_row *row)
759 {
760     netsnmp_request_info       *req;
761     netsnmp_table_request_info *table_info = NULL;
762     netsnmp_variable_list      *this_index = NULL;
763     netsnmp_variable_list      *that_index = NULL;
764     oid      base_oid[] = {0, 0};	/* Make sure index OIDs are legal! */
765     oid      this_oid[MAX_OID_LEN];
766     oid      that_oid[MAX_OID_LEN];
767     size_t   this_oid_len, that_oid_len;
768 
769     if (!request)
770         return;
771 
772     /*
773      * We'll add the new row information to any request
774      * structure with the same index values as the request
775      * passed in (which includes that one!).
776      *
777      * So construct an OID based on these index values.
778      */
779 
780     table_info = netsnmp_extract_table_info(request);
781     this_index = table_info->indexes;
782     build_oid_noalloc(this_oid, MAX_OID_LEN, &this_oid_len,
783                       base_oid, 2, this_index);
784 
785     /*
786      * We need to look through the whole of the request list
787      * (as received by the current handler), as there's no
788      * guarantee that this routine will be called by the first
789      * varbind that refers to this row.
790      *   In particular, a RowStatus controlled row creation
791      * may easily occur later in the variable list.
792      *
793      * So first, we rewind to the head of the list....
794      */
795     for (req=request; req->prev; req=req->prev)
796         ;
797 
798     /*
799      * ... and then start looking for matching indexes
800      * (by constructing OIDs from these index values)
801      */
802     for (; req; req=req->next) {
803         table_info = netsnmp_extract_table_info(req);
804         that_index = table_info->indexes;
805         build_oid_noalloc(that_oid, MAX_OID_LEN, &that_oid_len,
806                           base_oid, 2, that_index);
807 
808         /*
809          * This request has the same index values,
810          * so add the newly-created row information.
811          */
812         if (snmp_oid_compare(this_oid, this_oid_len,
813                              that_oid, that_oid_len) == 0) {
814             netsnmp_request_add_list_data(req,
815                 netsnmp_create_data_list(TABLE_DATA_ROW, row, NULL));
816         }
817     }
818 }
819 #endif /* NETSNMP_FEATURE_REMOVE_INSERT_TABLE_ROW */
820 
821 /* builds a result given a row, a varbind to set and the data */
822 int
netsnmp_table_data_build_result(netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * request,netsnmp_table_row * row,int column,u_char type,u_char * result_data,size_t result_data_len)823 netsnmp_table_data_build_result(netsnmp_handler_registration *reginfo,
824                                 netsnmp_agent_request_info *reqinfo,
825                                 netsnmp_request_info *request,
826                                 netsnmp_table_row *row,
827                                 int column,
828                                 u_char type,
829                                 u_char * result_data,
830                                 size_t result_data_len)
831 {
832     oid             build_space[MAX_OID_LEN];
833 
834     if (!reginfo || !reqinfo || !request)
835         return SNMPERR_GENERR;
836 
837     if (reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK) {
838         /*
839          * only need to do this for getnext type cases where oid is changing
840          */
841         memcpy(build_space, reginfo->rootoid,   /* registered oid */
842                reginfo->rootoid_len * sizeof(oid));
843         build_space[reginfo->rootoid_len] = 1;  /* entry */
844         build_space[reginfo->rootoid_len + 1] = column; /* column */
845         memcpy(build_space + reginfo->rootoid_len + 2,  /* index data */
846                row->index_oid, row->index_oid_len * sizeof(oid));
847         snmp_set_var_objid(request->requestvb, build_space,
848                            reginfo->rootoid_len + 2 + row->index_oid_len);
849     }
850     snmp_set_var_typed_value(request->requestvb, type,
851                              result_data, result_data_len);
852     return SNMPERR_SUCCESS;     /* WWWXXX: check for bounds */
853 }
854 
855 
856 /* ==================================
857  *
858  * Table Data API: Row operations
859  *     (table-independent rows)
860  *
861  * ================================== */
862 
863 /** returns the first row in the table */
864 netsnmp_table_row *
netsnmp_table_data_get_first_row(netsnmp_table_data * table)865 netsnmp_table_data_get_first_row(netsnmp_table_data *table)
866 {
867     if (!table)
868         return NULL;
869     return table->first_row;
870 }
871 
872 /** returns the next row in the table */
873 netsnmp_table_row *
netsnmp_table_data_get_next_row(netsnmp_table_data * table,netsnmp_table_row * row)874 netsnmp_table_data_get_next_row(netsnmp_table_data *table,
875                                 netsnmp_table_row  *row)
876 {
877     if (!row)
878         return NULL;
879     return row->next;
880 }
881 
882 /** finds the data in "datalist" stored at "indexes" */
883 netsnmp_table_row *
netsnmp_table_data_get(netsnmp_table_data * table,netsnmp_variable_list * indexes)884 netsnmp_table_data_get(netsnmp_table_data *table,
885                        netsnmp_variable_list * indexes)
886 {
887     oid             searchfor[MAX_OID_LEN];
888     size_t          searchfor_len = MAX_OID_LEN;
889 
890     build_oid_noalloc(searchfor, MAX_OID_LEN, &searchfor_len, NULL, 0,
891                       indexes);
892     return netsnmp_table_data_get_from_oid(table, searchfor,
893                                            searchfor_len);
894 }
895 
896 /** finds the data in "datalist" stored at the searchfor oid */
897 netsnmp_table_row *
netsnmp_table_data_get_from_oid(netsnmp_table_data * table,oid * searchfor,size_t searchfor_len)898 netsnmp_table_data_get_from_oid(netsnmp_table_data *table,
899                                 oid * searchfor, size_t searchfor_len)
900 {
901     netsnmp_table_row *row;
902     if (!table)
903         return NULL;
904 
905     for (row = table->first_row; row != NULL; row = row->next) {
906         if (row->index_oid &&
907             snmp_oid_compare(searchfor, searchfor_len,
908                              row->index_oid, row->index_oid_len) == 0)
909             return row;
910     }
911     return NULL;
912 }
913 
914 int
netsnmp_table_data_num_rows(netsnmp_table_data * table)915 netsnmp_table_data_num_rows(netsnmp_table_data *table)
916 {
917     int i=0;
918     netsnmp_table_row *row;
919     if (!table)
920         return 0;
921     for (row = table->first_row; row; row = row->next) {
922         i++;
923     }
924     return i;
925 }
926 
927     /* =====================================
928      * Generic API - mostly renamed wrappers
929      * ===================================== */
930 
931 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_ROW_FIRST
932 netsnmp_table_row *
netsnmp_table_data_row_first(netsnmp_table_data * table)933 netsnmp_table_data_row_first(netsnmp_table_data *table)
934 {
935     return netsnmp_table_data_get_first_row(table);
936 }
937 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_ROW_FIRST */
938 
939 netsnmp_table_row *
netsnmp_table_data_row_get(netsnmp_table_data * table,netsnmp_table_row * row)940 netsnmp_table_data_row_get(  netsnmp_table_data *table,
941                              netsnmp_table_row  *row)
942 {
943     if (!table || !row)
944         return NULL;
945     return netsnmp_table_data_get_from_oid(table, row->index_oid,
946                                                   row->index_oid_len);
947 }
948 
949 netsnmp_table_row *
netsnmp_table_data_row_next(netsnmp_table_data * table,netsnmp_table_row * row)950 netsnmp_table_data_row_next( netsnmp_table_data *table,
951                              netsnmp_table_row  *row)
952 {
953     return netsnmp_table_data_get_next_row(table, row);
954 }
955 
956 netsnmp_table_row *
netsnmp_table_data_row_get_byoid(netsnmp_table_data * table,oid * instance,size_t len)957 netsnmp_table_data_row_get_byoid( netsnmp_table_data *table,
958                                   oid *instance, size_t len)
959 {
960     return netsnmp_table_data_get_from_oid(table, instance, len);
961 }
962 
963 netsnmp_table_row *
netsnmp_table_data_row_next_byoid(netsnmp_table_data * table,oid * instance,size_t len)964 netsnmp_table_data_row_next_byoid(netsnmp_table_data *table,
965                                   oid *instance, size_t len)
966 {
967     netsnmp_table_row *row;
968 
969     if (!table || !instance)
970         return NULL;
971 
972     for (row = table->first_row; row; row = row->next) {
973         if (snmp_oid_compare(row->index_oid,
974                              row->index_oid_len,
975                              instance, len) > 0)
976             return row;
977     }
978     return NULL;
979 }
980 
981 netsnmp_table_row *
netsnmp_table_data_row_get_byidx(netsnmp_table_data * table,netsnmp_variable_list * indexes)982 netsnmp_table_data_row_get_byidx( netsnmp_table_data    *table,
983                                   netsnmp_variable_list *indexes)
984 {
985     return netsnmp_table_data_get(table, indexes);
986 }
987 
988 netsnmp_table_row *
netsnmp_table_data_row_next_byidx(netsnmp_table_data * table,netsnmp_variable_list * indexes)989 netsnmp_table_data_row_next_byidx(netsnmp_table_data    *table,
990                                   netsnmp_variable_list *indexes)
991 {
992     oid    instance[MAX_OID_LEN];
993     size_t len    = MAX_OID_LEN;
994 
995     if (!table || !indexes)
996         return NULL;
997 
998     build_oid_noalloc(instance, MAX_OID_LEN, &len, NULL, 0, indexes);
999     return netsnmp_table_data_row_next_byoid(table, instance, len);
1000 }
1001 
1002 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_ROW_COUNT
1003 int
netsnmp_table_data_row_count(netsnmp_table_data * table)1004 netsnmp_table_data_row_count(netsnmp_table_data *table)
1005 {
1006     return netsnmp_table_data_num_rows(table);
1007 }
1008 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_ROW_COUNT */
1009 
1010 
1011 /* ==================================
1012  *
1013  * Table Data API: Row operations
1014  *     (table-specific rows)
1015  *
1016  * ================================== */
1017 
1018 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_DATA_ROW_OPERATIONS
1019 void *
netsnmp_table_data_entry_first(netsnmp_table_data * table)1020 netsnmp_table_data_entry_first(netsnmp_table_data *table)
1021 {
1022     netsnmp_table_row *row =
1023         netsnmp_table_data_get_first_row(table);
1024     return (row ? row->data : NULL );
1025 }
1026 
1027 void *
netsnmp_table_data_entry_get(netsnmp_table_data * table,netsnmp_table_row * row)1028 netsnmp_table_data_entry_get(  netsnmp_table_data *table,
1029                                netsnmp_table_row  *row)
1030 {
1031     return (row ? row->data : NULL );
1032 }
1033 
1034 void *
netsnmp_table_data_entry_next(netsnmp_table_data * table,netsnmp_table_row * row)1035 netsnmp_table_data_entry_next( netsnmp_table_data *table,
1036                                netsnmp_table_row  *row)
1037 {
1038     row =
1039         netsnmp_table_data_row_next(table, row);
1040     return (row ? row->data : NULL );
1041 }
1042 
1043 void *
netsnmp_table_data_entry_get_byidx(netsnmp_table_data * table,netsnmp_variable_list * indexes)1044 netsnmp_table_data_entry_get_byidx( netsnmp_table_data    *table,
1045                                     netsnmp_variable_list *indexes)
1046 {
1047     netsnmp_table_row *row =
1048         netsnmp_table_data_row_get_byidx(table, indexes);
1049     return (row ? row->data : NULL );
1050 }
1051 
1052 void *
netsnmp_table_data_entry_next_byidx(netsnmp_table_data * table,netsnmp_variable_list * indexes)1053 netsnmp_table_data_entry_next_byidx(netsnmp_table_data    *table,
1054                                     netsnmp_variable_list *indexes)
1055 {
1056     netsnmp_table_row *row =
1057         netsnmp_table_data_row_next_byidx(table, indexes);
1058     return (row ? row->data : NULL );
1059 }
1060 
1061 void *
netsnmp_table_data_entry_get_byoid(netsnmp_table_data * table,oid * instance,size_t len)1062 netsnmp_table_data_entry_get_byoid( netsnmp_table_data *table,
1063                                     oid *instance, size_t len)
1064 {
1065     netsnmp_table_row *row =
1066         netsnmp_table_data_row_get_byoid(table, instance, len);
1067     return (row ? row->data : NULL );
1068 }
1069 
1070 void *
netsnmp_table_data_entry_next_byoid(netsnmp_table_data * table,oid * instance,size_t len)1071 netsnmp_table_data_entry_next_byoid(netsnmp_table_data *table,
1072                                     oid *instance, size_t len)
1073 {
1074     netsnmp_table_row *row =
1075         netsnmp_table_data_row_next_byoid(table, instance, len);
1076     return (row ? row->data : NULL );
1077 }
1078 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA_ROW_OPERATIONS */
1079 
1080     /* =====================================
1081      * Generic API - mostly renamed wrappers
1082      * ===================================== */
1083 
1084 /* ==================================
1085  *
1086  * Table Data API: Index operations
1087  *
1088  * ================================== */
1089 
1090 #else /* NETSNMP_FEATURE_REMOVE_TABLE_DATA */
1091 netsnmp_feature_unused(table_data);
1092 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_DATA */
1093 
1094 /** @}
1095  */
1096