2 * snmp_client.c - a toolkit of common functions for an SNMP client.
5 /**********************************************************************
6 Copyright 1988, 1989, 1991, 1992 by Carnegie Mellon University
10 Permission to use, copy, modify, and distribute this software and its
11 documentation for any purpose and without fee is hereby granted,
12 provided that the above copyright notice appear in all copies and that
13 both that copyright notice and this permission notice appear in
14 supporting documentation, and that the name of CMU not be
15 used in advertising or publicity pertaining to distribution of the
16 software without specific, written prior permission.
18 CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
19 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
20 CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
21 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
22 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
23 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
25 ******************************************************************/
27 #include <net-snmp/net-snmp-config.h>
42 #include <sys/types.h>
43 #if TIME_WITH_SYS_TIME
45 # include <sys/timeb.h>
47 # include <sys/time.h>
52 # include <sys/time.h>
58 #include <sys/param.h>
61 #include <netinet/in.h>
64 #include <arpa/inet.h>
67 #include <sys/select.h>
78 #include <net-snmp/types.h>
80 #include <net-snmp/library/snmp_api.h>
81 #include <net-snmp/library/snmp_client.h>
82 #include <net-snmp/library/snmp_secmod.h>
83 #include <net-snmp/library/mib.h>
93 #define NFDBITS (sizeof(fd_mask) * NBBY) /* bits per mask */
95 #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
96 #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
97 #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
98 #define FD_ZERO(p) memset((p), 0, sizeof(*(p)))
102 * Prototype definitions
104 static int snmp_synch_input(int op, netsnmp_session * session,
105 int reqid, netsnmp_pdu *pdu, void *magic);
108 snmp_pdu_create(int command)
112 pdu = (netsnmp_pdu *) calloc(1, sizeof(netsnmp_pdu));
114 pdu->version = SNMP_DEFAULT_VERSION;
115 pdu->command = command;
116 pdu->errstat = SNMP_DEFAULT_ERRSTAT;
117 pdu->errindex = SNMP_DEFAULT_ERRINDEX;
118 pdu->securityModel = SNMP_DEFAULT_SECMODEL;
119 pdu->transport_data = NULL;
120 pdu->transport_data_length = 0;
121 pdu->securityNameLen = 0;
122 pdu->contextNameLen = 0;
124 pdu->reqid = snmp_get_next_reqid();
125 pdu->msgid = snmp_get_next_msgid();
133 * Add a null variable with the requested name to the end of the list of
134 * variables for this pdu.
136 netsnmp_variable_list *
137 snmp_add_null_var(netsnmp_pdu *pdu, oid * name, size_t name_length)
139 return snmp_pdu_add_variable(pdu, name, name_length, ASN_NULL, NULL, 0);
144 snmp_synch_input(int op,
145 netsnmp_session * session,
146 int reqid, netsnmp_pdu *pdu, void *magic)
148 struct synch_state *state = (struct synch_state *) magic;
151 if (reqid != state->reqid && pdu && pdu->command != SNMP_MSG_REPORT) {
157 if (op == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
158 if (pdu->command == SNMP_MSG_REPORT) {
159 rpt_type = snmpv3_get_report_type(pdu);
160 if (SNMPV3_IGNORE_UNAUTH_REPORTS ||
161 rpt_type == SNMPERR_NOT_IN_TIME_WINDOW) {
165 state->status = STAT_ERROR;
166 session->s_snmp_errno = rpt_type;
167 SET_SNMP_ERROR(rpt_type);
168 } else if (pdu->command == SNMP_MSG_RESPONSE) {
170 * clone the pdu to return to snmp_synch_response
172 state->pdu = snmp_clone_pdu(pdu);
173 state->status = STAT_SUCCESS;
174 session->s_snmp_errno = SNMPERR_SUCCESS;
176 } else if (op == NETSNMP_CALLBACK_OP_TIMED_OUT) {
178 state->status = STAT_TIMEOUT;
179 session->s_snmp_errno = SNMPERR_TIMEOUT;
180 SET_SNMP_ERROR(SNMPERR_TIMEOUT);
181 } else if (op == NETSNMP_CALLBACK_OP_DISCONNECT) {
183 state->status = STAT_ERROR;
184 session->s_snmp_errno = SNMPERR_ABORT;
185 SET_SNMP_ERROR(SNMPERR_ABORT);
193 * Clone an SNMP variable data structure.
194 * Sets pointers to structure private storage, or
195 * allocates larger object identifiers and values as needed.
197 * Caller must make list association for cloned variable.
199 * Returns 0 if successful.
202 snmp_clone_var(netsnmp_variable_list * var, netsnmp_variable_list * newvar)
207 memmove(newvar, var, sizeof(netsnmp_variable_list));
208 newvar->next_variable = 0;
210 newvar->val.string = 0;
212 newvar->dataFreeHook = 0;
216 * Clone the object identifier and the value.
217 * Allocate memory iff original will not fit into local storage.
219 if (snmp_set_var_objid(newvar, var->name, var->name_length))
223 * need a pointer and a length to copy a string value.
225 if (var->val.string && var->val_len) {
226 if (var->val.string != &var->buf[0]) {
227 if (var->val_len <= sizeof(var->buf))
228 newvar->val.string = newvar->buf;
230 newvar->val.string = (u_char *) malloc(var->val_len);
231 if (!newvar->val.string)
234 memmove(newvar->val.string, var->val.string, var->val_len);
235 } else { /* fix the pointer to new local store */
236 newvar->val.string = newvar->buf;
239 newvar->val.string = 0;
248 * Possibly make a copy of source memory buffer.
249 * Will reset destination pointer if source pointer is NULL.
250 * Returns 0 if successful, 1 if memory allocation fails.
253 snmp_clone_mem(void **dstPtr, void *srcPtr, unsigned len)
257 *dstPtr = malloc(len + 1);
261 memmove(*dstPtr, srcPtr, len);
263 * this is for those routines that expect 0-terminated strings!!!
264 * someone should rather have called strdup
266 ((char *) *dstPtr)[len] = 0;
273 * Walks through a list of varbinds and frees and allocated memory,
274 * restoring pointers to local buffers
277 snmp_reset_var_buffers(netsnmp_variable_list * var)
280 if (var->name != var->name_loc) {
282 var->name = var->name_loc;
283 var->name_length = 0;
285 if (var->val.string != var->buf) {
286 free(var->val.string);
287 var->val.string = var->buf;
290 var = var->next_variable;
295 * Creates and allocates a clone of the input PDU,
296 * but does NOT copy the variables.
297 * This function should be used with another function,
298 * such as _copy_pdu_vars.
300 * Returns a pointer to the cloned PDU if successful.
301 * Returns 0 if failure.
305 _clone_pdu_header(netsnmp_pdu *pdu)
308 struct snmp_secmod_def *sptr;
310 newpdu = (netsnmp_pdu *) malloc(sizeof(netsnmp_pdu));
313 memmove(newpdu, pdu, sizeof(netsnmp_pdu));
316 * reset copied pointers if copy fails
318 newpdu->variables = 0;
319 newpdu->enterprise = 0;
320 newpdu->community = 0;
321 newpdu->securityEngineID = 0;
322 newpdu->securityName = 0;
323 newpdu->contextEngineID = 0;
324 newpdu->contextName = 0;
325 newpdu->transport_data = 0;
328 * copy buffers individually. If any copy fails, all are freed.
330 if (snmp_clone_mem((void **) &newpdu->enterprise, pdu->enterprise,
331 sizeof(oid) * pdu->enterprise_length) ||
332 snmp_clone_mem((void **) &newpdu->community, pdu->community,
333 pdu->community_len) ||
334 snmp_clone_mem((void **) &newpdu->contextEngineID,
335 pdu->contextEngineID, pdu->contextEngineIDLen)
336 || snmp_clone_mem((void **) &newpdu->securityEngineID,
337 pdu->securityEngineID, pdu->securityEngineIDLen)
338 || snmp_clone_mem((void **) &newpdu->contextName, pdu->contextName,
340 || snmp_clone_mem((void **) &newpdu->securityName,
341 pdu->securityName, pdu->securityNameLen)
342 || snmp_clone_mem((void **) &newpdu->transport_data,
344 pdu->transport_data_length)) {
345 snmp_free_pdu(newpdu);
348 #ifdef BRCM_SNMPV3_SUPPORT
349 if ((sptr = find_sec_mod(newpdu->securityModel)) != NULL &&
350 sptr->pdu_clone != NULL) {
352 * call security model if it needs to know about this
354 (*sptr->pdu_clone) (pdu, newpdu);
356 #endif /* BRCM_SNMPV3_SUPPORT */
361 netsnmp_variable_list *
362 _copy_varlist(netsnmp_variable_list * var, /* source varList */
363 int errindex, /* index of variable to drop (if any) */
365 { /* !=0 number variables to copy */
366 netsnmp_variable_list *newhead, *newvar, *oldvar;
372 while (var && (copy_count-- > 0)) {
374 * Drop the specified variable (if applicable)
376 if (++ii == errindex) {
377 var = var->next_variable;
382 * clone the next variable. Cleanup if alloc fails
384 newvar = (netsnmp_variable_list *)
385 malloc(sizeof(netsnmp_variable_list));
386 if (snmp_clone_var(var, newvar)) {
388 free((char *) newvar);
389 snmp_free_varbind(newhead);
394 * add cloned variable to new list
399 oldvar->next_variable = newvar;
402 var = var->next_variable;
409 * Copy some or all variables from source PDU to target PDU.
410 * This function consolidates many of the needs of PDU variables:
411 * Clone PDU : copy all the variables.
412 * Split PDU : skip over some variables to copy other variables.
413 * Fix PDU : remove variable associated with error index.
415 * Designed to work with _clone_pdu_header.
417 * If drop_err is set, drop any variable associated with errindex.
418 * If skip_count is set, skip the number of variable in pdu's list.
419 * While copy_count is greater than zero, copy pdu variables to newpdu.
421 * If an error occurs, newpdu is freed and pointer is set to 0.
423 * Returns a pointer to the cloned PDU if successful.
424 * Returns 0 if failure.
428 _copy_pdu_vars(netsnmp_pdu *pdu, /* source PDU */
429 netsnmp_pdu *newpdu, /* target PDU */
430 int drop_err, /* !=0 drop errored variable */
431 int skip_count, /* !=0 number of variables to skip */
433 { /* !=0 number of variables to copy */
434 netsnmp_variable_list *var, *oldvar;
435 int ii, copied, drop_idx;
438 return 0; /* where is PDU to copy to ? */
441 drop_idx = pdu->errindex - skip_count;
445 var = pdu->variables;
446 while (var && (skip_count-- > 0)) /* skip over pdu variables */
447 var = var->next_variable;
452 if (pdu->flags & UCD_MSG_FLAG_FORCE_PDU_COPY)
453 copied = 1; /* We're interested in 'empty' responses too */
455 newpdu->variables = _copy_varlist(var, drop_idx, copy_count);
456 if (newpdu->variables)
459 #if ALSO_TEMPORARILY_DISABLED
461 * Error if bad errindex or if target PDU has no variables copied
463 if ((drop_err && (ii < pdu->errindex))
464 #if TEMPORARILY_DISABLED
466 * SNMPv3 engineID probes are allowed to be empty.
467 * See the comment in snmp_api.c for further details
472 snmp_free_pdu(newpdu);
481 * Creates (allocates and copies) a clone of the input PDU.
482 * If drop_err is set, don't copy any variable associated with errindex.
483 * This function is called by snmp_clone_pdu and snmp_fix_pdu.
485 * Returns a pointer to the cloned PDU if successful.
486 * Returns 0 if failure.
490 _clone_pdu(netsnmp_pdu *pdu, int drop_err)
493 newpdu = _clone_pdu_header(pdu);
494 newpdu = _copy_pdu_vars(pdu, newpdu, drop_err, 0, 10000); /* skip none, copy all */
501 * This function will clone a full varbind list
503 * Returns a pointer to the cloned PDU if successful.
504 * Returns 0 if failure
506 netsnmp_variable_list *
507 snmp_clone_varbind(netsnmp_variable_list * varlist)
509 return _copy_varlist(varlist, 0, 10000); /* skip none, copy all */
513 * This function will clone a PDU including all of its variables.
515 * Returns a pointer to the cloned PDU if successful.
516 * Returns 0 if failure
519 snmp_clone_pdu(netsnmp_pdu *pdu)
521 return _clone_pdu(pdu, 0); /* copies all variables */
526 * This function will clone a PDU including some of its variables.
528 * If skip_count is not zero, it defines the number of variables to skip.
529 * If copy_count is not zero, it defines the number of variables to copy.
531 * Returns a pointer to the cloned PDU if successful.
532 * Returns 0 if failure.
535 snmp_split_pdu(netsnmp_pdu *pdu, int skip_count, int copy_count)
538 newpdu = _clone_pdu_header(pdu);
539 newpdu = _copy_pdu_vars(pdu, newpdu, 0, /* don't drop any variables */
540 skip_count, copy_count);
547 * If there was an error in the input pdu, creates a clone of the pdu
548 * that includes all the variables except the one marked by the errindex.
549 * The command is set to the input command and the reqid, errstat, and
550 * errindex are set to default values.
551 * If the error status didn't indicate an error, the error index didn't
552 * indicate a variable, the pdu wasn't a get response message, or there
553 * would be no remaining variables, this function will return 0.
554 * If everything was successful, a pointer to the fixed cloned pdu will
558 snmp_fix_pdu(netsnmp_pdu *pdu, int command)
562 if ((pdu->command != SNMP_MSG_RESPONSE)
563 || (pdu->errstat == SNMP_ERR_NOERROR)
564 || (0 == pdu->variables)
565 || (pdu->errindex <= 0)) {
566 return 0; /* pre-condition tests fail */
569 newpdu = _clone_pdu(pdu, 1); /* copies all except errored variable */
572 if (!newpdu->variables) {
573 snmp_free_pdu(newpdu);
574 return 0; /* no variables. "should not happen" */
576 newpdu->command = command;
577 newpdu->reqid = snmp_get_next_reqid();
578 newpdu->msgid = snmp_get_next_msgid();
579 newpdu->errstat = SNMP_DEFAULT_ERRSTAT;
580 newpdu->errindex = SNMP_DEFAULT_ERRINDEX;
587 * Returns the number of variables bound to a PDU structure
590 snmp_varbind_len(netsnmp_pdu *pdu)
592 register netsnmp_variable_list *vars;
593 unsigned long retVal = 0;
595 for (vars = pdu->variables; vars; vars = vars->next_variable) {
603 * Add object identifier name to SNMP variable.
604 * If the name is large, additional memory is allocated.
605 * Returns 0 if successful.
609 snmp_set_var_objid(netsnmp_variable_list * vp,
610 const oid * objid, size_t name_length)
612 size_t len = sizeof(oid) * name_length;
614 if (vp->name != vp->name_loc && vp->name != NULL &&
615 vp->name_length > (sizeof(vp->name_loc) / sizeof(oid))) {
617 * Probably previously-allocated "big storage". Better free it
618 * else memory leaks possible.
624 * use built-in storage for smaller values
626 if (len <= sizeof(vp->name_loc)) {
627 vp->name = vp->name_loc;
629 vp->name = (oid *) malloc(len);
634 memmove(vp->name, objid, len);
635 vp->name_length = name_length;
640 snmp_set_var_typed_value(netsnmp_variable_list * newvar, u_char type,
641 const u_char * val_str, size_t val_len)
644 return snmp_set_var_value(newvar, val_str, val_len);
648 count_varbinds(netsnmp_variable_list * var_ptr)
652 for (; var_ptr != NULL; var_ptr = var_ptr->next_variable)
659 count_varbinds_of_type(netsnmp_variable_list * var_ptr, u_char type)
663 for (; var_ptr != NULL; var_ptr = var_ptr->next_variable)
664 if (var_ptr->type == type)
670 netsnmp_variable_list *
671 find_varbind_of_type(netsnmp_variable_list * var_ptr, u_char type)
673 for (; var_ptr != NULL && var_ptr->type != type;
674 var_ptr = var_ptr->next_variable);
680 * Add some value to SNMP variable.
681 * If the value is large, additional memory is allocated.
682 * Returns 0 if successful.
686 snmp_set_var_value(netsnmp_variable_list * newvar,
687 const u_char * val_str, size_t val_len)
689 if (newvar->val.string && newvar->val.string != newvar->buf) {
690 free(newvar->val.string);
693 newvar->val.string = 0;
697 * need a pointer and a length to copy a string value.
699 if (val_str && val_len) {
700 if (val_len <= sizeof(newvar->buf))
701 newvar->val.string = newvar->buf;
703 newvar->val.string = (u_char *) malloc(val_len);
704 if (!newvar->val.string)
707 memmove(newvar->val.string, val_str, val_len);
708 newvar->val_len = val_len;
709 } else if (val_str) {
711 * NULL STRING != NULL ptr
713 newvar->val.string = newvar->buf;
714 newvar->val.string[0] = '\0';
721 snmp_replace_var_types(netsnmp_variable_list * vbl, u_char old_type,
725 if (vbl->type == old_type) {
726 snmp_set_var_typed_value(vbl, new_type, NULL, 0);
728 vbl = vbl->next_variable;
733 snmp_reset_var_types(netsnmp_variable_list * vbl, u_char new_type)
736 snmp_set_var_typed_value(vbl, new_type, NULL, 0);
737 vbl = vbl->next_variable;
742 snmp_synch_response_cb(netsnmp_session * ss,
744 netsnmp_pdu **response, snmp_callback pcb)
746 struct synch_state lstate, *state;
751 struct timeval timeout, *tvp;
754 memset((void *) &lstate, 0, sizeof(lstate));
756 cbsav = ss->callback;
757 cbmagsav = ss->callback_magic;
759 ss->callback_magic = (void *) state;
761 if ((state->reqid = snmp_send(ss, pdu)) == 0) {
763 state->status = STAT_ERROR;
767 while (state->waiting) {
773 snmp_select_info(&numfds, &fdset, tvp, &block);
775 tvp = NULL; /* block without timeout */
776 count = select(numfds, &fdset, 0, 0, tvp);
785 if (errno == EINTR) {
788 snmp_errno = SNMPERR_GENERR;
790 * CAUTION! if another thread closed the socket(s)
791 * waited on here, the session structure was freed.
792 * It would be nice, but we can't rely on the pointer.
793 * ss->s_snmp_errno = SNMPERR_GENERR;
794 * ss->s_errno = errno;
796 snmp_set_detail(strerror(errno));
802 state->status = STAT_ERROR;
806 *response = state->pdu;
807 ss->callback = cbsav;
808 ss->callback_magic = cbmagsav;
809 return state->status;
813 snmp_synch_response(netsnmp_session * ss,
814 netsnmp_pdu *pdu, netsnmp_pdu **response)
816 return snmp_synch_response_cb(ss, pdu, response, snmp_synch_input);
820 snmp_sess_synch_response(void *sessp,
821 netsnmp_pdu *pdu, netsnmp_pdu **response)
824 struct synch_state lstate, *state;
829 struct timeval timeout, *tvp;
832 ss = snmp_sess_session(sessp);
833 memset((void *) &lstate, 0, sizeof(lstate));
835 cbsav = ss->callback;
836 cbmagsav = ss->callback_magic;
837 ss->callback = snmp_synch_input;
838 ss->callback_magic = (void *) state;
840 if ((state->reqid = snmp_sess_send(sessp, pdu)) == 0) {
842 state->status = STAT_ERROR;
846 while (state->waiting) {
852 snmp_sess_select_info(sessp, &numfds, &fdset, tvp, &block);
854 tvp = NULL; /* block without timeout */
855 count = select(numfds, &fdset, 0, 0, tvp);
857 snmp_sess_read(sessp, &fdset);
861 snmp_sess_timeout(sessp);
864 if (errno == EINTR) {
867 snmp_errno = SNMPERR_GENERR;
869 * CAUTION! if another thread closed the socket(s)
870 * waited on here, the session structure was freed.
871 * It would be nice, but we can't rely on the pointer.
872 * ss->s_snmp_errno = SNMPERR_GENERR;
873 * ss->s_errno = errno;
875 snmp_set_detail(strerror(errno));
881 state->status = STAT_ERROR;
885 *response = state->pdu;
886 ss->callback = cbsav;
887 ss->callback_magic = cbmagsav;
888 return state->status;
892 const char *error_string[19] = {
893 "(noError) No Error",
894 "(tooBig) Response message would have been too large.",
895 "(noSuchName) There is no such variable name in this MIB.",
896 "(badValue) The value given has the wrong type or length.",
897 "(readOnly) The two parties used do not have access to use the specified SNMP PDU.",
898 "(genError) A general failure occured",
900 "wrongType (The set datatype does not match the data type the agent expects)",
901 "wrongLength (The set value has an illegal length from what the agent expects)",
903 "wrongValue (The set value is illegal or unsupported in some way)",
904 "noCreation (that table does not support row creation)",
905 "inconsistentValue (The set value is illegal or unsupported in some way)",
906 "resourceUnavailable (This is likely a out-of-memory failure within the agent)",
909 "authorizationError (access denied to that object)",
910 "notWritable (that object does not support modification)",
915 snmp_errstring(int errstat)
917 if (errstat <= MAX_SNMP_ERR && errstat >= SNMP_ERR_NOERROR) {
918 return error_string[errstat];
920 return "Unknown Error";