snmp_client.c
资源名称:snmp.src.rar [点击查看]
上传用户:cxs890
上传日期:2021-05-22
资源大小:347k
文件大小:20k
源码类别:
SNMP编程
开发平台:
C/C++
- /*
- * snmp_client.c - a toolkit of common functions for an SNMP client.
- *
- */
- /**********************************************************************
- Copyright 1988, 1989, 1991, 1992 by Carnegie Mellon University
- All Rights Reserved
- Permission to use, copy, modify, and distribute this software and its
- documentation for any purpose and without fee is hereby granted,
- provided that the above copyright notice appear in all copies and that
- both that copyright notice and this permission notice appear in
- supporting documentation, and that the name of CMU not be
- used in advertising or publicity pertaining to distribution of the
- software without specific, written prior permission.
- CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
- ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
- CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
- ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
- WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
- ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- SOFTWARE.
- ******************************************************************/
- #include "config.h"
- #include <stdio.h>
- #include <errno.h>
- #if HAVE_STDLIB_H
- #include <stdlib.h>
- #endif
- #if HAVE_STRING_H
- #include <string.h>
- #else
- #include <strings.h>
- #endif
- #if HAVE_UNISTD_H
- #include <unistd.h>
- #endif
- #include <types.h>
- #if TIME_WITH_SYS_TIME
- # ifdef WIN32
- # include <sys/timeb.h>
- # else
- # include <time.h>
- # endif
- # include <time.h>
- #else
- # if HAVE_SYS_TIME_H
- # include <sys/time.h>
- # else
- # include <time.h>
- # endif
- #endif
- #ifdef OS_VXWORKS
- #include <sys/times.h>
- #endif
- #if HAVE_SYS_PARAM_H
- #include <sys/param.h>
- #endif
- #if HAVE_NETINET_IN_H
- #include <netinet/in.h>
- #endif
- #if HAVE_ARPA_INET_H
- #include <arpa/inet.h>
- #endif
- #if HAVE_SYS_SELECT_H
- #include <sys/select.h>
- #endif
- #if HAVE_DMALLOC_H
- #include <dmalloc.h>
- #endif
- #include <ip/socket.h>
- #include <ip/in.h>
- #include "asn1.h"
- #include "snmp.h"
- #include "snmp_api.h"
- #include "snmp_impl.h"
- #include "snmp_client.h"
- #include "mib.h"
- #define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0
- #ifndef BSD4_3
- #define BSD4_2
- #endif
- #ifndef FD_SET
- typedef long fd_mask;
- #define NFDBITS (sizeof(fd_mask) * NBBY) /* bits per mask */
- #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
- #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
- #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
- #define FD_ZERO(p) memset((p), 0, sizeof(*(p)))
- #endif
- #define PARTY_MIB_BASE ".1.3.6.1.6.3.3.1.3.127.0.0.1.1"
- #define CONTEXT_MIB_BASE ".1.3.6.1.6.3.3.1.4.127.0.0.1.1"
- struct snmp_pdu *
- snmp_pdu_create(int command)
- {
- struct snmp_pdu *pdu;
- pdu = (struct snmp_pdu *)calloc(1,sizeof(struct snmp_pdu));
- if (pdu) {
- pdu->version = SNMP_DEFAULT_VERSION;
- pdu->command = command;
- pdu->errstat = SNMP_DEFAULT_ERRSTAT;
- pdu->errindex = SNMP_DEFAULT_ERRINDEX;
- pdu->securityNameLen = 0;
- pdu->contextNameLen = 0;
- pdu->reqid = snmp_get_next_reqid();
- pdu->msgid = snmp_get_next_msgid();
- pdu->available_community = FALSE;
- }
- return pdu;
- }
- /*
- * Add a null variable with the requested name to the end of the list of
- * variables for this pdu.
- */
- struct variable_list* snmp_add_null_var(struct snmp_pdu * pdu,
- oid *name,
- size_t name_length)
- {
- return snmp_pdu_add_variable(pdu, name, name_length, ASN_NULL, NULL, 0);
- }
- int
- snmp_synch_input(int op,
- struct snmp_session *session,
- int reqid,
- struct snmp_pdu *pdu,
- void *magic)
- {
- struct synch_state *state = (struct synch_state *)magic;
- int rpt_type;
- if (reqid != state->reqid && pdu->command != SNMP_MSG_REPORT)
- return 0;
- state->waiting = 0;
- if (op == RECEIVED_MESSAGE) {
- if (pdu->command == SNMP_MSG_REPORT) {
- rpt_type = snmpv3_get_report_type(pdu);
- if (SNMPV3_IGNORE_UNAUTH_REPORTS ||
- rpt_type == SNMPERR_NOT_IN_TIME_WINDOW)
- state->waiting = 1;
- state->pdu = NULL;
- state->status = STAT_ERROR;
- session->s_snmp_errno = rpt_type;
- SET_SNMP_ERROR(rpt_type);
- } else if (pdu->command == SNMP_MSG_RESPONSE) {
- /* clone the pdu to return to snmp_synch_response */
- state->pdu = snmp_clone_pdu(pdu);
- state->status = STAT_SUCCESS;
- session->s_snmp_errno = SNMPERR_SUCCESS;
- }
- } else if (op == TIMED_OUT){
- state->pdu = NULL;
- state->status = STAT_TIMEOUT;
- session->s_snmp_errno = SNMPERR_TIMEOUT;
- SET_SNMP_ERROR(SNMPERR_TIMEOUT);
- }
- return 1;
- }
- /*
- * Clone an SNMP variable data structure.
- * Sets pointers to structure private storage, or
- * allocates larger object identifiers and values as needed.
- *
- * Caller must make list association for cloned variable.
- *
- * Returns 0 if successful.
- */
- int
- snmp_clone_var(struct variable_list *var, struct variable_list *newvar)
- {
- if (!newvar || !var) return 1;
- memmove(newvar, var, sizeof(struct variable_list));
- newvar->next_variable = 0; newvar->name = 0; newvar->val.string = 0;
- /*
- * Clone the object identifier and the value.
- * Allocate memory iff original will not fit into local storage.
- */
- if (snmp_set_var_objid(newvar, var->name, var->name_length))
- return 1;
- /* need a pointer and a length to copy a string value. */
- if (var->val.string && var->val_len) {
- if (var->val.string != &var->buf[0]){
- if (var->val_len <= sizeof(var->buf))
- newvar->val.string = newvar->buf;
- else {
- newvar->val.string = (u_char *)malloc(var->val_len);
- if (!newvar->val.string) return 1;
- }
- memmove(newvar->val.string, var->val.string, var->val_len);
- }
- else { /* fix the pointer to new local store */
- newvar->val.string = newvar->buf;
- }
- }
- else {
- newvar->val.string = 0; newvar->val_len = 0;
- }
- return 0;
- }
- /*
- * Possibly make a copy of source memory buffer.
- * Will reset destination pointer if source pointer is NULL.
- * Returns 0 if successful, 1 if memory allocation fails.
- */
- int
- snmp_clone_mem(void ** dstPtr, void * srcPtr, unsigned len)
- {
- *dstPtr = 0;
- if (srcPtr){
- *dstPtr = malloc(len + 1);
- if (! *dstPtr){
- return 1;
- }
- memmove(*dstPtr, srcPtr, len);
- /* this is for those routines that expect 0-terminated strings!!!
- someone should rather have called strdup
- */
- ((char *)*dstPtr)[len] = 0;
- }
- return 0;
- }
- /*
- * Creates and allocates a clone of the input PDU,
- * but does NOT copy the variables.
- * This function should be used with another function,
- * such as _copy_pdu_vars.
- *
- * Returns a pointer to the cloned PDU if successful.
- * Returns 0 if failure.
- */
- static
- struct snmp_pdu *
- _clone_pdu_header(struct snmp_pdu *pdu)
- {
- struct snmp_pdu *newpdu;
- newpdu = (struct snmp_pdu *)malloc(sizeof(struct snmp_pdu));
- if (!newpdu) return 0;
- memmove(newpdu, pdu, sizeof(struct snmp_pdu));
- /* reset copied pointers if copy fails */
- newpdu->variables = 0; newpdu->enterprise = 0; newpdu->community = 0;
- newpdu->securityEngineID = 0; newpdu->securityName = 0;
- newpdu->contextEngineID = 0; newpdu->contextName = 0;
- newpdu->available_community = pdu->available_community;
- /* copy buffers individually. If any copy fails, all are freed. */
- if ( snmp_clone_mem((void **)&newpdu->enterprise, pdu->enterprise,
- sizeof(oid)*pdu->enterprise_length)
- || snmp_clone_mem((void **)&newpdu->community, pdu->community,
- pdu->community_len)
- || snmp_clone_mem((void **)&newpdu->contextEngineID, pdu->contextEngineID,
- pdu->contextEngineIDLen)
- || snmp_clone_mem((void **)&newpdu->securityEngineID, pdu->securityEngineID,
- pdu->securityEngineIDLen)
- || snmp_clone_mem((void **)&newpdu->contextName, pdu->contextName,
- pdu->contextNameLen)
- || snmp_clone_mem((void **)&newpdu->securityName, pdu->securityName,
- pdu->securityNameLen)
- )
- {
- snmp_free_pdu(newpdu); return 0;
- }
- return newpdu;
- }
- /*
- * Copy some or all variables from source PDU to target PDU.
- * This function consolidates many of the needs of PDU variables:
- * Clone PDU : copy all the variables.
- * Split PDU : skip over some variables to copy other variables.
- * Fix PDU : remove variable associated with error index.
- *
- * Designed to work with _clone_pdu_header.
- *
- * If drop_err is set, drop any variable associated with errindex.
- * If skip_count is set, skip the number of variable in pdu's list.
- * While copy_count is greater than zero, copy pdu variables to newpdu.
- *
- * If an error occurs, newpdu is freed and pointer is set to 0.
- *
- * Returns a pointer to the cloned PDU if successful.
- * Returns 0 if failure.
- */
- static
- struct snmp_pdu *
- _copy_pdu_vars(struct snmp_pdu *pdu, /* source PDU */
- struct snmp_pdu *newpdu, /* target PDU */
- int drop_err, /* !=0 drop errored variable */
- int skip_count, /* !=0 number of variables to skip */
- int copy_count) /* !=0 number of variables to copy */
- {
- struct variable_list *var, *newvar, *oldvar;
- int ii, copied;
- if (!newpdu) return 0; /* where is PDU to copy to ? */
- var = pdu->variables;
- while (var && (skip_count-- > 0)) /* skip over pdu variables */
- var = var->next_variable;
- oldvar = 0; ii = 0; copied = 0;
- if (pdu->flags & UCD_MSG_FLAG_FORCE_PDU_COPY)
- copied = 1; /* We're interested in 'empty' responses too */
- while (var && (copy_count-- > 0))
- {
- /* errindex starts from 1. If drop_err, skip the errored variable */
- if (drop_err && (++ii == pdu->errindex)) {
- var = var->next_variable; continue;
- }
- /* clone the next variable. Cleanup if alloc fails */
- newvar = (struct variable_list *)malloc(sizeof(struct variable_list));
- if (snmp_clone_var(var, newvar)){
- if (newvar) free((char *)newvar);
- snmp_free_pdu(newpdu); return 0;
- }
- copied++;
- /* add cloned variable to new PDU */
- if (0 == newpdu->variables) newpdu->variables = newvar;
- if (oldvar) oldvar->next_variable = newvar;
- oldvar = newvar;
- var = var->next_variable;
- }
- /* Error if bad errindex or if target PDU has no variables copied */
- if ((drop_err && (ii < pdu->errindex))
- #if TEMPORARILY_DISABLED
- /* SNMPv3 engineID probes are allowed to be empty.
- See the comment in snmp_api.c for further details */
- || copied == 0
- #endif
- ) {
- snmp_free_pdu(newpdu); return 0;
- }
- return newpdu;
- }
- /*
- * Creates (allocates and copies) a clone of the input PDU.
- * If drop_err is set, don't copy any variable associated with errindex.
- * This function is called by snmp_clone_pdu and snmp_fix_pdu.
- *
- * Returns a pointer to the cloned PDU if successful.
- * Returns 0 if failure.
- */
- static
- struct snmp_pdu *
- _clone_pdu(struct snmp_pdu *pdu, int drop_err)
- {
- struct snmp_pdu *newpdu;
- newpdu = _clone_pdu_header(pdu);
- newpdu = _copy_pdu_vars(pdu, newpdu,
- drop_err,
- 0, 10000); /* skip none, copy all */
- return newpdu;
- }
- /*
- * This function will clone a PDU including all of its variables.
- *
- * Returns a pointer to the cloned PDU if successful.
- * Returns 0 if failure
- */
- struct snmp_pdu *
- snmp_clone_pdu(struct snmp_pdu *pdu)
- {
- return _clone_pdu(pdu, 0); /* copies all variables */
- }
- /*
- * This function will clone a PDU including some of its variables.
- *
- * If skip_count is not zero, it defines the number of variables to skip.
- * If copy_count is not zero, it defines the number of variables to copy.
- *
- * Returns a pointer to the cloned PDU if successful.
- * Returns 0 if failure.
- */
- struct snmp_pdu *
- snmp_split_pdu(struct snmp_pdu *pdu, int skip_count, int copy_count)
- {
- struct snmp_pdu *newpdu;
- newpdu = _clone_pdu_header(pdu);
- newpdu = _copy_pdu_vars(pdu, newpdu,
- 0, /* don't drop any variables */
- skip_count,
- copy_count);
- return newpdu;
- }
- /*
- * If there was an error in the input pdu, creates a clone of the pdu
- * that includes all the variables except the one marked by the errindex.
- * The command is set to the input command and the reqid, errstat, and
- * errindex are set to default values.
- * If the error status didn't indicate an error, the error index didn't
- * indicate a variable, the pdu wasn't a get response message, or there
- * would be no remaining variables, this function will return 0.
- * If everything was successful, a pointer to the fixed cloned pdu will
- * be returned.
- */
- struct snmp_pdu *
- snmp_fix_pdu(struct snmp_pdu *pdu, int command)
- {
- struct snmp_pdu *newpdu;
- if ((pdu->command != SNMP_MSG_RESPONSE)
- || (pdu->errstat == SNMP_ERR_NOERROR)
- || (0 == pdu->variables)
- || (pdu->errindex <= 0))
- {
- return 0; /* pre-condition tests fail */
- }
- newpdu = _clone_pdu(pdu, 1); /* copies all except errored variable */
- if (!newpdu)
- return 0;
- if (!newpdu->variables) {
- snmp_free_pdu(newpdu);
- return 0; /* no variables. "should not happen" */
- }
- newpdu->command = command;
- newpdu->reqid = snmp_get_next_reqid();
- newpdu->msgid = snmp_get_next_msgid();
- newpdu->errstat = SNMP_DEFAULT_ERRSTAT;
- newpdu->errindex = SNMP_DEFAULT_ERRINDEX;
- return newpdu;
- }
- /*
- * Returns the number of variables bound to a PDU structure
- */
- unsigned long
- snmp_varbind_len(struct snmp_pdu * pdu)
- {
- register struct variable_list *vars;
- unsigned long retVal = 0;
- if (pdu)
- for (vars = pdu->variables; vars; vars = vars->next_variable)
- {
- retVal++;
- }
- return retVal;
- }
- /*
- * Add object identifier name to SNMP variable.
- * If the name is large, additional memory is allocated.
- * Returns 0 if successful.
- */
- int
- snmp_set_var_objid (struct variable_list *vp,
- const oid *objid, size_t name_length)
- {
- size_t len = sizeof(oid) * name_length;
- /* use built-in storage for smaller values */
- if (len <= sizeof(vp->name_loc)) {
- vp->name = vp->name_loc;
- }
- else {
- vp->name = (oid *)malloc(len);
- if (!vp->name) return 1;
- }
- memmove(vp->name, objid, len);
- vp->name_length = name_length;
- return 0;
- }
- /*
- * Add some value to SNMP variable.
- * If the value is large, additional memory is allocated.
- * Returns 0 if successful.
- */
- int
- snmp_set_var_value(struct variable_list *newvar,
- u_char *val_str, size_t val_len)
- {
- if (newvar->val.string &&
- newvar->val.string != newvar->buf)
- {
- free(newvar->val.string);
- }
- newvar->val.string = 0; newvar->val_len = 0;
- /* need a pointer and a length to copy a string value. */
- if (val_str && val_len)
- {
- if (val_len <= sizeof(newvar->buf))
- newvar->val.string = newvar->buf;
- else {
- newvar->val.string = (u_char *)malloc(val_len);
- if (!newvar->val.string) return 1;
- }
- memmove(newvar->val.string, val_str, val_len);
- newvar->val_len = val_len;
- }
- return 0;
- }
- int
- snmp_synch_response_cb(struct snmp_session *ss,
- struct snmp_pdu *pdu,
- struct snmp_pdu **response,
- snmp_callback pcb)
- {
- struct synch_state lstate, *state;
- snmp_callback cbsav;
- void * cbmagsav;
- int numfds, count;
- fd_set fdset;
- struct timeval timeout, *tvp;
- int block;
- memset((void *)&lstate, 0, sizeof(lstate));
- state = &lstate;
- cbsav = ss->callback;
- cbmagsav = ss->callback_magic;
- ss->callback = pcb;
- ss->callback_magic = (void *)state;
- if ((state->reqid = snmp_send(ss, pdu)) == 0){
- snmp_free_pdu(pdu);
- state->status = STAT_ERROR;
- }
- else
- state->waiting = 1;
- while(state->waiting){
- numfds = 0;
- FD_ZERO(&fdset);
- block = SNMPBLOCK;
- tvp = &timeout;
- timerclear(tvp);
- snmp_select_info(&numfds, &fdset, tvp, &block);
- if (block == 1)
- tvp = NULL; /* block without timeout */
- count = so_select(numfds, &fdset, 0, 0, tvp);
- if (count > 0){
- snmp_read(&fdset);
- } else switch(count){
- case 0:
- snmp_timeout();
- break;
- case -1:
- if (errno == EINTR){
- continue;
- } else {
- snmp_errno = SNMPERR_GENERR;
- /* CAUTION! if another thread closed the socket(s)
- waited on here, the session structure was freed.
- It would be nice, but we can't rely on the pointer.
- ss->s_snmp_errno = SNMPERR_GENERR;
- ss->s_errno = errno;
- */
- snmp_set_detail(strerror(errno));
- }
- /* FALLTHRU */
- default:
- state->status = STAT_ERROR;
- state->waiting = 0;
- }
- }
- *response = state->pdu;
- ss->callback = cbsav;
- ss->callback_magic = cbmagsav;
- return state->status;
- }
- int
- snmp_synch_response(struct snmp_session *ss,
- struct snmp_pdu *pdu,
- struct snmp_pdu **response)
- {
- return snmp_synch_response_cb(ss,pdu,response,snmp_synch_input);
- }
- int
- snmp_sess_synch_response(void *sessp,
- struct snmp_pdu *pdu,
- struct snmp_pdu **response)
- {
- struct snmp_session *ss;
- struct synch_state lstate, *state;
- snmp_callback cbsav;
- void * cbmagsav;
- int numfds, count;
- fd_set fdset;
- struct timeval timeout, *tvp;
- int block;
- ss = snmp_sess_session(sessp);
- memset((void *)&lstate, 0, sizeof(lstate));
- state = &lstate;
- cbsav = ss->callback;
- cbmagsav = ss->callback_magic;
- ss->callback = snmp_synch_input;
- ss->callback_magic = (void *)state;
- if ((state->reqid = snmp_sess_send(sessp, pdu)) == 0){
- snmp_free_pdu(pdu);
- state->status = STAT_ERROR;
- }
- else
- state->waiting = 1;
- while(state->waiting){
- numfds = 0;
- FD_ZERO(&fdset);
- block = SNMPBLOCK;
- tvp = &timeout;
- timerclear(tvp);
- snmp_sess_select_info(sessp, &numfds, &fdset, tvp, &block);
- if (block == 1)
- tvp = NULL; /* block without timeout */
- count = so_select(numfds, &fdset, 0, 0, tvp);
- if (count > 0){
- snmp_sess_read(sessp, &fdset);
- } else switch(count){
- case 0:
- snmp_sess_timeout(sessp);
- break;
- case -1:
- if (errno == EINTR){
- continue;
- } else {
- snmp_errno = SNMPERR_GENERR;
- /* CAUTION! if another thread closed the socket(s)
- waited on here, the session structure was freed.
- It would be nice, but we can't rely on the pointer.
- ss->s_snmp_errno = SNMPERR_GENERR;
- ss->s_errno = errno;
- */
- snmp_set_detail(strerror(errno));
- }
- /* FALLTHRU */
- default:
- state->status = STAT_ERROR;
- state->waiting = 0;
- }
- }
- *response = state->pdu;
- ss->callback = cbsav;
- ss->callback_magic = cbmagsav;
- return state->status;
- }
- const char *error_string[19] = {
- "(noError) No Error",
- "(tooBig) Response message would have been too large.",
- "(noSuchName) There is no such variable name in this MIB.",
- "(badValue) The value given has the wrong type or length.",
- "(readOnly) The two parties used do not have access to use the specified SNMP PDU.",
- "(genError) A general failure occured",
- "noAccess",
- "wrongType",
- "wrongLength",
- "wrongEncoding",
- "wrongValue",
- "noCreation",
- "inconsistentValue",
- "resourceUnavailable",
- "commitFailed",
- "undoFailed",
- "authorizationError",
- "notWritable",
- "inconsistentName"
- };
- const char *
- snmp_errstring(int errstat)
- {
- if (errstat <= MAX_SNMP_ERR && errstat >= SNMP_ERR_NOERROR){
- return error_string[errstat];
- } else {
- return "Unknown Error";
- }
- }