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