diff options
Diffstat (limited to 'snmplib/snmp_client.c')
-rw-r--r-- | snmplib/snmp_client.c | 1885 |
1 files changed, 1885 insertions, 0 deletions
diff --git a/snmplib/snmp_client.c b/snmplib/snmp_client.c new file mode 100644 index 0000000..c1aa9c4 --- /dev/null +++ b/snmplib/snmp_client.c @@ -0,0 +1,1885 @@ +/* + * snmp_client.c - a toolkit of common functions for an SNMP client. + * + */ +/* Portions of this file are subject to the following copyright(s). See + * the Net-SNMP's COPYING file for more details and other copyrights + * that may apply: + */ +/********************************************************************** + 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. +******************************************************************/ +/* + * Portions of this file are copyrighted by: + * Copyright © 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms specified in the COPYING file + * distributed with the Net-SNMP package. + */ + +/** @defgroup snmp_client various PDU processing routines + * @ingroup library + * + * @{ + */ +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-features.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 <sys/types.h> +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#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_SYSLOG_H +#include <syslog.h> +#endif + +#if HAVE_DMALLOC_H +#include <dmalloc.h> +#endif + +#include <net-snmp/types.h> + +#include <net-snmp/agent/ds_agent.h> +#include <net-snmp/library/default_store.h> +#include <net-snmp/library/snmp_api.h> +#include <net-snmp/library/snmp_client.h> +#include <net-snmp/library/snmp_secmod.h> +#include <net-snmp/library/snmpusm.h> +#include <net-snmp/library/mib.h> +#include <net-snmp/library/snmp_logging.h> +#include <net-snmp/library/snmp_assert.h> +#include <net-snmp/pdu_api.h> + +netsnmp_feature_child_of(snmp_client_all, libnetsnmp) + +netsnmp_feature_child_of(snmp_split_pdu, snmp_client_all) +netsnmp_feature_child_of(snmp_reset_var_types, snmp_client_all) +netsnmp_feature_child_of(query_set_default_session, snmp_client_all) +netsnmp_feature_child_of(row_create, snmp_client_all) + +#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 + +/* + * Prototype definitions + */ +static int snmp_synch_input(int op, netsnmp_session * session, + int reqid, netsnmp_pdu *pdu, void *magic); + +netsnmp_pdu * +snmp_pdu_create(int command) +{ + netsnmp_pdu *pdu; + + pdu = (netsnmp_pdu *) calloc(1, sizeof(netsnmp_pdu)); + if (pdu) { + pdu->version = SNMP_DEFAULT_VERSION; + pdu->command = command; + pdu->errstat = SNMP_DEFAULT_ERRSTAT; + pdu->errindex = SNMP_DEFAULT_ERRINDEX; + pdu->securityModel = SNMP_DEFAULT_SECMODEL; + pdu->transport_data = NULL; + pdu->transport_data_length = 0; + pdu->securityNameLen = 0; + pdu->contextNameLen = 0; + pdu->time = 0; + pdu->reqid = snmp_get_next_reqid(); + pdu->msgid = snmp_get_next_msgid(); + } + return pdu; + +} + + +/* + * Add a null variable with the requested name to the end of the list of + * variables for this pdu. + */ +netsnmp_variable_list * +snmp_add_null_var(netsnmp_pdu *pdu, const oid * name, size_t name_length) +{ + return snmp_pdu_add_variable(pdu, name, name_length, ASN_NULL, NULL, 0); +} + + +#include <net-snmp/library/snmp_debug.h> +static int +snmp_synch_input(int op, + netsnmp_session * session, + int reqid, netsnmp_pdu *pdu, void *magic) +{ + struct synch_state *state = (struct synch_state *) magic; + int rpt_type; + + if (reqid != state->reqid && pdu && pdu->command != SNMP_MSG_REPORT) { + DEBUGMSGTL(("snmp_synch", "Unexpected response (ReqID: %d,%d - Cmd %d)\n", + reqid, state->reqid, pdu->command )); + return 0; + } + + state->waiting = 0; + DEBUGMSGTL(("snmp_synch", "Response (ReqID: %d - Cmd %d)\n", + reqid, (pdu ? pdu->command : -1))); + + if (op == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE && pdu) { + 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 { + char msg_buf[50]; + state->status = STAT_ERROR; + session->s_snmp_errno = SNMPERR_PROTOCOL; + SET_SNMP_ERROR(SNMPERR_PROTOCOL); + snprintf(msg_buf, sizeof(msg_buf), "Expected RESPONSE-PDU but got %s-PDU", + snmp_pdu_type(pdu->command)); + snmp_set_detail(msg_buf); + return 0; + } + } else if (op == NETSNMP_CALLBACK_OP_TIMED_OUT) { + state->pdu = NULL; + state->status = STAT_TIMEOUT; + session->s_snmp_errno = SNMPERR_TIMEOUT; + SET_SNMP_ERROR(SNMPERR_TIMEOUT); + } else if (op == NETSNMP_CALLBACK_OP_DISCONNECT) { + state->pdu = NULL; + state->status = STAT_ERROR; + session->s_snmp_errno = SNMPERR_ABORT; + SET_SNMP_ERROR(SNMPERR_ABORT); + } + + 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(netsnmp_variable_list * var, netsnmp_variable_list * newvar) +{ + if (!newvar || !var) + return 1; + + memmove(newvar, var, sizeof(netsnmp_variable_list)); + newvar->next_variable = NULL; + newvar->name = NULL; + newvar->val.string = NULL; + newvar->data = NULL; + newvar->dataFreeHook = NULL; + newvar->index = 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 to copy a string value. + */ + if (var->val.string) { + 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; + /* + * no need for a memmove, since we copied the whole var + * struct (and thus var->buf) at the beginning of this function. + */ + } + } else { + newvar->val.string = NULL; + 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, const void *srcPtr, unsigned len) +{ + *dstPtr = NULL; + 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; +} + + +/* + * Walks through a list of varbinds and frees and allocated memory, + * restoring pointers to local buffers + */ +void +snmp_reset_var_buffers(netsnmp_variable_list * var) +{ + while (var) { + if (var->name != var->name_loc) { + if(NULL != var->name) + free(var->name); + var->name = var->name_loc; + var->name_length = 0; + } + if (var->val.string != var->buf) { + if (NULL != var->val.string) + free(var->val.string); + var->val.string = var->buf; + var->val_len = 0; + } + var = var->next_variable; + } +} + +/* + * 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 +netsnmp_pdu * +_clone_pdu_header(netsnmp_pdu *pdu) +{ + netsnmp_pdu *newpdu; + struct snmp_secmod_def *sptr; + int ret; + + newpdu = (netsnmp_pdu *) malloc(sizeof(netsnmp_pdu)); + if (!newpdu) + return NULL; + memmove(newpdu, pdu, sizeof(netsnmp_pdu)); + + /* + * reset copied pointers if copy fails + */ + newpdu->variables = NULL; + newpdu->enterprise = NULL; + newpdu->community = NULL; + newpdu->securityEngineID = NULL; + newpdu->securityName = NULL; + newpdu->contextEngineID = NULL; + newpdu->contextName = NULL; + newpdu->transport_data = NULL; + + /* + * 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_clone_mem((void **) &newpdu->transport_data, + pdu->transport_data, + pdu->transport_data_length)) { + snmp_free_pdu(newpdu); + return NULL; + } + + if (pdu != NULL && pdu->securityStateRef && + pdu->command == SNMP_MSG_TRAP2) { + + ret = usm_clone_usmStateReference((struct usmStateReference *) pdu->securityStateRef, + (struct usmStateReference **) &newpdu->securityStateRef ); + + if (ret) + { + snmp_free_pdu(newpdu); + return 0; + } + } + + if ((sptr = find_sec_mod(newpdu->securityModel)) != NULL && + sptr->pdu_clone != NULL) { + /* + * call security model if it needs to know about this + */ + (*sptr->pdu_clone) (pdu, newpdu); + } + + return newpdu; +} + +static +netsnmp_variable_list * +_copy_varlist(netsnmp_variable_list * var, /* source varList */ + int errindex, /* index of variable to drop (if any) */ + int copy_count) +{ /* !=0 number variables to copy */ + netsnmp_variable_list *newhead, *newvar, *oldvar; + int ii = 0; + + newhead = NULL; + oldvar = NULL; + + while (var && (copy_count-- > 0)) { + /* + * Drop the specified variable (if applicable) + */ + if (++ii == errindex) { + var = var->next_variable; + continue; + } + + /* + * clone the next variable. Cleanup if alloc fails + */ + newvar = (netsnmp_variable_list *) + malloc(sizeof(netsnmp_variable_list)); + if (snmp_clone_var(var, newvar)) { + if (newvar) + free((char *) newvar); + snmp_free_varbind(newhead); + return NULL; + } + + /* + * add cloned variable to new list + */ + if (NULL == newhead) + newhead = newvar; + if (oldvar) + oldvar->next_variable = newvar; + oldvar = newvar; + + var = var->next_variable; + } + return newhead; +} + + +/* + * 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 +netsnmp_pdu * +_copy_pdu_vars(netsnmp_pdu *pdu, /* source PDU */ + netsnmp_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 */ + netsnmp_variable_list *var; +#if TEMPORARILY_DISABLED + int copied; +#endif + int drop_idx; + + if (!newpdu) + return NULL; /* where is PDU to copy to ? */ + + if (drop_err) + drop_idx = pdu->errindex - skip_count; + else + drop_idx = 0; + + var = pdu->variables; + while (var && (skip_count-- > 0)) /* skip over pdu variables */ + var = var->next_variable; + +#if TEMPORARILY_DISABLED + copied = 0; + if (pdu->flags & UCD_MSG_FLAG_FORCE_PDU_COPY) + copied = 1; /* We're interested in 'empty' responses too */ +#endif + + newpdu->variables = _copy_varlist(var, drop_idx, copy_count); +#if TEMPORARILY_DISABLED + if (newpdu->variables) + copied = 1; +#endif + +#if ALSO_TEMPORARILY_DISABLED + /* + * 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; + } +#endif + 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 +netsnmp_pdu * +_clone_pdu(netsnmp_pdu *pdu, int drop_err) +{ + netsnmp_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 full varbind list + * + * Returns a pointer to the cloned varbind list if successful. + * Returns 0 if failure + */ +netsnmp_variable_list * +snmp_clone_varbind(netsnmp_variable_list * varlist) +{ + return _copy_varlist(varlist, 0, 10000); /* skip none, copy all */ +} + +/* + * This function will clone a PDU including all of its variables. + * + * Returns a pointer to the cloned PDU if successful. + * Returns 0 if failure + */ +netsnmp_pdu * +snmp_clone_pdu(netsnmp_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. + */ +#ifndef NETSNMP_FEATURE_REMOVE_SNMP_SPLIT_PDU +netsnmp_pdu * +snmp_split_pdu(netsnmp_pdu *pdu, int skip_count, int copy_count) +{ + netsnmp_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; +} +#endif /* NETSNMP_FEATURE_REMOVE_SNMP_SPLIT_PDU */ + + +/* + * 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, the + * marked variable was not present in the initial request, 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. + */ +netsnmp_pdu * +snmp_fix_pdu(netsnmp_pdu *pdu, int command) +{ + netsnmp_pdu *newpdu; + + if ((pdu->command != SNMP_MSG_RESPONSE) + || (pdu->errstat == SNMP_ERR_NOERROR) + || (NULL == pdu->variables) + || (pdu->errindex > (int)snmp_varbind_len(pdu)) + || (pdu->errindex <= 0)) { + return NULL; /* pre-condition tests fail */ + } + + newpdu = _clone_pdu(pdu, 1); /* copies all except errored variable */ + if (!newpdu) + return NULL; + if (!newpdu->variables) { + snmp_free_pdu(newpdu); + return NULL; /* 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(netsnmp_pdu *pdu) +{ + register netsnmp_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(netsnmp_variable_list * vp, + const oid * objid, size_t name_length) +{ + size_t len = sizeof(oid) * name_length; + + if (vp->name != vp->name_loc && vp->name != NULL) { + /* + * Probably previously-allocated "big storage". Better free it + * else memory leaks possible. + */ + free(vp->name); + } + + /* + * 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; + } + if (objid) + memmove(vp->name, objid, len); + vp->name_length = name_length; + return 0; +} + +/** + * snmp_set_var_typed_value is used to set data into the netsnmp_variable_list + * structure. Used to return data to the snmp request via the + * netsnmp_request_info structure's requestvb pointer. + * + * @param newvar the structure gets populated with the given data, type, + * val_str, and val_len. + * @param type is the asn data type to be copied + * @param val_str is a buffer containing the value to be copied into the + * newvar structure. + * @param val_len the length of val_str + * + * @return returns 0 on success and 1 on a malloc error + */ + +int +snmp_set_var_typed_value(netsnmp_variable_list * newvar, u_char type, + const void * val_str, size_t val_len) +{ + newvar->type = type; + return snmp_set_var_value(newvar, val_str, val_len); +} + +int +snmp_set_var_typed_integer(netsnmp_variable_list * newvar, + u_char type, long val) +{ + newvar->type = type; + return snmp_set_var_value(newvar, &val, sizeof(long)); +} + +int +count_varbinds(netsnmp_variable_list * var_ptr) +{ + int count = 0; + + for (; var_ptr != NULL; var_ptr = var_ptr->next_variable) + count++; + + return count; +} + +netsnmp_feature_child_of(count_varbinds_of_type, netsnmp_unused) +#ifndef NETSNMP_FEATURE_REMOVE_COUNT_VARBINDS_OF_TYPE +int +count_varbinds_of_type(netsnmp_variable_list * var_ptr, u_char type) +{ + int count = 0; + + for (; var_ptr != NULL; var_ptr = var_ptr->next_variable) + if (var_ptr->type == type) + count++; + + return count; +} +#endif /* NETSNMP_FEATURE_REMOVE_COUNT_VARBINDS_OF_TYPE */ + +netsnmp_feature_child_of(find_varind_of_type, netsnmp_unused) +#ifndef NETSNMP_FEATURE_REMOVE_FIND_VARIND_OF_TYPE +netsnmp_variable_list * +find_varbind_of_type(netsnmp_variable_list * var_ptr, u_char type) +{ + for (; var_ptr != NULL && var_ptr->type != type; + var_ptr = var_ptr->next_variable); + + return var_ptr; +} +#endif /* NETSNMP_FEATURE_REMOVE_FIND_VARIND_OF_TYPE */ + +netsnmp_variable_list* +find_varbind_in_list( netsnmp_variable_list *vblist, + const oid *name, size_t len) +{ + for (; vblist != NULL; vblist = vblist->next_variable) + if (!snmp_oid_compare(vblist->name, vblist->name_length, name, len)) + return vblist; + + return NULL; +} + +/* + * Add some value to SNMP variable. + * If the value is large, additional memory is allocated. + * Returns 0 if successful. + */ + +int +snmp_set_var_value(netsnmp_variable_list * vars, + const void * value, size_t len) +{ + int largeval = 1; + + /* + * xxx-rks: why the unconditional free? why not use existing + * memory, if len < vars->val_len ? + */ + if (vars->val.string && vars->val.string != vars->buf) { + free(vars->val.string); + } + vars->val.string = NULL; + vars->val_len = 0; + + if (value == NULL && len > 0) { + snmp_log(LOG_ERR, "bad size for NULL value\n"); + return 1; + } + + /* + * use built-in storage for smaller values + */ + if (len <= sizeof(vars->buf)) { + vars->val.string = (u_char *) vars->buf; + largeval = 0; + } + + if ((0 == len) || (NULL == value)) { + vars->val.string[0] = 0; + return 0; + } + + vars->val_len = len; + switch (vars->type) { + case ASN_INTEGER: + case ASN_UNSIGNED: + case ASN_TIMETICKS: + case ASN_COUNTER: + case ASN_UINTEGER: + if (vars->val_len == sizeof(int)) { + if (ASN_INTEGER == vars->type) { + const int *val_int + = (const int *) value; + *(vars->val.integer) = (long) *val_int; + } else { + const u_int *val_uint + = (const u_int *) value; + *(vars->val.integer) = (unsigned long) *val_uint; + } + } +#if SIZEOF_LONG != SIZEOF_INT + else if (vars->val_len == sizeof(long)){ + const u_long *val_ulong + = (const u_long *) value; + *(vars->val.integer) = *val_ulong; + if (*(vars->val.integer) > 0xffffffff) { + snmp_log(LOG_ERR,"truncating integer value > 32 bits\n"); + *(vars->val.integer) &= 0xffffffff; + } + } +#endif +#if defined(SIZEOF_LONG_LONG) && (SIZEOF_LONG != SIZEOF_LONG_LONG) +#if !defined(SIZEOF_INTMAX_T) || (SIZEOF_LONG_LONG != SIZEOF_INTMAX_T) + else if (vars->val_len == sizeof(long long)){ + const unsigned long long *val_ullong + = (const unsigned long long *) value; + *(vars->val.integer) = (long) *val_ullong; + if (*(vars->val.integer) > 0xffffffff) { + snmp_log(LOG_ERR,"truncating integer value > 32 bits\n"); + *(vars->val.integer) &= 0xffffffff; + } + } +#endif +#endif +#if defined(SIZEOF_INTMAX_T) && (SIZEOF_LONG != SIZEOF_INTMAX_T) + else if (vars->val_len == sizeof(intmax_t)){ + const uintmax_t *val_uintmax_t + = (const uintmax_t *) value; + *(vars->val.integer) = (long) *val_uintmax_t; + if (*(vars->val.integer) > 0xffffffff) { + snmp_log(LOG_ERR,"truncating integer value > 32 bits\n"); + *(vars->val.integer) &= 0xffffffff; + } + } +#endif +#if SIZEOF_SHORT != SIZEOF_INT + else if (vars->val_len == sizeof(short)) { + if (ASN_INTEGER == vars->type) { + const short *val_short + = (const short *) value; + *(vars->val.integer) = (long) *val_short; + } else { + const u_short *val_ushort + = (const u_short *) value; + *(vars->val.integer) = (unsigned long) *val_ushort; + } + } +#endif + else if (vars->val_len == sizeof(char)) { + if (ASN_INTEGER == vars->type) { + const char *val_char + = (const char *) value; + *(vars->val.integer) = (long) *val_char; + } else { + const u_char *val_uchar + = (const u_char *) value; + *(vars->val.integer) = (unsigned long) *val_uchar; + } + } + else { + snmp_log(LOG_ERR,"bad size for integer-like type (%d)\n", + (int)vars->val_len); + return (1); + } + vars->val_len = sizeof(long); + break; + + case ASN_OBJECT_ID: + case ASN_PRIV_IMPLIED_OBJECT_ID: + case ASN_PRIV_INCL_RANGE: + case ASN_PRIV_EXCL_RANGE: + if (largeval) { + vars->val.objid = (oid *) malloc(vars->val_len); + } + if (vars->val.objid == NULL) { + snmp_log(LOG_ERR,"no storage for OID\n"); + return 1; + } + memmove(vars->val.objid, value, vars->val_len); + break; + + case ASN_IPADDRESS: /* snmp_build_var_op treats IPADDR like a string */ + if (4 != vars->val_len) { + netsnmp_assert("ipaddress length == 4"); + } + /** FALL THROUGH */ + case ASN_PRIV_IMPLIED_OCTET_STR: + case ASN_OCTET_STR: + case ASN_BIT_STR: + case ASN_OPAQUE: + case ASN_NSAP: + if (vars->val_len >= sizeof(vars->buf)) { + vars->val.string = (u_char *) malloc(vars->val_len + 1); + } + if (vars->val.string == NULL) { + snmp_log(LOG_ERR,"no storage for string\n"); + return 1; + } + memmove(vars->val.string, value, vars->val_len); + /* + * Make sure the string is zero-terminated; some bits of code make + * this assumption. Easier to do this here than fix all these wrong + * assumptions. + */ + vars->val.string[vars->val_len] = '\0'; + break; + + case SNMP_NOSUCHOBJECT: + case SNMP_NOSUCHINSTANCE: + case SNMP_ENDOFMIBVIEW: + case ASN_NULL: + vars->val_len = 0; + vars->val.string = NULL; + break; + +#ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES + case ASN_OPAQUE_U64: + case ASN_OPAQUE_I64: +#endif /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */ + case ASN_COUNTER64: + if (largeval) { + snmp_log(LOG_ERR,"bad size for counter 64 (%d)\n", + (int)vars->val_len); + return (1); + } + vars->val_len = sizeof(struct counter64); + memmove(vars->val.counter64, value, vars->val_len); + break; + +#ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES + case ASN_OPAQUE_FLOAT: + if (largeval) { + snmp_log(LOG_ERR,"bad size for opaque float (%d)\n", + (int)vars->val_len); + return (1); + } + vars->val_len = sizeof(float); + memmove(vars->val.floatVal, value, vars->val_len); + break; + + case ASN_OPAQUE_DOUBLE: + if (largeval) { + snmp_log(LOG_ERR,"bad size for opaque double (%d)\n", + (int)vars->val_len); + return (1); + } + vars->val_len = sizeof(double); + memmove(vars->val.doubleVal, value, vars->val_len); + break; + +#endif /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */ + + default: + snmp_log(LOG_ERR,"Internal error in type switching\n"); + snmp_set_detail("Internal error in type switching\n"); + return (1); + } + + return 0; +} + +void +snmp_replace_var_types(netsnmp_variable_list * vbl, u_char old_type, + u_char new_type) +{ + while (vbl) { + if (vbl->type == old_type) { + snmp_set_var_typed_value(vbl, new_type, NULL, 0); + } + vbl = vbl->next_variable; + } +} + +#ifndef NETSNMP_FEATURE_REMOVE_SNMP_RESET_VAR_TYPES +void +snmp_reset_var_types(netsnmp_variable_list * vbl, u_char new_type) +{ + while (vbl) { + snmp_set_var_typed_value(vbl, new_type, NULL, 0); + vbl = vbl->next_variable; + } +} +#endif /* NETSNMP_FEATURE_REMOVE_SNMP_RESET_VAR_TYPES */ + +int +snmp_synch_response_cb(netsnmp_session * ss, + netsnmp_pdu *pdu, + netsnmp_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 = NETSNMP_SNMPBLOCK; + tvp = &timeout; + timerclear(tvp); + snmp_sess_select_info_flags(0, &numfds, &fdset, tvp, &block, + NETSNMP_SELECT_NOALARMS); + if (block == 1) + tvp = NULL; /* block without timeout */ + count = select(numfds, &fdset, NULL, NULL, 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; /*MTCRITICAL_RESOURCE */ + /* + * 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; + } + } + + if ( ss->flags & SNMP_FLAGS_RESP_CALLBACK ) { + void (*cb)(void); + cb = (void (*)(void))(ss->myvoid); + cb(); /* Used to invoke 'netsnmp_check_outstanding_agent_requests();' + on internal AgentX queries. */ + } + } + *response = state->pdu; + ss->callback = cbsav; + ss->callback_magic = cbmagsav; + return state->status; +} + +int +snmp_synch_response(netsnmp_session * ss, + netsnmp_pdu *pdu, netsnmp_pdu **response) +{ + return snmp_synch_response_cb(ss, pdu, response, snmp_synch_input); +} + +int +snmp_sess_synch_response(void *sessp, + netsnmp_pdu *pdu, netsnmp_pdu **response) +{ + netsnmp_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); + if (ss == NULL) { + return STAT_ERROR; + } + + 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 = NETSNMP_SNMPBLOCK; + tvp = &timeout; + timerclear(tvp); + snmp_sess_select_info_flags(sessp, &numfds, &fdset, tvp, &block, + NETSNMP_SELECT_NOALARMS); + if (block == 1) + tvp = NULL; /* block without timeout */ + count = select(numfds, &fdset, NULL, NULL, 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; /*MTCRITICAL_RESOURCE */ + /* + * 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 * +snmp_errstring(int errstat) +{ + const char * const 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 (The set datatype does not match the data type the agent expects)", + "wrongLength (The set value has an illegal length from what the agent expects)", + "wrongEncoding", + "wrongValue (The set value is illegal or unsupported in some way)", + "noCreation (That table does not support row creation or that object can not ever be created)", + "inconsistentValue (The set value is illegal or unsupported in some way)", + "resourceUnavailable (This is likely a out-of-memory failure within the agent)", + "commitFailed", + "undoFailed", + "authorizationError (access denied to that object)", + "notWritable (That object does not support modification)", + "inconsistentName (That object can not currently be created)" + }; + + if (errstat <= MAX_SNMP_ERR && errstat >= SNMP_ERR_NOERROR) { + return error_string[errstat]; + } else { + return "Unknown Error"; + } +} + + + +/* + * + * Convenience routines to make various requests + * over the specified SNMP session. + * + */ +#include <net-snmp/library/snmp_debug.h> + +static netsnmp_session *_def_query_session = NULL; + +#ifndef NETSNMP_FEATURE_REMOVE_QUERY_SET_DEFAULT_SESSION +void +netsnmp_query_set_default_session( netsnmp_session *sess) { + DEBUGMSGTL(("iquery", "set default session %p\n", sess)); + _def_query_session = sess; +} +#endif /* NETSNMP_FEATURE_REMOVE_QUERY_SET_DEFAULT_SESSION */ + +/** + * Return a pointer to the default internal query session. + */ +netsnmp_session * +netsnmp_query_get_default_session_unchecked( void ) { + DEBUGMSGTL(("iquery", "get default session %p\n", _def_query_session)); + return _def_query_session; +} + +/** + * Return a pointer to the default internal query session and log a + * warning message once if this session does not exist. + */ +netsnmp_session * +netsnmp_query_get_default_session( void ) { + static int warning_logged = 0; + + if (! _def_query_session && ! warning_logged) { + if (! netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_INTERNAL_SECNAME)) { + snmp_log(LOG_WARNING, + "iquerySecName has not been configured - internal queries will fail\n"); + } else { + snmp_log(LOG_WARNING, + "default session is not available - internal queries will fail\n"); + } + warning_logged = 1; + } + + return netsnmp_query_get_default_session_unchecked(); +} + + +/* + * Internal utility routine to actually send the query + */ +static int _query(netsnmp_variable_list *list, + int request, + netsnmp_session *session) { + + netsnmp_pdu *pdu = snmp_pdu_create( request ); + netsnmp_pdu *response = NULL; + netsnmp_variable_list *vb1, *vb2, *vtmp; + int ret, count; + + DEBUGMSGTL(("iquery", "query on session %p\n", session)); + /* + * Clone the varbind list into the request PDU... + */ + pdu->variables = snmp_clone_varbind( list ); +retry: + if ( session ) + ret = snmp_synch_response( session, pdu, &response ); + else if (_def_query_session) + ret = snmp_synch_response( _def_query_session, pdu, &response ); + else { + /* No session specified */ + snmp_free_pdu(pdu); + return SNMP_ERR_GENERR; + } + DEBUGMSGTL(("iquery", "query returned %d\n", ret)); + + /* + * ....then copy the results back into the + * list (assuming the request succeeded!). + * This avoids having to worry about how this + * list was originally allocated. + */ + if ( ret == SNMP_ERR_NOERROR ) { + if ( response->errstat != SNMP_ERR_NOERROR ) { + DEBUGMSGT(("iquery", "Error in packet: %s\n", + snmp_errstring(response->errstat))); + /* + * If the request failed, then remove the + * offending varbind and try again. + * (all except SET requests) + * + * XXX - implement a library version of + * NETSNMP_DS_APP_DONT_FIX_PDUS ?? + */ + ret = response->errstat; + if (response->errindex != 0) { + DEBUGMSGT(("iquery:result", "Failed object:\n")); + for (count = 1, vtmp = response->variables; + vtmp && count != response->errindex; + vtmp = vtmp->next_variable, count++) + /*EMPTY*/; + if (vtmp) + DEBUGMSGVAR(("iquery:result", vtmp)); + DEBUGMSG(("iquery:result", "\n")); + } +#ifndef NETSNMP_NO_WRITE_SUPPORT + if (request != SNMP_MSG_SET && + response->errindex != 0) { + DEBUGMSGTL(("iquery", "retrying query (%d, %ld)\n", ret, response->errindex)); + pdu = snmp_fix_pdu( response, request ); + snmp_free_pdu( response ); + response = NULL; + if ( pdu != NULL ) + goto retry; + } +#endif /* !NETSNMP_NO_WRITE_SUPPORT */ + } else { + for (vb1 = response->variables, vb2 = list; + vb1; + vb1 = vb1->next_variable, vb2 = vb2->next_variable) { + DEBUGMSGVAR(("iquery:result", vb1)); + DEBUGMSG(("iquery:results", "\n")); + if ( !vb2 ) { + ret = SNMP_ERR_GENERR; + break; + } + vtmp = vb2->next_variable; + snmp_free_var_internals( vb2 ); + snmp_clone_var( vb1, vb2 ); + vb2->next_variable = vtmp; + } + } + } else { + /* Distinguish snmp_send errors from SNMP errStat errors */ + ret = -ret; + } + snmp_free_pdu( response ); + return ret; +} + +/* + * These are simple wrappers round the internal utility routine + */ +int netsnmp_query_get(netsnmp_variable_list *list, + netsnmp_session *session){ + return _query( list, SNMP_MSG_GET, session ); +} + + +int netsnmp_query_getnext(netsnmp_variable_list *list, + netsnmp_session *session){ + return _query( list, SNMP_MSG_GETNEXT, session ); +} + + +#ifndef NETSNMP_NO_WRITE_SUPPORT +int netsnmp_query_set(netsnmp_variable_list *list, + netsnmp_session *session){ + return _query( list, SNMP_MSG_SET, session ); +} +#endif /* !NETSNMP_NO_WRITE_SUPPORT */ + +/* + * A walk needs a bit more work. + */ +int netsnmp_query_walk(netsnmp_variable_list *list, + netsnmp_session *session) { + /* + * Create a working copy of the original (single) + * varbind, so we can use this varbind parameter + * to check when we've finished walking this subtree. + */ + netsnmp_variable_list *vb = snmp_clone_varbind( list ); + netsnmp_variable_list *res_list = NULL; + netsnmp_variable_list *res_last = NULL; + int ret; + + /* + * Now walk the tree as usual + */ + ret = _query( vb, SNMP_MSG_GETNEXT, session ); + while ( ret == SNMP_ERR_NOERROR && + snmp_oidtree_compare( list->name, list->name_length, + vb->name, vb->name_length ) == 0) { + + /* + * Copy each response varbind to the end of the result list + * and then re-use this to ask for the next entry. + */ + if ( res_last ) { + res_last->next_variable = snmp_clone_varbind( vb ); + res_last = res_last->next_variable; + } else { + res_list = snmp_clone_varbind( vb ); + res_last = res_list; + } + ret = _query( vb, SNMP_MSG_GETNEXT, session ); + } + /* + * Copy the first result back into the original varbind parameter, + * add the rest of the results (if any), and clean up. + */ + if ( res_list ) { + snmp_clone_var( res_list, list ); + list->next_variable = res_list->next_variable; + res_list->next_variable = NULL; + snmp_free_varbind( res_list ); + } + snmp_free_varbind( vb ); + return ret; +} + +/** ************************************************************************** + * + * state machine + * + */ +int +netsnmp_state_machine_run( netsnmp_state_machine_input *input) +{ + netsnmp_state_machine_step *current, *last; + + netsnmp_require_ptr_LRV( input, SNMPERR_GENERR ); + netsnmp_require_ptr_LRV( input->steps, SNMPERR_GENERR ); + last = current = input->steps; + + DEBUGMSGT(("state_machine:run", "starting step: %s\n", current->name)); + + while (current) { + + /* + * log step and check for required data + */ + DEBUGMSGT(("state_machine:run", "at step: %s\n", current->name)); + if (NULL == current->run) { + DEBUGMSGT(("state_machine:run", "no run step\n")); + current->result = last->result; + break; + } + + /* + * run step + */ + DEBUGMSGT(("state_machine:run", "running step: %s\n", current->name)); + current->result = (*current->run)( input, current ); + ++input->steps_so_far; + + /* + * log result and move to next step + */ + input->last_run = current; + DEBUGMSGT(("state_machine:run:result", "step %s returned %d\n", + current->name, current->result)); + if (SNMPERR_SUCCESS == current->result) + current = current->on_success; + else if (SNMPERR_ABORT == current->result) { + DEBUGMSGT(("state_machine:run:result", "ABORT from %s\n", + current->name)); + break; + } + else + current = current->on_error; + } + + /* + * run cleanup + */ + if ((input->cleanup) && (input->cleanup->run)) + (*input->cleanup->run)( input, input->last_run ); + + return input->last_run->result; +} + +#ifndef NETSNMP_NO_WRITE_SUPPORT +#ifndef NETSNMP_FEATURE_REMOVE_ROW_CREATE +/** ************************************************************************** + * + * row create state machine steps + * + */ +typedef struct rowcreate_state_s { + + netsnmp_session *session; + netsnmp_variable_list *vars; + int row_status_index; +} rowcreate_state; + +static netsnmp_variable_list * +_get_vb_num(netsnmp_variable_list *vars, int index) +{ + for (; vars && index > 0; --index) + vars = vars->next_variable; + + if (!vars || index > 0) + return NULL; + + return vars; +} + + +/* + * cleanup + */ +static int +_row_status_state_cleanup(netsnmp_state_machine_input *input, + netsnmp_state_machine_step *step) +{ + rowcreate_state *ctx; + + netsnmp_require_ptr_LRV( input, SNMPERR_ABORT ); + netsnmp_require_ptr_LRV( step, SNMPERR_ABORT ); + + DEBUGMSGT(("row_create:called", "_row_status_state_cleanup, last run step was %s rc %d\n", + step->name, step->result)); + + ctx = (rowcreate_state *)input->input_context; + if (ctx && ctx->vars) + snmp_free_varbind( ctx->vars ); + + return SNMPERR_SUCCESS; +} + +/* + * send a request to activate the row + */ +static int +_row_status_state_activate(netsnmp_state_machine_input *input, + netsnmp_state_machine_step *step) +{ + rowcreate_state *ctx; + netsnmp_variable_list *rs_var, *var = NULL; + int32_t rc, val = RS_ACTIVE; + + netsnmp_require_ptr_LRV( input, SNMPERR_GENERR ); + netsnmp_require_ptr_LRV( step, SNMPERR_GENERR ); + netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR ); + + ctx = (rowcreate_state *)input->input_context; + + DEBUGMSGT(("row_create:called", "called %s\n", step->name)); + + /* + * just send the rowstatus varbind + */ + rs_var = _get_vb_num(ctx->vars, ctx->row_status_index); + netsnmp_require_ptr_LRV(rs_var, SNMPERR_GENERR); + + var = snmp_varlist_add_variable(&var, rs_var->name, rs_var->name_length, + rs_var->type, &val, sizeof(val)); + netsnmp_require_ptr_LRV( var, SNMPERR_GENERR ); + + /* + * send set + */ + rc = netsnmp_query_set( var, ctx->session ); + if (-2 == rc) + rc = SNMPERR_ABORT; + + snmp_free_varbind(var); + + return rc; +} + +/* + * send each non-row status column, one at a time + */ +static int +_row_status_state_single_value_cols(netsnmp_state_machine_input *input, + netsnmp_state_machine_step *step) +{ + rowcreate_state *ctx; + netsnmp_variable_list *var, *tmp_next, *row_status; + int rc = SNMPERR_GENERR; + + netsnmp_require_ptr_LRV( input, SNMPERR_GENERR ); + netsnmp_require_ptr_LRV( step, SNMPERR_GENERR ); + netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR ); + + ctx = (rowcreate_state *)input->input_context; + + DEBUGMSGT(("row_create:called", "called %s\n", step->name)); + + row_status = _get_vb_num(ctx->vars, ctx->row_status_index); + netsnmp_require_ptr_LRV(row_status, SNMPERR_GENERR); + + /* + * try one varbind at a time + */ + for (var = ctx->vars; var; var = var->next_variable) { + if (var == row_status) + continue; + + tmp_next = var->next_variable; + var->next_variable = NULL; + + /* + * send set + */ + rc = netsnmp_query_set( var, ctx->session ); + var->next_variable = tmp_next; + if (-2 == rc) + rc = SNMPERR_ABORT; + if (rc != SNMPERR_SUCCESS) + break; + } + + return rc; +} + +/* + * send all values except row status + */ +static int +_row_status_state_multiple_values_cols(netsnmp_state_machine_input *input, + netsnmp_state_machine_step *step) +{ + rowcreate_state *ctx; + netsnmp_variable_list *vars, *var, *last, *row_status; + int rc; + + netsnmp_require_ptr_LRV( input, SNMPERR_GENERR ); + netsnmp_require_ptr_LRV( step, SNMPERR_GENERR ); + netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR ); + + ctx = (rowcreate_state *)input->input_context; + + DEBUGMSGT(("row_create:called", "called %s\n", step->name)); + + vars = snmp_clone_varbind(ctx->vars); + netsnmp_require_ptr_LRV(vars, SNMPERR_GENERR); + + row_status = _get_vb_num(vars, ctx->row_status_index); + if (NULL == row_status) { + snmp_free_varbind(vars); + return SNMPERR_GENERR; + } + + /* + * remove row status varbind + */ + if (row_status == vars) { + vars = row_status->next_variable; + row_status->next_variable = NULL; + } + else { + for (last=vars, var=last->next_variable; + var; + last=var, var = var->next_variable) { + if (var == row_status) { + last->next_variable = var->next_variable; + break; + } + } + } + snmp_free_var(row_status); + + /* + * send set + */ + rc = netsnmp_query_set( vars, ctx->session ); + if (-2 == rc) + rc = SNMPERR_ABORT; + + snmp_free_varbind(vars); + + return rc; +} + +/* + * send a createAndWait request with no other values + */ +static int +_row_status_state_single_value_createAndWait(netsnmp_state_machine_input *input, + netsnmp_state_machine_step *step) +{ + rowcreate_state *ctx; + netsnmp_variable_list *var = NULL, *rs_var; + int32_t rc, val = RS_CREATEANDWAIT; + + netsnmp_require_ptr_LRV( input, SNMPERR_GENERR ); + netsnmp_require_ptr_LRV( step, SNMPERR_GENERR ); + netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR ); + + ctx = (rowcreate_state *)input->input_context; + + DEBUGMSGT(("row_create:called", "called %s\n", step->name)); + + rs_var = _get_vb_num(ctx->vars, ctx->row_status_index); + netsnmp_require_ptr_LRV(rs_var, SNMPERR_GENERR); + + var = snmp_varlist_add_variable(&var, rs_var->name, rs_var->name_length, + rs_var->type, &val, sizeof(val)); + netsnmp_require_ptr_LRV(var, SNMPERR_GENERR); + + /* + * send set + */ + rc = netsnmp_query_set( var, ctx->session ); + if (-2 == rc) + rc = SNMPERR_ABORT; + + snmp_free_varbind(var); + + return rc; +} + +/* + * send a creatAndWait request with all values + */ +static int +_row_status_state_all_values_createAndWait(netsnmp_state_machine_input *input, + netsnmp_state_machine_step *step) +{ + rowcreate_state *ctx; + netsnmp_variable_list *vars, *rs_var; + int rc; + + netsnmp_require_ptr_LRV( input, SNMPERR_GENERR ); + netsnmp_require_ptr_LRV( step, SNMPERR_GENERR ); + netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR ); + + ctx = (rowcreate_state *)input->input_context; + + DEBUGMSGT(("row_create:called", "called %s\n", step->name)); + + vars = snmp_clone_varbind(ctx->vars); + netsnmp_require_ptr_LRV(vars, SNMPERR_GENERR); + + /* + * make sure row stats is createAndWait + */ + rs_var = _get_vb_num(vars, ctx->row_status_index); + if (NULL == rs_var) { + snmp_free_varbind(vars); + return SNMPERR_GENERR; + } + + if (*rs_var->val.integer != RS_CREATEANDWAIT) + *rs_var->val.integer = RS_CREATEANDWAIT; + + /* + * send set + */ + rc = netsnmp_query_set( vars, ctx->session ); + if (-2 == rc) + rc = SNMPERR_ABORT; + + snmp_free_varbind(vars); + + return rc; +} + + +/** + * send createAndGo request with all values + */ +static int +_row_status_state_all_values_createAndGo(netsnmp_state_machine_input *input, + netsnmp_state_machine_step *step) +{ + rowcreate_state *ctx; + netsnmp_variable_list *vars, *rs_var; + int rc; + + netsnmp_require_ptr_LRV( input, SNMPERR_GENERR ); + netsnmp_require_ptr_LRV( step, SNMPERR_GENERR ); + netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR ); + + ctx = (rowcreate_state *)input->input_context; + + DEBUGMSGT(("row_create:called", "called %s\n", step->name)); + + vars = snmp_clone_varbind(ctx->vars); + netsnmp_require_ptr_LRV(vars, SNMPERR_GENERR); + + /* + * make sure row stats is createAndGo + */ + rs_var = _get_vb_num(vars, ctx->row_status_index + 1); + if (NULL == rs_var) { + snmp_free_varbind(vars); + return SNMPERR_GENERR; + } + + if (*rs_var->val.integer != RS_CREATEANDGO) + *rs_var->val.integer = RS_CREATEANDGO; + + /* + * send set + */ + rc = netsnmp_query_set( vars, ctx->session ); + if (-2 == rc) + rc = SNMPERR_ABORT; + + snmp_free_varbind(vars); + + return rc; +} + +/** ************************************************************************** + * + * row api + * + */ +int +netsnmp_row_create(netsnmp_session *sess, netsnmp_variable_list *vars, + int row_status_index) +{ + netsnmp_state_machine_step rc_cleanup = + { "row_create_cleanup", 0, _row_status_state_cleanup, + 0, NULL, NULL, 0, NULL }; + netsnmp_state_machine_step rc_activate = + { "row_create_activate", 0, _row_status_state_activate, + 0, NULL, NULL, 0, NULL }; + netsnmp_state_machine_step rc_sv_cols = + { "row_create_single_value_cols", 0, + _row_status_state_single_value_cols, 0, &rc_activate,NULL, 0, NULL }; + netsnmp_state_machine_step rc_mv_cols = + { "row_create_multiple_values_cols", 0, + _row_status_state_multiple_values_cols, 0, &rc_activate, &rc_sv_cols, + 0, NULL }; + netsnmp_state_machine_step rc_sv_caw = + { "row_create_single_value_createAndWait", 0, + _row_status_state_single_value_createAndWait, 0, &rc_mv_cols, NULL, + 0, NULL }; + netsnmp_state_machine_step rc_av_caw = + { "row_create_all_values_createAndWait", 0, + _row_status_state_all_values_createAndWait, 0, &rc_activate, + &rc_sv_caw, 0, NULL }; + netsnmp_state_machine_step rc_av_cag = + { "row_create_all_values_createAndGo", 0, + _row_status_state_all_values_createAndGo, 0, NULL, &rc_av_caw, 0, + NULL }; + + netsnmp_state_machine_input sm_input = { "row_create_machine", 0, + &rc_av_cag, &rc_cleanup }; + rowcreate_state state; + + netsnmp_require_ptr_LRV( sess, SNMPERR_GENERR); + netsnmp_require_ptr_LRV( vars, SNMPERR_GENERR); + + state.session = sess; + state.vars = vars; + + state.row_status_index = row_status_index; + sm_input.input_context = &state; + + netsnmp_state_machine_run( &sm_input); + + return SNMPERR_SUCCESS; +} +#endif /* NETSNMP_FEATURE_REMOVE_ROW_CREATE */ +#endif /* NETSNMP_NO_WRITE_SUPPORT */ + + +/** @} */ |