1 /*
2  * snmp_client.c - a toolkit of common functions for an SNMP client.
3  *
4  */
5 /* Portions of this file are subject to the following copyright(s).  See
6  * the Net-SNMP's COPYING file for more details and other copyrights
7  * that may apply:
8  */
9 /**********************************************************************
10 	Copyright 1988, 1989, 1991, 1992 by Carnegie Mellon University
11 
12                       All Rights Reserved
13 
14 Permission to use, copy, modify, and distribute this software and its
15 documentation for any purpose and without fee is hereby granted,
16 provided that the above copyright notice appear in all copies and that
17 both that copyright notice and this permission notice appear in
18 supporting documentation, and that the name of CMU not be
19 used in advertising or publicity pertaining to distribution of the
20 software without specific, written prior permission.
21 
22 CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
23 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
24 CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
25 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
26 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
27 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
28 SOFTWARE.
29 ******************************************************************/
30 /*
31  * Portions of this file are copyrighted by:
32  * Copyright � 2003 Sun Microsystems, Inc. All rights reserved.
33  * Use is subject to license terms specified in the COPYING file
34  * distributed with the Net-SNMP package.
35  *
36  * Portions of this file are copyrighted by:
37  * Copyright (c) 2016 VMware, Inc. All rights reserved.
38  * Use is subject to license terms specified in the COPYING file
39  * distributed with the Net-SNMP package.
40  */
41 
42 /** @defgroup snmp_client various PDU processing routines
43  *  @ingroup library
44  *
45  *  @{
46  */
47 #include <net-snmp/net-snmp-config.h>
48 #include <net-snmp/net-snmp-features.h>
49 
50 #include <stdio.h>
51 #include <errno.h>
52 #ifdef HAVE_INTTYPES_H
53 #include <inttypes.h>
54 #endif
55 #if HAVE_STDLIB_H
56 #include <stdlib.h>
57 #endif
58 #if HAVE_STRING_H
59 #include <string.h>
60 #else
61 #include <strings.h>
62 #endif
63 #if HAVE_UNISTD_H
64 #include <unistd.h>
65 #endif
66 #include <sys/types.h>
67 #if TIME_WITH_SYS_TIME
68 # include <sys/time.h>
69 # include <time.h>
70 #else
71 # if HAVE_SYS_TIME_H
72 #  include <sys/time.h>
73 # else
74 #  include <time.h>
75 # endif
76 #endif
77 #if HAVE_SYS_PARAM_H
78 #include <sys/param.h>
79 #endif
80 #if HAVE_NETINET_IN_H
81 #include <netinet/in.h>
82 #endif
83 #if HAVE_ARPA_INET_H
84 #include <arpa/inet.h>
85 #endif
86 #if HAVE_SYS_SELECT_H
87 #include <sys/select.h>
88 #endif
89 #if HAVE_SYSLOG_H
90 #include <syslog.h>
91 #endif
92 
93 #include <net-snmp/types.h>
94 
95 #include <net-snmp/agent/ds_agent.h>
96 #include <net-snmp/library/default_store.h>
97 #include <net-snmp/library/snmp_api.h>
98 #include <net-snmp/library/snmp_client.h>
99 #include <net-snmp/library/snmp_secmod.h>
100 #include <net-snmp/library/snmpusm.h>
101 #include <net-snmp/library/mib.h>
102 #include <net-snmp/library/snmp_logging.h>
103 #include <net-snmp/library/snmp_assert.h>
104 #include <net-snmp/library/large_fd_set.h>
105 #include <net-snmp/pdu_api.h>
106 
107 netsnmp_feature_child_of(snmp_client_all, libnetsnmp);
108 
109 netsnmp_feature_child_of(snmp_split_pdu, snmp_client_all);
110 netsnmp_feature_child_of(snmp_reset_var_types, snmp_client_all);
111 netsnmp_feature_child_of(query_set_default_session, snmp_client_all);
112 netsnmp_feature_child_of(row_create, snmp_client_all);
113 
114 #ifndef BSD4_3
115 #define BSD4_2
116 #endif
117 
118 
119 /*
120  * Prototype definitions
121  */
122 static int      snmp_synch_input(int op, netsnmp_session * session,
123                                  int reqid, netsnmp_pdu *pdu, void *magic);
124 
125 netsnmp_pdu    *
snmp_pdu_create(int command)126 snmp_pdu_create(int command)
127 {
128     netsnmp_pdu    *pdu;
129 
130     pdu = (netsnmp_pdu *) calloc(1, sizeof(netsnmp_pdu));
131     if (pdu) {
132         pdu->version = SNMP_DEFAULT_VERSION;
133         pdu->command = command;
134         pdu->errstat = SNMP_DEFAULT_ERRSTAT;
135         pdu->errindex = SNMP_DEFAULT_ERRINDEX;
136         pdu->securityModel = SNMP_DEFAULT_SECMODEL;
137         pdu->transport_data = NULL;
138         pdu->transport_data_length = 0;
139         pdu->securityNameLen = 0;
140         pdu->contextNameLen = 0;
141         pdu->time = 0;
142         pdu->reqid = snmp_get_next_reqid();
143         pdu->msgid = snmp_get_next_msgid();
144     }
145     return pdu;
146 
147 }
148 
149 
150 /*
151  * Add a null variable with the requested name to the end of the list of
152  * variables for this pdu.
153  */
154 netsnmp_variable_list *
snmp_add_null_var(netsnmp_pdu * pdu,const oid * name,size_t name_length)155 snmp_add_null_var(netsnmp_pdu *pdu, const oid * name, size_t name_length)
156 {
157     return snmp_pdu_add_variable(pdu, name, name_length, ASN_NULL, NULL, 0);
158 }
159 
160 
161 #include <net-snmp/library/snmp_debug.h>
162 static int
snmp_synch_input(int op,netsnmp_session * session,int reqid,netsnmp_pdu * pdu,void * magic)163 snmp_synch_input(int op,
164                  netsnmp_session * session,
165                  int reqid, netsnmp_pdu *pdu, void *magic)
166 {
167     struct synch_state *state = (struct synch_state *) magic;
168     int             rpt_type;
169 
170     if (reqid != state->reqid && pdu && pdu->command != SNMP_MSG_REPORT) {
171         DEBUGMSGTL(("snmp_synch", "Unexpected response (ReqID: %d,%d - Cmd %d)\n",
172                                    reqid, state->reqid, pdu->command ));
173         return 0;
174     }
175 
176     state->waiting = 0;
177     DEBUGMSGTL(("snmp_synch", "Response (ReqID: %d - Cmd %d)\n",
178                                reqid, (pdu ? pdu->command : -1)));
179 
180     if (op == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE && pdu) {
181         if (pdu->command == SNMP_MSG_REPORT) {
182             rpt_type = snmpv3_get_report_type(pdu);
183             if (SNMPV3_IGNORE_UNAUTH_REPORTS ||
184                 rpt_type == SNMPERR_NOT_IN_TIME_WINDOW) {
185                 state->waiting = 1;
186             }
187             state->pdu = NULL;
188             state->status = STAT_ERROR;
189             session->s_snmp_errno = rpt_type;
190             SET_SNMP_ERROR(rpt_type);
191         } else if (pdu->command == SNMP_MSG_RESPONSE) {
192             /*
193              * clone the pdu to return to snmp_synch_response
194              */
195             state->pdu = snmp_clone_pdu(pdu);
196             state->status = STAT_SUCCESS;
197             session->s_snmp_errno = SNMPERR_SUCCESS;
198         }
199         else {
200             char msg_buf[50];
201             state->status = STAT_ERROR;
202             session->s_snmp_errno = SNMPERR_PROTOCOL;
203             SET_SNMP_ERROR(SNMPERR_PROTOCOL);
204             snprintf(msg_buf, sizeof(msg_buf), "Expected RESPONSE-PDU but got %s-PDU",
205                      snmp_pdu_type(pdu->command));
206             snmp_set_detail(msg_buf);
207             return 0;
208         }
209     } else if (op == NETSNMP_CALLBACK_OP_TIMED_OUT) {
210         state->pdu = NULL;
211         state->status = STAT_TIMEOUT;
212         session->s_snmp_errno = SNMPERR_TIMEOUT;
213         SET_SNMP_ERROR(SNMPERR_TIMEOUT);
214     } else if (op == NETSNMP_CALLBACK_OP_SEC_ERROR) {
215         state->pdu = NULL;
216         /*
217          * If we already have an error in status, then leave it alone.
218          */
219         if (state->status == STAT_SUCCESS) {
220             state->status = STAT_ERROR;
221             session->s_snmp_errno = SNMPERR_GENERR;
222             SET_SNMP_ERROR(SNMPERR_GENERR);
223         }
224     } else if (op == NETSNMP_CALLBACK_OP_DISCONNECT) {
225         state->pdu = NULL;
226         state->status = STAT_ERROR;
227         session->s_snmp_errno = SNMPERR_ABORT;
228         SET_SNMP_ERROR(SNMPERR_ABORT);
229     }
230     DEBUGMSGTL(("snmp_synch", "status = %d errno = %d\n",
231                                state->status, session->s_snmp_errno));
232 
233     return 1;
234 }
235 
236 
237 /*
238  * Clone an SNMP variable data structure.
239  * Sets pointers to structure private storage, or
240  * allocates larger object identifiers and values as needed.
241  *
242  * Caller must make list association for cloned variable.
243  *
244  * Returns 0 if successful.
245  */
246 int
snmp_clone_var(netsnmp_variable_list * var,netsnmp_variable_list * newvar)247 snmp_clone_var(netsnmp_variable_list * var, netsnmp_variable_list * newvar)
248 {
249     if (!newvar || !var)
250         return 1;
251 
252     memmove(newvar, var, sizeof(netsnmp_variable_list));
253     newvar->next_variable = NULL;
254     newvar->name = NULL;
255     newvar->val.string = NULL;
256     newvar->data = NULL;
257     newvar->dataFreeHook = NULL;
258     newvar->index = 0;
259 
260     /*
261      * Clone the object identifier and the value.
262      * Allocate memory iff original will not fit into local storage.
263      */
264     if (snmp_set_var_objid(newvar, var->name, var->name_length))
265         return 1;
266 
267     /*
268      * need a pointer to copy a string value.
269      */
270     if (var->val.string) {
271         if (var->val.string != &var->buf[0]) {
272             if (var->val_len <= sizeof(var->buf))
273                 newvar->val.string = newvar->buf;
274             else {
275                 newvar->val.string = (u_char *) malloc(var->val_len);
276                 if (!newvar->val.string)
277                     return 1;
278             }
279             memmove(newvar->val.string, var->val.string, var->val_len);
280         } else {                /* fix the pointer to new local store */
281             newvar->val.string = newvar->buf;
282             /*
283              * no need for a memmove, since we copied the whole var
284              * struct (and thus var->buf) at the beginning of this function.
285              */
286         }
287     } else {
288         newvar->val.string = NULL;
289         newvar->val_len = 0;
290     }
291 
292     return 0;
293 }
294 
295 
296 /*
297  * Possibly make a copy of source memory buffer.
298  * Will reset destination pointer if source pointer is NULL.
299  * Returns 0 if successful, 1 if memory allocation fails.
300  */
301 int
snmp_clone_mem(void ** dstPtr,const void * srcPtr,unsigned len)302 snmp_clone_mem(void **dstPtr, const void *srcPtr, unsigned len)
303 {
304     *dstPtr = NULL;
305     if (srcPtr) {
306         *dstPtr = malloc(len + 1);
307         if (!*dstPtr) {
308             return 1;
309         }
310         memmove(*dstPtr, srcPtr, len);
311         /*
312          * this is for those routines that expect 0-terminated strings!!!
313          * someone should rather have called strdup
314          */
315         ((char *) *dstPtr)[len] = 0;
316     }
317     return 0;
318 }
319 
320 
321 /*
322  * Walks through a list of varbinds and frees and allocated memory,
323  * restoring pointers to local buffers
324  */
325 void
snmp_reset_var_buffers(netsnmp_variable_list * var)326 snmp_reset_var_buffers(netsnmp_variable_list * var)
327 {
328     while (var) {
329         if (var->name != var->name_loc) {
330             if(NULL != var->name)
331                 free(var->name);
332             var->name = var->name_loc;
333             var->name_length = 0;
334         }
335         if (var->val.string != var->buf) {
336             if (NULL != var->val.string)
337                 free(var->val.string);
338             var->val.string = var->buf;
339             var->val_len = 0;
340         }
341         var = var->next_variable;
342     }
343 }
344 
345 /*
346  * Creates and allocates a clone of the input PDU,
347  * but does NOT copy the variables.
348  * This function should be used with another function,
349  * such as _copy_pdu_vars.
350  *
351  * Returns a pointer to the cloned PDU if successful.
352  * Returns 0 if failure.
353  */
354 static
355 netsnmp_pdu    *
_clone_pdu_header(netsnmp_pdu * pdu)356 _clone_pdu_header(netsnmp_pdu *pdu)
357 {
358     netsnmp_pdu    *newpdu;
359     struct snmp_secmod_def *sptr;
360     int ret;
361 
362     if (!pdu)
363         return NULL;
364 
365     newpdu = (netsnmp_pdu *) malloc(sizeof(netsnmp_pdu));
366     if (!newpdu)
367         return NULL;
368     memmove(newpdu, pdu, sizeof(netsnmp_pdu));
369 
370     /*
371      * reset copied pointers if copy fails
372      */
373     newpdu->variables = NULL;
374     newpdu->enterprise = NULL;
375     newpdu->community = NULL;
376     newpdu->securityEngineID = NULL;
377     newpdu->securityName = NULL;
378     newpdu->contextEngineID = NULL;
379     newpdu->contextName = NULL;
380     newpdu->transport_data = NULL;
381 
382     /*
383      * copy buffers individually. If any copy fails, all are freed.
384      */
385     if (snmp_clone_mem((void **) &newpdu->enterprise, pdu->enterprise,
386                        sizeof(oid) * pdu->enterprise_length) ||
387         snmp_clone_mem((void **) &newpdu->community, pdu->community,
388                        pdu->community_len) ||
389         snmp_clone_mem((void **) &newpdu->contextEngineID,
390                        pdu->contextEngineID, pdu->contextEngineIDLen)
391         || snmp_clone_mem((void **) &newpdu->securityEngineID,
392                           pdu->securityEngineID, pdu->securityEngineIDLen)
393         || snmp_clone_mem((void **) &newpdu->contextName, pdu->contextName,
394                           pdu->contextNameLen)
395         || snmp_clone_mem((void **) &newpdu->securityName,
396                           pdu->securityName, pdu->securityNameLen)
397         || snmp_clone_mem((void **) &newpdu->transport_data,
398                           pdu->transport_data,
399                           pdu->transport_data_length)) {
400         snmp_free_pdu(newpdu);
401         return NULL;
402     }
403 
404     sptr = find_sec_mod(newpdu->securityModel);
405     if (sptr && sptr->pdu_clone) {
406         /* call security model if it needs to know about this */
407         ret = sptr->pdu_clone(pdu, newpdu);
408         if (ret) {
409             snmp_free_pdu(newpdu);
410             return NULL;
411         }
412     }
413 
414     return newpdu;
415 }
416 
417 static
418 netsnmp_variable_list *
_copy_varlist(netsnmp_variable_list * var,int errindex,int copy_count)419 _copy_varlist(netsnmp_variable_list * var,      /* source varList */
420               int errindex,     /* index of variable to drop (if any) */
421               int copy_count)
422 {                               /* !=0 number variables to copy */
423     netsnmp_variable_list *newhead, *newvar, *oldvar;
424     int             ii = 0;
425 
426     newhead = NULL;
427     oldvar = NULL;
428 
429     while (var && (copy_count-- > 0)) {
430         /*
431          * Drop the specified variable (if applicable)
432          * xxx hmm, is it intentional that dropping the errindex
433          *     counts towards copy_count?
434          */
435         if (++ii == errindex) {
436             var = var->next_variable;
437             continue;
438         }
439 
440         /*
441          * clone the next variable. Cleanup if alloc fails
442          */
443         newvar = (netsnmp_variable_list *)
444             malloc(sizeof(netsnmp_variable_list));
445         if (snmp_clone_var(var, newvar)) {
446             if (newvar)
447                 free((char *) newvar);
448             snmp_free_varbind(newhead);
449             return NULL;
450         }
451 
452         /*
453          * add cloned variable to new list
454          */
455         if (NULL == newhead)
456             newhead = newvar;
457         if (oldvar)
458             oldvar->next_variable = newvar;
459         oldvar = newvar;
460 
461         var = var->next_variable;
462     }
463     return newhead;
464 }
465 
466 
467 /*
468  * Copy some or all variables from source PDU to target PDU.
469  * This function consolidates many of the needs of PDU variables:
470  * Clone PDU : copy all the variables.
471  * Split PDU : skip over some variables to copy other variables.
472  * Fix PDU   : remove variable associated with error index.
473  *
474  * Designed to work with _clone_pdu_header.
475  *
476  * If drop_err is set, drop any variable associated with errindex.
477  * If skip_count is set, skip the number of variable in pdu's list.
478  * While copy_count is greater than zero, copy pdu variables to newpdu.
479  *
480  * If an error occurs, newpdu is freed and pointer is set to 0.
481  *
482  * Returns a pointer to the cloned PDU if successful.
483  * Returns 0 if failure.
484  */
485 static
486 netsnmp_pdu    *
_copy_pdu_vars(netsnmp_pdu * pdu,netsnmp_pdu * newpdu,int drop_err,int skip_count,int copy_count)487 _copy_pdu_vars(netsnmp_pdu *pdu,        /* source PDU */
488                netsnmp_pdu *newpdu,     /* target PDU */
489                int drop_err,    /* !=0 drop errored variable */
490                int skip_count,  /* !=0 number of variables to skip */
491                int copy_count)
492 {                               /* !=0 number of variables to copy */
493     netsnmp_variable_list *var;
494 #if TEMPORARILY_DISABLED
495     int             copied;
496 #endif
497     int             drop_idx;
498 
499     if (!newpdu)
500         return NULL;            /* where is PDU to copy to ? */
501 
502     if (drop_err)
503         drop_idx = pdu->errindex - skip_count;
504     else
505         drop_idx = 0;
506 
507     var = pdu->variables;
508     while (var && (skip_count-- > 0))   /* skip over pdu variables */
509         var = var->next_variable;
510 
511 #if TEMPORARILY_DISABLED
512     copied = 0;
513     if (pdu->flags & UCD_MSG_FLAG_FORCE_PDU_COPY)
514         copied = 1;             /* We're interested in 'empty' responses too */
515 #endif
516 
517     newpdu->variables = _copy_varlist(var, drop_idx, copy_count);
518 #if TEMPORARILY_DISABLED
519     if (newpdu->variables)
520         copied = 1;
521 #endif
522 
523 #if ALSO_TEMPORARILY_DISABLED
524     /*
525      * Error if bad errindex or if target PDU has no variables copied
526      */
527     if ((drop_err && (ii < pdu->errindex))
528 #if TEMPORARILY_DISABLED
529         /*
530          * SNMPv3 engineID probes are allowed to be empty.
531          * See the comment in snmp_api.c for further details
532          */
533         || copied == 0
534 #endif
535         ) {
536         snmp_free_pdu(newpdu);
537         return 0;
538     }
539 #endif
540     return newpdu;
541 }
542 
543 
544 /*
545  * Creates (allocates and copies) a clone of the input PDU.
546  * If drop_err is set, don't copy any variable associated with errindex.
547  * This function is called by snmp_clone_pdu and snmp_fix_pdu.
548  *
549  * Returns a pointer to the cloned PDU if successful.
550  * Returns 0 if failure.
551  */
552 static
553 netsnmp_pdu    *
_clone_pdu(netsnmp_pdu * pdu,int drop_err)554 _clone_pdu(netsnmp_pdu *pdu, int drop_err)
555 {
556     netsnmp_pdu    *newpdu;
557 
558     newpdu = _clone_pdu_header(pdu);
559     if (!newpdu)
560         return newpdu;
561     newpdu = _copy_pdu_vars(pdu, newpdu, drop_err, 0, 10000);   /* skip none, copy all */
562 
563     return newpdu;
564 }
565 
566 
567 /*
568  * This function will clone a full varbind list
569  *
570  * Returns a pointer to the cloned varbind list if successful.
571  * Returns 0 if failure
572  */
573 netsnmp_variable_list *
snmp_clone_varbind(netsnmp_variable_list * varlist)574 snmp_clone_varbind(netsnmp_variable_list * varlist)
575 {
576     return _copy_varlist(varlist, 0, 10000);    /* skip none, copy all */
577 }
578 
579 /*
580  * This function will clone a PDU including all of its variables.
581  *
582  * Returns a pointer to the cloned PDU if successful.
583  * Returns 0 if failure
584  */
585 netsnmp_pdu    *
snmp_clone_pdu(netsnmp_pdu * pdu)586 snmp_clone_pdu(netsnmp_pdu *pdu)
587 {
588     return _clone_pdu(pdu, 0);  /* copies all variables */
589 }
590 
591 
592 /*
593  * This function will clone a PDU including some of its variables.
594  *
595  * If skip_count is not zero, it defines the number of variables to skip.
596  * If copy_count is not zero, it defines the number of variables to copy.
597  *
598  * Returns a pointer to the cloned PDU if successful.
599  * Returns 0 if failure.
600  */
601 #ifndef NETSNMP_FEATURE_REMOVE_SNMP_SPLIT_PDU
602 netsnmp_pdu    *
snmp_split_pdu(netsnmp_pdu * pdu,int skip_count,int copy_count)603 snmp_split_pdu(netsnmp_pdu *pdu, int skip_count, int copy_count)
604 {
605     netsnmp_pdu    *newpdu;
606 
607     newpdu = _clone_pdu_header(pdu);
608     if (!newpdu)
609         return newpdu;
610     newpdu = _copy_pdu_vars(pdu, newpdu, 0,     /* don't drop any variables */
611                             skip_count, copy_count);
612 
613     return newpdu;
614 }
615 #endif /* NETSNMP_FEATURE_REMOVE_SNMP_SPLIT_PDU */
616 
617 
618 /*
619  * If there was an error in the input pdu, creates a clone of the pdu
620  * that includes all the variables except the one marked by the errindex.
621  * The command is set to the input command and the reqid, errstat, and
622  * errindex are set to default values.
623  * If the error status didn't indicate an error, the error index didn't
624  * indicate a variable, the pdu wasn't a get response message, the
625  * marked variable was not present in the initial request, or there
626  * would be no remaining variables, this function will return 0.
627  * If everything was successful, a pointer to the fixed cloned pdu will
628  * be returned.
629  */
630 netsnmp_pdu    *
snmp_fix_pdu(netsnmp_pdu * pdu,int command)631 snmp_fix_pdu(netsnmp_pdu *pdu, int command)
632 {
633     netsnmp_pdu    *newpdu;
634 
635     if ((pdu->command != SNMP_MSG_RESPONSE)
636         || (pdu->errstat == SNMP_ERR_NOERROR)
637         || (NULL == pdu->variables)
638         || (pdu->errindex > (int)snmp_varbind_len(pdu))
639         || (pdu->errindex <= 0)) {
640         return NULL;            /* pre-condition tests fail */
641     }
642 
643     newpdu = _clone_pdu(pdu, 1);        /* copies all except errored variable */
644     if (!newpdu)
645         return NULL;
646     if (!newpdu->variables) {
647         snmp_free_pdu(newpdu);
648         return NULL;            /* no variables. "should not happen" */
649     }
650     newpdu->command = command;
651     newpdu->reqid = snmp_get_next_reqid();
652     newpdu->msgid = snmp_get_next_msgid();
653     newpdu->errstat = SNMP_DEFAULT_ERRSTAT;
654     newpdu->errindex = SNMP_DEFAULT_ERRINDEX;
655 
656     return newpdu;
657 }
658 
659 
660 /*
661  * Returns the number of variables bound to a PDU structure
662  */
663 unsigned long
snmp_varbind_len(netsnmp_pdu * pdu)664 snmp_varbind_len(netsnmp_pdu *pdu)
665 {
666     register netsnmp_variable_list *vars;
667     unsigned long   retVal = 0;
668     if (pdu)
669         for (vars = pdu->variables; vars; vars = vars->next_variable) {
670             retVal++;
671         }
672 
673     return retVal;
674 }
675 
676 /*
677  * Add object identifier name to SNMP variable.
678  * If the name is large, additional memory is allocated.
679  * Returns 0 if successful.
680  */
681 
682 int
snmp_set_var_objid(netsnmp_variable_list * vp,const oid * objid,size_t name_length)683 snmp_set_var_objid(netsnmp_variable_list * vp,
684                    const oid * objid, size_t name_length)
685 {
686     size_t          len = sizeof(oid) * name_length;
687 
688     if (vp->name != vp->name_loc && vp->name != NULL) {
689         /*
690          * Probably previously-allocated "big storage".  Better free it
691          * else memory leaks possible.
692          */
693         free(vp->name);
694     }
695 
696     /*
697      * use built-in storage for smaller values
698      */
699     if (len <= sizeof(vp->name_loc)) {
700         vp->name = vp->name_loc;
701     } else {
702         vp->name = (oid *) malloc(len);
703         if (!vp->name)
704             return 1;
705     }
706     if (objid)
707         memmove(vp->name, objid, len);
708     vp->name_length = name_length;
709     return 0;
710 }
711 
712 /**
713  * snmp_set_var_typed_value is used to set data into the netsnmp_variable_list
714  * structure.  Used to return data to the snmp request via the
715  * netsnmp_request_info structure's requestvb pointer.
716  *
717  * @param newvar   the structure gets populated with the given data, type,
718  *                 val_str, and val_len.
719  * @param type     is the asn data type to be copied
720  * @param val_str  is a buffer containing the value to be copied into the
721  *                 newvar structure.
722  * @param val_len  the length of val_str
723  *
724  * @return returns 0 on success and 1 on a malloc error
725  */
726 
727 int
snmp_set_var_typed_value(netsnmp_variable_list * newvar,u_char type,const void * val_str,size_t val_len)728 snmp_set_var_typed_value(netsnmp_variable_list * newvar, u_char type,
729                          const void * val_str, size_t val_len)
730 {
731     newvar->type = type;
732     return snmp_set_var_value(newvar, val_str, val_len);
733 }
734 
735 int
snmp_set_var_typed_integer(netsnmp_variable_list * newvar,u_char type,long val)736 snmp_set_var_typed_integer(netsnmp_variable_list * newvar,
737                            u_char type, long val)
738 {
739     newvar->type = type;
740     return snmp_set_var_value(newvar, &val, sizeof(long));
741 }
742 
743 int
count_varbinds(netsnmp_variable_list * var_ptr)744 count_varbinds(netsnmp_variable_list * var_ptr)
745 {
746     int             count = 0;
747 
748     for (; var_ptr != NULL; var_ptr = var_ptr->next_variable)
749         count++;
750 
751     return count;
752 }
753 
754 netsnmp_feature_child_of(count_varbinds_of_type, netsnmp_unused);
755 #ifndef NETSNMP_FEATURE_REMOVE_COUNT_VARBINDS_OF_TYPE
756 int
count_varbinds_of_type(netsnmp_variable_list * var_ptr,u_char type)757 count_varbinds_of_type(netsnmp_variable_list * var_ptr, u_char type)
758 {
759     int             count = 0;
760 
761     for (; var_ptr != NULL; var_ptr = var_ptr->next_variable)
762         if (var_ptr->type == type)
763             count++;
764 
765     return count;
766 }
767 #endif /* NETSNMP_FEATURE_REMOVE_COUNT_VARBINDS_OF_TYPE */
768 
769 netsnmp_feature_child_of(find_varind_of_type, netsnmp_unused);
770 #ifndef NETSNMP_FEATURE_REMOVE_FIND_VARIND_OF_TYPE
771 netsnmp_variable_list *
find_varbind_of_type(netsnmp_variable_list * var_ptr,u_char type)772 find_varbind_of_type(netsnmp_variable_list * var_ptr, u_char type)
773 {
774     for (; var_ptr != NULL && var_ptr->type != type;
775          var_ptr = var_ptr->next_variable);
776 
777     return var_ptr;
778 }
779 #endif /* NETSNMP_FEATURE_REMOVE_FIND_VARIND_OF_TYPE */
780 
781 netsnmp_variable_list*
find_varbind_in_list(netsnmp_variable_list * vblist,const oid * name,size_t len)782 find_varbind_in_list( netsnmp_variable_list *vblist,
783                       const oid *name, size_t len)
784 {
785     for (; vblist != NULL; vblist = vblist->next_variable)
786         if (!snmp_oid_compare(vblist->name, vblist->name_length, name, len))
787             return vblist;
788 
789     return NULL;
790 }
791 
792 /*
793  * Add some value to SNMP variable.
794  * If the value is large, additional memory is allocated.
795  * Returns 0 if successful.
796  */
797 
798 int
snmp_set_var_value(netsnmp_variable_list * vars,const void * value,size_t len)799 snmp_set_var_value(netsnmp_variable_list * vars,
800                    const void * value, size_t len)
801 {
802     int             largeval = 1;
803 
804     /*
805      * xxx-rks: why the unconditional free? why not use existing
806      * memory, if len < vars->val_len ?
807      */
808     if (vars->val.string && vars->val.string != vars->buf) {
809         free(vars->val.string);
810     }
811     vars->val.string = NULL;
812     vars->val_len = 0;
813 
814     if (value == NULL && len > 0) {
815         snmp_log(LOG_ERR, "bad size for NULL value\n");
816         return 1;
817     }
818 
819     /*
820      * use built-in storage for smaller values
821      */
822     if (len <= sizeof(vars->buf)) {
823         vars->val.string = (u_char *) vars->buf;
824         largeval = 0;
825     }
826 
827     if ((0 == len) || (NULL == value)) {
828         vars->val.string[0] = 0;
829         return 0;
830     }
831 
832     vars->val_len = len;
833     switch (vars->type) {
834     case ASN_INTEGER:
835     case ASN_UNSIGNED:
836     case ASN_TIMETICKS:
837     case ASN_COUNTER:
838     case ASN_UINTEGER:
839         if (vars->val_len == sizeof(int)) {
840             if (ASN_INTEGER == vars->type) {
841                 const int      *val_int
842                     = (const int *) value;
843                 *(vars->val.integer) = (long) *val_int;
844             } else {
845                 const u_int    *val_uint
846                     = (const u_int *) value;
847                 *(vars->val.integer) = (unsigned long) *val_uint;
848             }
849         }
850 #if SIZEOF_LONG != SIZEOF_INT
851         else if (vars->val_len == sizeof(long)){
852             const u_long   *val_ulong
853                 = (const u_long *) value;
854             *(vars->val.integer) = *val_ulong;
855             if (*(vars->val.integer) > 0xffffffff) {
856                 snmp_log(LOG_ERR,"truncating integer value > 32 bits\n");
857                 *(vars->val.integer) &= 0xffffffff;
858             }
859         }
860 #endif
861 #if defined(SIZEOF_LONG_LONG) && (SIZEOF_LONG != SIZEOF_LONG_LONG)
862 #if !defined(SIZEOF_INTMAX_T) || (SIZEOF_LONG_LONG != SIZEOF_INTMAX_T)
863         else if (vars->val_len == sizeof(long long)){
864             const unsigned long long   *val_ullong
865                 = (const unsigned long long *) value;
866             *(vars->val.integer) = (long) *val_ullong;
867             if (*(vars->val.integer) > 0xffffffff) {
868                 snmp_log(LOG_ERR,"truncating integer value > 32 bits\n");
869                 *(vars->val.integer) &= 0xffffffff;
870             }
871         }
872 #endif
873 #endif
874 #if defined(SIZEOF_INTMAX_T) && (SIZEOF_LONG != SIZEOF_INTMAX_T)
875         else if (vars->val_len == sizeof(intmax_t)){
876             const uintmax_t *val_uintmax_t
877                 = (const uintmax_t *) value;
878             *(vars->val.integer) = (long) *val_uintmax_t;
879             if (*(vars->val.integer) > 0xffffffff) {
880                 snmp_log(LOG_ERR,"truncating integer value > 32 bits\n");
881                 *(vars->val.integer) &= 0xffffffff;
882             }
883         }
884 #endif
885 #if SIZEOF_SHORT != SIZEOF_INT
886         else if (vars->val_len == sizeof(short)) {
887             if (ASN_INTEGER == vars->type) {
888                 const short      *val_short
889                     = (const short *) value;
890                 *(vars->val.integer) = (long) *val_short;
891             } else {
892                 const u_short    *val_ushort
893                     = (const u_short *) value;
894                 *(vars->val.integer) = (unsigned long) *val_ushort;
895             }
896         }
897 #endif
898         else if (vars->val_len == sizeof(char)) {
899             if (ASN_INTEGER == vars->type) {
900                 const signed char   *val_char
901                     = (const signed char *) value;
902                 *(vars->val.integer) = (long) *val_char;
903             } else {
904                     const u_char    *val_uchar
905                     = (const u_char *) value;
906                 *(vars->val.integer) = (unsigned long) *val_uchar;
907             }
908         }
909         else {
910             snmp_log(LOG_ERR,"bad size for integer-like type (%d)\n",
911                      (int)vars->val_len);
912             return (1);
913         }
914         vars->val_len = sizeof(long);
915         break;
916 
917     case ASN_OBJECT_ID:
918     case ASN_PRIV_IMPLIED_OBJECT_ID:
919     case ASN_PRIV_INCL_RANGE:
920     case ASN_PRIV_EXCL_RANGE:
921         if (largeval) {
922             vars->val.objid = (oid *) malloc(vars->val_len);
923         }
924         if (vars->val.objid == NULL) {
925             snmp_log(LOG_ERR,"no storage for OID\n");
926             return 1;
927         }
928         memmove(vars->val.objid, value, vars->val_len);
929         break;
930 
931     case ASN_IPADDRESS: /* snmp_build_var_op treats IPADDR like a string */
932         if (4 != vars->val_len) {
933             netsnmp_assert("ipaddress length == 4");
934         }
935         /* FALL THROUGH */
936     case ASN_PRIV_IMPLIED_OCTET_STR:
937     case ASN_OCTET_STR:
938     case ASN_BIT_STR:
939     case ASN_OPAQUE:
940     case ASN_NSAP:
941         if (vars->val_len >= sizeof(vars->buf)) {
942             vars->val.string = (u_char *) malloc(vars->val_len + 1);
943         }
944         if (vars->val.string == NULL) {
945             snmp_log(LOG_ERR,"no storage for string\n");
946             return 1;
947         }
948         memmove(vars->val.string, value, vars->val_len);
949         /*
950          * Make sure the string is zero-terminated; some bits of code make
951          * this assumption.  Easier to do this here than fix all these wrong
952          * assumptions.
953          */
954         vars->val.string[vars->val_len] = '\0';
955         break;
956 
957     case SNMP_NOSUCHOBJECT:
958     case SNMP_NOSUCHINSTANCE:
959     case SNMP_ENDOFMIBVIEW:
960     case ASN_NULL:
961         vars->val_len = 0;
962         vars->val.string = NULL;
963         break;
964 
965 #ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES
966     case ASN_OPAQUE_U64:
967     case ASN_OPAQUE_I64:
968 #endif                          /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */
969     case ASN_COUNTER64:
970         if (largeval || vars->val_len != sizeof(struct counter64)) {
971             snmp_log(LOG_ERR,"bad size for counter 64 (%d)\n",
972                      (int)vars->val_len);
973             return (1);
974         }
975         vars->val_len = sizeof(struct counter64);
976         memmove(vars->val.counter64, value, vars->val_len);
977         break;
978 
979 #ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES
980     case ASN_OPAQUE_FLOAT:
981         if (largeval) {
982             snmp_log(LOG_ERR,"bad size for opaque float (%d)\n",
983                      (int)vars->val_len);
984             return (1);
985         }
986         vars->val_len = sizeof(float);
987         memmove(vars->val.floatVal, value, vars->val_len);
988         break;
989 
990     case ASN_OPAQUE_DOUBLE:
991         if (largeval) {
992             snmp_log(LOG_ERR,"bad size for opaque double (%d)\n",
993                      (int)vars->val_len);
994             return (1);
995         }
996         vars->val_len = sizeof(double);
997         memmove(vars->val.doubleVal, value, vars->val_len);
998         break;
999 
1000 #endif                          /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */
1001 
1002     default:
1003         snmp_log(LOG_ERR,"Internal error in type switching\n");
1004         snmp_set_detail("Internal error in type switching\n");
1005         return (1);
1006     }
1007 
1008     return 0;
1009 }
1010 
1011 void
snmp_replace_var_types(netsnmp_variable_list * vbl,u_char old_type,u_char new_type)1012 snmp_replace_var_types(netsnmp_variable_list * vbl, u_char old_type,
1013                        u_char new_type)
1014 {
1015     while (vbl) {
1016         if (vbl->type == old_type) {
1017             snmp_set_var_typed_value(vbl, new_type, NULL, 0);
1018         }
1019         vbl = vbl->next_variable;
1020     }
1021 }
1022 
1023 #ifndef NETSNMP_FEATURE_REMOVE_SNMP_RESET_VAR_TYPES
1024 void
snmp_reset_var_types(netsnmp_variable_list * vbl,u_char new_type)1025 snmp_reset_var_types(netsnmp_variable_list * vbl, u_char new_type)
1026 {
1027     while (vbl) {
1028         snmp_set_var_typed_value(vbl, new_type, NULL, 0);
1029         vbl = vbl->next_variable;
1030     }
1031 }
1032 #endif /* NETSNMP_FEATURE_REMOVE_SNMP_RESET_VAR_TYPES */
1033 
1034 int
snmp_synch_response_cb(netsnmp_session * ss,netsnmp_pdu * pdu,netsnmp_pdu ** response,snmp_callback pcb)1035 snmp_synch_response_cb(netsnmp_session * ss,
1036                        netsnmp_pdu *pdu,
1037                        netsnmp_pdu **response, snmp_callback pcb)
1038 {
1039     struct synch_state    lstate, *state;
1040     snmp_callback         cbsav;
1041     void                 *cbmagsav;
1042     int                   numfds, count;
1043     netsnmp_large_fd_set  fdset;
1044     struct timeval        timeout, *tvp;
1045     int                   block;
1046 
1047     memset((void *) &lstate, 0, sizeof(lstate));
1048     state = &lstate;
1049     cbsav = ss->callback;
1050     cbmagsav = ss->callback_magic;
1051     ss->callback = pcb;
1052     ss->callback_magic = (void *) state;
1053     netsnmp_large_fd_set_init(&fdset, FD_SETSIZE);
1054 
1055     if (snmp_send(ss, pdu) == 0) {
1056         snmp_free_pdu(pdu);
1057         state->status = STAT_ERROR;
1058     } else {
1059         state->reqid = pdu->reqid;
1060         state->waiting = 1;
1061     }
1062 
1063     while (state->waiting) {
1064         numfds = 0;
1065         NETSNMP_LARGE_FD_ZERO(&fdset);
1066         block = NETSNMP_SNMPBLOCK;
1067         tvp = &timeout;
1068         timerclear(tvp);
1069         snmp_sess_select_info2_flags(NULL, &numfds, &fdset, tvp, &block,
1070                                      NETSNMP_SELECT_NOALARMS);
1071         if (block == 1)
1072             tvp = NULL;         /* block without timeout */
1073         count = netsnmp_large_fd_set_select(numfds, &fdset, NULL, NULL, tvp);
1074         if (count > 0) {
1075             snmp_read2(&fdset);
1076         } else {
1077             switch (count) {
1078             case 0:
1079                 snmp_timeout();
1080                 break;
1081             case -1:
1082                 if (errno == EINTR) {
1083                     continue;
1084                 } else {
1085                     snmp_errno = SNMPERR_GENERR;    /*MTCRITICAL_RESOURCE */
1086                     /*
1087                      * CAUTION! if another thread closed the socket(s)
1088                      * waited on here, the session structure was freed.
1089                      * It would be nice, but we can't rely on the pointer.
1090                      * ss->s_snmp_errno = SNMPERR_GENERR;
1091                      * ss->s_errno = errno;
1092                      */
1093                     snmp_set_detail(strerror(errno));
1094                 }
1095                 /* FALLTHRU */
1096             default:
1097                 state->status = STAT_ERROR;
1098                 state->waiting = 0;
1099             }
1100         }
1101 
1102         if ( ss->flags & SNMP_FLAGS_RESP_CALLBACK ) {
1103             void (*cb)(void);
1104             cb = (void (*)(void))(ss->myvoid);
1105             cb();        /* Used to invoke 'netsnmp_check_outstanding_agent_requests();'
1106                             on internal AgentX queries.  */
1107         }
1108     }
1109     *response = state->pdu;
1110     ss->callback = cbsav;
1111     ss->callback_magic = cbmagsav;
1112     netsnmp_large_fd_set_cleanup(&fdset);
1113     return state->status;
1114 }
1115 
1116 int
snmp_synch_response(netsnmp_session * ss,netsnmp_pdu * pdu,netsnmp_pdu ** response)1117 snmp_synch_response(netsnmp_session * ss,
1118                     netsnmp_pdu *pdu, netsnmp_pdu **response)
1119 {
1120     return snmp_synch_response_cb(ss, pdu, response, snmp_synch_input);
1121 }
1122 
1123 int
snmp_sess_synch_response(void * sessp,netsnmp_pdu * pdu,netsnmp_pdu ** response)1124 snmp_sess_synch_response(void *sessp,
1125                          netsnmp_pdu *pdu, netsnmp_pdu **response)
1126 {
1127     netsnmp_session      *ss;
1128     struct synch_state    lstate, *state;
1129     snmp_callback         cbsav;
1130     void                 *cbmagsav;
1131     int                   numfds, count;
1132     netsnmp_large_fd_set  fdset;
1133     struct timeval        timeout, *tvp;
1134     int                   block;
1135 
1136     ss = snmp_sess_session(sessp);
1137     if (ss == NULL) {
1138         return STAT_ERROR;
1139     }
1140 
1141     memset((void *) &lstate, 0, sizeof(lstate));
1142     state = &lstate;
1143     cbsav = ss->callback;
1144     cbmagsav = ss->callback_magic;
1145     ss->callback = snmp_synch_input;
1146     ss->callback_magic = (void *) state;
1147     netsnmp_large_fd_set_init(&fdset, FD_SETSIZE);
1148 
1149     if (snmp_sess_send(sessp, pdu) == 0) {
1150         snmp_free_pdu(pdu);
1151         state->status = STAT_ERROR;
1152     } else {
1153         state->waiting = 1;
1154         state->reqid = pdu->reqid;
1155     }
1156 
1157     while (state->waiting) {
1158         numfds = 0;
1159         NETSNMP_LARGE_FD_ZERO(&fdset);
1160         block = NETSNMP_SNMPBLOCK;
1161         tvp = &timeout;
1162         timerclear(tvp);
1163         snmp_sess_select_info2_flags(sessp, &numfds, &fdset, tvp, &block,
1164                                      NETSNMP_SELECT_NOALARMS);
1165         if (block == 1)
1166             tvp = NULL;         /* block without timeout */
1167         count = netsnmp_large_fd_set_select(numfds, &fdset, NULL, NULL, tvp);
1168         if (count > 0) {
1169             snmp_sess_read2(sessp, &fdset);
1170         } else
1171             switch (count) {
1172             case 0:
1173                 snmp_sess_timeout(sessp);
1174                 break;
1175             case -1:
1176                 if (errno == EINTR) {
1177                     continue;
1178                 } else {
1179                     snmp_errno = SNMPERR_GENERR;    /*MTCRITICAL_RESOURCE */
1180                     /*
1181                      * CAUTION! if another thread closed the socket(s)
1182                      * waited on here, the session structure was freed.
1183                      * It would be nice, but we can't rely on the pointer.
1184                      * ss->s_snmp_errno = SNMPERR_GENERR;
1185                      * ss->s_errno = errno;
1186                      */
1187                     snmp_set_detail(strerror(errno));
1188                 }
1189                 /* FALLTHRU */
1190             default:
1191                 state->status = STAT_ERROR;
1192                 state->waiting = 0;
1193             }
1194     }
1195     *response = state->pdu;
1196     ss->callback = cbsav;
1197     ss->callback_magic = cbmagsav;
1198     netsnmp_large_fd_set_cleanup(&fdset);
1199     return state->status;
1200 }
1201 
1202 
1203 const char     *
snmp_errstring(int errstat)1204 snmp_errstring(int errstat)
1205 {
1206     const char * const error_string[19] = {
1207         "(noError) No Error",
1208         "(tooBig) Response message would have been too large.",
1209         "(noSuchName) There is no such variable name in this MIB.",
1210         "(badValue) The value given has the wrong type or length.",
1211         "(readOnly) The two parties used do not have access to use the specified SNMP PDU.",
1212         "(genError) A general failure occured",
1213         "noAccess",
1214         "wrongType (The set datatype does not match the data type the agent expects)",
1215         "wrongLength (The set value has an illegal length from what the agent expects)",
1216         "wrongEncoding",
1217         "wrongValue (The set value is illegal or unsupported in some way)",
1218         "noCreation (That table does not support row creation or that object can not ever be created)",
1219         "inconsistentValue (The set value is illegal or unsupported in some way)",
1220         "resourceUnavailable (This is likely a out-of-memory failure within the agent)",
1221         "commitFailed",
1222         "undoFailed",
1223         "authorizationError (access denied to that object)",
1224         "notWritable (That object does not support modification)",
1225         "inconsistentName (That object can not currently be created)"
1226     };
1227 
1228     if (errstat <= MAX_SNMP_ERR && errstat >= SNMP_ERR_NOERROR) {
1229         return error_string[errstat];
1230     } else {
1231         return "Unknown Error";
1232     }
1233 }
1234 
1235 
1236 
1237 /*
1238  *
1239  *  Convenience routines to make various requests
1240  *  over the specified SNMP session.
1241  *
1242  */
1243 #include <net-snmp/library/snmp_debug.h>
1244 
1245 static netsnmp_session *_def_query_session = NULL;
1246 
1247 #ifndef NETSNMP_FEATURE_REMOVE_QUERY_SET_DEFAULT_SESSION
1248 void
netsnmp_query_set_default_session(netsnmp_session * sess)1249 netsnmp_query_set_default_session( netsnmp_session *sess) {
1250     DEBUGMSGTL(("iquery", "set default session %p\n", sess));
1251     _def_query_session = sess;
1252 }
1253 #endif /* NETSNMP_FEATURE_REMOVE_QUERY_SET_DEFAULT_SESSION */
1254 
1255 /**
1256  * Return a pointer to the default internal query session.
1257  */
1258 netsnmp_session *
netsnmp_query_get_default_session_unchecked(void)1259 netsnmp_query_get_default_session_unchecked( void ) {
1260     DEBUGMSGTL(("iquery", "get default session %p\n", _def_query_session));
1261     return _def_query_session;
1262 }
1263 
1264 /**
1265  * Return a pointer to the default internal query session and log a
1266  * warning message once if this session does not exist.
1267  */
1268 netsnmp_session *
netsnmp_query_get_default_session(void)1269 netsnmp_query_get_default_session( void ) {
1270     static int warning_logged = 0;
1271 
1272     if (! _def_query_session && ! warning_logged) {
1273         if (! netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID,
1274                                     NETSNMP_DS_AGENT_INTERNAL_SECNAME)) {
1275             snmp_log(LOG_WARNING,
1276                      "iquerySecName has not been configured - internal queries will fail\n");
1277         } else {
1278             snmp_log(LOG_WARNING,
1279                      "default session is not available - internal queries will fail\n");
1280         }
1281         warning_logged = 1;
1282     }
1283 
1284     return netsnmp_query_get_default_session_unchecked();
1285 }
1286 
1287 
1288 /*
1289  * Internal utility routine to actually send the query
1290  */
_query(netsnmp_variable_list * list,int request,netsnmp_session * session)1291 static int _query(netsnmp_variable_list *list,
1292                   int                    request,
1293                   netsnmp_session       *session) {
1294 
1295     netsnmp_pdu *pdu;
1296     netsnmp_pdu *response = NULL;
1297     netsnmp_variable_list *vb1, *vb2, *vtmp;
1298     int ret, count;
1299 
1300     DEBUGMSGTL(("iquery", "query on session %p\n", session));
1301 
1302     if (NULL == list) {
1303         snmp_log(LOG_ERR, "empty variable list in _query\n");
1304         return SNMP_ERR_GENERR;
1305     }
1306 
1307     pdu = snmp_pdu_create( request );
1308     if (NULL == pdu) {
1309         snmp_log(LOG_ERR, "could not allocate pdu\n");
1310         return SNMP_ERR_GENERR;
1311     }
1312 
1313     /*
1314      * Clone the varbind list into the request PDU...
1315      */
1316     pdu->variables = snmp_clone_varbind( list );
1317     if (NULL == pdu->variables) {
1318         snmp_log(LOG_ERR, "could not clone variable list\n");
1319         snmp_free_pdu(pdu);
1320         return SNMP_ERR_GENERR;
1321     }
1322 
1323 #ifndef NETSNMP_NO_WRITE_SUPPORT
1324 retry:
1325 #endif
1326     if ( session )
1327         ret = snmp_synch_response(            session, pdu, &response );
1328     else if (_def_query_session)
1329         ret = snmp_synch_response( _def_query_session, pdu, &response );
1330     else {
1331         /* No session specified */
1332         snmp_free_pdu(pdu);
1333         return SNMP_ERR_GENERR;
1334     }
1335     DEBUGMSGTL(("iquery", "query returned %d\n", ret));
1336 
1337     /*
1338      * ....then copy the results back into the
1339      * list (assuming the request succeeded!).
1340      * This avoids having to worry about how this
1341      * list was originally allocated.
1342      */
1343     if ( ret == SNMP_ERR_NOERROR ) {
1344         if ( response->errstat != SNMP_ERR_NOERROR ) {
1345             DEBUGMSGT(("iquery", "Error in packet: %s\n",
1346                        snmp_errstring(response->errstat)));
1347             /*
1348              * If the request failed, then remove the
1349              *  offending varbind and try again.
1350              *  (all except SET requests)
1351              *
1352              * XXX - implement a library version of
1353              *       NETSNMP_DS_APP_DONT_FIX_PDUS ??
1354              */
1355             ret = response->errstat;
1356             if (response->errindex != 0) {
1357                 DEBUGMSGT(("iquery:result", "Failed object:\n"));
1358                 for (count = 1, vtmp = response->variables;
1359                      vtmp && count != response->errindex;
1360                      vtmp = vtmp->next_variable, count++)
1361                     /*EMPTY*/;
1362                 if (vtmp)
1363                     DEBUGMSGVAR(("iquery:result", vtmp));
1364                 DEBUGMSG(("iquery:result", "\n"));
1365             }
1366 #ifndef NETSNMP_NO_WRITE_SUPPORT
1367             if (request != SNMP_MSG_SET &&
1368                 response->errindex != 0) {
1369                 DEBUGMSGTL(("iquery", "retrying query (%d, %ld)\n", ret, response->errindex));
1370                 pdu = snmp_fix_pdu( response, request );
1371                 snmp_free_pdu( response );
1372                 response = NULL;
1373                 if ( pdu != NULL )
1374                     goto retry;
1375             }
1376 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
1377         } else {
1378             for (vb1 = response->variables, vb2 = list;
1379                  vb1;
1380                  vb1 = vb1->next_variable,  vb2 = vb2->next_variable) {
1381                 DEBUGMSGVAR(("iquery:result", vb1));
1382                 DEBUGMSG(("iquery:results", "\n"));
1383                 if ( !vb2 ) {
1384                     ret = SNMP_ERR_GENERR;
1385                     break;
1386                 }
1387                 vtmp = vb2->next_variable;
1388                 snmp_free_var_internals( vb2 );
1389                 snmp_clone_var( vb1, vb2 ); /* xxx: check return? */
1390                 vb2->next_variable = vtmp;
1391             }
1392         }
1393     } else {
1394         /* Distinguish snmp_send errors from SNMP errStat errors */
1395         ret = -ret;
1396     }
1397     snmp_free_pdu( response );
1398     return ret;
1399 }
1400 
1401 /*
1402  * These are simple wrappers round the internal utility routine
1403  */
netsnmp_query_get(netsnmp_variable_list * list,netsnmp_session * session)1404 int netsnmp_query_get(netsnmp_variable_list *list,
1405                       netsnmp_session       *session){
1406     return _query( list, SNMP_MSG_GET, session );
1407 }
1408 
1409 
netsnmp_query_getnext(netsnmp_variable_list * list,netsnmp_session * session)1410 int netsnmp_query_getnext(netsnmp_variable_list *list,
1411                           netsnmp_session       *session){
1412     return _query( list, SNMP_MSG_GETNEXT, session );
1413 }
1414 
1415 
1416 #ifndef NETSNMP_NO_WRITE_SUPPORT
netsnmp_query_set(netsnmp_variable_list * list,netsnmp_session * session)1417 int netsnmp_query_set(netsnmp_variable_list *list,
1418                       netsnmp_session       *session){
1419     return _query( list, SNMP_MSG_SET, session );
1420 }
1421 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
1422 
1423 /*
1424  * A walk needs a bit more work.
1425  */
netsnmp_query_walk(netsnmp_variable_list * list,netsnmp_session * session)1426 int netsnmp_query_walk(netsnmp_variable_list *list,
1427                        netsnmp_session       *session) {
1428     /*
1429      * Create a working copy of the original (single)
1430      * varbind, so we can use this varbind parameter
1431      * to check when we've finished walking this subtree.
1432      */
1433     netsnmp_variable_list *vb = snmp_clone_varbind( list );
1434     netsnmp_variable_list *res_list = NULL;
1435     netsnmp_variable_list *res_last = NULL;
1436     int ret;
1437 
1438     /*
1439      * Now walk the tree as usual
1440      */
1441     ret = _query( vb, SNMP_MSG_GETNEXT, session );
1442     while ( ret == SNMP_ERR_NOERROR &&
1443         snmp_oidtree_compare( list->name, list->name_length,
1444                                 vb->name,   vb->name_length ) == 0) {
1445 
1446 	if (vb->type == SNMP_ENDOFMIBVIEW ||
1447 	    vb->type == SNMP_NOSUCHOBJECT ||
1448 	    vb->type == SNMP_NOSUCHINSTANCE)
1449 	    break;
1450 
1451         /*
1452          * Copy each response varbind to the end of the result list
1453          * and then re-use this to ask for the next entry.
1454          */
1455         if ( res_last ) {
1456             res_last->next_variable = snmp_clone_varbind( vb );
1457             res_last = res_last->next_variable;
1458         } else {
1459             res_list = snmp_clone_varbind( vb );
1460             res_last = res_list;
1461         }
1462         ret = _query( vb, SNMP_MSG_GETNEXT, session );
1463     }
1464     /*
1465      * Copy the first result back into the original varbind parameter,
1466      * add the rest of the results (if any), and clean up.
1467      */
1468     if ( res_list ) {
1469         snmp_clone_var( res_list, list );
1470         list->next_variable = res_list->next_variable;
1471         res_list->next_variable = NULL;
1472         snmp_free_varbind( res_list );
1473     }
1474     snmp_free_varbind( vb );
1475     return ret;
1476 }
1477 
1478 /** **************************************************************************
1479  *
1480  * state machine
1481  *
1482  */
1483 int
netsnmp_state_machine_run(netsnmp_state_machine_input * input)1484 netsnmp_state_machine_run( netsnmp_state_machine_input *input)
1485 {
1486     netsnmp_state_machine_step *current, *last;
1487 
1488     netsnmp_require_ptr_LRV( input, SNMPERR_GENERR );
1489     netsnmp_require_ptr_LRV( input->steps, SNMPERR_GENERR );
1490     last = current = input->steps;
1491 
1492     DEBUGMSGT(("state_machine:run", "starting step: %s\n", current->name));
1493 
1494     while (current) {
1495 
1496         /*
1497          * log step and check for required data
1498          */
1499         DEBUGMSGT(("state_machine:run", "at step: %s\n", current->name));
1500         if (NULL == current->run) {
1501             DEBUGMSGT(("state_machine:run", "no run step\n"));
1502             current->result = last->result;
1503             break;
1504         }
1505 
1506         /*
1507          * run step
1508          */
1509         DEBUGMSGT(("state_machine:run", "running step: %s\n", current->name));
1510         current->result = (*current->run)( input, current );
1511         ++input->steps_so_far;
1512 
1513         /*
1514          * log result and move to next step
1515          */
1516         input->last_run = current;
1517         DEBUGMSGT(("state_machine:run:result", "step %s returned %d\n",
1518                    current->name, current->result));
1519         if (SNMPERR_SUCCESS == current->result)
1520             current = current->on_success;
1521         else if (SNMPERR_ABORT == current->result) {
1522             DEBUGMSGT(("state_machine:run:result", "ABORT from %s\n",
1523                        current->name));
1524             break;
1525         }
1526         else
1527             current = current->on_error;
1528     }
1529 
1530     /*
1531      * run cleanup
1532      */
1533     if ((input->cleanup) && (input->cleanup->run))
1534         (*input->cleanup->run)( input, input->last_run );
1535 
1536     return input->last_run->result;
1537 }
1538 
1539 #ifndef NETSNMP_NO_WRITE_SUPPORT
1540 #ifndef NETSNMP_FEATURE_REMOVE_ROW_CREATE
1541 /** **************************************************************************
1542  *
1543  * row create state machine steps
1544  *
1545  */
1546 typedef struct rowcreate_state_s {
1547 
1548     netsnmp_session        *session;
1549     netsnmp_variable_list  *vars;
1550     int                     row_status_index;
1551 } rowcreate_state;
1552 
1553 static netsnmp_variable_list *
_get_vb_num(netsnmp_variable_list * vars,int index)1554 _get_vb_num(netsnmp_variable_list *vars, int index)
1555 {
1556     for (; vars && index > 0; --index)
1557         vars = vars->next_variable;
1558 
1559     if (!vars || index > 0)
1560         return NULL;
1561 
1562     return vars;
1563 }
1564 
1565 
1566 /*
1567  * cleanup
1568  */
1569 static int
_row_status_state_cleanup(netsnmp_state_machine_input * input,netsnmp_state_machine_step * step)1570 _row_status_state_cleanup(netsnmp_state_machine_input *input,
1571                  netsnmp_state_machine_step *step)
1572 {
1573     rowcreate_state       *ctx;
1574 
1575     netsnmp_require_ptr_LRV( input, SNMPERR_ABORT );
1576     netsnmp_require_ptr_LRV( step, SNMPERR_ABORT );
1577 
1578     DEBUGMSGT(("row_create:called", "_row_status_state_cleanup, last run step was %s rc %d\n",
1579                step->name, step->result));
1580 
1581     ctx = (rowcreate_state *)input->input_context;
1582     if (ctx && ctx->vars)
1583         snmp_free_varbind( ctx->vars );
1584 
1585     return SNMPERR_SUCCESS;
1586 }
1587 
1588 /*
1589  * send a request to activate the row
1590  */
1591 static int
_row_status_state_activate(netsnmp_state_machine_input * input,netsnmp_state_machine_step * step)1592 _row_status_state_activate(netsnmp_state_machine_input *input,
1593                   netsnmp_state_machine_step *step)
1594 {
1595     rowcreate_state       *ctx;
1596     netsnmp_variable_list *rs_var, *var = NULL;
1597     int32_t                rc, val = RS_ACTIVE;
1598 
1599     netsnmp_require_ptr_LRV( input, SNMPERR_GENERR );
1600     netsnmp_require_ptr_LRV( step, SNMPERR_GENERR );
1601     netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR );
1602 
1603     ctx = (rowcreate_state *)input->input_context;
1604 
1605     DEBUGMSGT(("row_create:called", "called %s\n", step->name));
1606 
1607     /*
1608      * just send the rowstatus varbind
1609      */
1610     rs_var = _get_vb_num(ctx->vars, ctx->row_status_index);
1611     netsnmp_require_ptr_LRV(rs_var, SNMPERR_GENERR);
1612 
1613     var = snmp_varlist_add_variable(&var, rs_var->name, rs_var->name_length,
1614                                     rs_var->type, &val, sizeof(val));
1615     netsnmp_require_ptr_LRV( var, SNMPERR_GENERR );
1616 
1617     /*
1618      * send set
1619      */
1620     rc = netsnmp_query_set( var, ctx->session );
1621     if (-2 == rc)
1622         rc = SNMPERR_ABORT;
1623 
1624     snmp_free_varbind(var);
1625 
1626     return rc;
1627 }
1628 
1629 /*
1630  * send each non-row status column, one at a time
1631  */
1632 static int
_row_status_state_single_value_cols(netsnmp_state_machine_input * input,netsnmp_state_machine_step * step)1633 _row_status_state_single_value_cols(netsnmp_state_machine_input *input,
1634                                     netsnmp_state_machine_step *step)
1635 {
1636     rowcreate_state       *ctx;
1637     netsnmp_variable_list *var, *tmp_next, *row_status;
1638     int                    rc = SNMPERR_GENERR;
1639 
1640     netsnmp_require_ptr_LRV( input, SNMPERR_GENERR );
1641     netsnmp_require_ptr_LRV( step, SNMPERR_GENERR );
1642     netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR );
1643 
1644     ctx = (rowcreate_state *)input->input_context;
1645 
1646     DEBUGMSGT(("row_create:called", "called %s\n", step->name));
1647 
1648     row_status = _get_vb_num(ctx->vars, ctx->row_status_index);
1649     netsnmp_require_ptr_LRV(row_status, SNMPERR_GENERR);
1650 
1651     /*
1652      * try one varbind at a time
1653      */
1654     for (var = ctx->vars; var; var = var->next_variable) {
1655         if (var == row_status)
1656             continue;
1657 
1658         tmp_next = var->next_variable;
1659         var->next_variable = NULL;
1660 
1661         /*
1662          * send set
1663          */
1664         rc = netsnmp_query_set( var, ctx->session );
1665         var->next_variable = tmp_next;
1666         if (-2 == rc)
1667             rc = SNMPERR_ABORT;
1668         if (rc != SNMPERR_SUCCESS)
1669             break;
1670     }
1671 
1672     return rc;
1673 }
1674 
1675 /*
1676  * send all values except row status
1677  */
1678 static int
_row_status_state_multiple_values_cols(netsnmp_state_machine_input * input,netsnmp_state_machine_step * step)1679 _row_status_state_multiple_values_cols(netsnmp_state_machine_input *input,
1680                                        netsnmp_state_machine_step *step)
1681 {
1682     rowcreate_state       *ctx;
1683     netsnmp_variable_list *vars, *var, *last, *row_status;
1684     int                    rc;
1685 
1686     netsnmp_require_ptr_LRV( input, SNMPERR_GENERR );
1687     netsnmp_require_ptr_LRV( step, SNMPERR_GENERR );
1688     netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR );
1689 
1690     ctx = (rowcreate_state *)input->input_context;
1691 
1692     DEBUGMSGT(("row_create:called", "called %s\n", step->name));
1693 
1694     vars = snmp_clone_varbind(ctx->vars);
1695     netsnmp_require_ptr_LRV(vars, SNMPERR_GENERR);
1696 
1697     row_status = _get_vb_num(vars, ctx->row_status_index);
1698     if (NULL == row_status) {
1699         snmp_free_varbind(vars);
1700         return SNMPERR_GENERR;
1701     }
1702 
1703     /*
1704      * remove row status varbind
1705      */
1706     if (row_status == vars) {
1707         vars = row_status->next_variable;
1708         row_status->next_variable = NULL;
1709     }
1710     else {
1711         for (last=vars, var=last->next_variable;
1712              var;
1713              last=var, var = var->next_variable) {
1714             if (var == row_status) {
1715                 last->next_variable = var->next_variable;
1716                 break;
1717             }
1718         }
1719     }
1720     snmp_free_var(row_status);
1721 
1722     /*
1723      * send set
1724      */
1725     rc = netsnmp_query_set( vars, ctx->session );
1726     if (-2 == rc)
1727         rc = SNMPERR_ABORT;
1728 
1729     snmp_free_varbind(vars);
1730 
1731     return rc;
1732 }
1733 
1734 /*
1735  * send a createAndWait request with no other values
1736  */
1737 static int
_row_status_state_single_value_createAndWait(netsnmp_state_machine_input * input,netsnmp_state_machine_step * step)1738 _row_status_state_single_value_createAndWait(netsnmp_state_machine_input *input,
1739                                              netsnmp_state_machine_step *step)
1740 {
1741     rowcreate_state       *ctx;
1742     netsnmp_variable_list *var = NULL, *rs_var;
1743     int32_t                rc, val = RS_CREATEANDWAIT;
1744 
1745     netsnmp_require_ptr_LRV( input, SNMPERR_GENERR );
1746     netsnmp_require_ptr_LRV( step, SNMPERR_GENERR );
1747     netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR );
1748 
1749     ctx = (rowcreate_state *)input->input_context;
1750 
1751     DEBUGMSGT(("row_create:called", "called %s\n", step->name));
1752 
1753     rs_var = _get_vb_num(ctx->vars, ctx->row_status_index);
1754     netsnmp_require_ptr_LRV(rs_var, SNMPERR_GENERR);
1755 
1756     var = snmp_varlist_add_variable(&var, rs_var->name, rs_var->name_length,
1757                                     rs_var->type, &val, sizeof(val));
1758     netsnmp_require_ptr_LRV(var, SNMPERR_GENERR);
1759 
1760     /*
1761      * send set
1762      */
1763     rc = netsnmp_query_set( var, ctx->session );
1764     if (-2 == rc)
1765         rc = SNMPERR_ABORT;
1766 
1767     snmp_free_varbind(var);
1768 
1769     return rc;
1770 }
1771 
1772 /*
1773  * send a creatAndWait request with all values
1774  */
1775 static int
_row_status_state_all_values_createAndWait(netsnmp_state_machine_input * input,netsnmp_state_machine_step * step)1776 _row_status_state_all_values_createAndWait(netsnmp_state_machine_input *input,
1777                                            netsnmp_state_machine_step *step)
1778 {
1779     rowcreate_state       *ctx;
1780     netsnmp_variable_list *vars, *rs_var;
1781     int                    rc;
1782 
1783     netsnmp_require_ptr_LRV( input, SNMPERR_GENERR );
1784     netsnmp_require_ptr_LRV( step, SNMPERR_GENERR );
1785     netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR );
1786 
1787     ctx = (rowcreate_state *)input->input_context;
1788 
1789     DEBUGMSGT(("row_create:called", "called %s\n", step->name));
1790 
1791     vars = snmp_clone_varbind(ctx->vars);
1792     netsnmp_require_ptr_LRV(vars, SNMPERR_GENERR);
1793 
1794     /*
1795      * make sure row stats is createAndWait
1796      */
1797     rs_var = _get_vb_num(vars, ctx->row_status_index);
1798     if (NULL == rs_var) {
1799         snmp_free_varbind(vars);
1800         return SNMPERR_GENERR;
1801     }
1802 
1803     if (*rs_var->val.integer != RS_CREATEANDWAIT)
1804         *rs_var->val.integer = RS_CREATEANDWAIT;
1805 
1806     /*
1807      * send set
1808      */
1809     rc = netsnmp_query_set( vars, ctx->session );
1810     if (-2 == rc)
1811         rc = SNMPERR_ABORT;
1812 
1813     snmp_free_varbind(vars);
1814 
1815     return rc;
1816 }
1817 
1818 
1819 /**
1820  * send createAndGo request with all values
1821  */
1822 static int
_row_status_state_all_values_createAndGo(netsnmp_state_machine_input * input,netsnmp_state_machine_step * step)1823 _row_status_state_all_values_createAndGo(netsnmp_state_machine_input *input,
1824                                          netsnmp_state_machine_step *step)
1825 {
1826     rowcreate_state       *ctx;
1827     netsnmp_variable_list *vars, *rs_var;
1828     int                    rc;
1829 
1830     netsnmp_require_ptr_LRV( input, SNMPERR_GENERR );
1831     netsnmp_require_ptr_LRV( step, SNMPERR_GENERR );
1832     netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR );
1833 
1834     ctx = (rowcreate_state *)input->input_context;
1835 
1836     DEBUGMSGT(("row_create:called", "called %s\n", step->name));
1837 
1838     vars = snmp_clone_varbind(ctx->vars);
1839     netsnmp_require_ptr_LRV(vars, SNMPERR_GENERR);
1840 
1841     /*
1842      * make sure row stats is createAndGo
1843      */
1844     rs_var = _get_vb_num(vars, ctx->row_status_index + 1);
1845     if (NULL == rs_var) {
1846         snmp_free_varbind(vars);
1847         return SNMPERR_GENERR;
1848     }
1849 
1850     if (*rs_var->val.integer != RS_CREATEANDGO)
1851         *rs_var->val.integer = RS_CREATEANDGO;
1852 
1853     /*
1854      * send set
1855      */
1856     rc = netsnmp_query_set( vars, ctx->session );
1857     if (-2 == rc)
1858         rc = SNMPERR_ABORT;
1859 
1860     snmp_free_varbind(vars);
1861 
1862     return rc;
1863 }
1864 
1865 /** **************************************************************************
1866  *
1867  * row api
1868  *
1869  */
1870 int
netsnmp_row_create(netsnmp_session * sess,netsnmp_variable_list * vars,int row_status_index)1871 netsnmp_row_create(netsnmp_session *sess, netsnmp_variable_list *vars,
1872                    int row_status_index)
1873 {
1874     netsnmp_state_machine_step rc_cleanup =
1875         { "row_create_cleanup", 0, _row_status_state_cleanup,
1876           0, NULL, NULL, 0, NULL };
1877     netsnmp_state_machine_step rc_activate =
1878         { "row_create_activate", 0, _row_status_state_activate,
1879           0, NULL, NULL, 0, NULL };
1880     netsnmp_state_machine_step rc_sv_cols =
1881         { "row_create_single_value_cols", 0,
1882           _row_status_state_single_value_cols, 0, &rc_activate,NULL, 0, NULL };
1883     netsnmp_state_machine_step rc_mv_cols =
1884         { "row_create_multiple_values_cols", 0,
1885           _row_status_state_multiple_values_cols, 0, &rc_activate, &rc_sv_cols,
1886           0, NULL };
1887     netsnmp_state_machine_step rc_sv_caw =
1888         { "row_create_single_value_createAndWait", 0,
1889           _row_status_state_single_value_createAndWait, 0, &rc_mv_cols, NULL,
1890           0, NULL };
1891     netsnmp_state_machine_step rc_av_caw =
1892         { "row_create_all_values_createAndWait", 0,
1893           _row_status_state_all_values_createAndWait, 0, &rc_activate,
1894           &rc_sv_caw, 0, NULL };
1895     netsnmp_state_machine_step rc_av_cag =
1896         { "row_create_all_values_createAndGo", 0,
1897           _row_status_state_all_values_createAndGo, 0, NULL, &rc_av_caw, 0,
1898           NULL };
1899 
1900     netsnmp_state_machine_input sm_input = { "row_create_machine", 0,
1901                                              &rc_av_cag, &rc_cleanup };
1902     rowcreate_state state;
1903 
1904     netsnmp_require_ptr_LRV( sess, SNMPERR_GENERR);
1905     netsnmp_require_ptr_LRV( vars, SNMPERR_GENERR);
1906 
1907     state.session = sess;
1908     state.vars = vars;
1909 
1910     state.row_status_index = row_status_index;
1911     sm_input.input_context = &state;
1912 
1913     netsnmp_state_machine_run( &sm_input);
1914 
1915     return SNMPERR_SUCCESS;
1916 }
1917 #endif /* NETSNMP_FEATURE_REMOVE_ROW_CREATE */
1918 #endif /* NETSNMP_NO_WRITE_SUPPORT */
1919 
1920 
1921 /** @} */
1922