Logo
Home page Net-SNMP

Archive Search:

Require all words?

Site Search:
Google
Main Page | Modules | Data Structures | File List | Data Fields | Related Pages | Examples

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) {
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 SIZEOF_LONG != SIZEOF_LONG_LONG
00811 #if defined (WIN32) && !defined (mingw32)
00812             else if (vars->val_len == sizeof(__int64)){
00813                 const unsigned __int64   *val_ullong
00814                     = (const unsigned __int64 *) value;
00815 #else
00816                 else if (vars->val_len == sizeof(long long)){
00817                 const unsigned long long   *val_ullong
00818                     = (const unsigned long long *) value;
00819 #endif
00820                 *(vars->val.integer) = (long) *val_ullong;
00821                 if (*(vars->val.integer) > 0xffffffff) {
00822                     snmp_log(LOG_ERR,"truncating integer value > 32 bits\n");
00823                     *(vars->val.integer) &= 0xffffffff;
00824                 }
00825             }
00826 #endif
00827 #if SIZEOF_SHORT != SIZEOF_INT
00828             else if (vars->val_len == sizeof(short)) {
00829                 if (ASN_INTEGER == vars->type) {
00830                     const short      *val_short 
00831                         = (const short *) value;
00832                     *(vars->val.integer) = (long) *val_short;
00833                 } else {
00834                     const u_short    *val_ushort
00835                         = (const u_short *) value;
00836                     *(vars->val.integer) = (unsigned long) *val_ushort;
00837                 }
00838             }
00839 #endif
00840             else if (vars->val_len == sizeof(char)) {
00841                 if (ASN_INTEGER == vars->type) {
00842                     const char      *val_char 
00843                         = (const char *) value;
00844                     *(vars->val.integer) = (long) *val_char;
00845                 } else {
00846                     *(vars->val.integer) = (unsigned long) *value;
00847                 }
00848             }
00849             else {
00850                 snmp_log(LOG_ERR,"bad size for integer-like type (%d)\n",
00851                          vars->val_len);
00852                 return (1);
00853             }
00854         } else
00855             *(vars->val.integer) = 0;
00856         vars->val_len = sizeof(long);
00857         break;
00858 
00859     case ASN_OBJECT_ID:
00860     case ASN_PRIV_IMPLIED_OBJECT_ID:
00861     case ASN_PRIV_INCL_RANGE:
00862     case ASN_PRIV_EXCL_RANGE:
00863         if (largeval) {
00864             vars->val.objid = (oid *) malloc(vars->val_len);
00865         }
00866         if (vars->val.objid == NULL) {
00867             snmp_log(LOG_ERR,"no storage for OID\n");
00868             return 1;
00869         }
00870         memmove(vars->val.objid, value, vars->val_len);
00871         break;
00872 
00873     case ASN_IPADDRESS: /* snmp_build_var_op treats IPADDR like a string */
00874         if (4 != vars->val_len) {
00875             netsnmp_assert("ipaddress length == 4");
00876         }
00878     case ASN_PRIV_IMPLIED_OCTET_STR:
00879     case ASN_OCTET_STR:
00880     case ASN_BIT_STR:
00881     case ASN_OPAQUE:
00882     case ASN_NSAP:
00883         if (largeval) {
00884             vars->val.string = (u_char *) malloc(vars->val_len + 1);
00885         }
00886         if (vars->val.string == NULL) {
00887             snmp_log(LOG_ERR,"no storage for OID\n");
00888             return 1;
00889         }
00890         memmove(vars->val.string, value, vars->val_len);
00891         /*
00892          * Make sure the string is zero-terminated; some bits of code make
00893          * this assumption.  Easier to do this here than fix all these wrong
00894          * assumptions.  
00895          */
00896         vars->val.string[vars->val_len] = '\0';
00897         break;
00898 
00899     case SNMP_NOSUCHOBJECT:
00900     case SNMP_NOSUCHINSTANCE:
00901     case SNMP_ENDOFMIBVIEW:
00902     case ASN_NULL:
00903         vars->val_len = 0;
00904         vars->val.string = NULL;
00905         break;
00906 
00907 #ifdef OPAQUE_SPECIAL_TYPES
00908     case ASN_OPAQUE_U64:
00909     case ASN_OPAQUE_I64:
00910 #endif                          /* OPAQUE_SPECIAL_TYPES */
00911     case ASN_COUNTER64:
00912         if (largeval) {
00913             snmp_log(LOG_ERR,"bad size for counter 64 (%d)\n",
00914                      vars->val_len);
00915             return (1);
00916         }
00917         vars->val_len = sizeof(struct counter64);
00918         memmove(vars->val.counter64, value, vars->val_len);
00919         break;
00920 
00921 #ifdef OPAQUE_SPECIAL_TYPES
00922     case ASN_OPAQUE_FLOAT:
00923         if (largeval) {
00924             snmp_log(LOG_ERR,"bad size for opaque float (%d)\n",
00925                      vars->val_len);
00926             return (1);
00927         }
00928         vars->val_len = sizeof(float);
00929         memmove(vars->val.floatVal, value, vars->val_len);
00930         break;
00931 
00932     case ASN_OPAQUE_DOUBLE:
00933         if (largeval) {
00934             snmp_log(LOG_ERR,"bad size for opaque double (%d)\n",
00935                      vars->val_len);
00936             return (1);
00937         }
00938         vars->val_len = sizeof(double);
00939         memmove(vars->val.doubleVal, value, vars->val_len);
00940         break;
00941 
00942 #endif                          /* OPAQUE_SPECIAL_TYPES */
00943 
00944     default:
00945         snmp_log(LOG_ERR,"no storage for OID\n");
00946         snmp_set_detail("Internal error in type switching\n");
00947         return (1);
00948     }
00949 
00950     return 0;
00951 }
00952 
00953 void
00954 snmp_replace_var_types(netsnmp_variable_list * vbl, u_char old_type,
00955                        u_char new_type)
00956 {
00957     while (vbl) {
00958         if (vbl->type == old_type) {
00959             snmp_set_var_typed_value(vbl, new_type, NULL, 0);
00960         }
00961         vbl = vbl->next_variable;
00962     }
00963 }
00964 
00965 void
00966 snmp_reset_var_types(netsnmp_variable_list * vbl, u_char new_type)
00967 {
00968     while (vbl) {
00969         snmp_set_var_typed_value(vbl, new_type, NULL, 0);
00970         vbl = vbl->next_variable;
00971     }
00972 }
00973 
00974 int
00975 snmp_synch_response_cb(netsnmp_session * ss,
00976                        netsnmp_pdu *pdu,
00977                        netsnmp_pdu **response, snmp_callback pcb)
00978 {
00979     struct synch_state lstate, *state;
00980     snmp_callback   cbsav;
00981     void           *cbmagsav;
00982     int             numfds, count;
00983     fd_set          fdset;
00984     struct timeval  timeout, *tvp;
00985     int             block;
00986 
00987     memset((void *) &lstate, 0, sizeof(lstate));
00988     state = &lstate;
00989     cbsav = ss->callback;
00990     cbmagsav = ss->callback_magic;
00991     ss->callback = pcb;
00992     ss->callback_magic = (void *) state;
00993 
00994     if ((state->reqid = snmp_send(ss, pdu)) == 0) {
00995         snmp_free_pdu(pdu);
00996         state->status = STAT_ERROR;
00997     } else
00998         state->waiting = 1;
00999 
01000     while (state->waiting) {
01001         numfds = 0;
01002         FD_ZERO(&fdset);
01003         block = SNMPBLOCK;
01004         tvp = &timeout;
01005         timerclear(tvp);
01006         snmp_select_info(&numfds, &fdset, tvp, &block);
01007         if (block == 1)
01008             tvp = NULL;         /* block without timeout */
01009         count = select(numfds, &fdset, 0, 0, tvp);
01010         if (count > 0) {
01011             snmp_read(&fdset);
01012         } else
01013             switch (count) {
01014             case 0:
01015                 snmp_timeout();
01016                 break;
01017             case -1:
01018                 if (errno == EINTR) {
01019                     continue;
01020                 } else {
01021                     snmp_errno = SNMPERR_GENERR;    /*MTCRITICAL_RESOURCE */
01022                     /*
01023                      * CAUTION! if another thread closed the socket(s)
01024                      * waited on here, the session structure was freed.
01025                      * It would be nice, but we can't rely on the pointer.
01026                      * ss->s_snmp_errno = SNMPERR_GENERR;
01027                      * ss->s_errno = errno;
01028                      */
01029                     snmp_set_detail(strerror(errno));
01030                 }
01031                 /*
01032                  * FALLTHRU 
01033                  */
01034             default:
01035                 state->status = STAT_ERROR;
01036                 state->waiting = 0;
01037             }
01038     }
01039     *response = state->pdu;
01040     ss->callback = cbsav;
01041     ss->callback_magic = cbmagsav;
01042     return state->status;
01043 }
01044 
01045 int
01046 snmp_synch_response(netsnmp_session * ss,
01047                     netsnmp_pdu *pdu, netsnmp_pdu **response)
01048 {
01049     return snmp_synch_response_cb(ss, pdu, response, snmp_synch_input);
01050 }
01051 
01052 int
01053 snmp_sess_synch_response(void *sessp,
01054                          netsnmp_pdu *pdu, netsnmp_pdu **response)
01055 {
01056     netsnmp_session *ss;
01057     struct synch_state lstate, *state;
01058     snmp_callback   cbsav;
01059     void           *cbmagsav;
01060     int             numfds, count;
01061     fd_set          fdset;
01062     struct timeval  timeout, *tvp;
01063     int             block;
01064 
01065     ss = snmp_sess_session(sessp);
01066     memset((void *) &lstate, 0, sizeof(lstate));
01067     state = &lstate;
01068     cbsav = ss->callback;
01069     cbmagsav = ss->callback_magic;
01070     ss->callback = snmp_synch_input;
01071     ss->callback_magic = (void *) state;
01072 
01073     if ((state->reqid = snmp_sess_send(sessp, pdu)) == 0) {
01074         snmp_free_pdu(pdu);
01075         state->status = STAT_ERROR;
01076     } else
01077         state->waiting = 1;
01078 
01079     while (state->waiting) {
01080         numfds = 0;
01081         FD_ZERO(&fdset);
01082         block = SNMPBLOCK;
01083         tvp = &timeout;
01084         timerclear(tvp);
01085         snmp_sess_select_info(sessp, &numfds, &fdset, tvp, &block);
01086         if (block == 1)
01087             tvp = NULL;         /* block without timeout */
01088         count = select(numfds, &fdset, 0, 0, tvp);
01089         if (count > 0) {
01090             snmp_sess_read(sessp, &fdset);
01091         } else
01092             switch (count) {
01093             case 0:
01094                 snmp_sess_timeout(sessp);
01095                 break;
01096             case -1:
01097                 if (errno == EINTR) {
01098                     continue;
01099                 } else {
01100                     snmp_errno = SNMPERR_GENERR;    /*MTCRITICAL_RESOURCE */
01101                     /*
01102                      * CAUTION! if another thread closed the socket(s)
01103                      * waited on here, the session structure was freed.
01104                      * It would be nice, but we can't rely on the pointer.
01105                      * ss->s_snmp_errno = SNMPERR_GENERR;
01106                      * ss->s_errno = errno;
01107                      */
01108                     snmp_set_detail(strerror(errno));
01109                 }
01110                 /*
01111                  * FALLTHRU 
01112                  */
01113             default:
01114                 state->status = STAT_ERROR;
01115                 state->waiting = 0;
01116             }
01117     }
01118     *response = state->pdu;
01119     ss->callback = cbsav;
01120     ss->callback_magic = cbmagsav;
01121     return state->status;
01122 }
01123 
01124 
01125 const char     *error_string[19] = {
01126     "(noError) No Error",
01127     "(tooBig) Response message would have been too large.",
01128     "(noSuchName) There is no such variable name in this MIB.",
01129     "(badValue) The value given has the wrong type or length.",
01130     "(readOnly) The two parties used do not have access to use the specified SNMP PDU.",
01131     "(genError) A general failure occured",
01132     "noAccess",
01133     "wrongType (The set datatype does not match the data type the agent expects)",
01134     "wrongLength (The set value has an illegal length from what the agent expects)",
01135     "wrongEncoding",
01136     "wrongValue (The set value is illegal or unsupported in some way)",
01137     "noCreation (That table does not support row creation or that object can not ever be created)",
01138     "inconsistentValue (The set value is illegal or unsupported in some way)",
01139     "resourceUnavailable (This is likely a out-of-memory failure within the agent)",
01140     "commitFailed",
01141     "undoFailed",
01142     "authorizationError (access denied to that object)",
01143     "notWritable (That object does not support modification)",
01144     "inconsistentName (That object can not currently be created)"
01145 };
01146 
01147 const char     *
01148 snmp_errstring(int errstat)
01149 {
01150     if (errstat <= MAX_SNMP_ERR && errstat >= SNMP_ERR_NOERROR) {
01151         return error_string[errstat];
01152     } else {
01153         return "Unknown Error";
01154     }
01155 }
01156 
01157 
01158 
01159 /*
01160  *
01161  *  Convenience routines to make various requests
01162  *  over the specified SNMP session.
01163  *
01164  */
01165 static netsnmp_session *_def_query_session = NULL;
01166 void
01167 netsnmp_query_set_default_session( netsnmp_session *sess) {
01168     _def_query_session = sess;
01169 }
01170 
01171 netsnmp_session *
01172 netsnmp_query_get_default_session( void ) {
01173     return _def_query_session;
01174 }
01175 
01176 
01177 /*
01178  * Internal utility routine to actually send the query
01179  */
01180 static int _query(netsnmp_variable_list *list,
01181                   int                    request,
01182                   netsnmp_session       *session) {
01183 
01184     netsnmp_pdu *pdu      = snmp_pdu_create( request );
01185     netsnmp_pdu *response = NULL;
01186     netsnmp_variable_list *vb1, *vb2, *vtmp;
01187     int ret;
01188 
01189     /*
01190      * Clone the varbind list into the request PDU...
01191      */
01192     pdu->variables = snmp_clone_varbind( list );
01193     if ( session )
01194         ret = snmp_synch_response(            session, pdu, &response );
01195     else if (_def_query_session)
01196         ret = snmp_synch_response( _def_query_session, pdu, &response );
01197     else {
01198         /* No session specified */
01199         return SNMP_ERR_GENERR;
01200     }
01201 
01202     /*
01203      * ....then copy the results back into the
01204      * list (assuming the request succeeded!).
01205      * This avoids having to worry about how this
01206      * list was originally allocated.
01207      */
01208     if ( ret == SNMP_ERR_NOERROR ) {
01209         if ( response->errstat != SNMP_ERR_NOERROR ) {
01210             ret = response->errstat;
01211         } else {
01212             for (vb1 = response->variables, vb2 = list;
01213                  vb1;
01214                  vb1 = vb1->next_variable,  vb2 = vb2->next_variable) {
01215                 if ( !vb2 ) {
01216                     ret = SNMP_ERR_GENERR;
01217                     break;
01218                 }
01219                 vtmp = vb2->next_variable;
01220                 snmp_clone_var( vb1, vb2 );
01221                 vb2->next_variable = vtmp;
01222             }
01223         }
01224     } else {
01225         /* Distinguish snmp_send errors from SNMP errStat errors */
01226         ret = -ret;
01227     }
01228     snmp_free_pdu( response );
01229     return ret;
01230 }
01231 
01232 /*
01233  * These are simple wrappers round the internal utility routine
01234  */
01235 int netsnmp_query_get(netsnmp_variable_list *list,
01236                       netsnmp_session       *session){
01237     return _query( list, SNMP_MSG_GET, session );
01238 }
01239 
01240 
01241 int netsnmp_query_getnext(netsnmp_variable_list *list,
01242                           netsnmp_session       *session){
01243     return _query( list, SNMP_MSG_GETNEXT, session );
01244 }
01245 
01246 
01247 int netsnmp_query_set(netsnmp_variable_list *list,
01248                       netsnmp_session       *session){
01249     return _query( list, SNMP_MSG_SET, session );
01250 }
01251 
01252 /*
01253  * A walk needs a bit more work.
01254  */
01255 int netsnmp_query_walk(netsnmp_variable_list *list,
01256                        netsnmp_session       *session) {
01257     /*
01258      * Create a working copy of the original (single)
01259      * varbind, so we can use this varbind parameter
01260      * to check when we've finished walking this subtree.
01261      */
01262     netsnmp_variable_list *vb = snmp_clone_varbind( list );
01263     netsnmp_variable_list *res_list = NULL;
01264     netsnmp_variable_list *res_last = NULL;
01265     int ret;
01266 
01267     /*
01268      * Now walk the tree as usual
01269      */
01270     ret = _query( vb, SNMP_MSG_GETNEXT, session );
01271     while ( ret == SNMP_ERR_NOERROR &&
01272         snmp_oidtree_compare( list->name, list->name_length,
01273                                 vb->name,   vb->name_length ) == 0) {
01274 
01275         /*
01276          * Copy each response varbind to the end of the result list
01277          * and then re-use this to ask for the next entry.
01278          */
01279         if ( res_last ) {
01280             res_last->next_variable = snmp_clone_varbind( vb );
01281             res_last = res_last->next_variable;
01282         } else {
01283             res_list = snmp_clone_varbind( vb );
01284             res_last = res_list;
01285         }
01286         ret = _query( vb, SNMP_MSG_GETNEXT, session );
01287     }
01288     /*
01289      * Copy the first result back into the original varbind parameter,
01290      * add the rest of the results (if any), and clean up.
01291      */
01292     if ( res_list ) {
01293         snmp_clone_var( res_list, list );
01294         list->next_variable = res_list->next_variable;
01295         res_list->next_variable = NULL;
01296         snmp_free_varbind( res_list );
01297     }
01298     snmp_free_varbind( vb );
01299     return ret;
01300 }

Generated on Fri Dec 30 13:47:48 2005 for net-snmp by  doxygen 1.3.9.1

Valid CSS!


Last modified: Thursday, 01-Mar-2007 16:20:08 PST
For questions regarding web content and site functionality, please write to the net-snmp-users mail list.