net-snmp 5.7
|
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 }