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_tdata.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/table_container.h>
28 #include <net-snmp/agent/read_only.h>
29 
30 netsnmp_feature_child_of(table_tdata_all, mib_helpers);
31 netsnmp_feature_child_of(table_tdata, table_tdata_all);
32 netsnmp_feature_child_of(table_tdata_delete_table, table_tdata_all);
33 netsnmp_feature_child_of(table_tdata_extract_table, table_tdata_all);
34 netsnmp_feature_child_of(table_tdata_remove_row, table_tdata_all);
35 netsnmp_feature_child_of(table_tdata_insert_row, table_tdata_all);
36 
37 #ifdef NETSNMP_FEATURE_REQUIRE_TABLE_TDATA
38 netsnmp_feature_require(table_container_row_insert);
39 #ifdef NETSNMP_FEATURE_REQUIRE_TABLE_TDATA_REMOVE_ROW
40 netsnmp_feature_require(table_container_row_remove);
41 #endif /* NETSNMP_FEATURE_REQUIRE_TABLE_TDATA_REMOVE_ROW */
42 #endif /* NETSNMP_FEATURE_REQUIRE_TABLE_TDATA */
43 
44 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_TDATA
45 
46 /** @defgroup tdata tdata
47  *  Implement a table with datamatted storage.
48  *  @ingroup table
49  *
50  *  This helper helps you implement a table where all the rows are
51  *  expected to be stored within the agent itself and not in some
52  *  external storage location.  It can be used to store a list of
53  *  rows, where a row consists of the indexes to the table and a
54  *  generic data pointer.  You can then implement a subhandler which
55  *  is passed the exact row definition and data it must return data
56  *  for or accept data for.  Complex GETNEXT handling is greatly
57  *  simplified in this case.
58  *
59  *  @{
60  */
61 
62 /* ==================================
63  *
64  * TData API: Table maintenance
65  *
66  * ================================== */
67 
68 /*
69  * generates the index portion of an table oid from a varlist.
70  */
71 void
_netsnmp_tdata_generate_index_oid(netsnmp_tdata_row * row)72 _netsnmp_tdata_generate_index_oid(netsnmp_tdata_row *row)
73 {
74     build_oid(&row->oid_index.oids, &row->oid_index.len, NULL, 0, row->indexes);
75 }
76 
77 /** creates and returns a 'tdata' table data structure */
78 netsnmp_tdata *
netsnmp_tdata_create_table(const char * name,long flags)79 netsnmp_tdata_create_table(const char *name, long flags)
80 {
81     netsnmp_tdata *table = SNMP_MALLOC_TYPEDEF(netsnmp_tdata);
82     if ( !table )
83         return NULL;
84 
85     table->flags = flags;
86     if (name)
87         table->name = strdup(name);
88 
89     if (!(table->flags & TDATA_FLAG_NO_CONTAINER)) {
90         table->container = netsnmp_container_find( name );
91         if (!table->container)
92             table->container = netsnmp_container_find( "table_container" );
93         if (table->container && name)
94             table->container->container_name = strdup(name);
95     }
96     return table;
97 }
98 
99 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_TDATA_DELETE_TABLE
100 /** creates and returns a 'tdata' table data structure */
101 void
netsnmp_tdata_delete_table(netsnmp_tdata * table)102 netsnmp_tdata_delete_table(netsnmp_tdata *table)
103 {
104     if (!table)
105        return;
106 
107     if (table->name)
108        free(table->name);
109     if (table->container)
110        CONTAINER_FREE(table->container);
111 
112     SNMP_FREE(table);
113     return;
114 }
115 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_TDATA_DELETE_TABLE */
116 
117 /** creates and returns a pointer to new row data structure */
118 netsnmp_tdata_row *
netsnmp_tdata_create_row(void)119 netsnmp_tdata_create_row(void)
120 {
121     netsnmp_tdata_row *row = SNMP_MALLOC_TYPEDEF(netsnmp_tdata_row);
122     return row;
123 }
124 
125 /** clones a 'tdata' row. DOES NOT CLONE THE TABLE-SPECIFIC ENTRY DATA. */
126 netsnmp_feature_child_of(tdata_clone_row, table_tdata_all);
127 #ifndef NETSNMP_FEATURE_REMOVE_TDATA_CLONE_ROW
128 netsnmp_tdata_row *
netsnmp_tdata_clone_row(netsnmp_tdata_row * row)129 netsnmp_tdata_clone_row(netsnmp_tdata_row *row)
130 {
131     netsnmp_tdata_row *newrow = NULL;
132     if (!row)
133         return NULL;
134 
135     newrow = netsnmp_memdup(row, sizeof(netsnmp_tdata_row));
136     if (!newrow)
137         return NULL;
138 
139     if (row->indexes) {
140         newrow->indexes = snmp_clone_varbind(newrow->indexes);
141         if (!newrow->indexes) {
142             SNMP_FREE(newrow);
143             return NULL;
144         }
145     }
146 
147     if (row->oid_index.oids) {
148         newrow->oid_index.oids =
149             snmp_duplicate_objid(row->oid_index.oids, row->oid_index.len);
150         if (!newrow->oid_index.oids) {
151             if (newrow->indexes)
152                 snmp_free_varbind(newrow->indexes);
153             SNMP_FREE(newrow);
154             return NULL;
155         }
156     }
157 
158     return newrow;
159 }
160 #endif /* NETSNMP_FEATURE_REMOVE_TDATA_CLONE_ROW */
161 
162 /** copy the contents of a 'tdata' row.
163     DOES NOT COPY THE TABLE-SPECIFIC ENTRY DATA. */
164 netsnmp_feature_child_of(tdata_copy_row, table_tdata_all);
165 #ifndef NETSNMP_FEATURE_REMOVE_TDATA_COPY_ROW
166 int
netsnmp_tdata_copy_row(netsnmp_tdata_row * dst_row,netsnmp_tdata_row * src_row)167 netsnmp_tdata_copy_row(netsnmp_tdata_row *dst_row, netsnmp_tdata_row *src_row)
168 {
169      if ( !src_row || !dst_row )
170          return -1;
171 
172     memcpy((u_char *) dst_row, (u_char *) src_row,
173            sizeof(netsnmp_tdata_row));
174     if (src_row->indexes) {
175         dst_row->indexes = snmp_clone_varbind(src_row->indexes);
176         if (!dst_row->indexes)
177             return -1;
178     }
179 
180     if (src_row->oid_index.oids) {
181         dst_row->oid_index.oids = snmp_duplicate_objid(src_row->oid_index.oids,
182                                                        src_row->oid_index.len);
183         if (!dst_row->oid_index.oids)
184             return -1;
185     }
186 
187     return 0;
188 }
189 #endif /* NETSNMP_FEATURE_REMOVE_TDATA_COPY_ROW */
190 
191 /** deletes the memory used by the specified row
192  *  returns the table-specific entry data
193  *  (that it doesn't know how to delete) */
194 void           *
netsnmp_tdata_delete_row(netsnmp_tdata_row * row)195 netsnmp_tdata_delete_row(netsnmp_tdata_row *row)
196 {
197     void           *data;
198 
199     if (!row)
200         return NULL;
201 
202     /*
203      * free the memory we can
204      */
205     if (row->indexes)
206         snmp_free_varbind(row->indexes);
207     SNMP_FREE(row->oid_index.oids);
208     data = row->data;
209     free(row);
210 
211     /*
212      * return the void * pointer
213      */
214     return data;
215 }
216 
217 /**
218  * Adds a row to the given table (stored in proper lexographical order).
219  *
220  * returns SNMPERR_SUCCESS on successful addition.
221  *      or SNMPERR_GENERR  on failure (E.G., indexes already existed)
222  */
223 int
netsnmp_tdata_add_row(netsnmp_tdata * table,netsnmp_tdata_row * row)224 netsnmp_tdata_add_row(netsnmp_tdata     *table,
225                       netsnmp_tdata_row *row)
226 {
227     if (!row || !table)
228         return SNMPERR_GENERR;
229 
230     if (row->indexes)
231         _netsnmp_tdata_generate_index_oid(row);
232 
233     if (!row->oid_index.oids) {
234         snmp_log(LOG_ERR,
235                  "illegal data attempted to be added to table %s (no index)\n",
236                  table->name);
237         return SNMPERR_GENERR;
238     }
239 
240     /*
241      * The individual index values probably won't be needed,
242      *    so this memory can be released.
243      * Note that this is purely internal to the helper.
244      * The calling application can set this flag as
245      *    a hint to the helper that these values aren't
246      *    required, but it's up to the helper as to
247      *    whether it takes any notice or not!
248      */
249     if (table->flags & TDATA_FLAG_NO_STORE_INDEXES) {
250         snmp_free_varbind(row->indexes);
251         row->indexes = NULL;
252     }
253 
254     /*
255      * add this row to the stored table
256      */
257     if (CONTAINER_INSERT( table->container, row ) != 0)
258         return SNMPERR_GENERR;
259 
260     DEBUGMSGTL(("tdata_add_row", "added row (%p)\n", row));
261 
262     return SNMPERR_SUCCESS;
263 }
264 
265 /** swaps out origrow with newrow.  This does *not* delete/free anything! */
266 netsnmp_feature_child_of(tdata_replace_row, table_tdata_all);
267 #ifndef NETSNMP_FEATURE_REMOVE_TDATA_REPLACE_ROW
268 void
netsnmp_tdata_replace_row(netsnmp_tdata * table,netsnmp_tdata_row * origrow,netsnmp_tdata_row * newrow)269 netsnmp_tdata_replace_row(netsnmp_tdata *table,
270                                netsnmp_tdata_row *origrow,
271                                netsnmp_tdata_row *newrow)
272 {
273     netsnmp_tdata_remove_row(table, origrow);
274     netsnmp_tdata_add_row(table, newrow);
275 }
276 #endif /* NETSNMP_FEATURE_REMOVE_TDATA_REPLACE_ROW */
277 
278 /**
279  * removes a row from the given table and returns it (no free's called)
280  *
281  * returns the row pointer itself on successful removing.
282  *      or NULL on failure (bad arguments)
283  */
284 netsnmp_tdata_row *
netsnmp_tdata_remove_row(netsnmp_tdata * table,netsnmp_tdata_row * row)285 netsnmp_tdata_remove_row(netsnmp_tdata *table,
286                               netsnmp_tdata_row *row)
287 {
288     if (!row || !table)
289         return NULL;
290 
291     CONTAINER_REMOVE( table->container, row );
292     return row;
293 }
294 
295 /**
296  * removes and frees a row of the given table and
297  *  returns the table-specific entry data
298  *
299  * returns the void * pointer on successful deletion.
300  *      or NULL on failure (bad arguments)
301  */
302 void           *
netsnmp_tdata_remove_and_delete_row(netsnmp_tdata * table,netsnmp_tdata_row * row)303 netsnmp_tdata_remove_and_delete_row(netsnmp_tdata     *table,
304                                     netsnmp_tdata_row *row)
305 {
306     if (!row || !table)
307         return NULL;
308 
309     /*
310      * remove it from the list
311      */
312     netsnmp_tdata_remove_row(table, row);
313     return netsnmp_tdata_delete_row(row);
314 }
315 
316 
317 /* ==================================
318  *
319  * TData API: MIB maintenance
320  *
321  * ================================== */
322 
323 Netsnmp_Node_Handler _netsnmp_tdata_helper_handler;
324 
325 /** Creates a tdata handler and returns it */
326 netsnmp_mib_handler *
netsnmp_get_tdata_handler(netsnmp_tdata * table)327 netsnmp_get_tdata_handler(netsnmp_tdata *table)
328 {
329     netsnmp_mib_handler *ret = NULL;
330 
331     if (!table) {
332         snmp_log(LOG_INFO,
333                  "netsnmp_get_tdata_handler(NULL) called\n");
334         return NULL;
335     }
336 
337     ret = netsnmp_create_handler(TABLE_TDATA_NAME,
338                                _netsnmp_tdata_helper_handler);
339     if (ret) {
340         ret->flags |= MIB_HANDLER_AUTO_NEXT;
341         ret->myvoid = (void *) table;
342     }
343     return ret;
344 }
345 
346 /*
347  * The helper handler that takes care of passing a specific row of
348  * data down to the lower handler(s).  The table_container helper
349  * has already taken care of identifying the appropriate row of the
350  * table (and converting GETNEXT requests into an equivalent GET request)
351  * So all we need to do here is make sure that the row is accessible
352  * using tdata-style retrieval techniques as well.
353  */
354 int
_netsnmp_tdata_helper_handler(netsnmp_mib_handler * handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)355 _netsnmp_tdata_helper_handler(netsnmp_mib_handler *handler,
356                                   netsnmp_handler_registration *reginfo,
357                                   netsnmp_agent_request_info *reqinfo,
358                                   netsnmp_request_info *requests)
359 {
360     netsnmp_tdata *table = (netsnmp_tdata *) handler->myvoid;
361     netsnmp_request_info       *request;
362     netsnmp_table_request_info *table_info;
363     netsnmp_tdata_row          *row;
364     int                         need_processing;
365 
366     switch ( reqinfo->mode ) {
367     case MODE_GET:
368 #ifndef NETSNMP_NO_WRITE_SUPPORT
369     case MODE_SET_RESERVE1:
370 #endif /* NETSNMP_NO_WRITE_SUPPORT */
371 
372         need_processing = reqinfo->mode == MODE_GET ? 0 : 1;
373         for (request = requests; request; request = request->next) {
374             if (request->processed)
375                 continue;
376 
377             table_info = netsnmp_extract_table_info(request);
378             if (!table_info) {
379                 netsnmp_assert(table_info); /* yes, this will always hit */
380                 netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR);
381                 continue;           /* eek */
382             }
383             row = (netsnmp_tdata_row*)netsnmp_container_table_row_extract( request );
384             if (!row && (reqinfo->mode == MODE_GET)) {
385                 netsnmp_assert(row); /* yes, this will always hit */
386                 netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR);
387                 continue;           /* eek */
388             }
389             ++need_processing;
390             netsnmp_request_add_list_data(request,
391                                       netsnmp_create_data_list(
392                                           TABLE_TDATA_TABLE, table, NULL));
393             netsnmp_request_add_list_data(request,
394                                       netsnmp_create_data_list(
395                                           TABLE_TDATA_ROW,   row,   NULL));
396         }
397         /** skip next handler if processing not needed */
398         if (!need_processing)
399             handler->flags |= MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE;
400     }
401 
402     /* next handler called automatically - 'AUTO_NEXT' */
403     return SNMP_ERR_NOERROR;
404 }
405 
406 
407 /** registers a tdata-based MIB table */
408 int
netsnmp_tdata_register(netsnmp_handler_registration * reginfo,netsnmp_tdata * table,netsnmp_table_registration_info * table_info)409 netsnmp_tdata_register(netsnmp_handler_registration    *reginfo,
410                        netsnmp_tdata                   *table,
411                        netsnmp_table_registration_info *table_info)
412 {
413     netsnmp_mib_handler *handler = netsnmp_get_tdata_handler(table);
414 
415     if (!reginfo || !table || !table_info || !handler ||
416         (netsnmp_inject_handler(reginfo, handler) != SNMPERR_SUCCESS)) {
417         snmp_log(LOG_ERR, "could not create tdata handler\n");
418         netsnmp_handler_free(handler);
419         netsnmp_handler_registration_free(reginfo);
420         return SNMP_ERR_GENERR;
421     }
422 
423     return netsnmp_container_table_register(reginfo, table_info,
424                   table->container, TABLE_CONTAINER_KEY_NETSNMP_INDEX);
425 }
426 
427 netsnmp_feature_child_of(tdata_unregister, table_tdata_all);
428 #ifndef NETSNMP_FEATURE_REMOVE_TDATA_UNREGISTER
429 int
netsnmp_tdata_unregister(netsnmp_handler_registration * reginfo)430 netsnmp_tdata_unregister(netsnmp_handler_registration    *reginfo)
431 {
432     /* free table; */
433     return netsnmp_container_table_unregister(reginfo);
434 }
435 #endif /* NETSNMP_FEATURE_REMOVE_TDATA_UNREGISTER */
436 
437 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_TDATA_EXTRACT_TABLE
438 /** extracts the tdata table from the request structure */
439 netsnmp_tdata *
netsnmp_tdata_extract_table(netsnmp_request_info * request)440 netsnmp_tdata_extract_table(netsnmp_request_info *request)
441 {
442     return (netsnmp_tdata *) netsnmp_request_get_list_data(request,
443                                                            TABLE_TDATA_TABLE);
444 }
445 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_TDATA_EXTRACT_TABLE */
446 
447 /** extracts the tdata container from the request structure */
448 netsnmp_feature_child_of(tdata_extract_container, table_tdata_all);
449 #ifndef NETSNMP_FEATURE_REMOVE_TDATA_EXTRACT_CONTAINER
450 netsnmp_container *
netsnmp_tdata_extract_container(netsnmp_request_info * request)451 netsnmp_tdata_extract_container(netsnmp_request_info *request)
452 {
453     netsnmp_tdata *tdata = (netsnmp_tdata*)
454         netsnmp_request_get_list_data(request, TABLE_TDATA_TABLE);
455     return ( tdata ? tdata->container : NULL );
456 }
457 #endif /* NETSNMP_FEATURE_REMOVE_TDATA_EXTRACT_CONTAINER */
458 
459 /** extracts the tdata row being accessed from the request structure */
460 netsnmp_tdata_row *
netsnmp_tdata_extract_row(netsnmp_request_info * request)461 netsnmp_tdata_extract_row(netsnmp_request_info *request)
462 {
463     return (netsnmp_tdata_row *) netsnmp_container_table_row_extract(request);
464 }
465 
466 /** extracts the (table-specific) entry being accessed from the
467  *  request structure */
468 void           *
netsnmp_tdata_extract_entry(netsnmp_request_info * request)469 netsnmp_tdata_extract_entry(netsnmp_request_info *request)
470 {
471     netsnmp_tdata_row *row =
472         (netsnmp_tdata_row *) netsnmp_tdata_extract_row(request);
473     if (row)
474         return row->data;
475     else
476         return NULL;
477 }
478 
479 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_TDATA_INSERT_ROW
480 /** inserts a newly created tdata row into a request */
481 NETSNMP_INLINE void
netsnmp_insert_tdata_row(netsnmp_request_info * request,netsnmp_tdata_row * row)482 netsnmp_insert_tdata_row(netsnmp_request_info *request,
483                          netsnmp_tdata_row *row)
484 {
485     netsnmp_container_table_row_insert(request, (netsnmp_index *)row);
486 }
487 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_TDATA_INSERT_ROW */
488 
489 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_TDATA_REMOVE_ROW
490 /** inserts a newly created tdata row into a request */
491 NETSNMP_INLINE void
netsnmp_remove_tdata_row(netsnmp_request_info * request,netsnmp_tdata_row * row)492 netsnmp_remove_tdata_row(netsnmp_request_info *request,
493                          netsnmp_tdata_row *row)
494 {
495     netsnmp_container_table_row_remove(request, (netsnmp_index *)row);
496 }
497 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_TDATA_REMOVE_ROW */
498 
499 
500 /* ==================================
501  *
502  * Generic API: Row operations
503  *
504  * ================================== */
505 
506 /** returns the (table-specific) entry data for a given row */
507 void *
netsnmp_tdata_row_entry(netsnmp_tdata_row * row)508 netsnmp_tdata_row_entry( netsnmp_tdata_row *row )
509 {
510     if (row)
511         return row->data;
512     else
513         return NULL;
514 }
515 
516 /** returns the first row in the table */
517 netsnmp_tdata_row *
netsnmp_tdata_row_first(netsnmp_tdata * table)518 netsnmp_tdata_row_first(netsnmp_tdata *table)
519 {
520     return (netsnmp_tdata_row *)CONTAINER_FIRST( table->container );
521 }
522 
523 /** finds a row in the 'tdata' table given another row */
524 netsnmp_tdata_row *
netsnmp_tdata_row_get(netsnmp_tdata * table,netsnmp_tdata_row * row)525 netsnmp_tdata_row_get(  netsnmp_tdata     *table,
526                         netsnmp_tdata_row *row)
527 {
528     return (netsnmp_tdata_row*)CONTAINER_FIND( table->container, row );
529 }
530 
531 /** returns the next row in the table */
532 netsnmp_tdata_row *
netsnmp_tdata_row_next(netsnmp_tdata * table,netsnmp_tdata_row * row)533 netsnmp_tdata_row_next( netsnmp_tdata      *table,
534                         netsnmp_tdata_row  *row)
535 {
536     return (netsnmp_tdata_row *)CONTAINER_NEXT( table->container, row  );
537 }
538 
539 /** finds a row in the 'tdata' table given the index values */
540 netsnmp_tdata_row *
netsnmp_tdata_row_get_byidx(netsnmp_tdata * table,netsnmp_variable_list * indexes)541 netsnmp_tdata_row_get_byidx(netsnmp_tdata         *table,
542                             netsnmp_variable_list *indexes)
543 {
544     oid             searchfor[      MAX_OID_LEN];
545     size_t          searchfor_len = MAX_OID_LEN;
546 
547     build_oid_noalloc(searchfor, MAX_OID_LEN, &searchfor_len, NULL, 0,
548                       indexes);
549     return netsnmp_tdata_row_get_byoid(table, searchfor, searchfor_len);
550 }
551 
552 /** finds a row in the 'tdata' table given the index OID */
553 netsnmp_tdata_row *
netsnmp_tdata_row_get_byoid(netsnmp_tdata * table,oid * searchfor,size_t searchfor_len)554 netsnmp_tdata_row_get_byoid(netsnmp_tdata *table,
555                             oid * searchfor, size_t searchfor_len)
556 {
557     netsnmp_index index;
558     if (!table)
559         return NULL;
560 
561     index.oids = searchfor;
562     index.len  = searchfor_len;
563     return (netsnmp_tdata_row*)CONTAINER_FIND( table->container, &index );
564 }
565 
566 /** finds the lexically next row in the 'tdata' table
567     given the index values */
568 netsnmp_tdata_row *
netsnmp_tdata_row_next_byidx(netsnmp_tdata * table,netsnmp_variable_list * indexes)569 netsnmp_tdata_row_next_byidx(netsnmp_tdata         *table,
570                              netsnmp_variable_list *indexes)
571 {
572     oid             searchfor[      MAX_OID_LEN];
573     size_t          searchfor_len = MAX_OID_LEN;
574 
575     build_oid_noalloc(searchfor, MAX_OID_LEN, &searchfor_len, NULL, 0,
576                       indexes);
577     return netsnmp_tdata_row_next_byoid(table, searchfor, searchfor_len);
578 }
579 
580 /** finds the lexically next row in the 'tdata' table
581     given the index OID */
582 netsnmp_tdata_row *
netsnmp_tdata_row_next_byoid(netsnmp_tdata * table,oid * searchfor,size_t searchfor_len)583 netsnmp_tdata_row_next_byoid(netsnmp_tdata *table,
584                              oid * searchfor, size_t searchfor_len)
585 {
586     netsnmp_index index;
587     if (!table)
588         return NULL;
589 
590     index.oids = searchfor;
591     index.len  = searchfor_len;
592     return (netsnmp_tdata_row*)CONTAINER_NEXT( table->container, &index );
593 }
594 
595 netsnmp_feature_child_of(tdata_row_count, table_tdata_all);
596 #ifndef NETSNMP_FEATURE_REMOVE_TDATA_ROW_COUNT
597 int
netsnmp_tdata_row_count(netsnmp_tdata * table)598 netsnmp_tdata_row_count(netsnmp_tdata *table)
599 {
600     if (!table)
601         return 0;
602     return CONTAINER_SIZE( table->container );
603 }
604 #endif /* NETSNMP_FEATURE_REMOVE_TDATA_ROW_COUNT */
605 
606 /* ==================================
607  *
608  * Generic API: Index operations on a 'tdata' table
609  *
610  * ================================== */
611 
612 
613 /** compare a row with the given index values */
614 netsnmp_feature_child_of(tdata_compare_idx, table_tdata_all);
615 #ifndef NETSNMP_FEATURE_REMOVE_TDATA_COMPARE_IDX
616 int
netsnmp_tdata_compare_idx(netsnmp_tdata_row * row,netsnmp_variable_list * indexes)617 netsnmp_tdata_compare_idx(netsnmp_tdata_row     *row,
618                           netsnmp_variable_list *indexes)
619 {
620     oid             searchfor[      MAX_OID_LEN];
621     size_t          searchfor_len = MAX_OID_LEN;
622 
623     build_oid_noalloc(searchfor, MAX_OID_LEN, &searchfor_len, NULL, 0,
624                       indexes);
625     return netsnmp_tdata_compare_oid(row, searchfor, searchfor_len);
626 }
627 #endif /* NETSNMP_FEATURE_REMOVE_TDATA_COMPARE_IDX */
628 
629 /** compare a row with the given index OID */
630 int
netsnmp_tdata_compare_oid(netsnmp_tdata_row * row,oid * compareto,size_t compareto_len)631 netsnmp_tdata_compare_oid(netsnmp_tdata_row     *row,
632                           oid * compareto, size_t compareto_len)
633 {
634     netsnmp_index *index = (netsnmp_index *)row;
635     return snmp_oid_compare( index->oids, index->len,
636                              compareto,   compareto_len);
637 }
638 
639 int
netsnmp_tdata_compare_subtree_idx(netsnmp_tdata_row * row,netsnmp_variable_list * indexes)640 netsnmp_tdata_compare_subtree_idx(netsnmp_tdata_row     *row,
641                                   netsnmp_variable_list *indexes)
642 {
643     oid             searchfor[      MAX_OID_LEN];
644     size_t          searchfor_len = MAX_OID_LEN;
645 
646     build_oid_noalloc(searchfor, MAX_OID_LEN, &searchfor_len, NULL, 0,
647                       indexes);
648     return netsnmp_tdata_compare_subtree_oid(row, searchfor, searchfor_len);
649 }
650 
651 int
netsnmp_tdata_compare_subtree_oid(netsnmp_tdata_row * row,oid * compareto,size_t compareto_len)652 netsnmp_tdata_compare_subtree_oid(netsnmp_tdata_row     *row,
653                                   oid * compareto, size_t compareto_len)
654 {
655     netsnmp_index *index = (netsnmp_index *)row;
656     return snmp_oidtree_compare( index->oids, index->len,
657                                  compareto,   compareto_len);
658 }
659 #else /* NETSNMP_FEATURE_REMOVE_TABLE_TDATA */
660 netsnmp_feature_unused(table_tdata);
661 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_TDATA */
662 
663 
664 /** @}
665  */
666