00001 /* 00002 * snmp_client.c - a toolkit of common functions for an SNMP client. 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 Copyright 1988, 1989, 1991, 1992 by Carnegie Mellon University 00011 00012 All Rights Reserved 00013 00014 Permission to use, copy, modify, and distribute this software and its 00015 documentation for any purpose and without fee is hereby granted, 00016 provided that the above copyright notice appear in all copies and that 00017 both that copyright notice and this permission notice appear in 00018 supporting documentation, and that the name of CMU not be 00019 used in advertising or publicity pertaining to distribution of the 00020 software without specific, written prior permission. 00021 00022 CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 00023 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 00024 CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 00025 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 00026 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 00027 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 00028 SOFTWARE. 00029 ******************************************************************/ 00030 /* 00031 * Portions of this file are copyrighted by: 00032 * Copyright © 2003 Sun Microsystems, Inc. All rights reserved. 00033 * Use is subject to license terms specified in the COPYING file 00034 * distributed with the Net-SNMP package. 00035 */ 00036 00042 #include <net-snmp/net-snmp-config.h> 00043 00044 #include <stdio.h> 00045 #include <errno.h> 00046 #if HAVE_STDLIB_H 00047 #include <stdlib.h> 00048 #endif 00049 #if HAVE_STRING_H 00050 #include <string.h> 00051 #else 00052 #include <strings.h> 00053 #endif 00054 #if HAVE_UNISTD_H 00055 #include <unistd.h> 00056 #endif 00057 #include <sys/types.h> 00058 #if TIME_WITH_SYS_TIME 00059 # ifdef WIN32 00060 # include <sys/timeb.h> 00061 # else 00062 # include <sys/time.h> 00063 # endif 00064 # include <time.h> 00065 #else 00066 # if HAVE_SYS_TIME_H 00067 # include <sys/time.h> 00068 # else 00069 # include <time.h> 00070 # endif 00071 #endif 00072 #if HAVE_SYS_PARAM_H 00073 #include <sys/param.h> 00074 #endif 00075 #if HAVE_NETINET_IN_H 00076 #include <netinet/in.h> 00077 #endif 00078 #if HAVE_ARPA_INET_H 00079 #include <arpa/inet.h> 00080 #endif 00081 #if HAVE_SYS_SELECT_H 00082 #include <sys/select.h> 00083 #endif 00084 #if HAVE_SYSLOG_H 00085 #include <syslog.h> 00086 #endif 00087 00088 #if HAVE_DMALLOC_H 00089 #include <dmalloc.h> 00090 #endif 00091 00092 #if HAVE_WINSOCK_H 00093 #include <winsock.h> 00094 #endif 00095 00096 #include <net-snmp/types.h> 00097 00098 #include <net-snmp/library/snmp_api.h> 00099 #include <net-snmp/library/snmp_client.h> 00100 #include <net-snmp/library/snmp_secmod.h> 00101 #include <net-snmp/library/mib.h> 00102 #include <net-snmp/library/snmp_logging.h> 00103 #include <net-snmp/library/snmp_assert.h> 00104 00105 00106 #ifndef BSD4_3 00107 #define BSD4_2 00108 #endif 00109 00110 #ifndef FD_SET 00111 00112 typedef long fd_mask; 00113 #define NFDBITS (sizeof(fd_mask) * NBBY) /* bits per mask */ 00114 00115 #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) 00116 #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) 00117 #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) 00118 #define FD_ZERO(p) memset((p), 0, sizeof(*(p))) 00119 #endif 00120 00121 /* 00122 * Prototype definitions 00123 */ 00124 static int snmp_synch_input(int op, netsnmp_session * session, 00125 int reqid, netsnmp_pdu *pdu, void *magic); 00126 00127 netsnmp_pdu * 00128 snmp_pdu_create(int command) 00129 { 00130 netsnmp_pdu *pdu; 00131 00132 pdu = (netsnmp_pdu *) calloc(1, sizeof(netsnmp_pdu)); 00133 if (pdu) { 00134 pdu->version = SNMP_DEFAULT_VERSION; 00135 pdu->command = command; 00136 pdu->errstat = SNMP_DEFAULT_ERRSTAT; 00137 pdu->errindex = SNMP_DEFAULT_ERRINDEX; 00138 pdu->securityModel = SNMP_DEFAULT_SECMODEL; 00139 pdu->transport_data = NULL; 00140 pdu->transport_data_length = 0; 00141 pdu->securityNameLen = 0; 00142 pdu->contextNameLen = 0; 00143 pdu->time = 0; 00144 pdu->reqid = snmp_get_next_reqid(); 00145 pdu->msgid = snmp_get_next_msgid(); 00146 } 00147 return pdu; 00148 00149 } 00150 00151 00152 /* 00153 * Add a null variable with the requested name to the end of the list of 00154 * variables for this pdu. 00155 */ 00156 netsnmp_variable_list * 00157 snmp_add_null_var(netsnmp_pdu *pdu, const oid * name, size_t name_length) 00158 { 00159 return snmp_pdu_add_variable(pdu, name, name_length, ASN_NULL, NULL, 0); 00160 } 00161 00162 00163 #include <net-snmp/library/snmp_debug.h> 00164 static int 00165 snmp_synch_input(int op, 00166 netsnmp_session * session, 00167 int reqid, netsnmp_pdu *pdu, void *magic) 00168 { 00169 struct synch_state *state = (struct synch_state *) magic; 00170 int rpt_type; 00171 00172 if (reqid != state->reqid && pdu && pdu->command != SNMP_MSG_REPORT) { 00173 DEBUGMSGTL(("snmp_synch", "Unexpected response (ReqID: %d,%d - Cmd %d)\n", 00174 reqid, state->reqid, pdu->command )); 00175 return 0; 00176 } 00177 00178 state->waiting = 0; 00179 DEBUGMSGTL(("snmp_synch", "Response (ReqID: %d - Cmd %d)\n", 00180 reqid, (pdu ? pdu->command : -1))); 00181 00182 if (op == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE && pdu) { 00183 if (pdu->command == SNMP_MSG_REPORT) { 00184 rpt_type = snmpv3_get_report_type(pdu); 00185 if (SNMPV3_IGNORE_UNAUTH_REPORTS || 00186 rpt_type == SNMPERR_NOT_IN_TIME_WINDOW) { 00187 state->waiting = 1; 00188 } 00189 state->pdu = NULL; 00190 state->status = STAT_ERROR; 00191 session->s_snmp_errno = rpt_type; 00192 SET_SNMP_ERROR(rpt_type); 00193 } else if (pdu->command == SNMP_MSG_RESPONSE) { 00194 /* 00195 * clone the pdu to return to snmp_synch_response 00196 */ 00197 state->pdu = snmp_clone_pdu(pdu); 00198 state->status = STAT_SUCCESS; 00199 session->s_snmp_errno = SNMPERR_SUCCESS; 00200 } 00201 else { 00202 char msg_buf[50]; 00203 state->status = STAT_ERROR; 00204 session->s_snmp_errno = SNMPERR_PROTOCOL; 00205 SET_SNMP_ERROR(SNMPERR_PROTOCOL); 00206 snprintf(msg_buf, sizeof(msg_buf), "Expected RESPONSE-PDU but got %s-PDU", 00207 snmp_pdu_type(pdu->command)); 00208 snmp_set_detail(msg_buf); 00209 return 0; 00210 } 00211 } else if (op == NETSNMP_CALLBACK_OP_TIMED_OUT) { 00212 state->pdu = NULL; 00213 state->status = STAT_TIMEOUT; 00214 session->s_snmp_errno = SNMPERR_TIMEOUT; 00215 SET_SNMP_ERROR(SNMPERR_TIMEOUT); 00216 } else if (op == NETSNMP_CALLBACK_OP_DISCONNECT) { 00217 state->pdu = NULL; 00218 state->status = STAT_ERROR; 00219 session->s_snmp_errno = SNMPERR_ABORT; 00220 SET_SNMP_ERROR(SNMPERR_ABORT); 00221 } 00222 00223 return 1; 00224 } 00225 00226 00227 /* 00228 * Clone an SNMP variable data structure. 00229 * Sets pointers to structure private storage, or 00230 * allocates larger object identifiers and values as needed. 00231 * 00232 * Caller must make list association for cloned variable. 00233 * 00234 * Returns 0 if successful. 00235 */ 00236 int 00237 snmp_clone_var(netsnmp_variable_list * var, netsnmp_variable_list * newvar) 00238 { 00239 if (!newvar || !var) 00240 return 1; 00241 00242 memmove(newvar, var, sizeof(netsnmp_variable_list)); 00243 newvar->next_variable = NULL; 00244 newvar->name = NULL; 00245 newvar->val.string = NULL; 00246 newvar->data = NULL; 00247 newvar->dataFreeHook = NULL; 00248 newvar->index = 0; 00249 00250 /* 00251 * Clone the object identifier and the value. 00252 * Allocate memory iff original will not fit into local storage. 00253 */ 00254 if (snmp_set_var_objid(newvar, var->name, var->name_length)) 00255 return 1; 00256 00257 /* 00258 * need a pointer to copy a string value. 00259 */ 00260 if (var->val.string) { 00261 if (var->val.string != &var->buf[0]) { 00262 if (var->val_len <= sizeof(var->buf)) 00263 newvar->val.string = newvar->buf; 00264 else { 00265 newvar->val.string = (u_char *) malloc(var->val_len); 00266 if (!newvar->val.string) 00267 return 1; 00268 } 00269 memmove(newvar->val.string, var->val.string, var->val_len); 00270 } else { /* fix the pointer to new local store */ 00271 newvar->val.string = newvar->buf; 00272 /* 00273 * no need for a memmove, since we copied the whole var 00274 * struct (and thus var->buf) at the beginning of this function. 00275 */ 00276 } 00277 } else { 00278 newvar->val.string = NULL; 00279 newvar->val_len = 0; 00280 } 00281 00282 return 0; 00283 } 00284 00285 00286 /* 00287 * Possibly make a copy of source memory buffer. 00288 * Will reset destination pointer if source pointer is NULL. 00289 * Returns 0 if successful, 1 if memory allocation fails. 00290 */ 00291 int 00292 snmp_clone_mem(void **dstPtr, const void *srcPtr, unsigned len) 00293 { 00294 *dstPtr = NULL; 00295 if (srcPtr) { 00296 *dstPtr = malloc(len + 1); 00297 if (!*dstPtr) { 00298 return 1; 00299 } 00300 memmove(*dstPtr, srcPtr, len); 00301 /* 00302 * this is for those routines that expect 0-terminated strings!!! 00303 * someone should rather have called strdup 00304 */ 00305 ((char *) *dstPtr)[len] = 0; 00306 } 00307 return 0; 00308 } 00309 00310 00311 /* 00312 * Walks through a list of varbinds and frees and allocated memory, 00313 * restoring pointers to local buffers 00314 */ 00315 void 00316 snmp_reset_var_buffers(netsnmp_variable_list * var) 00317 { 00318 while (var) { 00319 if (var->name != var->name_loc) { 00320 if(NULL != var->name) 00321 free(var->name); 00322 var->name = var->name_loc; 00323 var->name_length = 0; 00324 } 00325 if (var->val.string != var->buf) { 00326 if (NULL != var->val.string) 00327 free(var->val.string); 00328 var->val.string = var->buf; 00329 var->val_len = 0; 00330 } 00331 var = var->next_variable; 00332 } 00333 } 00334 00335 /* 00336 * Creates and allocates a clone of the input PDU, 00337 * but does NOT copy the variables. 00338 * This function should be used with another function, 00339 * such as _copy_pdu_vars. 00340 * 00341 * Returns a pointer to the cloned PDU if successful. 00342 * Returns 0 if failure. 00343 */ 00344 static 00345 netsnmp_pdu * 00346 _clone_pdu_header(netsnmp_pdu *pdu) 00347 { 00348 netsnmp_pdu *newpdu; 00349 struct snmp_secmod_def *sptr; 00350 00351 newpdu = (netsnmp_pdu *) malloc(sizeof(netsnmp_pdu)); 00352 if (!newpdu) 00353 return NULL; 00354 memmove(newpdu, pdu, sizeof(netsnmp_pdu)); 00355 00356 /* 00357 * reset copied pointers if copy fails 00358 */ 00359 newpdu->variables = NULL; 00360 newpdu->enterprise = NULL; 00361 newpdu->community = NULL; 00362 newpdu->securityEngineID = NULL; 00363 newpdu->securityName = NULL; 00364 newpdu->contextEngineID = NULL; 00365 newpdu->contextName = NULL; 00366 newpdu->transport_data = NULL; 00367 00368 /* 00369 * copy buffers individually. If any copy fails, all are freed. 00370 */ 00371 if (snmp_clone_mem((void **) &newpdu->enterprise, pdu->enterprise, 00372 sizeof(oid) * pdu->enterprise_length) || 00373 snmp_clone_mem((void **) &newpdu->community, pdu->community, 00374 pdu->community_len) || 00375 snmp_clone_mem((void **) &newpdu->contextEngineID, 00376 pdu->contextEngineID, pdu->contextEngineIDLen) 00377 || snmp_clone_mem((void **) &newpdu->securityEngineID, 00378 pdu->securityEngineID, pdu->securityEngineIDLen) 00379 || snmp_clone_mem((void **) &newpdu->contextName, pdu->contextName, 00380 pdu->contextNameLen) 00381 || snmp_clone_mem((void **) &newpdu->securityName, 00382 pdu->securityName, pdu->securityNameLen) 00383 || snmp_clone_mem((void **) &newpdu->transport_data, 00384 pdu->transport_data, 00385 pdu->transport_data_length)) { 00386 snmp_free_pdu(newpdu); 00387 return NULL; 00388 } 00389 if ((sptr = find_sec_mod(newpdu->securityModel)) != NULL && 00390 sptr->pdu_clone != NULL) { 00391 /* 00392 * call security model if it needs to know about this 00393 */ 00394 (*sptr->pdu_clone) (pdu, newpdu); 00395 } 00396 00397 return newpdu; 00398 } 00399 00400 static 00401 netsnmp_variable_list * 00402 _copy_varlist(netsnmp_variable_list * var, /* source varList */ 00403 int errindex, /* index of variable to drop (if any) */ 00404 int copy_count) 00405 { /* !=0 number variables to copy */ 00406 netsnmp_variable_list *newhead, *newvar, *oldvar; 00407 int ii = 0; 00408 00409 newhead = NULL; 00410 oldvar = NULL; 00411 00412 while (var && (copy_count-- > 0)) { 00413 /* 00414 * Drop the specified variable (if applicable) 00415 */ 00416 if (++ii == errindex) { 00417 var = var->next_variable; 00418 continue; 00419 } 00420 00421 /* 00422 * clone the next variable. Cleanup if alloc fails 00423 */ 00424 newvar = (netsnmp_variable_list *) 00425 malloc(sizeof(netsnmp_variable_list)); 00426 if (snmp_clone_var(var, newvar)) { 00427 if (newvar) 00428 free((char *) newvar); 00429 snmp_free_varbind(newhead); 00430 return NULL; 00431 } 00432 00433 /* 00434 * add cloned variable to new list 00435 */ 00436 if (NULL == newhead) 00437 newhead = newvar; 00438 if (oldvar) 00439 oldvar->next_variable = newvar; 00440 oldvar = newvar; 00441 00442 var = var->next_variable; 00443 } 00444 return newhead; 00445 } 00446 00447 00448 /* 00449 * Copy some or all variables from source PDU to target PDU. 00450 * This function consolidates many of the needs of PDU variables: 00451 * Clone PDU : copy all the variables. 00452 * Split PDU : skip over some variables to copy other variables. 00453 * Fix PDU : remove variable associated with error index. 00454 * 00455 * Designed to work with _clone_pdu_header. 00456 * 00457 * If drop_err is set, drop any variable associated with errindex. 00458 * If skip_count is set, skip the number of variable in pdu's list. 00459 * While copy_count is greater than zero, copy pdu variables to newpdu. 00460 * 00461 * If an error occurs, newpdu is freed and pointer is set to 0. 00462 * 00463 * Returns a pointer to the cloned PDU if successful. 00464 * Returns 0 if failure. 00465 */ 00466 static 00467 netsnmp_pdu * 00468 _copy_pdu_vars(netsnmp_pdu *pdu, /* source PDU */ 00469 netsnmp_pdu *newpdu, /* target PDU */ 00470 int drop_err, /* !=0 drop errored variable */ 00471 int skip_count, /* !=0 number of variables to skip */ 00472 int copy_count) 00473 { /* !=0 number of variables to copy */ 00474 netsnmp_variable_list *var, *oldvar; 00475 int ii, copied, drop_idx; 00476 00477 if (!newpdu) 00478 return NULL; /* where is PDU to copy to ? */ 00479 00480 if (drop_err) 00481 drop_idx = pdu->errindex - skip_count; 00482 else 00483 drop_idx = 0; 00484 00485 var = pdu->variables; 00486 while (var && (skip_count-- > 0)) /* skip over pdu variables */ 00487 var = var->next_variable; 00488 00489 oldvar = NULL; 00490 ii = 0; 00491 copied = 0; 00492 if (pdu->flags & UCD_MSG_FLAG_FORCE_PDU_COPY) 00493 copied = 1; /* We're interested in 'empty' responses too */ 00494 00495 newpdu->variables = _copy_varlist(var, drop_idx, copy_count); 00496 if (newpdu->variables) 00497 copied = 1; 00498 00499 #if ALSO_TEMPORARILY_DISABLED 00500 /* 00501 * Error if bad errindex or if target PDU has no variables copied 00502 */ 00503 if ((drop_err && (ii < pdu->errindex)) 00504 #if TEMPORARILY_DISABLED 00505 /* 00506 * SNMPv3 engineID probes are allowed to be empty. 00507 * See the comment in snmp_api.c for further details 00508 */ 00509 || copied == 0 00510 #endif 00511 ) { 00512 snmp_free_pdu(newpdu); 00513 return 0; 00514 } 00515 #endif 00516 return newpdu; 00517 } 00518 00519 00520 /* 00521 * Creates (allocates and copies) a clone of the input PDU. 00522 * If drop_err is set, don't copy any variable associated with errindex. 00523 * This function is called by snmp_clone_pdu and snmp_fix_pdu. 00524 * 00525 * Returns a pointer to the cloned PDU if successful. 00526 * Returns 0 if failure. 00527 */ 00528 static 00529 netsnmp_pdu * 00530 _clone_pdu(netsnmp_pdu *pdu, int drop_err) 00531 { 00532 netsnmp_pdu *newpdu; 00533 newpdu = _clone_pdu_header(pdu); 00534 newpdu = _copy_pdu_vars(pdu, newpdu, drop_err, 0, 10000); /* skip none, copy all */ 00535 00536 return newpdu; 00537 } 00538 00539 00540 /* 00541 * This function will clone a full varbind list 00542 * 00543 * Returns a pointer to the cloned PDU if successful. 00544 * Returns 0 if failure 00545 */ 00546 netsnmp_variable_list * 00547 snmp_clone_varbind(netsnmp_variable_list * varlist) 00548 { 00549 return _copy_varlist(varlist, 0, 10000); /* skip none, copy all */ 00550 } 00551 00552 /* 00553 * This function will clone a PDU including all of its variables. 00554 * 00555 * Returns a pointer to the cloned PDU if successful. 00556 * Returns 0 if failure 00557 */ 00558 netsnmp_pdu * 00559 snmp_clone_pdu(netsnmp_pdu *pdu) 00560 { 00561 return _clone_pdu(pdu, 0); /* copies all variables */ 00562 } 00563 00564 00565 /* 00566 * This function will clone a PDU including some of its variables. 00567 * 00568 * If skip_count is not zero, it defines the number of variables to skip. 00569 * If copy_count is not zero, it defines the number of variables to copy. 00570 * 00571 * Returns a pointer to the cloned PDU if successful. 00572 * Returns 0 if failure. 00573 */ 00574 netsnmp_pdu * 00575 snmp_split_pdu(netsnmp_pdu *pdu, int skip_count, int copy_count) 00576 { 00577 netsnmp_pdu *newpdu; 00578 newpdu = _clone_pdu_header(pdu); 00579 newpdu = _copy_pdu_vars(pdu, newpdu, 0, /* don't drop any variables */ 00580 skip_count, copy_count); 00581 00582 return newpdu; 00583 } 00584 00585 00586 /* 00587 * If there was an error in the input pdu, creates a clone of the pdu 00588 * that includes all the variables except the one marked by the errindex. 00589 * The command is set to the input command and the reqid, errstat, and 00590 * errindex are set to default values. 00591 * If the error status didn't indicate an error, the error index didn't 00592 * indicate a variable, the pdu wasn't a get response message, the 00593 * marked variable was not present in the initial request, or there 00594 * would be no remaining variables, this function will return 0. 00595 * If everything was successful, a pointer to the fixed cloned pdu will 00596 * be returned. 00597 */ 00598 netsnmp_pdu * 00599 snmp_fix_pdu(netsnmp_pdu *pdu, int command) 00600 { 00601 netsnmp_pdu *newpdu; 00602 00603 if ((pdu->command != SNMP_MSG_RESPONSE) 00604 || (pdu->errstat == SNMP_ERR_NOERROR) 00605 || (NULL == pdu->variables) 00606 || (pdu->errindex > snmp_varbind_len(pdu)) 00607 || (pdu->errindex <= 0)) { 00608 return NULL; /* pre-condition tests fail */ 00609 } 00610 00611 newpdu = _clone_pdu(pdu, 1); /* copies all except errored variable */ 00612 if (!newpdu) 00613 return NULL; 00614 if (!newpdu->variables) { 00615 snmp_free_pdu(newpdu); 00616 return NULL; /* no variables. "should not happen" */ 00617 } 00618 newpdu->command = command; 00619 newpdu->reqid = snmp_get_next_reqid(); 00620 newpdu->msgid = snmp_get_next_msgid(); 00621 newpdu->errstat = SNMP_DEFAULT_ERRSTAT; 00622 newpdu->errindex = SNMP_DEFAULT_ERRINDEX; 00623 00624 return newpdu; 00625 } 00626 00627 00628 /* 00629 * Returns the number of variables bound to a PDU structure 00630 */ 00631 unsigned long 00632 snmp_varbind_len(netsnmp_pdu *pdu) 00633 { 00634 register netsnmp_variable_list *vars; 00635 unsigned long retVal = 0; 00636 if (pdu) 00637 for (vars = pdu->variables; vars; vars = vars->next_variable) { 00638 retVal++; 00639 } 00640 00641 return retVal; 00642 } 00643 00644 /* 00645 * Add object identifier name to SNMP variable. 00646 * If the name is large, additional memory is allocated. 00647 * Returns 0 if successful. 00648 */ 00649 00650 int 00651 snmp_set_var_objid(netsnmp_variable_list * vp, 00652 const oid * objid, size_t name_length) 00653 { 00654 size_t len = sizeof(oid) * name_length; 00655 00656 if (vp->name != vp->name_loc && vp->name != NULL && 00657 vp->name_length > (sizeof(vp->name_loc) / sizeof(oid))) { 00658 /* 00659 * Probably previously-allocated "big storage". Better free it 00660 * else memory leaks possible. 00661 */ 00662 free(vp->name); 00663 } 00664 00665 /* 00666 * use built-in storage for smaller values 00667 */ 00668 if (len <= sizeof(vp->name_loc)) { 00669 vp->name = vp->name_loc; 00670 } else { 00671 vp->name = (oid *) malloc(len); 00672 if (!vp->name) 00673 return 1; 00674 } 00675 if (objid) 00676 memmove(vp->name, objid, len); 00677 vp->name_length = name_length; 00678 return 0; 00679 } 00680 00696 int 00697 snmp_set_var_typed_value(netsnmp_variable_list * newvar, u_char type, 00698 const void * val_str, size_t val_len) 00699 { 00700 newvar->type = type; 00701 return snmp_set_var_value(newvar, val_str, val_len); 00702 } 00703 00704 int 00705 snmp_set_var_typed_integer(netsnmp_variable_list * newvar, 00706 u_char type, long val) 00707 { 00708 newvar->type = type; 00709 return snmp_set_var_value(newvar, &val, sizeof(long)); 00710 } 00711 00712 int 00713 count_varbinds(netsnmp_variable_list * var_ptr) 00714 { 00715 int count = 0; 00716 00717 for (; var_ptr != NULL; var_ptr = var_ptr->next_variable) 00718 count++; 00719 00720 return count; 00721 } 00722 00723 int 00724 count_varbinds_of_type(netsnmp_variable_list * var_ptr, u_char type) 00725 { 00726 int count = 0; 00727 00728 for (; var_ptr != NULL; var_ptr = var_ptr->next_variable) 00729 if (var_ptr->type == type) 00730 count++; 00731 00732 return count; 00733 } 00734 00735 netsnmp_variable_list * 00736 find_varbind_of_type(netsnmp_variable_list * var_ptr, u_char type) 00737 { 00738 for (; var_ptr != NULL && var_ptr->type != type; 00739 var_ptr = var_ptr->next_variable); 00740 00741 return var_ptr; 00742 } 00743 00744 netsnmp_variable_list* 00745 find_varbind_in_list( netsnmp_variable_list *vblist, 00746 oid *name, size_t len) 00747 { 00748 for (; vblist != NULL; vblist = vblist->next_variable) 00749 if (!snmp_oid_compare(vblist->name, vblist->name_length, name, len)) 00750 return vblist; 00751 00752 return NULL; 00753 } 00754 00755 /* 00756 * Add some value to SNMP variable. 00757 * If the value is large, additional memory is allocated. 00758 * Returns 0 if successful. 00759 */ 00760 00761 int 00762 snmp_set_var_value(netsnmp_variable_list * vars, 00763 const void * value, size_t len) 00764 { 00765 int largeval = 1; 00766 00767 /* 00768 * xxx-rks: why the unconditional free? why not use existing 00769 * memory, if len < vars->val_len ? 00770 */ 00771 if (vars->val.string && vars->val.string != vars->buf) { 00772 free(vars->val.string); 00773 } 00774 vars->val.string = NULL; 00775 vars->val_len = 0; 00776 00777 /* 00778 * use built-in storage for smaller values 00779 */ 00780 if (len <= sizeof(vars->buf)) { 00781 vars->val.string = (u_char *) vars->buf; 00782 largeval = 0; 00783 } 00784 00785 if ((0 == len) || (NULL == value)) { 00786 vars->val.string[0] = 0; 00787 return 0; 00788 } 00789 00790 vars->val_len = len; 00791 switch (vars->type) { 00792 case ASN_INTEGER: 00793 case ASN_UNSIGNED: 00794 case ASN_TIMETICKS: 00795 case ASN_COUNTER: 00796 if (value) { 00797 if (vars->val_len == sizeof(int)) { 00798 if (ASN_INTEGER == vars->type) { 00799 const int *val_int 00800 = (const int *) value; 00801 *(vars->val.integer) = (long) *val_int; 00802 } else { 00803 const u_int *val_uint 00804 = (const u_int *) value; 00805 *(vars->val.integer) = (unsigned long) *val_uint; 00806 } 00807 } 00808 #if SIZEOF_LONG != SIZEOF_INT 00809 else if (vars->val_len == sizeof(long)){ 00810 const u_long *val_ulong 00811 = (const u_long *) value; 00812 *(vars->val.integer) = *val_ulong; 00813 if (*(vars->val.integer) > 0xffffffff) { 00814 snmp_log(LOG_ERR,"truncating integer value > 32 bits\n"); 00815 *(vars->val.integer) &= 0xffffffff; 00816 } 00817 } 00818 #endif 00819 #if defined(SIZEOF_LONG_LONG) && (SIZEOF_LONG != SIZEOF_LONG_LONG) 00820 #if !defined(SIZEOF_INTMAX_T) || (SIZEOF_LONG_LONG != SIZEOF_INTMAX_T) 00821 else if (vars->val_len == sizeof(long long)){ 00822 const unsigned long long *val_ullong 00823 = (const unsigned long long *) value; 00824 *(vars->val.integer) = (long) *val_ullong; 00825 if (*(vars->val.integer) > 0xffffffff) { 00826 snmp_log(LOG_ERR,"truncating integer value > 32 bits\n"); 00827 *(vars->val.integer) &= 0xffffffff; 00828 } 00829 } 00830 #endif 00831 #endif 00832 #if defined(SIZEOF_INTMAX_T) && (SIZEOF_LONG != SIZEOF_INTMAX_T) 00833 else if (vars->val_len == sizeof(intmax_t)){ 00834 const uintmax_t *val_uintmax_t 00835 = (const uintmax_t *) value; 00836 *(vars->val.integer) = (long) *val_uintmax_t; 00837 if (*(vars->val.integer) > 0xffffffff) { 00838 snmp_log(LOG_ERR,"truncating integer value > 32 bits\n"); 00839 *(vars->val.integer) &= 0xffffffff; 00840 } 00841 } 00842 #endif 00843 #if SIZEOF_SHORT != SIZEOF_INT 00844 else if (vars->val_len == sizeof(short)) { 00845 if (ASN_INTEGER == vars->type) { 00846 const short *val_short 00847 = (const short *) value; 00848 *(vars->val.integer) = (long) *val_short; 00849 } else { 00850 const u_short *val_ushort 00851 = (const u_short *) value; 00852 *(vars->val.integer) = (unsigned long) *val_ushort; 00853 } 00854 } 00855 #endif 00856 else if (vars->val_len == sizeof(char)) { 00857 if (ASN_INTEGER == vars->type) { 00858 const char *val_char 00859 = (const char *) value; 00860 *(vars->val.integer) = (long) *val_char; 00861 } else { 00862 const u_char *val_uchar 00863 = (const u_char *) value; 00864 *(vars->val.integer) = (unsigned long) *val_uchar; 00865 } 00866 } 00867 else { 00868 snmp_log(LOG_ERR,"bad size for integer-like type (%d)\n", 00869 (int)vars->val_len); 00870 return (1); 00871 } 00872 } else 00873 *(vars->val.integer) = 0; 00874 vars->val_len = sizeof(long); 00875 break; 00876 00877 case ASN_OBJECT_ID: 00878 case ASN_PRIV_IMPLIED_OBJECT_ID: 00879 case ASN_PRIV_INCL_RANGE: 00880 case ASN_PRIV_EXCL_RANGE: 00881 if (largeval) { 00882 vars->val.objid = (oid *) malloc(vars->val_len); 00883 } 00884 if (vars->val.objid == NULL) { 00885 snmp_log(LOG_ERR,"no storage for OID\n"); 00886 return 1; 00887 } 00888 memmove(vars->val.objid, value, vars->val_len); 00889 break; 00890 00891 case ASN_IPADDRESS: /* snmp_build_var_op treats IPADDR like a string */ 00892 if (4 != vars->val_len) { 00893 netsnmp_assert("ipaddress length == 4"); 00894 } 00896 case ASN_PRIV_IMPLIED_OCTET_STR: 00897 case ASN_OCTET_STR: 00898 case ASN_BIT_STR: 00899 case ASN_OPAQUE: 00900 case ASN_NSAP: 00901 if (vars->val_len >= sizeof(vars->buf)) { 00902 vars->val.string = (u_char *) malloc(vars->val_len + 1); 00903 } 00904 if (vars->val.string == NULL) { 00905 snmp_log(LOG_ERR,"no storage for string\n"); 00906 return 1; 00907 } 00908 memmove(vars->val.string, value, vars->val_len); 00909 /* 00910 * Make sure the string is zero-terminated; some bits of code make 00911 * this assumption. Easier to do this here than fix all these wrong 00912 * assumptions. 00913 */ 00914 vars->val.string[vars->val_len] = '\0'; 00915 break; 00916 00917 case SNMP_NOSUCHOBJECT: 00918 case SNMP_NOSUCHINSTANCE: 00919 case SNMP_ENDOFMIBVIEW: 00920 case ASN_NULL: 00921 vars->val_len = 0; 00922 vars->val.string = NULL; 00923 break; 00924 00925 #ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES 00926 case ASN_OPAQUE_U64: 00927 case ASN_OPAQUE_I64: 00928 #endif /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */ 00929 case ASN_COUNTER64: 00930 if (largeval) { 00931 snmp_log(LOG_ERR,"bad size for counter 64 (%d)\n", 00932 (int)vars->val_len); 00933 return (1); 00934 } 00935 vars->val_len = sizeof(struct counter64); 00936 memmove(vars->val.counter64, value, vars->val_len); 00937 break; 00938 00939 #ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES 00940 case ASN_OPAQUE_FLOAT: 00941 if (largeval) { 00942 snmp_log(LOG_ERR,"bad size for opaque float (%d)\n", 00943 (int)vars->val_len); 00944 return (1); 00945 } 00946 vars->val_len = sizeof(float); 00947 memmove(vars->val.floatVal, value, vars->val_len); 00948 break; 00949 00950 case ASN_OPAQUE_DOUBLE: 00951 if (largeval) { 00952 snmp_log(LOG_ERR,"bad size for opaque double (%d)\n", 00953 (int)vars->val_len); 00954 return (1); 00955 } 00956 vars->val_len = sizeof(double); 00957 memmove(vars->val.doubleVal, value, vars->val_len); 00958 break; 00959 00960 #endif /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */ 00961 00962 default: 00963 snmp_log(LOG_ERR,"Internal error in type switching\n"); 00964 snmp_set_detail("Internal error in type switching\n"); 00965 return (1); 00966 } 00967 00968 return 0; 00969 } 00970 00971 void 00972 snmp_replace_var_types(netsnmp_variable_list * vbl, u_char old_type, 00973 u_char new_type) 00974 { 00975 while (vbl) { 00976 if (vbl->type == old_type) { 00977 snmp_set_var_typed_value(vbl, new_type, NULL, 0); 00978 } 00979 vbl = vbl->next_variable; 00980 } 00981 } 00982 00983 void 00984 snmp_reset_var_types(netsnmp_variable_list * vbl, u_char new_type) 00985 { 00986 while (vbl) { 00987 snmp_set_var_typed_value(vbl, new_type, NULL, 0); 00988 vbl = vbl->next_variable; 00989 } 00990 } 00991 00992 int 00993 snmp_synch_response_cb(netsnmp_session * ss, 00994 netsnmp_pdu *pdu, 00995 netsnmp_pdu **response, snmp_callback pcb) 00996 { 00997 struct synch_state lstate, *state; 00998 snmp_callback cbsav; 00999 void *cbmagsav; 01000 int numfds, count; 01001 fd_set fdset; 01002 struct timeval timeout, *tvp; 01003 int block; 01004 01005 memset((void *) &lstate, 0, sizeof(lstate)); 01006 state = &lstate; 01007 cbsav = ss->callback; 01008 cbmagsav = ss->callback_magic; 01009 ss->callback = pcb; 01010 ss->callback_magic = (void *) state; 01011 01012 if ((state->reqid = snmp_send(ss, pdu)) == 0) { 01013 snmp_free_pdu(pdu); 01014 state->status = STAT_ERROR; 01015 } else 01016 state->waiting = 1; 01017 01018 while (state->waiting) { 01019 numfds = 0; 01020 FD_ZERO(&fdset); 01021 block = NETSNMP_SNMPBLOCK; 01022 tvp = &timeout; 01023 timerclear(tvp); 01024 snmp_select_info(&numfds, &fdset, tvp, &block); 01025 if (block == 1) 01026 tvp = NULL; /* block without timeout */ 01027 count = select(numfds, &fdset, NULL, NULL, tvp); 01028 if (count > 0) { 01029 snmp_read(&fdset); 01030 } else { 01031 switch (count) { 01032 case 0: 01033 snmp_timeout(); 01034 break; 01035 case -1: 01036 if (errno == EINTR) { 01037 continue; 01038 } else { 01039 snmp_errno = SNMPERR_GENERR; /*MTCRITICAL_RESOURCE */ 01040 /* 01041 * CAUTION! if another thread closed the socket(s) 01042 * waited on here, the session structure was freed. 01043 * It would be nice, but we can't rely on the pointer. 01044 * ss->s_snmp_errno = SNMPERR_GENERR; 01045 * ss->s_errno = errno; 01046 */ 01047 snmp_set_detail(strerror(errno)); 01048 } 01049 /* 01050 * FALLTHRU 01051 */ 01052 default: 01053 state->status = STAT_ERROR; 01054 state->waiting = 0; 01055 } 01056 } 01057 01058 if ( ss->flags & SNMP_FLAGS_RESP_CALLBACK ) { 01059 void (*cb)(void); 01060 cb = (void (*)(void))(ss->myvoid); 01061 cb(); /* Used to invoke 'netsnmp_check_outstanding_agent_requests();' 01062 on internal AgentX queries. */ 01063 } 01064 } 01065 *response = state->pdu; 01066 ss->callback = cbsav; 01067 ss->callback_magic = cbmagsav; 01068 return state->status; 01069 } 01070 01071 int 01072 snmp_synch_response(netsnmp_session * ss, 01073 netsnmp_pdu *pdu, netsnmp_pdu **response) 01074 { 01075 return snmp_synch_response_cb(ss, pdu, response, snmp_synch_input); 01076 } 01077 01078 int 01079 snmp_sess_synch_response(void *sessp, 01080 netsnmp_pdu *pdu, netsnmp_pdu **response) 01081 { 01082 netsnmp_session *ss; 01083 struct synch_state lstate, *state; 01084 snmp_callback cbsav; 01085 void *cbmagsav; 01086 int numfds, count; 01087 fd_set fdset; 01088 struct timeval timeout, *tvp; 01089 int block; 01090 01091 ss = snmp_sess_session(sessp); 01092 memset((void *) &lstate, 0, sizeof(lstate)); 01093 state = &lstate; 01094 cbsav = ss->callback; 01095 cbmagsav = ss->callback_magic; 01096 ss->callback = snmp_synch_input; 01097 ss->callback_magic = (void *) state; 01098 01099 if ((state->reqid = snmp_sess_send(sessp, pdu)) == 0) { 01100 snmp_free_pdu(pdu); 01101 state->status = STAT_ERROR; 01102 } else 01103 state->waiting = 1; 01104 01105 while (state->waiting) { 01106 numfds = 0; 01107 FD_ZERO(&fdset); 01108 block = NETSNMP_SNMPBLOCK; 01109 tvp = &timeout; 01110 timerclear(tvp); 01111 snmp_sess_select_info(sessp, &numfds, &fdset, tvp, &block); 01112 if (block == 1) 01113 tvp = NULL; /* block without timeout */ 01114 count = select(numfds, &fdset, NULL, NULL, tvp); 01115 if (count > 0) { 01116 snmp_sess_read(sessp, &fdset); 01117 } else 01118 switch (count) { 01119 case 0: 01120 snmp_sess_timeout(sessp); 01121 break; 01122 case -1: 01123 if (errno == EINTR) { 01124 continue; 01125 } else { 01126 snmp_errno = SNMPERR_GENERR; /*MTCRITICAL_RESOURCE */ 01127 /* 01128 * CAUTION! if another thread closed the socket(s) 01129 * waited on here, the session structure was freed. 01130 * It would be nice, but we can't rely on the pointer. 01131 * ss->s_snmp_errno = SNMPERR_GENERR; 01132 * ss->s_errno = errno; 01133 */ 01134 snmp_set_detail(strerror(errno)); 01135 } 01136 /* 01137 * FALLTHRU 01138 */ 01139 default: 01140 state->status = STAT_ERROR; 01141 state->waiting = 0; 01142 } 01143 } 01144 *response = state->pdu; 01145 ss->callback = cbsav; 01146 ss->callback_magic = cbmagsav; 01147 return state->status; 01148 } 01149 01150 01151 const char * 01152 snmp_errstring(int errstat) 01153 { 01154 const char * const error_string[19] = { 01155 "(noError) No Error", 01156 "(tooBig) Response message would have been too large.", 01157 "(noSuchName) There is no such variable name in this MIB.", 01158 "(badValue) The value given has the wrong type or length.", 01159 "(readOnly) The two parties used do not have access to use the specified SNMP PDU.", 01160 "(genError) A general failure occured", 01161 "noAccess", 01162 "wrongType (The set datatype does not match the data type the agent expects)", 01163 "wrongLength (The set value has an illegal length from what the agent expects)", 01164 "wrongEncoding", 01165 "wrongValue (The set value is illegal or unsupported in some way)", 01166 "noCreation (That table does not support row creation or that object can not ever be created)", 01167 "inconsistentValue (The set value is illegal or unsupported in some way)", 01168 "resourceUnavailable (This is likely a out-of-memory failure within the agent)", 01169 "commitFailed", 01170 "undoFailed", 01171 "authorizationError (access denied to that object)", 01172 "notWritable (That object does not support modification)", 01173 "inconsistentName (That object can not currently be created)" 01174 }; 01175 01176 if (errstat <= MAX_SNMP_ERR && errstat >= SNMP_ERR_NOERROR) { 01177 return error_string[errstat]; 01178 } else { 01179 return "Unknown Error"; 01180 } 01181 } 01182 01183 01184 01185 /* 01186 * 01187 * Convenience routines to make various requests 01188 * over the specified SNMP session. 01189 * 01190 */ 01191 #include <net-snmp/library/snmp_debug.h> 01192 static netsnmp_session *_def_query_session = NULL; 01193 void 01194 netsnmp_query_set_default_session( netsnmp_session *sess) { 01195 DEBUGMSGTL(("iquery", "set default session %p\n", sess)); 01196 _def_query_session = sess; 01197 } 01198 01199 netsnmp_session * 01200 netsnmp_query_get_default_session( void ) { 01201 DEBUGMSGTL(("iquery", "get default session %p\n", _def_query_session)); 01202 return _def_query_session; 01203 } 01204 01205 01206 /* 01207 * Internal utility routine to actually send the query 01208 */ 01209 static int _query(netsnmp_variable_list *list, 01210 int request, 01211 netsnmp_session *session) { 01212 01213 netsnmp_pdu *pdu = snmp_pdu_create( request ); 01214 netsnmp_pdu *response = NULL; 01215 netsnmp_variable_list *vb1, *vb2, *vtmp; 01216 int ret; 01217 01218 DEBUGMSGTL(("iquery", "query on session %p\n", session)); 01219 /* 01220 * Clone the varbind list into the request PDU... 01221 */ 01222 pdu->variables = snmp_clone_varbind( list ); 01223 retry: 01224 if ( session ) 01225 ret = snmp_synch_response( session, pdu, &response ); 01226 else if (_def_query_session) 01227 ret = snmp_synch_response( _def_query_session, pdu, &response ); 01228 else { 01229 /* No session specified */ 01230 snmp_free_pdu(pdu); 01231 return SNMP_ERR_GENERR; 01232 } 01233 DEBUGMSGTL(("iquery", "query returned %d\n", ret)); 01234 01235 /* 01236 * ....then copy the results back into the 01237 * list (assuming the request succeeded!). 01238 * This avoids having to worry about how this 01239 * list was originally allocated. 01240 */ 01241 if ( ret == SNMP_ERR_NOERROR ) { 01242 if ( response->errstat != SNMP_ERR_NOERROR ) { 01243 /* 01244 * If the request failed, then remove the 01245 * offending varbind and try again. 01246 * (all except SET requests) 01247 * 01248 * XXX - implement a library version of 01249 * NETSNMP_DS_APP_DONT_FIX_PDUS ?? 01250 */ 01251 ret = response->errstat; 01252 if (request != SNMP_MSG_SET && 01253 response->errindex != 0) { 01254 DEBUGMSGTL(("iquery", "retrying query (%d, %ld)\n", ret, response->errindex)); 01255 pdu = snmp_fix_pdu( response, request ); 01256 snmp_free_pdu( response ); 01257 response = NULL; 01258 if ( pdu != NULL ) 01259 goto retry; 01260 } 01261 } else { 01262 for (vb1 = response->variables, vb2 = list; 01263 vb1; 01264 vb1 = vb1->next_variable, vb2 = vb2->next_variable) { 01265 if ( !vb2 ) { 01266 ret = SNMP_ERR_GENERR; 01267 break; 01268 } 01269 vtmp = vb2->next_variable; 01270 snmp_free_var_internals( vb2 ); 01271 snmp_clone_var( vb1, vb2 ); 01272 vb2->next_variable = vtmp; 01273 } 01274 } 01275 } else { 01276 /* Distinguish snmp_send errors from SNMP errStat errors */ 01277 ret = -ret; 01278 } 01279 snmp_free_pdu( response ); 01280 return ret; 01281 } 01282 01283 /* 01284 * These are simple wrappers round the internal utility routine 01285 */ 01286 int netsnmp_query_get(netsnmp_variable_list *list, 01287 netsnmp_session *session){ 01288 return _query( list, SNMP_MSG_GET, session ); 01289 } 01290 01291 01292 int netsnmp_query_getnext(netsnmp_variable_list *list, 01293 netsnmp_session *session){ 01294 return _query( list, SNMP_MSG_GETNEXT, session ); 01295 } 01296 01297 01298 int netsnmp_query_set(netsnmp_variable_list *list, 01299 netsnmp_session *session){ 01300 return _query( list, SNMP_MSG_SET, session ); 01301 } 01302 01303 /* 01304 * A walk needs a bit more work. 01305 */ 01306 int netsnmp_query_walk(netsnmp_variable_list *list, 01307 netsnmp_session *session) { 01308 /* 01309 * Create a working copy of the original (single) 01310 * varbind, so we can use this varbind parameter 01311 * to check when we've finished walking this subtree. 01312 */ 01313 netsnmp_variable_list *vb = snmp_clone_varbind( list ); 01314 netsnmp_variable_list *res_list = NULL; 01315 netsnmp_variable_list *res_last = NULL; 01316 int ret; 01317 01318 /* 01319 * Now walk the tree as usual 01320 */ 01321 ret = _query( vb, SNMP_MSG_GETNEXT, session ); 01322 while ( ret == SNMP_ERR_NOERROR && 01323 snmp_oidtree_compare( list->name, list->name_length, 01324 vb->name, vb->name_length ) == 0) { 01325 01326 /* 01327 * Copy each response varbind to the end of the result list 01328 * and then re-use this to ask for the next entry. 01329 */ 01330 if ( res_last ) { 01331 res_last->next_variable = snmp_clone_varbind( vb ); 01332 res_last = res_last->next_variable; 01333 } else { 01334 res_list = snmp_clone_varbind( vb ); 01335 res_last = res_list; 01336 } 01337 ret = _query( vb, SNMP_MSG_GETNEXT, session ); 01338 } 01339 /* 01340 * Copy the first result back into the original varbind parameter, 01341 * add the rest of the results (if any), and clean up. 01342 */ 01343 if ( res_list ) { 01344 snmp_clone_var( res_list, list ); 01345 list->next_variable = res_list->next_variable; 01346 res_list->next_variable = NULL; 01347 snmp_free_varbind( res_list ); 01348 } 01349 snmp_free_varbind( vb ); 01350 return ret; 01351 }
1.5.7.1
Last modified: Tuesday, 23-Dec-2025 17:22:04 UTC
For questions regarding web content and site functionality, please write to the net-snmp-users mail list.