net-snmp 5.7
snmp_client.c
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 #include <net-snmp/net-snmp-features.h>
00044 
00045 #include <stdio.h>
00046 #include <errno.h>
00047 #if HAVE_STDLIB_H
00048 #include <stdlib.h>
00049 #endif
00050 #if HAVE_STRING_H
00051 #include <string.h>
00052 #else
00053 #include <strings.h>
00054 #endif
00055 #if HAVE_UNISTD_H
00056 #include <unistd.h>
00057 #endif
00058 #include <sys/types.h>
00059 #if TIME_WITH_SYS_TIME
00060 # include <sys/time.h>
00061 # include <time.h>
00062 #else
00063 # if HAVE_SYS_TIME_H
00064 #  include <sys/time.h>
00065 # else
00066 #  include <time.h>
00067 # endif
00068 #endif
00069 #if HAVE_SYS_PARAM_H
00070 #include <sys/param.h>
00071 #endif
00072 #if HAVE_NETINET_IN_H
00073 #include <netinet/in.h>
00074 #endif
00075 #if HAVE_ARPA_INET_H
00076 #include <arpa/inet.h>
00077 #endif
00078 #if HAVE_SYS_SELECT_H
00079 #include <sys/select.h>
00080 #endif
00081 #if HAVE_SYSLOG_H
00082 #include <syslog.h>
00083 #endif
00084 
00085 #if HAVE_DMALLOC_H
00086 #include <dmalloc.h>
00087 #endif
00088 
00089 #include <net-snmp/types.h>
00090 
00091 #include <net-snmp/agent/ds_agent.h>
00092 #include <net-snmp/library/default_store.h>
00093 #include <net-snmp/library/snmp_api.h>
00094 #include <net-snmp/library/snmp_client.h>
00095 #include <net-snmp/library/snmp_secmod.h>
00096 #include <net-snmp/library/mib.h>
00097 #include <net-snmp/library/snmp_logging.h>
00098 #include <net-snmp/library/snmp_assert.h>
00099 #include <net-snmp/pdu_api.h>
00100 
00101 netsnmp_feature_child_of(snmp_client_all, libnetsnmp)
00102 
00103 netsnmp_feature_child_of(snmp_split_pdu, snmp_client_all)
00104 netsnmp_feature_child_of(snmp_reset_var_types, snmp_client_all)
00105 netsnmp_feature_child_of(query_set_default_session, snmp_client_all)
00106 netsnmp_feature_child_of(row_create, snmp_client_all)
00107 
00108 #ifndef BSD4_3
00109 #define BSD4_2
00110 #endif
00111 
00112 #ifndef FD_SET
00113 
00114 typedef long    fd_mask;
00115 #define NFDBITS (sizeof(fd_mask) * NBBY)        /* bits per mask */
00116 
00117 #define FD_SET(n, p)    ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
00118 #define FD_CLR(n, p)    ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
00119 #define FD_ISSET(n, p)  ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
00120 #define FD_ZERO(p)      memset((p), 0, sizeof(*(p)))
00121 #endif
00122 
00123 /*
00124  * Prototype definitions 
00125  */
00126 static int      snmp_synch_input(int op, netsnmp_session * session,
00127                                  int reqid, netsnmp_pdu *pdu, void *magic);
00128 
00129 netsnmp_pdu    *
00130 snmp_pdu_create(int command)
00131 {
00132     netsnmp_pdu    *pdu;
00133 
00134     pdu = (netsnmp_pdu *) calloc(1, sizeof(netsnmp_pdu));
00135     if (pdu) {
00136         pdu->version = SNMP_DEFAULT_VERSION;
00137         pdu->command = command;
00138         pdu->errstat = SNMP_DEFAULT_ERRSTAT;
00139         pdu->errindex = SNMP_DEFAULT_ERRINDEX;
00140         pdu->securityModel = SNMP_DEFAULT_SECMODEL;
00141         pdu->transport_data = NULL;
00142         pdu->transport_data_length = 0;
00143         pdu->securityNameLen = 0;
00144         pdu->contextNameLen = 0;
00145         pdu->time = 0;
00146         pdu->reqid = snmp_get_next_reqid();
00147         pdu->msgid = snmp_get_next_msgid();
00148     }
00149     return pdu;
00150 
00151 }
00152 
00153 
00154 /*
00155  * Add a null variable with the requested name to the end of the list of
00156  * variables for this pdu.
00157  */
00158 netsnmp_variable_list *
00159 snmp_add_null_var(netsnmp_pdu *pdu, const oid * name, size_t name_length)
00160 {
00161     return snmp_pdu_add_variable(pdu, name, name_length, ASN_NULL, NULL, 0);
00162 }
00163 
00164 
00165 #include <net-snmp/library/snmp_debug.h>
00166 static int
00167 snmp_synch_input(int op,
00168                  netsnmp_session * session,
00169                  int reqid, netsnmp_pdu *pdu, void *magic)
00170 {
00171     struct synch_state *state = (struct synch_state *) magic;
00172     int             rpt_type;
00173 
00174     if (reqid != state->reqid && pdu && pdu->command != SNMP_MSG_REPORT) {
00175         DEBUGMSGTL(("snmp_synch", "Unexpected response (ReqID: %d,%d - Cmd %d)\n",
00176                                    reqid, state->reqid, pdu->command ));
00177         return 0;
00178     }
00179 
00180     state->waiting = 0;
00181     DEBUGMSGTL(("snmp_synch", "Response (ReqID: %d - Cmd %d)\n",
00182                                reqid, (pdu ? pdu->command : -1)));
00183 
00184     if (op == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE && pdu) {
00185         if (pdu->command == SNMP_MSG_REPORT) {
00186             rpt_type = snmpv3_get_report_type(pdu);
00187             if (SNMPV3_IGNORE_UNAUTH_REPORTS ||
00188                 rpt_type == SNMPERR_NOT_IN_TIME_WINDOW) {
00189                 state->waiting = 1;
00190             }
00191             state->pdu = NULL;
00192             state->status = STAT_ERROR;
00193             session->s_snmp_errno = rpt_type;
00194             SET_SNMP_ERROR(rpt_type);
00195         } else if (pdu->command == SNMP_MSG_RESPONSE) {
00196             /*
00197              * clone the pdu to return to snmp_synch_response 
00198              */
00199             state->pdu = snmp_clone_pdu(pdu);
00200             state->status = STAT_SUCCESS;
00201             session->s_snmp_errno = SNMPERR_SUCCESS;
00202         }
00203         else {
00204             char msg_buf[50];
00205             state->status = STAT_ERROR;
00206             session->s_snmp_errno = SNMPERR_PROTOCOL;
00207             SET_SNMP_ERROR(SNMPERR_PROTOCOL);
00208             snprintf(msg_buf, sizeof(msg_buf), "Expected RESPONSE-PDU but got %s-PDU",
00209                      snmp_pdu_type(pdu->command));
00210             snmp_set_detail(msg_buf);
00211             return 0;
00212         }
00213     } else if (op == NETSNMP_CALLBACK_OP_TIMED_OUT) {
00214         state->pdu = NULL;
00215         state->status = STAT_TIMEOUT;
00216         session->s_snmp_errno = SNMPERR_TIMEOUT;
00217         SET_SNMP_ERROR(SNMPERR_TIMEOUT);
00218     } else if (op == NETSNMP_CALLBACK_OP_DISCONNECT) {
00219         state->pdu = NULL;
00220         state->status = STAT_ERROR;
00221         session->s_snmp_errno = SNMPERR_ABORT;
00222         SET_SNMP_ERROR(SNMPERR_ABORT);
00223     }
00224 
00225     return 1;
00226 }
00227 
00228 
00229 /*
00230  * Clone an SNMP variable data structure.
00231  * Sets pointers to structure private storage, or
00232  * allocates larger object identifiers and values as needed.
00233  *
00234  * Caller must make list association for cloned variable.
00235  *
00236  * Returns 0 if successful.
00237  */
00238 int
00239 snmp_clone_var(netsnmp_variable_list * var, netsnmp_variable_list * newvar)
00240 {
00241     if (!newvar || !var)
00242         return 1;
00243 
00244     memmove(newvar, var, sizeof(netsnmp_variable_list));
00245     newvar->next_variable = NULL;
00246     newvar->name = NULL;
00247     newvar->val.string = NULL;
00248     newvar->data = NULL;
00249     newvar->dataFreeHook = NULL;
00250     newvar->index = 0;
00251 
00252     /*
00253      * Clone the object identifier and the value.
00254      * Allocate memory iff original will not fit into local storage.
00255      */
00256     if (snmp_set_var_objid(newvar, var->name, var->name_length))
00257         return 1;
00258 
00259     /*
00260      * need a pointer to copy a string value. 
00261      */
00262     if (var->val.string) {
00263         if (var->val.string != &var->buf[0]) {
00264             if (var->val_len <= sizeof(var->buf))
00265                 newvar->val.string = newvar->buf;
00266             else {
00267                 newvar->val.string = (u_char *) malloc(var->val_len);
00268                 if (!newvar->val.string)
00269                     return 1;
00270             }
00271             memmove(newvar->val.string, var->val.string, var->val_len);
00272         } else {                /* fix the pointer to new local store */
00273             newvar->val.string = newvar->buf;
00274             /*
00275              * no need for a memmove, since we copied the whole var
00276              * struct (and thus var->buf) at the beginning of this function.
00277              */
00278         }
00279     } else {
00280         newvar->val.string = NULL;
00281         newvar->val_len = 0;
00282     }
00283 
00284     return 0;
00285 }
00286 
00287 
00288 /*
00289  * Possibly make a copy of source memory buffer.
00290  * Will reset destination pointer if source pointer is NULL.
00291  * Returns 0 if successful, 1 if memory allocation fails.
00292  */
00293 int
00294 snmp_clone_mem(void **dstPtr, const void *srcPtr, unsigned len)
00295 {
00296     *dstPtr = NULL;
00297     if (srcPtr) {
00298         *dstPtr = malloc(len + 1);
00299         if (!*dstPtr) {
00300             return 1;
00301         }
00302         memmove(*dstPtr, srcPtr, len);
00303         /*
00304          * this is for those routines that expect 0-terminated strings!!!
00305          * someone should rather have called strdup
00306          */
00307         ((char *) *dstPtr)[len] = 0;
00308     }
00309     return 0;
00310 }
00311 
00312 
00313 /*
00314  * Walks through a list of varbinds and frees and allocated memory,
00315  * restoring pointers to local buffers
00316  */
00317 void
00318 snmp_reset_var_buffers(netsnmp_variable_list * var)
00319 {
00320     while (var) {
00321         if (var->name != var->name_loc) {
00322             if(NULL != var->name)
00323                 free(var->name);
00324             var->name = var->name_loc;
00325             var->name_length = 0;
00326         }
00327         if (var->val.string != var->buf) {
00328             if (NULL != var->val.string)
00329                 free(var->val.string);
00330             var->val.string = var->buf;
00331             var->val_len = 0;
00332         }
00333         var = var->next_variable;
00334     }
00335 }
00336 
00337 /*
00338  * Creates and allocates a clone of the input PDU,
00339  * but does NOT copy the variables.
00340  * This function should be used with another function,
00341  * such as _copy_pdu_vars.
00342  *
00343  * Returns a pointer to the cloned PDU if successful.
00344  * Returns 0 if failure.
00345  */
00346 static
00347 netsnmp_pdu    *
00348 _clone_pdu_header(netsnmp_pdu *pdu)
00349 {
00350     netsnmp_pdu    *newpdu;
00351     struct snmp_secmod_def *sptr;
00352 
00353     newpdu = (netsnmp_pdu *) malloc(sizeof(netsnmp_pdu));
00354     if (!newpdu)
00355         return NULL;
00356     memmove(newpdu, pdu, sizeof(netsnmp_pdu));
00357 
00358     /*
00359      * reset copied pointers if copy fails 
00360      */
00361     newpdu->variables = NULL;
00362     newpdu->enterprise = NULL;
00363     newpdu->community = NULL;
00364     newpdu->securityEngineID = NULL;
00365     newpdu->securityName = NULL;
00366     newpdu->contextEngineID = NULL;
00367     newpdu->contextName = NULL;
00368     newpdu->transport_data = NULL;
00369 
00370     /*
00371      * copy buffers individually. If any copy fails, all are freed. 
00372      */
00373     if (snmp_clone_mem((void **) &newpdu->enterprise, pdu->enterprise,
00374                        sizeof(oid) * pdu->enterprise_length) ||
00375         snmp_clone_mem((void **) &newpdu->community, pdu->community,
00376                        pdu->community_len) ||
00377         snmp_clone_mem((void **) &newpdu->contextEngineID,
00378                        pdu->contextEngineID, pdu->contextEngineIDLen)
00379         || snmp_clone_mem((void **) &newpdu->securityEngineID,
00380                           pdu->securityEngineID, pdu->securityEngineIDLen)
00381         || snmp_clone_mem((void **) &newpdu->contextName, pdu->contextName,
00382                           pdu->contextNameLen)
00383         || snmp_clone_mem((void **) &newpdu->securityName,
00384                           pdu->securityName, pdu->securityNameLen)
00385         || snmp_clone_mem((void **) &newpdu->transport_data,
00386                           pdu->transport_data,
00387                           pdu->transport_data_length)) {
00388         snmp_free_pdu(newpdu);
00389         return NULL;
00390     }
00391     if ((sptr = find_sec_mod(newpdu->securityModel)) != NULL &&
00392         sptr->pdu_clone != NULL) {
00393         /*
00394          * call security model if it needs to know about this 
00395          */
00396         (*sptr->pdu_clone) (pdu, newpdu);
00397     }
00398 
00399     return newpdu;
00400 }
00401 
00402 static
00403 netsnmp_variable_list *
00404 _copy_varlist(netsnmp_variable_list * var,      /* source varList */
00405               int errindex,     /* index of variable to drop (if any) */
00406               int copy_count)
00407 {                               /* !=0 number variables to copy */
00408     netsnmp_variable_list *newhead, *newvar, *oldvar;
00409     int             ii = 0;
00410 
00411     newhead = NULL;
00412     oldvar = NULL;
00413 
00414     while (var && (copy_count-- > 0)) {
00415         /*
00416          * Drop the specified variable (if applicable) 
00417          */
00418         if (++ii == errindex) {
00419             var = var->next_variable;
00420             continue;
00421         }
00422 
00423         /*
00424          * clone the next variable. Cleanup if alloc fails 
00425          */
00426         newvar = (netsnmp_variable_list *)
00427             malloc(sizeof(netsnmp_variable_list));
00428         if (snmp_clone_var(var, newvar)) {
00429             if (newvar)
00430                 free((char *) newvar);
00431             snmp_free_varbind(newhead);
00432             return NULL;
00433         }
00434 
00435         /*
00436          * add cloned variable to new list  
00437          */
00438         if (NULL == newhead)
00439             newhead = newvar;
00440         if (oldvar)
00441             oldvar->next_variable = newvar;
00442         oldvar = newvar;
00443 
00444         var = var->next_variable;
00445     }
00446     return newhead;
00447 }
00448 
00449 
00450 /*
00451  * Copy some or all variables from source PDU to target PDU.
00452  * This function consolidates many of the needs of PDU variables:
00453  * Clone PDU : copy all the variables.
00454  * Split PDU : skip over some variables to copy other variables.
00455  * Fix PDU   : remove variable associated with error index.
00456  *
00457  * Designed to work with _clone_pdu_header.
00458  *
00459  * If drop_err is set, drop any variable associated with errindex.
00460  * If skip_count is set, skip the number of variable in pdu's list.
00461  * While copy_count is greater than zero, copy pdu variables to newpdu.
00462  *
00463  * If an error occurs, newpdu is freed and pointer is set to 0.
00464  *
00465  * Returns a pointer to the cloned PDU if successful.
00466  * Returns 0 if failure.
00467  */
00468 static
00469 netsnmp_pdu    *
00470 _copy_pdu_vars(netsnmp_pdu *pdu,        /* source PDU */
00471                netsnmp_pdu *newpdu,     /* target PDU */
00472                int drop_err,    /* !=0 drop errored variable */
00473                int skip_count,  /* !=0 number of variables to skip */
00474                int copy_count)
00475 {                               /* !=0 number of variables to copy */
00476     netsnmp_variable_list *var, *oldvar;
00477     int             ii, copied, drop_idx;
00478 
00479     if (!newpdu)
00480         return NULL;            /* where is PDU to copy to ? */
00481 
00482     if (drop_err)
00483         drop_idx = pdu->errindex - skip_count;
00484     else
00485         drop_idx = 0;
00486 
00487     var = pdu->variables;
00488     while (var && (skip_count-- > 0))   /* skip over pdu variables */
00489         var = var->next_variable;
00490 
00491     oldvar = NULL;
00492     ii = 0;
00493     copied = 0;
00494     if (pdu->flags & UCD_MSG_FLAG_FORCE_PDU_COPY)
00495         copied = 1;             /* We're interested in 'empty' responses too */
00496 
00497     newpdu->variables = _copy_varlist(var, drop_idx, copy_count);
00498     if (newpdu->variables)
00499         copied = 1;
00500 
00501 #if ALSO_TEMPORARILY_DISABLED
00502     /*
00503      * Error if bad errindex or if target PDU has no variables copied 
00504      */
00505     if ((drop_err && (ii < pdu->errindex))
00506 #if TEMPORARILY_DISABLED
00507         /*
00508          * SNMPv3 engineID probes are allowed to be empty.
00509          * See the comment in snmp_api.c for further details 
00510          */
00511         || copied == 0
00512 #endif
00513         ) {
00514         snmp_free_pdu(newpdu);
00515         return 0;
00516     }
00517 #endif
00518     return newpdu;
00519 }
00520 
00521 
00522 /*
00523  * Creates (allocates and copies) a clone of the input PDU.
00524  * If drop_err is set, don't copy any variable associated with errindex.
00525  * This function is called by snmp_clone_pdu and snmp_fix_pdu.
00526  *
00527  * Returns a pointer to the cloned PDU if successful.
00528  * Returns 0 if failure.
00529  */
00530 static
00531 netsnmp_pdu    *
00532 _clone_pdu(netsnmp_pdu *pdu, int drop_err)
00533 {
00534     netsnmp_pdu    *newpdu;
00535     newpdu = _clone_pdu_header(pdu);
00536     newpdu = _copy_pdu_vars(pdu, newpdu, drop_err, 0, 10000);   /* skip none, copy all */
00537 
00538     return newpdu;
00539 }
00540 
00541 
00542 /*
00543  * This function will clone a full varbind list
00544  *
00545  * Returns a pointer to the cloned varbind list if successful.
00546  * Returns 0 if failure
00547  */
00548 netsnmp_variable_list *
00549 snmp_clone_varbind(netsnmp_variable_list * varlist)
00550 {
00551     return _copy_varlist(varlist, 0, 10000);    /* skip none, copy all */
00552 }
00553 
00554 /*
00555  * This function will clone a PDU including all of its variables.
00556  *
00557  * Returns a pointer to the cloned PDU if successful.
00558  * Returns 0 if failure
00559  */
00560 netsnmp_pdu    *
00561 snmp_clone_pdu(netsnmp_pdu *pdu)
00562 {
00563     return _clone_pdu(pdu, 0);  /* copies all variables */
00564 }
00565 
00566 
00567 /*
00568  * This function will clone a PDU including some of its variables.
00569  *
00570  * If skip_count is not zero, it defines the number of variables to skip.
00571  * If copy_count is not zero, it defines the number of variables to copy.
00572  *
00573  * Returns a pointer to the cloned PDU if successful.
00574  * Returns 0 if failure.
00575  */
00576 #ifndef NETSNMP_FEATURE_REMOVE_SNMP_SPLIT_PDU
00577 netsnmp_pdu    *
00578 snmp_split_pdu(netsnmp_pdu *pdu, int skip_count, int copy_count)
00579 {
00580     netsnmp_pdu    *newpdu;
00581     newpdu = _clone_pdu_header(pdu);
00582     newpdu = _copy_pdu_vars(pdu, newpdu, 0,     /* don't drop any variables */
00583                             skip_count, copy_count);
00584 
00585     return newpdu;
00586 }
00587 #endif /* NETSNMP_FEATURE_REMOVE_SNMP_SPLIT_PDU */
00588 
00589 
00590 /*
00591  * If there was an error in the input pdu, creates a clone of the pdu
00592  * that includes all the variables except the one marked by the errindex.
00593  * The command is set to the input command and the reqid, errstat, and
00594  * errindex are set to default values.
00595  * If the error status didn't indicate an error, the error index didn't
00596  * indicate a variable, the pdu wasn't a get response message, the
00597  * marked variable was not present in the initial request, or there
00598  * would be no remaining variables, this function will return 0.
00599  * If everything was successful, a pointer to the fixed cloned pdu will
00600  * be returned.
00601  */
00602 netsnmp_pdu    *
00603 snmp_fix_pdu(netsnmp_pdu *pdu, int command)
00604 {
00605     netsnmp_pdu    *newpdu;
00606 
00607     if ((pdu->command != SNMP_MSG_RESPONSE)
00608         || (pdu->errstat == SNMP_ERR_NOERROR)
00609         || (NULL == pdu->variables)
00610         || (pdu->errindex > (int)snmp_varbind_len(pdu))
00611         || (pdu->errindex <= 0)) {
00612         return NULL;            /* pre-condition tests fail */
00613     }
00614 
00615     newpdu = _clone_pdu(pdu, 1);        /* copies all except errored variable */
00616     if (!newpdu)
00617         return NULL;
00618     if (!newpdu->variables) {
00619         snmp_free_pdu(newpdu);
00620         return NULL;            /* no variables. "should not happen" */
00621     }
00622     newpdu->command = command;
00623     newpdu->reqid = snmp_get_next_reqid();
00624     newpdu->msgid = snmp_get_next_msgid();
00625     newpdu->errstat = SNMP_DEFAULT_ERRSTAT;
00626     newpdu->errindex = SNMP_DEFAULT_ERRINDEX;
00627 
00628     return newpdu;
00629 }
00630 
00631 
00632 /*
00633  * Returns the number of variables bound to a PDU structure
00634  */
00635 unsigned long
00636 snmp_varbind_len(netsnmp_pdu *pdu)
00637 {
00638     register netsnmp_variable_list *vars;
00639     unsigned long   retVal = 0;
00640     if (pdu)
00641         for (vars = pdu->variables; vars; vars = vars->next_variable) {
00642             retVal++;
00643         }
00644 
00645     return retVal;
00646 }
00647 
00648 /*
00649  * Add object identifier name to SNMP variable.
00650  * If the name is large, additional memory is allocated.
00651  * Returns 0 if successful.
00652  */
00653 
00654 int
00655 snmp_set_var_objid(netsnmp_variable_list * vp,
00656                    const oid * objid, size_t name_length)
00657 {
00658     size_t          len = sizeof(oid) * name_length;
00659 
00660     if (vp->name != vp->name_loc && vp->name != NULL) {
00661         /*
00662          * Probably previously-allocated "big storage".  Better free it
00663          * else memory leaks possible.  
00664          */
00665         free(vp->name);
00666     }
00667 
00668     /*
00669      * use built-in storage for smaller values 
00670      */
00671     if (len <= sizeof(vp->name_loc)) {
00672         vp->name = vp->name_loc;
00673     } else {
00674         vp->name = (oid *) malloc(len);
00675         if (!vp->name)
00676             return 1;
00677     }
00678     if (objid)
00679         memmove(vp->name, objid, len);
00680     vp->name_length = name_length;
00681     return 0;
00682 }
00683 
00699 int
00700 snmp_set_var_typed_value(netsnmp_variable_list * newvar, u_char type,
00701                          const void * val_str, size_t val_len)
00702 {
00703     newvar->type = type;
00704     return snmp_set_var_value(newvar, val_str, val_len);
00705 }
00706 
00707 int
00708 snmp_set_var_typed_integer(netsnmp_variable_list * newvar,
00709                            u_char type, long val)
00710 {
00711     newvar->type = type;
00712     return snmp_set_var_value(newvar, &val, sizeof(long));
00713 }
00714 
00715 int
00716 count_varbinds(netsnmp_variable_list * var_ptr)
00717 {
00718     int             count = 0;
00719 
00720     for (; var_ptr != NULL; var_ptr = var_ptr->next_variable)
00721         count++;
00722 
00723     return count;
00724 }
00725 
00726 netsnmp_feature_child_of(count_varbinds_of_type, netsnmp_unused)
00727 #ifndef NETSNMP_FEATURE_REMOVE_COUNT_VARBINDS_OF_TYPE
00728 int
00729 count_varbinds_of_type(netsnmp_variable_list * var_ptr, u_char type)
00730 {
00731     int             count = 0;
00732 
00733     for (; var_ptr != NULL; var_ptr = var_ptr->next_variable)
00734         if (var_ptr->type == type)
00735             count++;
00736 
00737     return count;
00738 }
00739 #endif /* NETSNMP_FEATURE_REMOVE_COUNT_VARBINDS_OF_TYPE */
00740 
00741 netsnmp_feature_child_of(find_varind_of_type, netsnmp_unused)
00742 #ifndef NETSNMP_FEATURE_REMOVE_FIND_VARIND_OF_TYPE
00743 netsnmp_variable_list *
00744 find_varbind_of_type(netsnmp_variable_list * var_ptr, u_char type)
00745 {
00746     for (; var_ptr != NULL && var_ptr->type != type;
00747          var_ptr = var_ptr->next_variable);
00748 
00749     return var_ptr;
00750 }
00751 #endif /* NETSNMP_FEATURE_REMOVE_FIND_VARIND_OF_TYPE */
00752 
00753 netsnmp_variable_list*
00754 find_varbind_in_list( netsnmp_variable_list *vblist,
00755                       const oid *name, size_t len)
00756 {
00757     for (; vblist != NULL; vblist = vblist->next_variable)
00758         if (!snmp_oid_compare(vblist->name, vblist->name_length, name, len))
00759             return vblist;
00760 
00761     return NULL;
00762 }
00763 
00764 /*
00765  * Add some value to SNMP variable.
00766  * If the value is large, additional memory is allocated.
00767  * Returns 0 if successful.
00768  */
00769 
00770 int
00771 snmp_set_var_value(netsnmp_variable_list * vars,
00772                    const void * value, size_t len)
00773 {
00774     int             largeval = 1;
00775 
00776     /*
00777      * xxx-rks: why the unconditional free? why not use existing
00778      * memory, if len < vars->val_len ?
00779      */
00780     if (vars->val.string && vars->val.string != vars->buf) {
00781         free(vars->val.string);
00782     }
00783     vars->val.string = NULL;
00784     vars->val_len = 0;
00785 
00786     /*
00787      * use built-in storage for smaller values 
00788      */
00789     if (len <= sizeof(vars->buf)) {
00790         vars->val.string = (u_char *) vars->buf;
00791         largeval = 0;
00792     }
00793 
00794     if ((0 == len) || (NULL == value)) {
00795         vars->val.string[0] = 0;
00796         return 0;
00797     }
00798 
00799     vars->val_len = len;
00800     switch (vars->type) {
00801     case ASN_INTEGER:
00802     case ASN_UNSIGNED:
00803     case ASN_TIMETICKS:
00804     case ASN_COUNTER:
00805         if (value) {
00806             if (vars->val_len == sizeof(int)) {
00807                 if (ASN_INTEGER == vars->type) {
00808                     const int      *val_int 
00809                         = (const int *) value;
00810                     *(vars->val.integer) = (long) *val_int;
00811                 } else {
00812                     const u_int    *val_uint
00813                         = (const u_int *) value;
00814                     *(vars->val.integer) = (unsigned long) *val_uint;
00815                 }
00816             }
00817 #if SIZEOF_LONG != SIZEOF_INT
00818             else if (vars->val_len == sizeof(long)){
00819                 const u_long   *val_ulong
00820                     = (const u_long *) value;
00821                 *(vars->val.integer) = *val_ulong;
00822                 if (*(vars->val.integer) > 0xffffffff) {
00823                     snmp_log(LOG_ERR,"truncating integer value > 32 bits\n");
00824                     *(vars->val.integer) &= 0xffffffff;
00825                 }
00826             }
00827 #endif
00828 #if defined(SIZEOF_LONG_LONG) && (SIZEOF_LONG != SIZEOF_LONG_LONG)
00829 #if !defined(SIZEOF_INTMAX_T) || (SIZEOF_LONG_LONG != SIZEOF_INTMAX_T)
00830             else if (vars->val_len == sizeof(long long)){
00831                 const unsigned long long   *val_ullong
00832                     = (const unsigned long long *) value;
00833                 *(vars->val.integer) = (long) *val_ullong;
00834                 if (*(vars->val.integer) > 0xffffffff) {
00835                     snmp_log(LOG_ERR,"truncating integer value > 32 bits\n");
00836                     *(vars->val.integer) &= 0xffffffff;
00837                 }
00838             }
00839 #endif
00840 #endif
00841 #if defined(SIZEOF_INTMAX_T) && (SIZEOF_LONG != SIZEOF_INTMAX_T)
00842             else if (vars->val_len == sizeof(intmax_t)){
00843                 const uintmax_t *val_uintmax_t
00844                     = (const uintmax_t *) value;
00845                 *(vars->val.integer) = (long) *val_uintmax_t;
00846                 if (*(vars->val.integer) > 0xffffffff) {
00847                     snmp_log(LOG_ERR,"truncating integer value > 32 bits\n");
00848                     *(vars->val.integer) &= 0xffffffff;
00849                 }
00850             }
00851 #endif
00852 #if SIZEOF_SHORT != SIZEOF_INT
00853             else if (vars->val_len == sizeof(short)) {
00854                 if (ASN_INTEGER == vars->type) {
00855                     const short      *val_short 
00856                         = (const short *) value;
00857                     *(vars->val.integer) = (long) *val_short;
00858                 } else {
00859                     const u_short    *val_ushort
00860                         = (const u_short *) value;
00861                     *(vars->val.integer) = (unsigned long) *val_ushort;
00862                 }
00863             }
00864 #endif
00865             else if (vars->val_len == sizeof(char)) {
00866                 if (ASN_INTEGER == vars->type) {
00867                     const char      *val_char 
00868                         = (const char *) value;
00869                     *(vars->val.integer) = (long) *val_char;
00870                 } else {
00871                     const u_char    *val_uchar
00872                         = (const u_char *) value;
00873                     *(vars->val.integer) = (unsigned long) *val_uchar;
00874                 }
00875             }
00876             else {
00877                 snmp_log(LOG_ERR,"bad size for integer-like type (%d)\n",
00878                          (int)vars->val_len);
00879                 return (1);
00880             }
00881         } else
00882             *(vars->val.integer) = 0;
00883         vars->val_len = sizeof(long);
00884         break;
00885 
00886     case ASN_OBJECT_ID:
00887     case ASN_PRIV_IMPLIED_OBJECT_ID:
00888     case ASN_PRIV_INCL_RANGE:
00889     case ASN_PRIV_EXCL_RANGE:
00890         if (largeval) {
00891             vars->val.objid = (oid *) malloc(vars->val_len);
00892         }
00893         if (vars->val.objid == NULL) {
00894             snmp_log(LOG_ERR,"no storage for OID\n");
00895             return 1;
00896         }
00897         memmove(vars->val.objid, value, vars->val_len);
00898         break;
00899 
00900     case ASN_IPADDRESS: /* snmp_build_var_op treats IPADDR like a string */
00901         if (4 != vars->val_len) {
00902             netsnmp_assert("ipaddress length == 4");
00903         }
00905     case ASN_PRIV_IMPLIED_OCTET_STR:
00906     case ASN_OCTET_STR:
00907     case ASN_BIT_STR:
00908     case ASN_OPAQUE:
00909     case ASN_NSAP:
00910         if (vars->val_len >= sizeof(vars->buf)) {
00911             vars->val.string = (u_char *) malloc(vars->val_len + 1);
00912         }
00913         if (vars->val.string == NULL) {
00914             snmp_log(LOG_ERR,"no storage for string\n");
00915             return 1;
00916         }
00917         memmove(vars->val.string, value, vars->val_len);
00918         /*
00919          * Make sure the string is zero-terminated; some bits of code make
00920          * this assumption.  Easier to do this here than fix all these wrong
00921          * assumptions.  
00922          */
00923         vars->val.string[vars->val_len] = '\0';
00924         break;
00925 
00926     case SNMP_NOSUCHOBJECT:
00927     case SNMP_NOSUCHINSTANCE:
00928     case SNMP_ENDOFMIBVIEW:
00929     case ASN_NULL:
00930         vars->val_len = 0;
00931         vars->val.string = NULL;
00932         break;
00933 
00934 #ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES
00935     case ASN_OPAQUE_U64:
00936     case ASN_OPAQUE_I64:
00937 #endif                          /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */
00938     case ASN_COUNTER64:
00939         if (largeval) {
00940             snmp_log(LOG_ERR,"bad size for counter 64 (%d)\n",
00941                      (int)vars->val_len);
00942             return (1);
00943         }
00944         vars->val_len = sizeof(struct counter64);
00945         memmove(vars->val.counter64, value, vars->val_len);
00946         break;
00947 
00948 #ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES
00949     case ASN_OPAQUE_FLOAT:
00950         if (largeval) {
00951             snmp_log(LOG_ERR,"bad size for opaque float (%d)\n",
00952                      (int)vars->val_len);
00953             return (1);
00954         }
00955         vars->val_len = sizeof(float);
00956         memmove(vars->val.floatVal, value, vars->val_len);
00957         break;
00958 
00959     case ASN_OPAQUE_DOUBLE:
00960         if (largeval) {
00961             snmp_log(LOG_ERR,"bad size for opaque double (%d)\n",
00962                      (int)vars->val_len);
00963             return (1);
00964         }
00965         vars->val_len = sizeof(double);
00966         memmove(vars->val.doubleVal, value, vars->val_len);
00967         break;
00968 
00969 #endif                          /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */
00970 
00971     default:
00972         snmp_log(LOG_ERR,"Internal error in type switching\n");
00973         snmp_set_detail("Internal error in type switching\n");
00974         return (1);
00975     }
00976 
00977     return 0;
00978 }
00979 
00980 void
00981 snmp_replace_var_types(netsnmp_variable_list * vbl, u_char old_type,
00982                        u_char new_type)
00983 {
00984     while (vbl) {
00985         if (vbl->type == old_type) {
00986             snmp_set_var_typed_value(vbl, new_type, NULL, 0);
00987         }
00988         vbl = vbl->next_variable;
00989     }
00990 }
00991 
00992 #ifndef NETSNMP_FEATURE_REMOVE_SNMP_RESET_VAR_TYPES
00993 void
00994 snmp_reset_var_types(netsnmp_variable_list * vbl, u_char new_type)
00995 {
00996     while (vbl) {
00997         snmp_set_var_typed_value(vbl, new_type, NULL, 0);
00998         vbl = vbl->next_variable;
00999     }
01000 }
01001 #endif /* NETSNMP_FEATURE_REMOVE_SNMP_RESET_VAR_TYPES */
01002 
01003 int
01004 snmp_synch_response_cb(netsnmp_session * ss,
01005                        netsnmp_pdu *pdu,
01006                        netsnmp_pdu **response, snmp_callback pcb)
01007 {
01008     struct synch_state lstate, *state;
01009     snmp_callback   cbsav;
01010     void           *cbmagsav;
01011     int             numfds, count;
01012     fd_set          fdset;
01013     struct timeval  timeout, *tvp;
01014     int             block;
01015 
01016     memset((void *) &lstate, 0, sizeof(lstate));
01017     state = &lstate;
01018     cbsav = ss->callback;
01019     cbmagsav = ss->callback_magic;
01020     ss->callback = pcb;
01021     ss->callback_magic = (void *) state;
01022 
01023     if ((state->reqid = snmp_send(ss, pdu)) == 0) {
01024         snmp_free_pdu(pdu);
01025         state->status = STAT_ERROR;
01026     } else
01027         state->waiting = 1;
01028 
01029     while (state->waiting) {
01030         numfds = 0;
01031         FD_ZERO(&fdset);
01032         block = NETSNMP_SNMPBLOCK;
01033         tvp = &timeout;
01034         timerclear(tvp);
01035         snmp_sess_select_info_flags(0, &numfds, &fdset, tvp, &block,
01036                                     NETSNMP_SELECT_NOALARMS);
01037         if (block == 1)
01038             tvp = NULL;         /* block without timeout */
01039         count = select(numfds, &fdset, NULL, NULL, tvp);
01040         if (count > 0) {
01041             snmp_read(&fdset);
01042         } else {
01043             switch (count) {
01044             case 0:
01045                 snmp_timeout();
01046                 break;
01047             case -1:
01048                 if (errno == EINTR) {
01049                     continue;
01050                 } else {
01051                     snmp_errno = SNMPERR_GENERR;    /*MTCRITICAL_RESOURCE */
01052                     /*
01053                      * CAUTION! if another thread closed the socket(s)
01054                      * waited on here, the session structure was freed.
01055                      * It would be nice, but we can't rely on the pointer.
01056                      * ss->s_snmp_errno = SNMPERR_GENERR;
01057                      * ss->s_errno = errno;
01058                      */
01059                     snmp_set_detail(strerror(errno));
01060                 }
01061                 /*
01062                  * FALLTHRU 
01063                  */
01064             default:
01065                 state->status = STAT_ERROR;
01066                 state->waiting = 0;
01067             }
01068         }
01069 
01070         if ( ss->flags & SNMP_FLAGS_RESP_CALLBACK ) {
01071             void (*cb)(void);
01072             cb = (void (*)(void))(ss->myvoid);
01073             cb();        /* Used to invoke 'netsnmp_check_outstanding_agent_requests();'
01074                             on internal AgentX queries.  */
01075         }
01076     }
01077     *response = state->pdu;
01078     ss->callback = cbsav;
01079     ss->callback_magic = cbmagsav;
01080     return state->status;
01081 }
01082 
01083 int
01084 snmp_synch_response(netsnmp_session * ss,
01085                     netsnmp_pdu *pdu, netsnmp_pdu **response)
01086 {
01087     return snmp_synch_response_cb(ss, pdu, response, snmp_synch_input);
01088 }
01089 
01090 int
01091 snmp_sess_synch_response(void *sessp,
01092                          netsnmp_pdu *pdu, netsnmp_pdu **response)
01093 {
01094     netsnmp_session *ss;
01095     struct synch_state lstate, *state;
01096     snmp_callback   cbsav;
01097     void           *cbmagsav;
01098     int             numfds, count;
01099     fd_set          fdset;
01100     struct timeval  timeout, *tvp;
01101     int             block;
01102 
01103     ss = snmp_sess_session(sessp);
01104     if (ss == NULL) {
01105         return STAT_ERROR;
01106     }
01107 
01108     memset((void *) &lstate, 0, sizeof(lstate));
01109     state = &lstate;
01110     cbsav = ss->callback;
01111     cbmagsav = ss->callback_magic;
01112     ss->callback = snmp_synch_input;
01113     ss->callback_magic = (void *) state;
01114 
01115     if ((state->reqid = snmp_sess_send(sessp, pdu)) == 0) {
01116         snmp_free_pdu(pdu);
01117         state->status = STAT_ERROR;
01118     } else
01119         state->waiting = 1;
01120 
01121     while (state->waiting) {
01122         numfds = 0;
01123         FD_ZERO(&fdset);
01124         block = NETSNMP_SNMPBLOCK;
01125         tvp = &timeout;
01126         timerclear(tvp);
01127         snmp_sess_select_info_flags(sessp, &numfds, &fdset, tvp, &block,
01128                                     NETSNMP_SELECT_NOALARMS);
01129         if (block == 1)
01130             tvp = NULL;         /* block without timeout */
01131         count = select(numfds, &fdset, NULL, NULL, tvp);
01132         if (count > 0) {
01133             snmp_sess_read(sessp, &fdset);
01134         } else
01135             switch (count) {
01136             case 0:
01137                 snmp_sess_timeout(sessp);
01138                 break;
01139             case -1:
01140                 if (errno == EINTR) {
01141                     continue;
01142                 } else {
01143                     snmp_errno = SNMPERR_GENERR;    /*MTCRITICAL_RESOURCE */
01144                     /*
01145                      * CAUTION! if another thread closed the socket(s)
01146                      * waited on here, the session structure was freed.
01147                      * It would be nice, but we can't rely on the pointer.
01148                      * ss->s_snmp_errno = SNMPERR_GENERR;
01149                      * ss->s_errno = errno;
01150                      */
01151                     snmp_set_detail(strerror(errno));
01152                 }
01153                 /*
01154                  * FALLTHRU 
01155                  */
01156             default:
01157                 state->status = STAT_ERROR;
01158                 state->waiting = 0;
01159             }
01160     }
01161     *response = state->pdu;
01162     ss->callback = cbsav;
01163     ss->callback_magic = cbmagsav;
01164     return state->status;
01165 }
01166 
01167 
01168 const char     *
01169 snmp_errstring(int errstat)
01170 {
01171     const char * const error_string[19] = {
01172         "(noError) No Error",
01173         "(tooBig) Response message would have been too large.",
01174         "(noSuchName) There is no such variable name in this MIB.",
01175         "(badValue) The value given has the wrong type or length.",
01176         "(readOnly) The two parties used do not have access to use the specified SNMP PDU.",
01177         "(genError) A general failure occured",
01178         "noAccess",
01179         "wrongType (The set datatype does not match the data type the agent expects)",
01180         "wrongLength (The set value has an illegal length from what the agent expects)",
01181         "wrongEncoding",
01182         "wrongValue (The set value is illegal or unsupported in some way)",
01183         "noCreation (That table does not support row creation or that object can not ever be created)",
01184         "inconsistentValue (The set value is illegal or unsupported in some way)",
01185         "resourceUnavailable (This is likely a out-of-memory failure within the agent)",
01186         "commitFailed",
01187         "undoFailed",
01188         "authorizationError (access denied to that object)",
01189         "notWritable (That object does not support modification)",
01190         "inconsistentName (That object can not currently be created)"
01191     };
01192 
01193     if (errstat <= MAX_SNMP_ERR && errstat >= SNMP_ERR_NOERROR) {
01194         return error_string[errstat];
01195     } else {
01196         return "Unknown Error";
01197     }
01198 }
01199 
01200 
01201 
01202 /*
01203  *
01204  *  Convenience routines to make various requests
01205  *  over the specified SNMP session.
01206  *
01207  */
01208 #include <net-snmp/library/snmp_debug.h>
01209 
01210 static netsnmp_session *_def_query_session = NULL;
01211 
01212 #ifndef NETSNMP_FEATURE_REMOVE_QUERY_SET_DEFAULT_SESSION
01213 void
01214 netsnmp_query_set_default_session( netsnmp_session *sess) {
01215     DEBUGMSGTL(("iquery", "set default session %p\n", sess));
01216     _def_query_session = sess;
01217 }
01218 #endif /* NETSNMP_FEATURE_REMOVE_QUERY_SET_DEFAULT_SESSION */
01219 
01223 netsnmp_session *
01224 netsnmp_query_get_default_session_unchecked( void ) {
01225     DEBUGMSGTL(("iquery", "get default session %p\n", _def_query_session));
01226     return _def_query_session;
01227 }
01228 
01233 netsnmp_session *
01234 netsnmp_query_get_default_session( void ) {
01235     static int warning_logged = 0;
01236 
01237     if (! _def_query_session && ! warning_logged) {
01238         if (! netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID,
01239                                     NETSNMP_DS_AGENT_INTERNAL_SECNAME)) {
01240             snmp_log(LOG_WARNING,
01241                      "iquerySecName has not been configured - internal queries will fail\n");
01242         } else {
01243             snmp_log(LOG_WARNING,
01244                      "default session is not available - internal queries will fail\n");
01245         }
01246         warning_logged = 1;
01247     }
01248 
01249     return netsnmp_query_get_default_session_unchecked();
01250 }
01251 
01252 
01253 /*
01254  * Internal utility routine to actually send the query
01255  */
01256 static int _query(netsnmp_variable_list *list,
01257                   int                    request,
01258                   netsnmp_session       *session) {
01259 
01260     netsnmp_pdu *pdu      = snmp_pdu_create( request );
01261     netsnmp_pdu *response = NULL;
01262     netsnmp_variable_list *vb1, *vb2, *vtmp;
01263     int ret, count;
01264 
01265     DEBUGMSGTL(("iquery", "query on session %p\n", session));
01266     /*
01267      * Clone the varbind list into the request PDU...
01268      */
01269     pdu->variables = snmp_clone_varbind( list );
01270 retry:
01271     if ( session )
01272         ret = snmp_synch_response(            session, pdu, &response );
01273     else if (_def_query_session)
01274         ret = snmp_synch_response( _def_query_session, pdu, &response );
01275     else {
01276         /* No session specified */
01277         snmp_free_pdu(pdu);
01278         return SNMP_ERR_GENERR;
01279     }
01280     DEBUGMSGTL(("iquery", "query returned %d\n", ret));
01281 
01282     /*
01283      * ....then copy the results back into the
01284      * list (assuming the request succeeded!).
01285      * This avoids having to worry about how this
01286      * list was originally allocated.
01287      */
01288     if ( ret == SNMP_ERR_NOERROR ) {
01289         if ( response->errstat != SNMP_ERR_NOERROR ) {
01290             DEBUGMSGT(("iquery", "Error in packet: %s\n",
01291                        snmp_errstring(response->errstat)));
01292             /*
01293              * If the request failed, then remove the
01294              *  offending varbind and try again.
01295              *  (all except SET requests)
01296              *
01297              * XXX - implement a library version of
01298              *       NETSNMP_DS_APP_DONT_FIX_PDUS ??
01299              */
01300             ret = response->errstat;
01301             if (response->errindex != 0) {
01302                 DEBUGMSGT(("iquery:result", "Failed object:\n"));
01303                 for (count = 1, vtmp = response->variables;
01304                      vtmp && count != response->errindex;
01305                      vtmp = vtmp->next_variable, count++)
01306                     /*EMPTY*/;
01307                 if (vtmp)
01308                     DEBUGMSGVAR(("iquery:result", vtmp));
01309                 DEBUGMSG(("iquery:result", "\n"));
01310             }
01311 #ifndef NETSNMP_NO_WRITE_SUPPORT
01312             if (request != SNMP_MSG_SET &&
01313                 response->errindex != 0) {
01314                 DEBUGMSGTL(("iquery", "retrying query (%d, %ld)\n", ret, response->errindex));
01315                 pdu = snmp_fix_pdu( response, request );
01316                 snmp_free_pdu( response );
01317                 response = NULL;
01318                 if ( pdu != NULL )
01319                     goto retry;
01320             }
01321 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
01322         } else {
01323             for (vb1 = response->variables, vb2 = list;
01324                  vb1;
01325                  vb1 = vb1->next_variable,  vb2 = vb2->next_variable) {
01326                 DEBUGMSGVAR(("iquery:result", vb1));
01327                 DEBUGMSG(("iquery:results", "\n"));
01328                 if ( !vb2 ) {
01329                     ret = SNMP_ERR_GENERR;
01330                     break;
01331                 }
01332                 vtmp = vb2->next_variable;
01333                 snmp_free_var_internals( vb2 );
01334                 snmp_clone_var( vb1, vb2 );
01335                 vb2->next_variable = vtmp;
01336             }
01337         }
01338     } else {
01339         /* Distinguish snmp_send errors from SNMP errStat errors */
01340         ret = -ret;
01341     }
01342     snmp_free_pdu( response );
01343     return ret;
01344 }
01345 
01346 /*
01347  * These are simple wrappers round the internal utility routine
01348  */
01349 int netsnmp_query_get(netsnmp_variable_list *list,
01350                       netsnmp_session       *session){
01351     return _query( list, SNMP_MSG_GET, session );
01352 }
01353 
01354 
01355 int netsnmp_query_getnext(netsnmp_variable_list *list,
01356                           netsnmp_session       *session){
01357     return _query( list, SNMP_MSG_GETNEXT, session );
01358 }
01359 
01360 
01361 #ifndef NETSNMP_NO_WRITE_SUPPORT
01362 int netsnmp_query_set(netsnmp_variable_list *list,
01363                       netsnmp_session       *session){
01364     return _query( list, SNMP_MSG_SET, session );
01365 }
01366 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
01367 
01368 /*
01369  * A walk needs a bit more work.
01370  */
01371 int netsnmp_query_walk(netsnmp_variable_list *list,
01372                        netsnmp_session       *session) {
01373     /*
01374      * Create a working copy of the original (single)
01375      * varbind, so we can use this varbind parameter
01376      * to check when we've finished walking this subtree.
01377      */
01378     netsnmp_variable_list *vb = snmp_clone_varbind( list );
01379     netsnmp_variable_list *res_list = NULL;
01380     netsnmp_variable_list *res_last = NULL;
01381     int ret;
01382 
01383     /*
01384      * Now walk the tree as usual
01385      */
01386     ret = _query( vb, SNMP_MSG_GETNEXT, session );
01387     while ( ret == SNMP_ERR_NOERROR &&
01388         snmp_oidtree_compare( list->name, list->name_length,
01389                                 vb->name,   vb->name_length ) == 0) {
01390 
01391         /*
01392          * Copy each response varbind to the end of the result list
01393          * and then re-use this to ask for the next entry.
01394          */
01395         if ( res_last ) {
01396             res_last->next_variable = snmp_clone_varbind( vb );
01397             res_last = res_last->next_variable;
01398         } else {
01399             res_list = snmp_clone_varbind( vb );
01400             res_last = res_list;
01401         }
01402         ret = _query( vb, SNMP_MSG_GETNEXT, session );
01403     }
01404     /*
01405      * Copy the first result back into the original varbind parameter,
01406      * add the rest of the results (if any), and clean up.
01407      */
01408     if ( res_list ) {
01409         snmp_clone_var( res_list, list );
01410         list->next_variable = res_list->next_variable;
01411         res_list->next_variable = NULL;
01412         snmp_free_varbind( res_list );
01413     }
01414     snmp_free_varbind( vb );
01415     return ret;
01416 }
01417 
01423 int
01424 netsnmp_state_machine_run( netsnmp_state_machine_input *input)
01425 {
01426     netsnmp_state_machine_step *current, *last;
01427 
01428     netsnmp_require_ptr_LRV( input, SNMPERR_GENERR );
01429     netsnmp_require_ptr_LRV( input->steps, SNMPERR_GENERR );
01430     last = current = input->steps;
01431 
01432     DEBUGMSGT(("state_machine:run", "starting step: %s\n", current->name));
01433 
01434     while (current) {
01435 
01436         /*
01437          * log step and check for required data
01438          */
01439         DEBUGMSGT(("state_machine:run", "at step: %s\n", current->name));
01440         if (NULL == current->run) {
01441             DEBUGMSGT(("state_machine:run", "no run step\n"));
01442             current->result = last->result;
01443             break;
01444         }
01445 
01446         /*
01447          * run step
01448          */
01449         DEBUGMSGT(("state_machine:run", "running step: %s\n", current->name));
01450         current->result = (*current->run)( input, current );
01451         ++input->steps_so_far;
01452         
01453         /*
01454          * log result and move to next step
01455          */
01456         input->last_run = current;
01457         DEBUGMSGT(("state_machine:run:result", "step %s returned %d\n",
01458                    current->name, current->result));
01459         if (SNMPERR_SUCCESS == current->result)
01460             current = current->on_success;
01461         else if (SNMPERR_ABORT == current->result) {
01462             DEBUGMSGT(("state_machine:run:result", "ABORT from %s\n",
01463                        current->name));
01464             break;
01465         }
01466         else
01467             current = current->on_error;
01468     }
01469 
01470     /*
01471      * run cleanup
01472      */
01473     if ((input->cleanup) && (input->cleanup->run))
01474         (*input->cleanup->run)( input, input->last_run );
01475 
01476     return input->last_run->result;
01477 }
01478 
01479 #ifndef NETSNMP_NO_WRITE_SUPPORT
01480 #ifndef NETSNMP_FEATURE_REMOVE_ROW_CREATE
01481 
01486 typedef struct rowcreate_state_s {
01487 
01488     netsnmp_session        *session;
01489     netsnmp_variable_list  *vars;
01490     int                     row_status_index;
01491 } rowcreate_state;
01492 
01493 static netsnmp_variable_list *
01494 _get_vb_num(netsnmp_variable_list *vars, int index)
01495 {
01496     for (; vars && index > 0; --index)
01497         vars = vars->next_variable;
01498 
01499     if (!vars || index > 0)
01500         return NULL;
01501     
01502     return vars;
01503 }
01504 
01505 
01506 /*
01507  * cleanup
01508  */
01509 static int 
01510 _row_status_state_cleanup(netsnmp_state_machine_input *input,
01511                  netsnmp_state_machine_step *step)
01512 {
01513     rowcreate_state       *ctx;
01514 
01515     netsnmp_require_ptr_LRV( input, SNMPERR_ABORT );
01516     netsnmp_require_ptr_LRV( step, SNMPERR_ABORT );
01517 
01518     DEBUGMSGT(("row_create:called", "_row_status_state_cleanup, last run step was %s rc %d\n",
01519                step->name, step->result));
01520 
01521     ctx = (rowcreate_state *)input->input_context;
01522     if (ctx && ctx->vars)
01523         snmp_free_varbind( ctx->vars );
01524 
01525     return SNMPERR_SUCCESS;
01526 }
01527 
01528 /*
01529  * send a request to activate the row
01530  */
01531 static int 
01532 _row_status_state_activate(netsnmp_state_machine_input *input,
01533                   netsnmp_state_machine_step *step)
01534 {
01535     rowcreate_state       *ctx;
01536     netsnmp_variable_list *rs_var, *var = NULL;
01537     int32_t                rc, val = RS_ACTIVE;
01538 
01539     netsnmp_require_ptr_LRV( input, SNMPERR_GENERR );
01540     netsnmp_require_ptr_LRV( step, SNMPERR_GENERR );
01541     netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR );
01542 
01543     ctx = (rowcreate_state *)input->input_context;
01544 
01545     DEBUGMSGT(("row_create:called", "called %s\n", step->name));
01546 
01547     /*
01548      * just send the rowstatus varbind
01549      */
01550     rs_var = _get_vb_num(ctx->vars, ctx->row_status_index);
01551     netsnmp_require_ptr_LRV(rs_var, SNMPERR_GENERR);
01552 
01553     var = snmp_varlist_add_variable(&var, rs_var->name, rs_var->name_length,
01554                                     rs_var->type, &val, sizeof(val));
01555     netsnmp_require_ptr_LRV( var, SNMPERR_GENERR );
01556 
01557     /*
01558      * send set
01559      */
01560     rc = netsnmp_query_set( var, ctx->session );
01561     if (-2 == rc)
01562         rc = SNMPERR_ABORT;
01563 
01564     snmp_free_varbind(var);
01565 
01566     return rc;
01567 }
01568 
01569 /*
01570  * send each non-row status column, one at a time
01571  */
01572 static int 
01573 _row_status_state_single_value_cols(netsnmp_state_machine_input *input,
01574                                     netsnmp_state_machine_step *step)
01575 {
01576     rowcreate_state       *ctx;
01577     netsnmp_variable_list *var, *tmp_next, *row_status;
01578     int                    rc = SNMPERR_GENERR;
01579 
01580     netsnmp_require_ptr_LRV( input, SNMPERR_GENERR );
01581     netsnmp_require_ptr_LRV( step, SNMPERR_GENERR );
01582     netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR );
01583 
01584     ctx = (rowcreate_state *)input->input_context;
01585 
01586     DEBUGMSGT(("row_create:called", "called %s\n", step->name));
01587 
01588     row_status = _get_vb_num(ctx->vars, ctx->row_status_index);
01589     netsnmp_require_ptr_LRV(row_status, SNMPERR_GENERR);
01590 
01591     /*
01592      * try one varbind at a time
01593      */
01594     for (var = ctx->vars; var; var = var->next_variable) {
01595         if (var == row_status)
01596             continue;
01597 
01598         tmp_next = var->next_variable;
01599         var->next_variable = NULL;
01600 
01601         /*
01602          * send set
01603          */
01604         rc = netsnmp_query_set( var, ctx->session );
01605         var->next_variable = tmp_next;
01606         if (-2 == rc)
01607             rc = SNMPERR_ABORT;
01608         if (rc != SNMPERR_SUCCESS)
01609             break;
01610     }
01611 
01612     return rc;
01613 }
01614 
01615 /*
01616  * send all values except row status
01617  */
01618 static int 
01619 _row_status_state_multiple_values_cols(netsnmp_state_machine_input *input,
01620                                        netsnmp_state_machine_step *step)
01621 {
01622     rowcreate_state       *ctx;
01623     netsnmp_variable_list *vars, *var, *last, *row_status;
01624     int                    rc;
01625 
01626     netsnmp_require_ptr_LRV( input, SNMPERR_GENERR );
01627     netsnmp_require_ptr_LRV( step, SNMPERR_GENERR );
01628     netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR );
01629 
01630     ctx = (rowcreate_state *)input->input_context;
01631 
01632     DEBUGMSGT(("row_create:called", "called %s\n", step->name));
01633 
01634     vars = snmp_clone_varbind(ctx->vars);
01635     netsnmp_require_ptr_LRV(vars, SNMPERR_GENERR);
01636 
01637     row_status = _get_vb_num(vars, ctx->row_status_index);
01638     if (NULL == row_status) {
01639         snmp_free_varbind(vars);
01640         return SNMPERR_GENERR;
01641     }
01642 
01643     /*
01644      * remove row status varbind
01645      */
01646     if (row_status == vars) {
01647         vars = row_status->next_variable;
01648         row_status->next_variable = NULL;
01649     }
01650     else {
01651         for (last=vars, var=last->next_variable;
01652              var;
01653              last=var, var = var->next_variable) {
01654             if (var == row_status) {
01655                 last->next_variable = var->next_variable;
01656                 break;
01657             }
01658         }
01659     }
01660     snmp_free_var(row_status);
01661 
01662     /*
01663      * send set
01664      */
01665     rc = netsnmp_query_set( vars, ctx->session );
01666     if (-2 == rc)
01667         rc = SNMPERR_ABORT;
01668 
01669     snmp_free_varbind(vars);
01670 
01671     return rc;
01672 }
01673 
01674 /*
01675  * send a createAndWait request with no other values
01676  */
01677 static int 
01678 _row_status_state_single_value_createAndWait(netsnmp_state_machine_input *input,
01679                                              netsnmp_state_machine_step *step)
01680 {
01681     rowcreate_state       *ctx;
01682     netsnmp_variable_list *var = NULL, *rs_var;
01683     int32_t                rc, val = RS_CREATEANDWAIT;
01684 
01685     netsnmp_require_ptr_LRV( input, SNMPERR_GENERR );
01686     netsnmp_require_ptr_LRV( step, SNMPERR_GENERR );
01687     netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR );
01688 
01689     ctx = (rowcreate_state *)input->input_context;
01690 
01691     DEBUGMSGT(("row_create:called", "called %s\n", step->name));
01692 
01693     rs_var = _get_vb_num(ctx->vars, ctx->row_status_index);
01694     netsnmp_require_ptr_LRV(rs_var, SNMPERR_GENERR);
01695 
01696     var = snmp_varlist_add_variable(&var, rs_var->name, rs_var->name_length,
01697                                     rs_var->type, &val, sizeof(val));
01698     netsnmp_require_ptr_LRV(var, SNMPERR_GENERR);
01699 
01700     /*
01701      * send set
01702      */
01703     rc = netsnmp_query_set( var, ctx->session );
01704     if (-2 == rc)
01705         rc = SNMPERR_ABORT;
01706 
01707     snmp_free_varbind(var);
01708 
01709     return rc;
01710 }
01711 
01712 /*
01713  * send a creatAndWait request with all values
01714  */
01715 static int 
01716 _row_status_state_all_values_createAndWait(netsnmp_state_machine_input *input,
01717                                            netsnmp_state_machine_step *step)
01718 {
01719     rowcreate_state       *ctx;
01720     netsnmp_variable_list *vars, *rs_var;
01721     int                    rc;
01722 
01723     netsnmp_require_ptr_LRV( input, SNMPERR_GENERR );
01724     netsnmp_require_ptr_LRV( step, SNMPERR_GENERR );
01725     netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR );
01726 
01727     ctx = (rowcreate_state *)input->input_context;
01728 
01729     DEBUGMSGT(("row_create:called", "called %s\n", step->name));
01730 
01731     vars = snmp_clone_varbind(ctx->vars);
01732     netsnmp_require_ptr_LRV(vars, SNMPERR_GENERR);
01733 
01734     /*
01735      * make sure row stats is createAndWait
01736      */
01737     rs_var = _get_vb_num(vars, ctx->row_status_index);
01738     if (NULL == rs_var) {
01739         snmp_free_varbind(vars);
01740         return SNMPERR_GENERR;
01741     }
01742 
01743     if (*rs_var->val.integer != RS_CREATEANDWAIT)
01744         *rs_var->val.integer = RS_CREATEANDWAIT;
01745 
01746     /*
01747      * send set
01748      */
01749     rc = netsnmp_query_set( vars, ctx->session );
01750     if (-2 == rc)
01751         rc = SNMPERR_ABORT;
01752 
01753     snmp_free_varbind(vars);
01754 
01755     return rc;
01756 }
01757 
01758 
01762 static int 
01763 _row_status_state_all_values_createAndGo(netsnmp_state_machine_input *input,
01764                                          netsnmp_state_machine_step *step)
01765 {
01766     rowcreate_state       *ctx;
01767     netsnmp_variable_list *vars, *rs_var;
01768     int                    rc;
01769 
01770     netsnmp_require_ptr_LRV( input, SNMPERR_GENERR );
01771     netsnmp_require_ptr_LRV( step, SNMPERR_GENERR );
01772     netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR );
01773 
01774     ctx = (rowcreate_state *)input->input_context;
01775 
01776     DEBUGMSGT(("row_create:called", "called %s\n", step->name));
01777 
01778     vars = snmp_clone_varbind(ctx->vars);
01779     netsnmp_require_ptr_LRV(vars, SNMPERR_GENERR);
01780 
01781     /*
01782      * make sure row stats is createAndGo
01783      */
01784     rs_var = _get_vb_num(vars, ctx->row_status_index + 1);
01785     if (NULL == rs_var) {
01786         snmp_free_varbind(vars);
01787         return SNMPERR_GENERR;
01788     }
01789 
01790     if (*rs_var->val.integer != RS_CREATEANDGO)
01791         *rs_var->val.integer = RS_CREATEANDGO;
01792 
01793     /*
01794      * send set
01795      */
01796     rc = netsnmp_query_set( vars, ctx->session );
01797     if (-2 == rc)
01798         rc = SNMPERR_ABORT;
01799 
01800     snmp_free_varbind(vars);
01801 
01802     return rc;
01803 }
01804 
01810 int
01811 netsnmp_row_create(netsnmp_session *sess, netsnmp_variable_list *vars,
01812                    int row_status_index)
01813 {
01814     netsnmp_state_machine_step rc_cleanup =
01815         { "row_create_cleanup", 0, _row_status_state_cleanup,
01816           0, NULL, NULL, 0, NULL };
01817     netsnmp_state_machine_step rc_activate =
01818         { "row_create_activate", 0, _row_status_state_activate,
01819           0, NULL, NULL, 0, NULL };
01820     netsnmp_state_machine_step rc_sv_cols =
01821         { "row_create_single_value_cols", 0,
01822           _row_status_state_single_value_cols, 0, &rc_activate,NULL, 0, NULL };
01823     netsnmp_state_machine_step rc_mv_cols =
01824         { "row_create_multiple_values_cols", 0,
01825           _row_status_state_multiple_values_cols, 0, &rc_activate, &rc_sv_cols,
01826           0, NULL };
01827     netsnmp_state_machine_step rc_sv_caw =
01828         { "row_create_single_value_createAndWait", 0,
01829           _row_status_state_single_value_createAndWait, 0, &rc_mv_cols, NULL,
01830           0, NULL };
01831     netsnmp_state_machine_step rc_av_caw =
01832         { "row_create_all_values_createAndWait", 0,
01833           _row_status_state_all_values_createAndWait, 0, &rc_activate,
01834           &rc_sv_caw, 0, NULL };
01835     netsnmp_state_machine_step rc_av_cag =
01836         { "row_create_all_values_createAndGo", 0,
01837           _row_status_state_all_values_createAndGo, 0, NULL, &rc_av_caw, 0,
01838           NULL };
01839 
01840     netsnmp_state_machine_input sm_input = { "row_create_machine", 0,
01841                                              &rc_av_cag, &rc_cleanup };
01842     rowcreate_state state;
01843 
01844     netsnmp_require_ptr_LRV( sess, SNMPERR_GENERR);
01845     netsnmp_require_ptr_LRV( vars, SNMPERR_GENERR);
01846 
01847     state.session = sess;
01848     state.vars = vars;
01849 
01850     state.row_status_index = row_status_index;
01851     sm_input.input_context = &state;
01852 
01853     netsnmp_state_machine_run( &sm_input);
01854 
01855     return SNMPERR_SUCCESS;
01856 }
01857 #endif /* NETSNMP_FEATURE_REMOVE_ROW_CREATE */
01858 #endif /* NETSNMP_NO_WRITE_SUPPORT */
01859 
01860