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