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