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