net-snmp 5.7
table.c
00001 /*
00002  * table.c 
00003  */
00004 
00005 /* Portions of this file are subject to the following copyright(s).  See
00006  * the Net-SNMP's COPYING file for more details and other copyrights
00007  * that may apply:
00008  */
00009 /*
00010  * Portions of this file are copyrighted by:
00011  * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
00012  * Use is subject to license terms specified in the COPYING file
00013  * distributed with the Net-SNMP package.
00014  */
00015 /*
00016  * Portions of this file are copyrighted by:
00017  * Copyright (C) 2007 Apple, Inc. All rights reserved.
00018  * Use is subject to license terms specified in the COPYING file
00019  * distributed with the Net-SNMP package.
00020  */
00021 
00022 #include <net-snmp/net-snmp-config.h>
00023 
00024 #include <net-snmp/net-snmp-features.h>
00025 #include <net-snmp/net-snmp-includes.h>
00026 #include <net-snmp/agent/net-snmp-agent-includes.h>
00027 
00028 #include <net-snmp/agent/table.h>
00029 
00030 #ifndef NETSNMP_NO_WRITE_SUPPORT
00031 netsnmp_feature_require(oid_stash)
00032 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
00033 
00034 #if HAVE_STRING_H
00035 #include <string.h>
00036 #else
00037 #include <strings.h>
00038 #endif
00039 
00040 #include <net-snmp/library/snmp_assert.h>
00041 
00042 netsnmp_feature_child_of(table_all, mib_helpers)
00043 
00044 netsnmp_feature_child_of(table_build_result, table_all)
00045 netsnmp_feature_child_of(table_get_or_create_row_stash, table_all)
00046 netsnmp_feature_child_of(registration_owns_table_info, table_all)
00047 netsnmp_feature_child_of(table_sparse, table_all)
00048 
00049 static void     table_helper_cleanup(netsnmp_agent_request_info *reqinfo,
00050                                      netsnmp_request_info *request,
00051                                      int status);
00052 static void     table_data_free_func(void *data);
00053 static int
00054 sparse_table_helper_handler(netsnmp_mib_handler *handler,
00055                             netsnmp_handler_registration *reginfo,
00056                             netsnmp_agent_request_info *reqinfo,
00057                             netsnmp_request_info *requests);
00058 
00102 netsnmp_mib_handler *
00103 netsnmp_get_table_handler(netsnmp_table_registration_info *tabreq)
00104 {
00105     netsnmp_mib_handler *ret = NULL;
00106 
00107     if (!tabreq) {
00108         snmp_log(LOG_INFO, "netsnmp_get_table_handler(NULL) called\n");
00109         return NULL;
00110     }
00111 
00112     ret = netsnmp_create_handler(TABLE_HANDLER_NAME, table_helper_handler);
00113     if (ret) {
00114         ret->myvoid = (void *) tabreq;
00115         tabreq->number_indexes = count_varbinds(tabreq->indexes);
00116     }
00117     return ret;
00118 }
00119 
00124 void netsnmp_handler_owns_table_info(netsnmp_mib_handler *handler)
00125 {
00126     netsnmp_assert(handler);
00127     netsnmp_assert(handler->myvoid);
00128     handler->data_clone
00129         = (void *(*)(void *)) netsnmp_table_registration_info_clone;
00130     handler->data_free
00131         = (void (*)(void *)) netsnmp_table_registration_info_free;
00132 }
00133 
00138 #ifndef NETSNMP_FEATURE_REMOVE_REGISTRATION_OWNS_TABLE_INFO
00139 void netsnmp_registration_owns_table_info(netsnmp_handler_registration *reg)
00140 {
00141     if (reg)
00142         netsnmp_handler_owns_table_info(reg->handler);
00143 }
00144 #endif /* NETSNMP_FEATURE_REMOVE_REGISTRATION_OWNS_TABLE_INFO */
00145 
00150 int
00151 netsnmp_register_table(netsnmp_handler_registration *reginfo,
00152                        netsnmp_table_registration_info *tabreq)
00153 {
00154     int rc = netsnmp_inject_handler(reginfo, netsnmp_get_table_handler(tabreq));
00155     if (SNMPERR_SUCCESS != rc)
00156         return rc;
00157 
00158     return netsnmp_register_handler(reginfo);
00159 }
00160 
00161 int
00162 netsnmp_unregister_table(netsnmp_handler_registration *reginfo)
00163 {
00164     /* Locate "this" reginfo */
00165     /* SNMP_FREE(reginfo->myvoid); */
00166     return netsnmp_unregister_handler(reginfo);
00167 }
00168 
00178 NETSNMP_INLINE netsnmp_table_request_info *
00179 netsnmp_extract_table_info(netsnmp_request_info *request)
00180 {
00181     return (netsnmp_table_request_info *)
00182         netsnmp_request_get_list_data(request, TABLE_HANDLER_NAME);
00183 }
00184 
00187 netsnmp_table_registration_info *
00188 netsnmp_find_table_registration_info(netsnmp_handler_registration *reginfo)
00189 {
00190     return (netsnmp_table_registration_info *)
00191         netsnmp_find_handler_data_by_name(reginfo, TABLE_HANDLER_NAME);
00192 }
00193 
00195 int
00196 table_helper_handler(netsnmp_mib_handler *handler,
00197                      netsnmp_handler_registration *reginfo,
00198                      netsnmp_agent_request_info *reqinfo,
00199                      netsnmp_request_info *requests)
00200 {
00201 
00202     netsnmp_request_info *request;
00203     netsnmp_table_registration_info *tbl_info;
00204     int             oid_index_pos;
00205     unsigned int    oid_column_pos;
00206     unsigned int    tmp_idx;
00207     ssize_t         tmp_len;
00208     int             incomplete, out_of_range;
00209     int             status = SNMP_ERR_NOERROR, need_processing = 0;
00210     oid            *tmp_name;
00211     netsnmp_table_request_info *tbl_req_info;
00212     netsnmp_variable_list *vb;
00213 
00214     if (!reginfo || !handler)
00215         return SNMPERR_GENERR;
00216 
00217     oid_index_pos  = reginfo->rootoid_len + 2;
00218     oid_column_pos = reginfo->rootoid_len + 1;
00219     tbl_info = (netsnmp_table_registration_info *) handler->myvoid;
00220 
00221     if ((!handler->myvoid) || (!tbl_info->indexes)) {
00222         snmp_log(LOG_ERR, "improperly registered table found\n");
00223         snmp_log(LOG_ERR, "name: %s, table info: %p, indexes: %p\n",
00224                  handler->handler_name, handler->myvoid, tbl_info->indexes);
00225 
00226         /*
00227          * XXX-rks: unregister table? 
00228          */
00229         return SNMP_ERR_GENERR;
00230     }
00231 
00232     DEBUGIF("helper:table:req") {
00233         DEBUGMSGTL(("helper:table:req",
00234                     "Got %s (%d) mode request for handler %s: base oid:",
00235                     se_find_label_in_slist("agent_mode", reqinfo->mode),
00236                     reqinfo->mode, handler->handler_name));
00237         DEBUGMSGOID(("helper:table:req", reginfo->rootoid,
00238                      reginfo->rootoid_len));
00239         DEBUGMSG(("helper:table:req", "\n"));
00240     }
00241     
00242     /*
00243      * if the agent request info has a state reference, then this is a 
00244      * later pass of a set request and we can skip all the lookup stuff.
00245      *
00246      * xxx-rks: this might break for handlers which only handle one varbind
00247      * at a time... those handlers should not save data by their handler_name
00248      * in the netsnmp_agent_request_info. 
00249      */
00250     if (netsnmp_agent_get_list_data(reqinfo, handler->next->handler_name)) {
00251 #ifndef NETSNMP_NO_WRITE_SUPPORT
00252         if (MODE_IS_SET(reqinfo->mode)) {
00253             return netsnmp_call_next_handler(handler, reginfo, reqinfo,
00254                                              requests);
00255         } else {
00256 #endif /* NETSNMP_NO_WRITE_SUPPORT */
00257 
00258             netsnmp_free_agent_data_sets(reqinfo);
00259 #ifndef NETSNMP_NO_WRITE_SUPPORT
00260         }
00261 #endif /* NETSNMP_NO_WRITE_SUPPORT */
00262     }
00263 
00264 #ifndef NETSNMP_NO_WRITE_SUPPORT
00265     if ( MODE_IS_SET(reqinfo->mode) &&
00266          (reqinfo->mode != MODE_SET_RESERVE1)) {
00267         /*
00268          * for later set modes, we can skip all the index parsing,
00269          * and we always need to let child handlers have a chance
00270          * to clean up, if they were called in the first place (i.e. have
00271          * a valid table info pointer).
00272          */
00273         if(NULL == netsnmp_extract_table_info(requests)) {
00274             DEBUGMSGTL(("helper:table","no table info for set - skipping\n"));
00275         }
00276         else
00277             need_processing = 1;
00278     }
00279     else {
00280 #endif /* NETSNMP_NO_WRITE_SUPPORT */
00281         /*
00282          * for GETS, only continue if we have at least one valid request.
00283          * for RESERVE1, only continue if we have indexes for all requests.
00284          */
00285            
00286     /*
00287      * loop through requests
00288      */
00289 
00290     for (request = requests; request; request = request->next) {
00291         netsnmp_variable_list *var = request->requestvb;
00292 
00293         DEBUGMSGOID(("verbose:table", var->name, var->name_length));
00294         DEBUGMSG(("verbose:table", "\n"));
00295 
00296         if (request->processed) {
00297             DEBUGMSG(("verbose:table", "already processed\n"));
00298             continue;
00299         }
00300         netsnmp_assert(request->status == SNMP_ERR_NOERROR);
00301 
00302         /*
00303          * this should probably be handled further up 
00304          */
00305         if ((reqinfo->mode == MODE_GET) && (var->type != ASN_NULL)) {
00306             /*
00307              * valid request if ASN_NULL 
00308              */
00309             DEBUGMSGTL(("helper:table",
00310                         "  GET var type is not ASN_NULL\n"));
00311             netsnmp_set_request_error(reqinfo, request,
00312                                       SNMP_ERR_WRONGTYPE);
00313             continue;
00314         }
00315 
00316 #ifndef NETSNMP_NO_WRITE_SUPPORT
00317         if (reqinfo->mode == MODE_SET_RESERVE1) {
00318             DEBUGIF("helper:table:set") {
00319                 u_char         *buf = NULL;
00320                 size_t          buf_len = 0, out_len = 0;
00321                 DEBUGMSGTL(("helper:table:set", " SET_REQUEST for OID: "));
00322                 DEBUGMSGOID(("helper:table:set", var->name, var->name_length));
00323                 out_len = 0;
00324                 if (sprint_realloc_by_type(&buf, &buf_len, &out_len, 1,
00325                                            var, NULL, NULL, NULL)) {
00326                     DEBUGMSG(("helper:table:set"," type=%d(%02x), value=%s\n",
00327                               var->type, var->type, buf));
00328                 } else {
00329                     if (buf != NULL) {
00330                         DEBUGMSG(("helper:table:set",
00331                                   " type=%d(%02x), value=%s [TRUNCATED]\n",
00332                                   var->type, var->type, buf));
00333                     } else {
00334                         DEBUGMSG(("helper:table:set",
00335                                   " type=%d(%02x), value=[NIL] [TRUNCATED]\n",
00336                                   var->type, var->type));
00337                     }
00338                 }
00339                 if (buf != NULL) {
00340                     free(buf);
00341                 }
00342             }
00343         }
00344 #endif /* NETSNMP_NO_WRITE_SUPPORT */
00345 
00346         /*
00347          * check to make sure its in table range 
00348          */
00349 
00350         out_of_range = 0;
00351         /*
00352          * if our root oid is > var->name and this is not a GETNEXT, 
00353          * then the oid is out of range. (only compare up to shorter 
00354          * length) 
00355          */
00356         if (reginfo->rootoid_len > var->name_length)
00357             tmp_len = var->name_length;
00358         else
00359             tmp_len = reginfo->rootoid_len;
00360         if (snmp_oid_compare(reginfo->rootoid, reginfo->rootoid_len,
00361                              var->name, tmp_len) > 0) {
00362             if (reqinfo->mode == MODE_GETNEXT) {
00363                 if (var->name != var->name_loc)
00364                     SNMP_FREE(var->name);
00365                 snmp_set_var_objid(var, reginfo->rootoid,
00366                                    reginfo->rootoid_len);
00367             } else {
00368                 DEBUGMSGTL(("helper:table", "  oid is out of range.\n"));
00369                 out_of_range = 1;
00370             }
00371         }
00372         /*
00373          * if var->name is longer than the root, make sure it is 
00374          * table.1 (table.ENTRY).  
00375          */
00376         else if ((var->name_length > reginfo->rootoid_len) &&
00377                  (var->name[reginfo->rootoid_len] != 1)) {
00378             if ((var->name[reginfo->rootoid_len] < 1) &&
00379                 (reqinfo->mode == MODE_GETNEXT)) {
00380                 var->name[reginfo->rootoid_len] = 1;
00381                 var->name_length = reginfo->rootoid_len;
00382             } else {
00383                 out_of_range = 1;
00384                 DEBUGMSGTL(("helper:table", "  oid is out of range.\n"));
00385             }
00386         }
00387         /*
00388          * if it is not in range, then mark it in the request list 
00389          * because we can't process it, and set an error so
00390          * nobody else wastes time trying to process it either.  
00391          */
00392         if (out_of_range) {
00393             DEBUGMSGTL(("helper:table", "  Not processed: "));
00394             DEBUGMSGOID(("helper:table", var->name, var->name_length));
00395             DEBUGMSG(("helper:table", "\n"));
00396 
00397             /*
00398              *  Reject requests of the form 'myTable.N'   (N != 1)
00399              */
00400 #ifndef NETSNMP_NO_WRITE_SUPPORT
00401             if (reqinfo->mode == MODE_SET_RESERVE1)
00402                 table_helper_cleanup(reqinfo, request,
00403                                      SNMP_ERR_NOTWRITABLE);
00404             else
00405 #endif /* NETSNMP_NO_WRITE_SUPPORT */
00406             if (reqinfo->mode == MODE_GET)
00407                 table_helper_cleanup(reqinfo, request,
00408                                      SNMP_NOSUCHOBJECT);
00409             continue;
00410         }
00411 
00412 
00413         /*
00414          * Check column ranges; set-up to pull out indexes from OID. 
00415          */
00416 
00417         incomplete = 0;
00418         tbl_req_info = netsnmp_extract_table_info(request);
00419         if (NULL == tbl_req_info) {
00420             tbl_req_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_request_info);
00421             if (tbl_req_info == NULL) {
00422                 table_helper_cleanup(reqinfo, request,
00423                                      SNMP_ERR_GENERR);
00424                 continue;
00425             }
00426             tbl_req_info->reg_info = tbl_info;
00427             tbl_req_info->indexes = snmp_clone_varbind(tbl_info->indexes);
00428             tbl_req_info->number_indexes = 0;       /* none yet */
00429             netsnmp_request_add_list_data(request,
00430                                           netsnmp_create_data_list
00431                                           (TABLE_HANDLER_NAME,
00432                                            (void *) tbl_req_info,
00433                                            table_data_free_func));
00434         } else {
00435             DEBUGMSGTL(("helper:table", "  using existing tbl_req_info\n "));
00436         }
00437 
00438         /*
00439          * do we have a column?
00440          */
00441         if (var->name_length > oid_column_pos) {
00442             /*
00443              * oid is long enough to contain COLUMN info
00444              */
00445             DEBUGMSGTL(("helper:table:col",
00446                         "  have at least a column (%" NETSNMP_PRIo "d)\n",
00447                         var->name[oid_column_pos]));
00448             if (var->name[oid_column_pos] < tbl_info->min_column) {
00449                 DEBUGMSGTL(("helper:table:col",
00450                             "    but it's less than min (%d)\n",
00451                             tbl_info->min_column));
00452                 if (reqinfo->mode == MODE_GETNEXT) {
00453                     /*
00454                      * fix column, truncate useless column info 
00455                      */
00456                     var->name_length = oid_column_pos;
00457                     tbl_req_info->colnum = tbl_info->min_column;
00458                 } else
00459                     out_of_range = 1;
00460             } else if (var->name[oid_column_pos] > tbl_info->max_column)
00461                 out_of_range = 1;
00462             else
00463                 tbl_req_info->colnum = var->name[oid_column_pos];
00464 
00465             if (out_of_range) {
00466                 /*
00467                  * this is out of range...  remove from requests, free
00468                  * memory 
00469                  */
00470                 DEBUGMSGTL(("helper:table",
00471                             "    oid is out of range. Not processed: "));
00472                 DEBUGMSGOID(("helper:table", var->name, var->name_length));
00473                 DEBUGMSG(("helper:table", "\n"));
00474 
00475                 /*
00476                  *  Reject requests of the form 'myEntry.N'   (invalid N)
00477                  */
00478 #ifndef NETSNMP_NO_WRITE_SUPPORT
00479                 if (reqinfo->mode == MODE_SET_RESERVE1)
00480                     table_helper_cleanup(reqinfo, request,
00481                                          SNMP_ERR_NOTWRITABLE);
00482                 else if (reqinfo->mode == MODE_GET)
00483 #endif /* NETSNMP_NO_WRITE_SUPPORT */
00484                     table_helper_cleanup(reqinfo, request,
00485                                          SNMP_NOSUCHOBJECT);
00486                 continue;
00487             }
00488             /*
00489              * use column verification 
00490              */
00491             else if (tbl_info->valid_columns) {
00492                 tbl_req_info->colnum =
00493                     netsnmp_closest_column(var->name[oid_column_pos],
00494                                            tbl_info->valid_columns);
00495                 DEBUGMSGTL(("helper:table:col", "    closest column is %d\n",
00496                             tbl_req_info->colnum));
00497                 /*
00498                  * xxx-rks: document why the continue...
00499                  */
00500                 if (tbl_req_info->colnum == 0)
00501                     continue;
00502                 if (tbl_req_info->colnum != var->name[oid_column_pos]) {
00503                     DEBUGMSGTL(("helper:table:col",
00504                                 "    which doesn't match req "
00505                                 "%" NETSNMP_PRIo "d - truncating index info\n",
00506                                 var->name[oid_column_pos]));
00507                     /*
00508                      * different column! truncate useless index info 
00509                      */
00510                     var->name_length = oid_column_pos + 1; /* pos is 0 based */
00511                 }
00512             }
00513             /*
00514              * var->name_length may have changed - check again 
00515              */
00516             if ((int)var->name_length <= oid_index_pos) { /* pos is 0 based */
00517                 DEBUGMSGTL(("helper:table", "    not enough for indexes\n"));
00518                 tbl_req_info->index_oid_len = 0; 
00519             } else {
00520                 /*
00521                  * oid is long enough to contain INDEX info
00522                  */
00523                 tbl_req_info->index_oid_len =
00524                     var->name_length - oid_index_pos;
00525                 DEBUGMSGTL(("helper:table", "    have %lu bytes of index\n",
00526                             (unsigned long)tbl_req_info->index_oid_len));
00527                 netsnmp_assert(tbl_req_info->index_oid_len < MAX_OID_LEN);
00528                 memcpy(tbl_req_info->index_oid, &var->name[oid_index_pos],
00529                        tbl_req_info->index_oid_len * sizeof(oid));
00530                 tmp_name = tbl_req_info->index_oid;
00531             }
00532         } else if (reqinfo->mode == MODE_GETNEXT ||
00533                    reqinfo->mode == MODE_GETBULK) {
00534             /*
00535              * oid is NOT long enough to contain column or index info, so start
00536              * at the minimum column. Set index oid len to 0 because we don't
00537              * have any index info in the OID.
00538              */
00539             DEBUGMSGTL(("helper:table", "  no column/index in request\n"));
00540             tbl_req_info->index_oid_len = 0;
00541             tbl_req_info->colnum = tbl_info->min_column;
00542         } else {
00543             /*
00544              * oid is NOT long enough to contain index info,
00545              * so we can't do anything with it.
00546              *
00547              * Reject requests of the form 'myTable' or 'myEntry'
00548              */
00549             if (reqinfo->mode == MODE_GET ) {
00550                 table_helper_cleanup(reqinfo, request, SNMP_NOSUCHOBJECT);
00551 #ifndef NETSNMP_NO_WRITE_SUPPORT
00552             } else if (reqinfo->mode == MODE_SET_RESERVE1 ) {
00553                 table_helper_cleanup(reqinfo, request, SNMP_ERR_NOTWRITABLE);
00554 #endif /* NETSNMP_NO_WRITE_SUPPORT */
00555             }
00556             continue;
00557         }
00558 
00559         /*
00560          * set up tmp_len to be the number of OIDs we have beyond the column;
00561          * these should be the index(s) for the table. If the index_oid_len
00562          * is 0, set tmp_len to -1 so that when we try to parse the index below,
00563          * we just zero fill everything.
00564          */
00565         if (tbl_req_info->index_oid_len == 0) {
00566             incomplete = 1;
00567             tmp_len = -1;
00568         } else
00569             tmp_len = tbl_req_info->index_oid_len;
00570 
00571 
00572         /*
00573          * for each index type, try to extract the index from var->name
00574          */
00575         DEBUGMSGTL(("helper:table", "  looking for %d indexes\n",
00576                     tbl_info->number_indexes));
00577         for (tmp_idx = 0, vb = tbl_req_info->indexes;
00578              tmp_idx < tbl_info->number_indexes;
00579              ++tmp_idx, vb = vb->next_variable) {
00580             size_t parsed_oid_len;
00581 
00582             if (incomplete && tmp_len) {
00583                 /*
00584                  * incomplete/illegal OID, set up dummy 0 to parse 
00585                  */
00586                 DEBUGMSGTL(("helper:table",
00587                             "  oid indexes not complete: "));
00588                 DEBUGMSGOID(("helper:table", var->name, var->name_length));
00589                 DEBUGMSG(("helper:table", "\n"));
00590 
00591                 /*
00592                  * no sense in trying anymore if this is a GET/SET. 
00593                  *
00594                  * Reject requests of the form 'myObject'   (no instance)
00595                  */
00596                 tmp_len = 0;
00597                 tmp_name = NULL;
00598                 break;
00599             }
00600             /*
00601              * try and parse current index 
00602              */
00603             netsnmp_assert(tmp_len >= 0);
00604             parsed_oid_len = tmp_len;
00605             if (parse_one_oid_index(&tmp_name, &parsed_oid_len,
00606                                     vb, 1) != SNMPERR_SUCCESS) {
00607                 incomplete = 1;
00608                 tmp_len = -1;   /* is this necessary? Better safe than
00609                                  * sorry */
00610             } else {
00611                 tmp_len = parsed_oid_len;
00612                 DEBUGMSGTL(("helper:table", "  got 1 (incomplete=%d)\n",
00613                             incomplete));
00614                 /*
00615                  * do not count incomplete indexes 
00616                  */
00617                 if (incomplete)
00618                     continue;
00619                 ++tbl_req_info->number_indexes; 
00620                 if (tmp_len <= 0) {
00621                     incomplete = 1;
00622                     tmp_len = -1;       /* is this necessary? Better safe
00623                                          * than sorry */
00624                 }
00625             }
00626         }                       
00628         DEBUGIF("helper:table:results") {
00629                 unsigned int    count;
00630                 u_char         *buf = NULL;
00631                 size_t          buf_len = 0, out_len = 0;
00632                 DEBUGMSGTL(("helper:table:results", "  found %d indexes\n",
00633                             tbl_req_info->number_indexes));
00634                 DEBUGMSGTL(("helper:table:results",
00635                             "  column: %d, indexes: %d",
00636                             tbl_req_info->colnum,
00637                             tbl_req_info->number_indexes));
00638                 for (vb = tbl_req_info->indexes, count = 0;
00639                      vb && count < tbl_req_info->number_indexes;
00640                      count++, vb = vb->next_variable) {
00641                     out_len = 0;
00642                     if (sprint_realloc_by_type(&buf, &buf_len, &out_len, 1,
00643                                                vb, NULL, NULL, NULL)) {
00644                         DEBUGMSG(("helper:table:results",
00645                                   "   index: type=%d(%02x), value=%s",
00646                                   vb->type, vb->type, buf));
00647                     } else {
00648                         if (buf != NULL) {
00649                             DEBUGMSG(("helper:table:results",
00650                                       "   index: type=%d(%02x), value=%s [TRUNCATED]",
00651                                       vb->type, vb->type, buf));
00652                         } else {
00653                             DEBUGMSG(("helper:table:results",
00654                                       "   index: type=%d(%02x), value=[NIL] [TRUNCATED]",
00655                                       vb->type, vb->type));
00656                         }
00657                     }
00658                 }
00659                 if (buf != NULL) {
00660                     free(buf);
00661                 }
00662                 DEBUGMSG(("helper:table:results", "\n"));
00663         }
00664 
00665 
00666         /*
00667          * do we have sufficient index info to continue?
00668          */
00669 
00670         if ((reqinfo->mode != MODE_GETNEXT) &&
00671             ((tbl_req_info->number_indexes != tbl_info->number_indexes) ||
00672              (tmp_len != -1))) {
00673 
00674             DEBUGMSGTL(("helper:table",
00675                         "invalid index(es) for table - skipping\n"));
00676 
00677 #ifndef NETSNMP_NO_WRITE_SUPPORT
00678             if ( MODE_IS_SET(reqinfo->mode) ) {
00679                 /*
00680                  * no point in continuing without indexes for set.
00681                  */
00682                 netsnmp_assert(reqinfo->mode == MODE_SET_RESERVE1);
00684                 netsnmp_free_request_data_sets(requests);
00686                 table_helper_cleanup(reqinfo, request, SNMP_ERR_NOCREATION);
00687                 need_processing = 0; /* don't call next handler */
00688                 break;
00689             }
00690 #endif /* NETSNMP_NO_WRITE_SUPPORT */
00691             table_helper_cleanup(reqinfo, request, SNMP_NOSUCHINSTANCE);
00692             continue;
00693         }
00694         netsnmp_assert(request->status == SNMP_ERR_NOERROR);
00695         
00696         ++need_processing;
00697 
00698     }                           /* for each request */
00699 #ifndef NETSNMP_NO_WRITE_SUPPORT
00700     }
00701 #endif /* NETSNMP_NO_WRITE_SUPPORT */
00702 
00703     /*
00704      * bail if there is nothing for our child handlers
00705      */
00706     if (0 == need_processing)
00707         return status;
00708 
00709     /*
00710      * call our child access function 
00711      */
00712     status =
00713         netsnmp_call_next_handler(handler, reginfo, reqinfo, requests);
00714 
00715     /*
00716      * check for sparse tables
00717      */
00718     if (reqinfo->mode == MODE_GETNEXT)
00719         sparse_table_helper_handler( handler, reginfo, reqinfo, requests );
00720 
00721     return status;
00722 }
00723 
00724 #define SPARSE_TABLE_HANDLER_NAME "sparse_table"
00725 
00734 static int
00735 sparse_table_helper_handler(netsnmp_mib_handler *handler,
00736                      netsnmp_handler_registration *reginfo,
00737                      netsnmp_agent_request_info *reqinfo,
00738                      netsnmp_request_info *requests)
00739 {
00740     int             status = SNMP_ERR_NOERROR;
00741     netsnmp_request_info *request;
00742     oid             coloid[MAX_OID_LEN];
00743     netsnmp_table_request_info *table_info;
00744 
00745     /*
00746      * since we don't call child handlers, warn if one was registered
00747      * beneath us. A special exception for the table helper, which calls
00748      * the handler directly. Use handle custom flag to only log once.
00749      */
00750     if((table_helper_handler != handler->access_method) &&
00751        (NULL != handler->next)) {
00752         /*
00753          * always warn if called without our own handler. If we
00754          * have our own handler, use custom bit 1 to only log once.
00755          */
00756         if((sparse_table_helper_handler != handler->access_method) ||
00757            !(handler->flags & MIB_HANDLER_CUSTOM1)) {
00758             snmp_log(LOG_WARNING, "handler (%s) registered after sparse table "
00759                      "hander will not be called\n",
00760                      handler->next->handler_name ?
00761                      handler->next->handler_name : "" );
00762             if(sparse_table_helper_handler == handler->access_method)
00763                 handler->flags |= MIB_HANDLER_CUSTOM1;
00764         }
00765     }
00766 
00767     if (reqinfo->mode == MODE_GETNEXT) {
00768         for(request = requests ; request; request = request->next) {
00769             if ((request->requestvb->type == ASN_NULL && request->processed) ||
00770                 request->delegated)
00771                 continue;
00772             if (request->requestvb->type == SNMP_NOSUCHINSTANCE) {
00773                 /*
00774                  * get next skipped this value for this column, we
00775                  * need to keep searching forward 
00776                  */
00777                 DEBUGMSGT(("sparse", "retry for NOSUCHINSTANCE\n"));
00778                 request->requestvb->type = ASN_PRIV_RETRY;
00779             }
00780             if (request->requestvb->type == SNMP_NOSUCHOBJECT ||
00781                 request->requestvb->type == SNMP_ENDOFMIBVIEW) {
00782                 /*
00783                  * get next has completely finished with this column,
00784                  * so we need to try with the next column (if any)
00785                  */
00786                 DEBUGMSGT(("sparse", "retry for NOSUCHOBJECT\n"));
00787                 table_info = netsnmp_extract_table_info(request);
00788                 table_info->colnum = netsnmp_table_next_column(table_info);
00789                 if (0 != table_info->colnum) {
00790                     memcpy(coloid, reginfo->rootoid,
00791                            reginfo->rootoid_len * sizeof(oid));
00792                     coloid[reginfo->rootoid_len]   = 1;   /* table.entry node */
00793                     coloid[reginfo->rootoid_len+1] = table_info->colnum;
00794                     snmp_set_var_objid(request->requestvb,
00795                                        coloid, reginfo->rootoid_len + 2);
00796                     
00797                     request->requestvb->type = ASN_PRIV_RETRY;
00798                 }
00799                 else {
00800                     /*
00801                      * If we don't have column info, reset to null so
00802                      * the agent will move on to the next table.
00803                      */
00804                     request->requestvb->type = ASN_NULL;
00805                 }
00806             }
00807         }
00808     }
00809     return status;
00810 }
00811 
00814 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_SPARSE
00815 netsnmp_mib_handler *
00816 netsnmp_sparse_table_handler_get(void)
00817 {
00818     return netsnmp_create_handler(SPARSE_TABLE_HANDLER_NAME,
00819                                   sparse_table_helper_handler);
00820 }
00821 
00826 int
00827 netsnmp_sparse_table_register(netsnmp_handler_registration *reginfo,
00828                        netsnmp_table_registration_info *tabreq)
00829 {
00830     netsnmp_mib_handler *handler1, *handler2;
00831     int rc;
00832 
00833     handler1 = netsnmp_create_handler(SPARSE_TABLE_HANDLER_NAME,
00834                                      sparse_table_helper_handler);
00835     if (NULL == handler1)
00836         return SNMP_ERR_GENERR;
00837 
00838     handler2 = netsnmp_get_table_handler(tabreq);
00839     if (NULL == handler2 ) {
00840         netsnmp_handler_free(handler1);
00841         return SNMP_ERR_GENERR;
00842     }
00843 
00844     rc = netsnmp_inject_handler(reginfo, handler1);
00845     if (SNMPERR_SUCCESS != rc) {
00846         netsnmp_handler_free(handler1);
00847         netsnmp_handler_free(handler2);
00848         return rc;
00849     }
00850 
00851     rc = netsnmp_inject_handler(reginfo, handler2);
00852     if (SNMPERR_SUCCESS != rc) {
00854         netsnmp_handler_free(handler2);
00855         return rc;
00856     }
00857 
00859     return netsnmp_register_handler(reginfo);
00860 }
00861 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_SPARSE */
00862 
00863 
00864 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_BUILD_RESULT
00865 
00871 int
00872 netsnmp_table_build_result(netsnmp_handler_registration *reginfo,
00873                            netsnmp_request_info *reqinfo,
00874                            netsnmp_table_request_info *table_info,
00875                            u_char type, u_char * result, size_t result_len)
00876 {
00877 
00878     netsnmp_variable_list *var;
00879 
00880     if (!reqinfo || !table_info)
00881         return SNMPERR_GENERR;
00882 
00883     var = reqinfo->requestvb;
00884 
00885     if (var->name != var->name_loc)
00886         free(var->name);
00887     var->name = NULL;
00888 
00889     if (netsnmp_table_build_oid(reginfo, reqinfo, table_info) !=
00890         SNMPERR_SUCCESS)
00891         return SNMPERR_GENERR;
00892 
00893     snmp_set_var_typed_value(var, type, result, result_len);
00894 
00895     return SNMPERR_SUCCESS;
00896 }
00897 
00903 int
00904 netsnmp_table_build_oid(netsnmp_handler_registration *reginfo,
00905                         netsnmp_request_info *reqinfo,
00906                         netsnmp_table_request_info *table_info)
00907 {
00908     oid             tmpoid[MAX_OID_LEN];
00909     netsnmp_variable_list *var;
00910 
00911     if (!reginfo || !reqinfo || !table_info)
00912         return SNMPERR_GENERR;
00913 
00914     /*
00915      * xxx-rks: inefficent. we do a copy here, then build_oid does it
00916      *          again. either come up with a new utility routine, or
00917      *          do some hijinks here to eliminate extra copy.
00918      *          Probably could make sure all callers have the
00919      *          index & variable list updated, and use
00920      *          netsnmp_table_build_oid_from_index() instead of all this.
00921      */
00922     memcpy(tmpoid, reginfo->rootoid, reginfo->rootoid_len * sizeof(oid));
00923     tmpoid[reginfo->rootoid_len] = 1;   
00924     tmpoid[reginfo->rootoid_len + 1] = table_info->colnum; 
00926     var = reqinfo->requestvb;
00927     if (build_oid(&var->name, &var->name_length,
00928                   tmpoid, reginfo->rootoid_len + 2, table_info->indexes)
00929         != SNMPERR_SUCCESS)
00930         return SNMPERR_GENERR;
00931 
00932     return SNMPERR_SUCCESS;
00933 }
00934 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_BUILD_RESULT */
00935 
00941 int
00942 netsnmp_table_build_oid_from_index(netsnmp_handler_registration *reginfo,
00943                                    netsnmp_request_info *reqinfo,
00944                                    netsnmp_table_request_info *table_info)
00945 {
00946     oid             tmpoid[MAX_OID_LEN];
00947     netsnmp_variable_list *var;
00948     int             len;
00949 
00950     if (!reginfo || !reqinfo || !table_info)
00951         return SNMPERR_GENERR;
00952 
00953     var = reqinfo->requestvb;
00954     len = reginfo->rootoid_len;
00955     memcpy(tmpoid, reginfo->rootoid, len * sizeof(oid));
00956     tmpoid[len++] = 1;          /* .Entry */
00957     tmpoid[len++] = table_info->colnum; /* .column */
00958     memcpy(&tmpoid[len], table_info->index_oid,
00959            table_info->index_oid_len * sizeof(oid));
00960     len += table_info->index_oid_len;
00961     snmp_set_var_objid( var, tmpoid, len );
00962 
00963     return SNMPERR_SUCCESS;
00964 }
00965 
00967 int
00968 netsnmp_update_variable_list_from_index(netsnmp_table_request_info *tri)
00969 {
00970     if (!tri)
00971         return SNMPERR_GENERR;
00972 
00973     /*
00974      * free any existing allocated memory, then parse oid into varbinds
00975      */
00976     snmp_reset_var_buffers( tri->indexes);
00977 
00978     return parse_oid_indexes(tri->index_oid, tri->index_oid_len,
00979                              tri->indexes);
00980 }
00981 
00983 int
00984 netsnmp_update_indexes_from_variable_list(netsnmp_table_request_info *tri)
00985 {
00986     if (!tri)
00987         return SNMPERR_GENERR;
00988 
00989     return build_oid_noalloc(tri->index_oid, sizeof(tri->index_oid),
00990                              &tri->index_oid_len, NULL, 0, tri->indexes);
00991 }
00992 
01001 int
01002 netsnmp_check_getnext_reply(netsnmp_request_info *request,
01003                             oid * prefix,
01004                             size_t prefix_len,
01005                             netsnmp_variable_list * newvar,
01006                             netsnmp_variable_list ** outvar)
01007 {
01008     oid      myname[MAX_OID_LEN];
01009     size_t   myname_len;
01010 
01011     build_oid_noalloc(myname, MAX_OID_LEN, &myname_len,
01012                       prefix, prefix_len, newvar);
01013     /*
01014      * is the build of the new indexes less than our current result 
01015      */
01016     if ((!(*outvar) || snmp_oid_compare(myname + prefix_len,
01017                                         myname_len - prefix_len,
01018                                         (*outvar)->name + prefix_len,
01019                                         (*outvar)->name_length -
01020                                         prefix_len) < 0)) {
01021         /*
01022          * and greater than the requested oid 
01023          */
01024         if (snmp_oid_compare(myname, myname_len,
01025                              request->requestvb->name,
01026                              request->requestvb->name_length) > 0) {
01027             /*
01028              * the new result must be better than the old 
01029              */
01030 #ifdef ONLY_WORKS_WITH_ONE_VARBIND
01031             if (!*outvar)
01032                 *outvar = snmp_clone_varbind(newvar);
01033             else
01034                 /* 
01035                  * TODO: walk the full varbind list, setting
01036                  *       *all* the values - not just the first.
01037                  */
01038                 snmp_set_var_typed_value(*outvar, newvar->type,
01039                                 newvar->val.string, newvar->val_len);
01040 #else  /* Interim replacement approach - less efficient, but it works! */
01041             if (*outvar)
01042                 snmp_free_varbind(*outvar);
01043             *outvar = snmp_clone_varbind(newvar);
01044 #endif
01045             snmp_set_var_objid(*outvar, myname, myname_len);
01046 
01047             return 1;
01048         }
01049     }
01050     return 0;
01051 }
01052 
01053 netsnmp_table_registration_info *
01054 netsnmp_table_registration_info_clone(netsnmp_table_registration_info *tri)
01055 {
01056     netsnmp_table_registration_info *copy;
01057     copy = malloc(sizeof(*copy));
01058     if (copy) {
01059         *copy = *tri;
01060         copy->indexes = snmp_clone_varbind(tri->indexes);
01061         if (!copy->indexes) {
01062             free(copy);
01063             copy = NULL;
01064         }
01065     }
01066     return copy;
01067 }
01068 
01069 void
01070 netsnmp_table_registration_info_free(netsnmp_table_registration_info *tri)
01071 {
01072     if (NULL == tri)
01073         return;
01074 
01075     if (NULL != tri->indexes)
01076         snmp_free_varbind(tri->indexes);
01077 
01078 #if 0
01079     /*
01080      * sigh... example use of valid_columns points to static memory,
01081      * so freeing it would be bad... we'll just have to live with any
01082      * leaks, for now...
01083      */
01084 #endif
01085 
01086     free(tri);
01087 }
01088 
01091 /*
01092  * internal routines 
01093  */
01094 void
01095 table_data_free_func(void *data)
01096 {
01097     netsnmp_table_request_info *info = (netsnmp_table_request_info *) data;
01098     if (!info)
01099         return;
01100     snmp_free_varbind(info->indexes);
01101     free(info);
01102 }
01103 
01104 
01105 
01106 static void
01107 table_helper_cleanup(netsnmp_agent_request_info *reqinfo,
01108                      netsnmp_request_info *request, int status)
01109 {
01110     netsnmp_set_request_error(reqinfo, request, status);
01111     netsnmp_free_request_data_sets(request);
01112     if (!request)
01113         return;
01114     request->parent_data = NULL;
01115 }
01116 
01117 
01118 /*
01119  * find the closest column to current (which may be current).
01120  *
01121  * called when a table runs out of rows for column X. This
01122  * function is called with current = X + 1, to verify that
01123  * X + 1 is a valid column, or find the next closest column if not.
01124  *
01125  * All list types should be sorted, lowest to highest.
01126  */
01127 unsigned int
01128 netsnmp_closest_column(unsigned int current,
01129                        netsnmp_column_info *valid_columns)
01130 {
01131     unsigned int    closest = 0;
01132     int             idx;
01133 
01134     if (valid_columns == NULL)
01135         return 0;
01136 
01137     for( ; valid_columns; valid_columns = valid_columns->next) {
01138 
01139         if (valid_columns->isRange) {
01140             /*
01141              * if current < low range, it might be closest.
01142              * otherwise, if it's < high range, current is in
01143              * the range, and thus is an exact match.
01144              */
01145             if (current < valid_columns->details.range[0]) {
01146                 if ( (valid_columns->details.range[0] < closest) ||
01147                      (0 == closest)) {
01148                     closest = valid_columns->details.range[0];
01149                 }
01150             } else if (current <= valid_columns->details.range[1]) {
01151                 closest = current;
01152                 break;       /* can not get any closer! */
01153             }
01154 
01155         } /* range */
01156         else {                  /* list */
01157             /*
01158              * if current < first item, no need to iterate over list.
01159              * that item is either closest, or not.
01160              */
01161             if (current < valid_columns->details.list[0]) {
01162                 if ((valid_columns->details.list[0] < closest) ||
01163                     (0 == closest))
01164                     closest = valid_columns->details.list[0];
01165                 continue;
01166             }
01167 
01169             if (current >
01170                 valid_columns->details.list[(int)valid_columns->list_count - 1])
01171                 continue;       /* not in list range. */
01172 
01174             for (idx = 0; valid_columns->details.list[idx] < current; ++idx)
01175                 ;
01176             
01178             if (current == valid_columns->details.list[idx]) {
01179                 closest = current;
01180                 break;      /* can not get any closer! */
01181             }
01182             
01184             if ((valid_columns->details.list[idx] < closest) ||
01185                 (0 == closest))
01186                 closest = valid_columns->details.list[idx];
01187 
01188         }                       /* list */
01189     }                           /* for */
01190 
01191     return closest;
01192 }
01193 
01213 void
01214 netsnmp_table_helper_add_indexes(netsnmp_table_registration_info *tinfo,
01215                                  ...)
01216 {
01217     va_list         debugargs;
01218     int             type;
01219 
01220     va_start(debugargs, tinfo);
01221     while ((type = va_arg(debugargs, int)) != 0) {
01222         netsnmp_table_helper_add_index(tinfo, type);
01223     }
01224     va_end(debugargs);
01225 }
01226 
01227 #ifndef NETSNMP_NO_WRITE_SUPPORT
01228 static void
01229 _row_stash_data_list_free(void *ptr) {
01230     netsnmp_oid_stash_node **tmp = (netsnmp_oid_stash_node **)ptr;
01231     netsnmp_oid_stash_free(tmp, NULL);
01232     free(ptr);
01233 }
01234 
01235 #ifndef NETSNMP_FEATURE_REMOVE_TABLE_GET_OR_CREATE_ROW_STASH
01236 
01238 netsnmp_oid_stash_node **
01239 netsnmp_table_get_or_create_row_stash(netsnmp_agent_request_info *reqinfo,
01240                                       const u_char * storage_name)
01241 {
01242     netsnmp_oid_stash_node **stashp = NULL;
01243     stashp = (netsnmp_oid_stash_node **)
01244         netsnmp_agent_get_list_data(reqinfo, (const char *) storage_name);
01245 
01246     if (!stashp) {
01247         /*
01248          * hasn't be created yet.  we create it here. 
01249          */
01250         stashp = SNMP_MALLOC_TYPEDEF(netsnmp_oid_stash_node *);
01251 
01252         if (!stashp)
01253             return NULL;        /* ack. out of mem */
01254 
01255         netsnmp_agent_add_list_data(reqinfo,
01256                                     netsnmp_create_data_list((const char *) storage_name,
01257                                                              stashp,
01258                                                              _row_stash_data_list_free));
01259     }
01260     return stashp;
01261 }
01262 #endif /* NETSNMP_FEATURE_REMOVE_TABLE_GET_OR_CREATE_ROW_STASH */
01263 #endif /* NETSNMP_NO_WRITE_SUPPORT */
01264 
01265 /*
01266  * advance the table info colnum to the next column, or 0 if there are no more
01267  *
01268  * @return new column, or 0 if there are no more
01269  */
01270 unsigned int
01271 netsnmp_table_next_column(netsnmp_table_request_info *table_info)
01272 {
01273     if (NULL == table_info)
01274         return 0;
01275 
01276     /*
01277      * try and validate next column
01278      */
01279     if (table_info->reg_info->valid_columns)
01280         return netsnmp_closest_column(table_info->colnum + 1,
01281                                       table_info->reg_info->valid_columns);
01282     
01283     /*
01284      * can't validate. assume 1..max_column are valid
01285      */
01286     if (table_info->colnum < table_info->reg_info->max_column)
01287         return table_info->colnum + 1;
01288     
01289     return 0; /* out of range */
01290 }