xref: /freebsd/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.c (revision 8ef24a0d4b28fe230e20637f56869cc4148cd2ca)
1 /*-
2  * Copyright (c) 2005-2006 The FreeBSD Project
3  * All rights reserved.
4  *
5  * Author: Shteryana Shopova <syrinx@FreeBSD.org>
6  *
7  * Redistribution of this software and documentation and use in source and
8  * binary forms, with or without modification, are permitted provided that
9  * the following conditions are met:
10  *
11  * 1. Redistributions of source code or documentation must retain the above
12  *    copyright notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * Helper functions for snmp client tools
30  *
31  * $FreeBSD$
32  */
33 
34 #include <sys/param.h>
35 #include <sys/queue.h>
36 #include <sys/uio.h>
37 
38 #include <assert.h>
39 #include <ctype.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <syslog.h>
47 #include <unistd.h>
48 
49 #include <bsnmp/asn1.h>
50 #include <bsnmp/snmp.h>
51 #include <bsnmp/snmpclient.h>
52 #include "bsnmptc.h"
53 #include "bsnmptools.h"
54 
55 /* Internal varibale to turn on library debugging for testing and to
56  * find bugs. It is not exported via the header file.
57  * XXX should we cover it by some #ifdef BSNMPTOOLS_DEBUG? */
58 int _bsnmptools_debug = 0;
59 
60 /* Default files to import mapping from if none explicitly provided. */
61 #define	bsnmpd_defs		"/usr/share/snmp/defs/tree.def"
62 #define	mibII_defs		"/usr/share/snmp/defs/mibII_tree.def"
63 
64 /*
65  * The .iso.org.dod oid that has to be prepended to every OID when requesting
66  * a value.
67  */
68 const struct asn_oid IsoOrgDod_OID = {
69 	3, { 1, 3, 6 }
70 };
71 
72 
73 #define	SNMP_ERR_UNKNOWN	0
74 
75 /*
76  * An array of error strings corresponding to error definitions from libbsnmp.
77  */
78 static const struct {
79 	const char *str;
80 	int32_t error;
81 } error_strings[] = {
82 	{ "Unknown", SNMP_ERR_UNKNOWN },
83 	{ "Too big ", SNMP_ERR_TOOBIG },
84 	{ "No such Name", SNMP_ERR_NOSUCHNAME },
85 	{ "Bad Value", SNMP_ERR_BADVALUE },
86 	{ "Readonly", SNMP_ERR_READONLY },
87 	{ "General error", SNMP_ERR_GENERR },
88 	{ "No access", SNMP_ERR_NO_ACCESS },
89 	{ "Wrong type", SNMP_ERR_WRONG_TYPE },
90 	{ "Wrong length", SNMP_ERR_WRONG_LENGTH },
91 	{ "Wrong encoding", SNMP_ERR_WRONG_ENCODING },
92 	{ "Wrong value", SNMP_ERR_WRONG_VALUE },
93 	{ "No creation", SNMP_ERR_NO_CREATION },
94 	{ "Inconsistent value", SNMP_ERR_INCONS_VALUE },
95 	{ "Resource unavailable", SNMP_ERR_RES_UNAVAIL },
96 	{ "Commit failed", SNMP_ERR_COMMIT_FAILED },
97 	{ "Undo failed", SNMP_ERR_UNDO_FAILED },
98 	{ "Authorization error", SNMP_ERR_AUTH_ERR },
99 	{ "Not writable", SNMP_ERR_NOT_WRITEABLE },
100 	{ "Inconsistent name", SNMP_ERR_INCONS_NAME },
101 	{ NULL, 0 }
102 };
103 
104 /* This one and any following are exceptions. */
105 #define	SNMP_SYNTAX_UNKNOWN	SNMP_SYNTAX_NOSUCHOBJECT
106 
107 static const struct {
108 	const char *str;
109 	enum snmp_syntax stx;
110 } syntax_strings[] = {
111 	{ "Null", SNMP_SYNTAX_NULL },
112 	{ "Integer", SNMP_SYNTAX_INTEGER },
113 	{ "OctetString", SNMP_SYNTAX_OCTETSTRING },
114 	{ "OID", SNMP_SYNTAX_OID },
115 	{ "IpAddress", SNMP_SYNTAX_IPADDRESS },
116 	{ "Counter32", SNMP_SYNTAX_COUNTER },
117 	{ "Gauge", SNMP_SYNTAX_GAUGE },
118 	{ "TimeTicks", SNMP_SYNTAX_TIMETICKS },
119 	{ "Counter64", SNMP_SYNTAX_COUNTER64 },
120 	{ "Unknown", SNMP_SYNTAX_UNKNOWN },
121 };
122 
123 int
124 snmptool_init(struct snmp_toolinfo *snmptoolctx)
125 {
126 	char *str;
127 	size_t slen;
128 
129 	memset(snmptoolctx, 0, sizeof(struct snmp_toolinfo));
130 	snmptoolctx->objects = 0;
131 	snmptoolctx->mappings = NULL;
132 	snmptoolctx->flags = SNMP_PDU_GET;	/* XXX */
133 	SLIST_INIT(&snmptoolctx->filelist);
134 	snmp_client_init(&snmp_client);
135 	SET_MAXREP(snmptoolctx, SNMP_MAX_REPETITIONS);
136 
137 	if (add_filename(snmptoolctx, bsnmpd_defs, &IsoOrgDod_OID, 0) < 0)
138 		warnx("Error adding file %s to list", bsnmpd_defs);
139 
140 	if (add_filename(snmptoolctx, mibII_defs, &IsoOrgDod_OID, 0) < 0)
141 		warnx("Error adding file %s to list", mibII_defs);
142 
143 	/* Read the environment */
144 	if ((str = getenv("SNMPAUTH")) != NULL) {
145 		slen = strlen(str);
146 		if (slen == strlen("md5") && strcasecmp(str, "md5") == 0)
147 			snmp_client.user.auth_proto = SNMP_AUTH_HMAC_MD5;
148 		else if (slen == strlen("sha")&& strcasecmp(str, "sha") == 0)
149 			snmp_client.user.auth_proto = SNMP_AUTH_HMAC_SHA;
150 		else if (slen != 0)
151 			warnx("Bad authentication type - %s in SNMPAUTH", str);
152 	}
153 
154 	if ((str = getenv("SNMPPRIV")) != NULL) {
155 		slen = strlen(str);
156 		if (slen == strlen("des") && strcasecmp(str, "des") == 0)
157 			snmp_client.user.priv_proto = SNMP_PRIV_DES;
158 		else if (slen == strlen("aes")&& strcasecmp(str, "aes") == 0)
159 			snmp_client.user.priv_proto = SNMP_PRIV_AES;
160 		else if (slen != 0)
161 			warnx("Bad privacy type - %s in SNMPPRIV", str);
162 	}
163 
164 	if ((str = getenv("SNMPUSER")) != NULL) {
165 		if ((slen = strlen(str)) > sizeof(snmp_client.user.sec_name)) {
166 			warnx("Username too long - %s in SNMPUSER", str);
167 			return (-1);
168 		}
169 		if (slen > 0) {
170 			strlcpy(snmp_client.user.sec_name, str,
171 			    sizeof(snmp_client.user.sec_name));
172 			snmp_client.version = SNMP_V3;
173 		}
174 	}
175 
176 	if ((str = getenv("SNMPPASSWD")) != NULL) {
177 		if ((slen = strlen(str)) > MAXSTR)
178 			slen = MAXSTR - 1;
179 		if ((snmptoolctx->passwd = malloc(slen + 1)) == NULL) {
180 			warnx("malloc() failed - %s", strerror(errno));
181 			return (-1);
182 		}
183 		if (slen > 0)
184 			strlcpy(snmptoolctx->passwd, str, slen + 1);
185 	}
186 
187 	return (0);
188 }
189 
190 #define	OBJECT_IDX_LIST(o)	o->info->table_idx->index_list
191 
192 /*
193  * Walk through the file list and import string<->oid mappings from each file.
194  */
195 int32_t
196 snmp_import_all(struct snmp_toolinfo *snmptoolctx)
197 {
198 	int32_t fc;
199 	struct fname *tmp;
200 
201 	if (snmptoolctx == NULL)
202 		return (-1);
203 
204 	if (ISSET_NUMERIC(snmptoolctx))
205 		return (0);
206 
207 	if ((snmptoolctx->mappings = snmp_mapping_init()) == NULL)
208 		return (-1);
209 
210 	fc = 0;
211 	if (SLIST_EMPTY(&snmptoolctx->filelist)) {
212 		warnx("No files to read OID <-> string conversions from");
213 		return (-1);
214 	} else {
215 		SLIST_FOREACH(tmp, &snmptoolctx->filelist, link) {
216 			if (tmp->done)
217 				continue;
218 			if (snmp_import_file(snmptoolctx, tmp) < 0) {
219 				fc = -1;
220 				break;
221 			}
222 			fc++;
223 		}
224 	}
225 
226 	snmp_mapping_dump(snmptoolctx);
227 	return (fc);
228 }
229 
230 /*
231  * Add a filename to the file list - the initial idea of keeping a list with all
232  * files to read OIDs from was that an application might want to have loaded in
233  * memory the OIDs from a single file only and when done with them read the OIDs
234  * from another file. This is not used yet but might be a good idea at some
235  * point. Size argument is number of bytes in string including trailing '\0',
236  * not string length.
237  */
238 int32_t
239 add_filename(struct snmp_toolinfo *snmptoolctx, const char *filename,
240     const struct asn_oid *cut, int32_t done)
241 {
242 	char *fstring;
243 	struct fname *entry;
244 
245 	if (snmptoolctx == NULL)
246 		return (-1);
247 
248 	/* Make sure file was not in list. */
249 	SLIST_FOREACH(entry, &snmptoolctx->filelist, link) {
250 		if (strncmp(entry->name, filename, strlen(entry->name)) == 0)
251 			return (0);
252 	}
253 
254 	if ((fstring = strdup(filename)) == NULL) {
255 		warnx("strdup() failed - %s", strerror(errno));
256 		return (-1);
257 	}
258 
259 	if ((entry = calloc(1, sizeof(struct fname))) == NULL) {
260 		warnx("calloc() failed - %s", strerror(errno));
261 		free(fstring);
262 		return (-1);
263 	}
264 
265 	if (cut != NULL)
266 		asn_append_oid(&(entry->cut), cut);
267 	entry->name = fstring;
268 	entry->done = done;
269 	SLIST_INSERT_HEAD(&snmptoolctx->filelist, entry, link);
270 
271 	return (1);
272 }
273 
274 void
275 free_filelist(struct snmp_toolinfo *snmptoolctx)
276 {
277 	struct fname *f;
278 
279 	if (snmptoolctx == NULL)
280 		return; /* XXX error handling */
281 
282 	while ((f = SLIST_FIRST(&snmptoolctx->filelist)) != NULL) {
283 		SLIST_REMOVE_HEAD(&snmptoolctx->filelist, link);
284 		if (f->name)
285 			free(f->name);
286 		free(f);
287 	}
288 }
289 
290 static char
291 isvalid_fchar(char c, int pos)
292 {
293 	if (isalpha(c)|| c == '/'|| c == '_' || c == '.' || c == '~' ||
294 	    (pos != 0 && isdigit(c))){
295 		return (c);
296 	}
297 
298 	if (c == '\0')
299 		return (0);
300 
301 	if (!isascii(c) || !isprint(c))
302 		warnx("Unexpected character %#2x", (u_int) c);
303 	else
304 		warnx("Illegal character '%c'", c);
305 
306 	return (-1);
307 }
308 
309 /*
310  * Re-implement getsubopt from scratch, because the second argument is broken
311  * and will not compile with WARNS=5.
312  * Copied from src/contrib/bsnmp/snmpd/main.c.
313  */
314 static int
315 getsubopt1(char **arg, const char *const *options, char **valp, char **optp)
316 {
317 	static const char *const delim = ",\t ";
318 	u_int i;
319 	char *ptr;
320 
321 	*optp = NULL;
322 
323 	/* Skip leading junk. */
324 	for (ptr = *arg; *ptr != '\0'; ptr++)
325 		if (strchr(delim, *ptr) == NULL)
326 			break;
327 	if (*ptr == '\0') {
328 		*arg = ptr;
329 		return (-1);
330 	}
331 	*optp = ptr;
332 
333 	/* Find the end of the option. */
334 	while (*++ptr != '\0')
335 		if (strchr(delim, *ptr) != NULL || *ptr == '=')
336 			break;
337 
338 	if (*ptr != '\0') {
339 		if (*ptr == '=') {
340 			*ptr++ = '\0';
341 			*valp = ptr;
342 			while (*ptr != '\0' && strchr(delim, *ptr) == NULL)
343 				ptr++;
344 			if (*ptr != '\0')
345 				*ptr++ = '\0';
346 		} else
347 			*ptr++ = '\0';
348 	}
349 
350 	*arg = ptr;
351 
352 	for (i = 0; *options != NULL; options++, i++)
353 		if (strcmp(*optp, *options) == 0)
354 			return (i);
355 	return (-1);
356 }
357 
358 static int32_t
359 parse_path(char *value)
360 {
361 	int32_t i, len;
362 
363 	if (value == NULL)
364 		return (-1);
365 
366 	for (len = 0; len < MAXPATHLEN; len++) {
367 		i = isvalid_fchar(*(value + len), len) ;
368 
369 		if (i == 0)
370 			break;
371 		else if (i < 0)
372 			return (-1);
373 	}
374 
375 	if (len >= MAXPATHLEN || value[len] != '\0') {
376 		warnx("Bad pathname - '%s'", value);
377 		return (-1);
378 	}
379 
380 	return (len);
381 }
382 
383 static int32_t
384 parse_flist(struct snmp_toolinfo *snmptoolctx, char *value, char *path,
385     const struct asn_oid *cut)
386 {
387 	int32_t namelen;
388 	char filename[MAXPATHLEN + 1];
389 
390 	if (value == NULL)
391 		return (-1);
392 
393 	do {
394 		memset(filename, 0, MAXPATHLEN + 1);
395 
396 		if (isalpha(*value) && (path == NULL || path[0] == '\0')) {
397 			strlcpy(filename, SNMP_DEFS_DIR, MAXPATHLEN + 1);
398 			namelen = strlen(SNMP_DEFS_DIR);
399 		} else if (path != NULL){
400 			strlcpy(filename, path, MAXPATHLEN + 1);
401 			namelen = strlen(path);
402 		} else
403 			namelen = 0;
404 
405 		for ( ; namelen < MAXPATHLEN; value++) {
406 			if (isvalid_fchar(*value, namelen) > 0) {
407 				filename[namelen++] = *value;
408 				continue;
409 			}
410 
411 			if (*value == ',' )
412 				value++;
413 			else if (*value == '\0')
414 				;
415 			else {
416 				if (!isascii(*value) || !isprint(*value))
417 					warnx("Unexpected character %#2x in"
418 					    " filename", (u_int) *value);
419 				else
420 					warnx("Illegal character '%c' in"
421 					    " filename", *value);
422 				return (-1);
423 			}
424 
425 			filename[namelen]='\0';
426 			break;
427 		}
428 
429 		if ((namelen == MAXPATHLEN) && (filename[MAXPATHLEN] != '\0')) {
430 			warnx("Filename %s too long", filename);
431 			return (-1);
432 		}
433 
434 		if (add_filename(snmptoolctx, filename, cut, 0) < 0) {
435 			warnx("Error adding file %s to list", filename);
436 			return (-1);
437 		}
438 	} while (*value != '\0');
439 
440 	return(1);
441 }
442 
443 static int32_t
444 parse_ascii(char *ascii, uint8_t *binstr, size_t binlen)
445 {
446 	char dptr[3];
447 	size_t count;
448 	int32_t alen, i, saved_errno;
449 	uint32_t val;
450 
451 	/* Filter 0x at the beginning */
452 	if ((alen = strlen(ascii)) > 2 && ascii[0] == '0' && ascii[1] == 'x')
453 		i = 2;
454 	else
455 		i = 0;
456 
457 	saved_errno = errno;
458 	errno = 0;
459 	for (count = 0; i < alen; i += 2) {
460 		/* XXX: consider strlen(ascii) % 2 != 0 */
461 		dptr[0] = ascii[i];
462 		dptr[1] = ascii[i + 1];
463 		dptr[2] = '\0';
464 		if ((val = strtoul(dptr, NULL, 16)) > 0xFF || errno != 0) {
465 			errno = saved_errno;
466 			return (-1);
467 		}
468 		binstr[count] = (uint8_t) val;
469 		if (++count >= binlen) {
470 			warnx("Key %s too long - truncating to %zu octets",
471 			    ascii, binlen);
472 			break;
473 		}
474 	}
475 
476 	return (count);
477 }
478 
479 /*
480  * Functions to parse common input options for client tools and fill in the
481  * snmp_client structure.
482  */
483 int32_t
484 parse_authentication(struct snmp_toolinfo *snmptoolctx __unused, char *opt_arg)
485 {
486 	int32_t count, subopt;
487 	char *val, *option;
488 	const char *const subopts[] = {
489 		"proto",
490 		"key",
491 		NULL
492 	};
493 
494 	assert(opt_arg != NULL);
495 	count = 1;
496 	while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) {
497 		switch (subopt) {
498 		case 0:
499 			if (val == NULL) {
500 				warnx("Suboption 'proto' requires an argument");
501 				return (-1);
502 			}
503 			if (strlen(val) != 3) {
504 				warnx("Unknown auth protocol - %s", val);
505 				return (-1);
506 			}
507 			if (strncasecmp("md5", val, strlen("md5")) == 0)
508 				snmp_client.user.auth_proto =
509 				    SNMP_AUTH_HMAC_MD5;
510 			else if (strncasecmp("sha", val, strlen("sha")) == 0)
511 				snmp_client.user.auth_proto =
512 				    SNMP_AUTH_HMAC_SHA;
513 			else {
514 				warnx("Unknown auth protocol - %s", val);
515 				return (-1);
516 			}
517 			break;
518 		case 1:
519 			if (val == NULL) {
520 				warnx("Suboption 'key' requires an argument");
521 				return (-1);
522 			}
523 			if (parse_ascii(val, snmp_client.user.auth_key,
524 			    SNMP_AUTH_KEY_SIZ) < 0) {
525 				warnx("Bad authentication key- %s", val);
526 				return (-1);
527 			}
528 			break;
529 		default:
530 			warnx("Unknown suboption - '%s'", suboptarg);
531 			return (-1);
532 		}
533 		count += 1;
534 	}
535 	return (2/* count */);
536 }
537 
538 int32_t
539 parse_privacy(struct snmp_toolinfo *snmptoolctx __unused, char *opt_arg)
540 {
541 	int32_t count, subopt;
542 	char *val, *option;
543 	const char *const subopts[] = {
544 		"proto",
545 		"key",
546 		NULL
547 	};
548 
549 	assert(opt_arg != NULL);
550 	count = 1;
551 	while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) {
552 		switch (subopt) {
553 		case 0:
554 			if (val == NULL) {
555 				warnx("Suboption 'proto' requires an argument");
556 				return (-1);
557 			}
558 			if (strlen(val) != 3) {
559 				warnx("Unknown privacy protocol - %s", val);
560 				return (-1);
561 			}
562 			if (strncasecmp("aes", val, strlen("aes")) == 0)
563 				snmp_client.user.priv_proto = SNMP_PRIV_AES;
564 			else if (strncasecmp("des", val, strlen("des")) == 0)
565 				snmp_client.user.priv_proto = SNMP_PRIV_DES;
566 			else {
567 				warnx("Unknown privacy protocol - %s", val);
568 				return (-1);
569 			}
570 			break;
571 		case 1:
572 			if (val == NULL) {
573 				warnx("Suboption 'key' requires an argument");
574 				return (-1);
575 			}
576 			if (parse_ascii(val, snmp_client.user.priv_key,
577 			    SNMP_PRIV_KEY_SIZ) < 0) {
578 				warnx("Bad privacy key- %s", val);
579 				return (-1);
580 			}
581 			break;
582 		default:
583 			warnx("Unknown suboption - '%s'", suboptarg);
584 			return (-1);
585 		}
586 		count += 1;
587 	}
588 	return (2/* count */);
589 }
590 
591 int32_t
592 parse_context(struct snmp_toolinfo *snmptoolctx __unused, char *opt_arg)
593 {
594 	int32_t count, subopt;
595 	char *val, *option;
596 	const char *const subopts[] = {
597 		"context",
598 		"context-engine",
599 		NULL
600 	};
601 
602 	assert(opt_arg != NULL);
603 	count = 1;
604 	while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) {
605 		switch (subopt) {
606 		case 0:
607 			if (val == NULL) {
608 				warnx("Suboption 'context' - no argument");
609 				return (-1);
610 			}
611 			strlcpy(snmp_client.cname, val, SNMP_CONTEXT_NAME_SIZ);
612 			break;
613 		case 1:
614 			if (val == NULL) {
615 				warnx("Suboption 'context-engine' - no argument");
616 				return (-1);
617 			}
618 			if ((int32_t)(snmp_client.clen = parse_ascii(val,
619 			    snmp_client.cengine, SNMP_ENGINE_ID_SIZ)) == -1) {
620 				warnx("Bad EngineID - %s", val);
621 				return (-1);
622 			}
623 			break;
624 		default:
625 			warnx("Unknown suboption - '%s'", suboptarg);
626 			return (-1);
627 		}
628 		count += 1;
629 	}
630 	return (2/* count */);
631 }
632 
633 int32_t
634 parse_user_security(struct snmp_toolinfo *snmptoolctx __unused, char *opt_arg)
635 {
636 	int32_t count, subopt, saved_errno;
637 	char *val, *option;
638 	const char *const subopts[] = {
639 		"engine",
640 		"engine-boots",
641 		"engine-time",
642 		"name",
643 		NULL
644 	};
645 
646 	assert(opt_arg != NULL);
647 	count = 1;
648 	while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) {
649 		switch (subopt) {
650 		case 0:
651 			if (val == NULL) {
652 				warnx("Suboption 'engine' - no argument");
653 				return (-1);
654 			}
655 			snmp_client.engine.engine_len = parse_ascii(val,
656 			    snmp_client.engine.engine_id, SNMP_ENGINE_ID_SIZ);
657 			if ((int32_t)snmp_client.engine.engine_len == -1) {
658 				warnx("Bad EngineID - %s", val);
659 				return (-1);
660 			}
661 			break;
662 		case 1:
663 			if (val == NULL) {
664 				warnx("Suboption 'engine-boots' - no argument");
665 				return (-1);
666 			}
667 			saved_errno = errno;
668 			errno = 0;
669 			snmp_client.engine.engine_boots = strtoul(val, NULL, 10);
670 			if (errno != 0) {
671 				warnx("Bad 'engine-boots' value %s - %s", val,
672 				    strerror(errno));
673 				errno = saved_errno;
674 				return (-1);
675 			}
676 			errno = saved_errno;
677 			break;
678 		case 2:
679 			if (val == NULL) {
680 				warnx("Suboption 'engine-time' - no argument");
681 				return (-1);
682 			}
683 			saved_errno = errno;
684 			errno = 0;
685 			snmp_client.engine.engine_time = strtoul(val, NULL, 10);
686 			if (errno != 0) {
687 				warnx("Bad 'engine-time' value %s - %s", val,
688 				    strerror(errno));
689 				errno = saved_errno;
690 				return (-1);
691 			}
692 			errno = saved_errno;
693 			break;
694 		case 3:
695 			strlcpy(snmp_client.user.sec_name, val,
696 			    SNMP_ADM_STR32_SIZ);
697 			break;
698 		default:
699 			warnx("Unknown suboption - '%s'", suboptarg);
700 			return (-1);
701 		}
702 		count += 1;
703 	}
704 	return (2/* count */);
705 }
706 
707 int32_t
708 parse_file(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
709 {
710 	assert(opt_arg != NULL);
711 
712 	if (parse_flist(snmptoolctx, opt_arg, NULL, &IsoOrgDod_OID) < 0)
713 		return (-1);
714 
715 	return (2);
716 }
717 
718 int32_t
719 parse_include(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
720 {
721 	char path[MAXPATHLEN + 1];
722 	int32_t cut_dflt, len, subopt;
723 	struct asn_oid cut;
724 	char *val, *option;
725 	const char *const subopts[] = {
726 		"cut",
727 		"path",
728 		"file",
729 		NULL
730 	};
731 
732 #define	INC_CUT		0
733 #define	INC_PATH	1
734 #define	INC_LIST	2
735 
736 	assert(opt_arg != NULL);
737 
738 	/* if (opt == 'i')
739 		free_filelist(snmptoolctx, ); */
740 	/*
741 	 * This function should be called only after getopt(3) - otherwise if
742 	 * no previous validation of opt_arg strlen() may not return what is
743 	 * expected.
744 	 */
745 
746 	path[0] = '\0';
747 	memset(&cut, 0, sizeof(struct asn_oid));
748 	cut_dflt = -1;
749 
750 	while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) {
751 		switch (subopt) {
752 		    case INC_CUT:
753 			if (val == NULL) {
754 				warnx("Suboption 'cut' requires an argument");
755 				return (-1);
756 			} else {
757 				if (snmp_parse_numoid(val, &cut) < 0)
758 					return (-1);
759 			}
760 			cut_dflt = 1;
761 			break;
762 
763 		    case INC_PATH:
764 			if ((len = parse_path(val)) < 0)
765 				return (-1);
766 			strlcpy(path, val, len + 1);
767 			break;
768 
769 		    case INC_LIST:
770 			if (val == NULL)
771 				return (-1);
772 			if (cut_dflt == -1)
773 				len = parse_flist(snmptoolctx, val, path, &IsoOrgDod_OID);
774 			else
775 				len = parse_flist(snmptoolctx, val, path, &cut);
776 			if (len < 0)
777 				return (-1);
778 			break;
779 
780 		    default:
781 			warnx("Unknown suboption - '%s'", suboptarg);
782 			return (-1);
783 		}
784 	}
785 
786 	/* XXX: Fix me - returning two is wrong here */
787 	return (2);
788 }
789 
790 int32_t
791 parse_server(char *opt_arg)
792 {
793 	assert(opt_arg != NULL);
794 
795 	if (snmp_parse_server(&snmp_client, opt_arg) < 0)
796 		return (-1);
797 
798 	if (snmp_client.trans > SNMP_TRANS_UDP && snmp_client.chost == NULL) {
799 		if ((snmp_client.chost = malloc(strlen(SNMP_DEFAULT_LOCAL) + 1))
800 		    == NULL) {
801 			syslog(LOG_ERR, "malloc() failed: %s", strerror(errno));
802 			return (-1);
803 		}
804 		strcpy(snmp_client.chost, SNMP_DEFAULT_LOCAL);
805 	}
806 
807 	return (2);
808 }
809 
810 int32_t
811 parse_timeout(char *opt_arg)
812 {
813 	int32_t v, saved_errno;
814 
815 	assert(opt_arg != NULL);
816 
817 	saved_errno = errno;
818 	errno = 0;
819 
820 	v = strtol(opt_arg, NULL, 10);
821 	if (errno != 0) {
822 		warnx( "Error parsing timeout value - %s", strerror(errno));
823 		errno = saved_errno;
824 		return (-1);
825 	}
826 
827 	snmp_client.timeout.tv_sec = v;
828 	errno = saved_errno;
829 	return (2);
830 }
831 
832 int32_t
833 parse_retry(char *opt_arg)
834 {
835 	uint32_t v;
836 	int32_t saved_errno;
837 
838 	assert(opt_arg != NULL);
839 
840 	saved_errno = errno;
841 	errno = 0;
842 
843 	v = strtoul(opt_arg, NULL, 10);
844 	if (errno != 0) {
845 		warnx("Error parsing retries count - %s", strerror(errno));
846 		errno = saved_errno;
847 		return (-1);
848 	}
849 
850 	snmp_client.retries = v;
851 	errno = saved_errno;
852 	return (2);
853 }
854 
855 int32_t
856 parse_version(char *opt_arg)
857 {
858 	uint32_t v;
859 	int32_t saved_errno;
860 
861 	assert(opt_arg != NULL);
862 
863 	saved_errno = errno;
864 	errno = 0;
865 
866 	v = strtoul(opt_arg, NULL, 10);
867 	if (errno != 0) {
868 		warnx("Error parsing version - %s", strerror(errno));
869 		errno = saved_errno;
870 		return (-1);
871 	}
872 
873 	switch (v) {
874 		case 1:
875 			snmp_client.version = SNMP_V1;
876 			break;
877 		case 2:
878 			snmp_client.version = SNMP_V2c;
879 			break;
880 		case 3:
881 			snmp_client.version = SNMP_V3;
882 			break;
883 		default:
884 			warnx("Unsupported SNMP version - %u", v);
885 			errno = saved_errno;
886 			return (-1);
887 	}
888 
889 	errno = saved_errno;
890 	return (2);
891 }
892 
893 int32_t
894 parse_local_path(char *opt_arg)
895 {
896 	assert(opt_arg != NULL);
897 
898 	if (sizeof(opt_arg) > sizeof(SNMP_LOCAL_PATH)) {
899 		warnx("Filename too long - %s", opt_arg);
900 		return (-1);
901 	}
902 
903 	strlcpy(snmp_client.local_path, opt_arg, sizeof(SNMP_LOCAL_PATH));
904 	return (2);
905 }
906 
907 int32_t
908 parse_buflen(char *opt_arg)
909 {
910 	uint32_t size;
911 	int32_t saved_errno;
912 
913 	assert(opt_arg != NULL);
914 
915 	saved_errno = errno;
916 	errno = 0;
917 
918 	size = strtoul(opt_arg, NULL, 10);
919 	if (errno != 0) {
920 		warnx("Error parsing buffer size - %s", strerror(errno));
921 		errno = saved_errno;
922 		return (-1);
923 	}
924 
925 	if (size > MAX_BUFF_SIZE) {
926 		warnx("Buffer size too big - %d max allowed", MAX_BUFF_SIZE);
927 		errno = saved_errno;
928 		return (-1);
929 	}
930 
931 	snmp_client.txbuflen = snmp_client.rxbuflen = size;
932 	errno = saved_errno;
933 	return (2);
934 }
935 
936 int32_t
937 parse_debug(void)
938 {
939 	snmp_client.dump_pdus = 1;
940 	return (1);
941 }
942 
943 int32_t
944 parse_discovery(struct snmp_toolinfo *snmptoolctx)
945 {
946 	SET_EDISCOVER(snmptoolctx);
947 	snmp_client.version = SNMP_V3;
948 	return (1);
949 }
950 
951 int32_t
952 parse_local_key(struct snmp_toolinfo *snmptoolctx)
953 {
954 	SET_LOCALKEY(snmptoolctx);
955 	snmp_client.version = SNMP_V3;
956 	return (1);
957 }
958 
959 int32_t
960 parse_num_oids(struct snmp_toolinfo *snmptoolctx)
961 {
962 	SET_NUMERIC(snmptoolctx);
963 	return (1);
964 }
965 
966 int32_t
967 parse_output(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
968 {
969 	assert(opt_arg != NULL);
970 
971 	if (strlen(opt_arg) > strlen("verbose")) {
972 		warnx( "Invalid output option - %s",opt_arg);
973 		return (-1);
974 	}
975 
976 	if (strncasecmp(opt_arg, "short", strlen(opt_arg)) == 0)
977 		SET_OUTPUT(snmptoolctx, OUTPUT_SHORT);
978 	else if (strncasecmp(opt_arg, "verbose", strlen(opt_arg)) == 0)
979 		SET_OUTPUT(snmptoolctx, OUTPUT_VERBOSE);
980 	else if (strncasecmp(opt_arg,"tabular", strlen(opt_arg)) == 0)
981 		SET_OUTPUT(snmptoolctx, OUTPUT_TABULAR);
982 	else if (strncasecmp(opt_arg, "quiet", strlen(opt_arg)) == 0)
983 		SET_OUTPUT(snmptoolctx, OUTPUT_QUIET);
984 	else {
985 		warnx( "Invalid output option - %s", opt_arg);
986 		return (-1);
987 	}
988 
989 	return (2);
990 }
991 
992 int32_t
993 parse_errors(struct snmp_toolinfo *snmptoolctx)
994 {
995 	SET_RETRY(snmptoolctx);
996 	return (1);
997 }
998 
999 int32_t
1000 parse_skip_access(struct snmp_toolinfo *snmptoolctx)
1001 {
1002 	SET_ERRIGNORE(snmptoolctx);
1003 	return (1);
1004 }
1005 
1006 char *
1007 snmp_parse_suboid(char *str, struct asn_oid *oid)
1008 {
1009 	char *endptr;
1010 	asn_subid_t suboid;
1011 
1012 	if (*str == '.')
1013 		str++;
1014 
1015 	if (*str < '0' || *str > '9')
1016 		return (str);
1017 
1018 	do {
1019 		suboid = strtoul(str, &endptr, 10);
1020 		if ((asn_subid_t) suboid > ASN_MAXID) {
1021 			warnx("Suboid %u > ASN_MAXID", suboid);
1022 			return (NULL);
1023 		}
1024 		if (snmp_suboid_append(oid, suboid) < 0)
1025 			return (NULL);
1026 		str = endptr + 1;
1027 	} while (*endptr == '.');
1028 
1029 	return (endptr);
1030 }
1031 
1032 static char *
1033 snmp_int2asn_oid(char *str, struct asn_oid *oid)
1034 {
1035 	char *endptr;
1036 	int32_t v, saved_errno;
1037 
1038 	saved_errno = errno;
1039 	errno = 0;
1040 
1041 	v = strtol(str, &endptr, 10);
1042 	if (errno != 0) {
1043 		warnx("Integer value %s not supported - %s", str,
1044 		    strerror(errno));
1045 		errno = saved_errno;
1046 		return (NULL);
1047 	}
1048 	errno = saved_errno;
1049 
1050 	if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
1051 		return (NULL);
1052 
1053 	return (endptr);
1054 }
1055 
1056 /* It is a bit weird to have a table indexed by OID but still... */
1057 static char *
1058 snmp_oid2asn_oid(struct snmp_toolinfo *snmptoolctx, char *str,
1059     struct asn_oid *oid)
1060 {
1061 	int32_t i;
1062 	char string[MAXSTR + 1], *endptr;
1063 	struct snmp_object obj;
1064 
1065 	for (i = 0; i < MAXSTR; i++)
1066 		if (isalpha (*(str + i)) == 0)
1067 			break;
1068 
1069 	endptr = str + i;
1070 	memset(&obj, 0, sizeof(struct snmp_object));
1071 	if (i == 0) {
1072 		if ((endptr = snmp_parse_suboid(str, &(obj.val.var))) == NULL)
1073 			return (NULL);
1074 		if (snmp_suboid_append(oid, (asn_subid_t) obj.val.var.len) < 0)
1075 			return (NULL);
1076 	} else {
1077 		strlcpy(string, str, i + 1);
1078 		if (snmp_lookup_enumoid(snmptoolctx, &obj, string) < 0) {
1079 			warnx("Unknown string - %s", string);
1080 			return (NULL);
1081 		}
1082 	}
1083 
1084 	asn_append_oid(oid, &(obj.val.var));
1085 	return (endptr);
1086 }
1087 
1088 static char *
1089 snmp_ip2asn_oid(char *str, struct asn_oid *oid)
1090 {
1091 	uint32_t v;
1092 	int32_t i;
1093 	char *endptr, *ptr;
1094 
1095 	ptr = str;
1096 	for (i = 0; i < 4; i++) {
1097 		v = strtoul(ptr, &endptr, 10);
1098 		if (v > 0xff)
1099 			return (NULL);
1100 		if (*endptr != '.' && strchr("],\0", *endptr) == NULL && i != 3)
1101 			return (NULL);
1102 		if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
1103 			return (NULL);
1104 		ptr = endptr + 1;
1105 	}
1106 
1107 	return (endptr);
1108 }
1109 
1110 /* 32-bit counter, gauge, timeticks. */
1111 static char *
1112 snmp_uint2asn_oid(char *str, struct asn_oid *oid)
1113 {
1114 	char *endptr;
1115 	uint32_t v;
1116 	int32_t saved_errno;
1117 
1118 	saved_errno = errno;
1119 	errno = 0;
1120 
1121 	v = strtoul(str, &endptr, 10);
1122 	if (errno != 0) {
1123 		warnx("Integer value %s not supported - %s\n", str,
1124 		    strerror(errno));
1125 		errno = saved_errno;
1126 		return (NULL);
1127 	}
1128 	errno = saved_errno;
1129 	if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
1130 		return (NULL);
1131 
1132 	return (endptr);
1133 }
1134 
1135 static char *
1136 snmp_cnt64_2asn_oid(char *str, struct asn_oid *oid)
1137 {
1138 	char *endptr;
1139 	uint64_t v;
1140 	int32_t saved_errno;
1141 
1142 	saved_errno = errno;
1143 	errno = 0;
1144 
1145 	v = strtoull(str, &endptr, 10);
1146 
1147 	if (errno != 0) {
1148 		warnx("Integer value %s not supported - %s", str,
1149 		    strerror(errno));
1150 		errno = saved_errno;
1151 		return (NULL);
1152 	}
1153 	errno = saved_errno;
1154 	if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xffffffff)) < 0)
1155 		return (NULL);
1156 
1157 	if (snmp_suboid_append(oid, (asn_subid_t) (v >> 32)) < 0)
1158 		return (NULL);
1159 
1160 	return (endptr);
1161 }
1162 
1163 enum snmp_syntax
1164 parse_syntax(char *str)
1165 {
1166 	int32_t i;
1167 
1168 	for (i = 0; i < SNMP_SYNTAX_UNKNOWN; i++) {
1169 		if (strncmp(syntax_strings[i].str, str,
1170 		    strlen(syntax_strings[i].str)) == 0)
1171 			return (syntax_strings[i].stx);
1172 	}
1173 
1174 	return (SNMP_SYNTAX_NULL);
1175 }
1176 
1177 static char *
1178 snmp_parse_subindex(struct snmp_toolinfo *snmptoolctx, char *str,
1179     struct index *idx, struct snmp_object *object)
1180 {
1181 	char *ptr;
1182 	int32_t i;
1183 	enum snmp_syntax stx;
1184 	char syntax[MAX_CMD_SYNTAX_LEN];
1185 
1186 	ptr = str;
1187 	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) {
1188 		for (i = 0; i < MAX_CMD_SYNTAX_LEN ; i++) {
1189 			if (*(ptr + i) == ':')
1190 				break;
1191 		}
1192 
1193 		if (i >= MAX_CMD_SYNTAX_LEN) {
1194 			warnx("Unknown syntax in OID - %s", str);
1195 			return (NULL);
1196 		}
1197 		/* Expect a syntax string here. */
1198 		if ((stx = parse_syntax(str)) <= SNMP_SYNTAX_NULL) {
1199 			warnx("Invalid  syntax - %s",syntax);
1200 			return (NULL);
1201 		}
1202 
1203 		if (stx != idx->syntax && !ISSET_ERRIGNORE(snmptoolctx)) {
1204 			warnx("Syntax mismatch - %d expected, %d given",
1205 			    idx->syntax, stx);
1206 			return (NULL);
1207 		}
1208 		/*
1209 		 * That is where the suboid started + the syntax length + one
1210 		 * character for ':'.
1211 		 */
1212 		ptr = str + i + 1;
1213 	} else
1214 		stx = idx->syntax;
1215 
1216 	switch (stx) {
1217 		case SNMP_SYNTAX_INTEGER:
1218 			return (snmp_int2asn_oid(ptr, &(object->val.var)));
1219 		case SNMP_SYNTAX_OID:
1220 			return (snmp_oid2asn_oid(snmptoolctx, ptr,
1221 			    &(object->val.var)));
1222 		case SNMP_SYNTAX_IPADDRESS:
1223 			return (snmp_ip2asn_oid(ptr, &(object->val.var)));
1224 		case SNMP_SYNTAX_COUNTER:
1225 			/* FALLTHROUGH */
1226 		case SNMP_SYNTAX_GAUGE:
1227 			/* FALLTHROUGH */
1228 		case SNMP_SYNTAX_TIMETICKS:
1229 			return (snmp_uint2asn_oid(ptr, &(object->val.var)));
1230 		case SNMP_SYNTAX_COUNTER64:
1231 			return (snmp_cnt64_2asn_oid(ptr, &(object->val.var)));
1232 		case SNMP_SYNTAX_OCTETSTRING:
1233 			return (snmp_tc2oid(idx->tc, ptr, &(object->val.var)));
1234 		default:
1235 			/* NOTREACHED */
1236 			break;
1237 	}
1238 
1239 	return (NULL);
1240 }
1241 
1242 char *
1243 snmp_parse_index(struct snmp_toolinfo *snmptoolctx, char *str,
1244     struct snmp_object *object)
1245 {
1246 	char *ptr;
1247 	struct index *temp;
1248 
1249 	if (object->info->table_idx == NULL)
1250 		return (NULL);
1251 
1252 	ptr = NULL;
1253 	STAILQ_FOREACH(temp, &(OBJECT_IDX_LIST(object)), link) {
1254 		if ((ptr = snmp_parse_subindex(snmptoolctx, str, temp, object))
1255 		    == NULL)
1256 			return (NULL);
1257 
1258 		if (*ptr != ',' && *ptr != ']')
1259 			return (NULL);
1260 		str = ptr + 1;
1261 	}
1262 
1263 	if (ptr == NULL || *ptr != ']') {
1264 		warnx("Mismatching index - %s", str);
1265 		return (NULL);
1266 	}
1267 
1268 	return (ptr + 1);
1269 }
1270 
1271 /*
1272  * Fill in the struct asn_oid member of snmp_value with suboids from input.
1273  * If an error occurs - print message on stderr and return (-1).
1274  * If all is ok - return the length of the oid.
1275  */
1276 int32_t
1277 snmp_parse_numoid(char *argv, struct asn_oid *var)
1278 {
1279 	char *endptr, *str;
1280 	asn_subid_t suboid;
1281 
1282 	str = argv;
1283 
1284 	if (*str == '.')
1285 		str++;
1286 
1287 	do {
1288 		if (var->len == ASN_MAXOIDLEN) {
1289 			warnx("Oid too long - %u", var->len);
1290 			return (-1);
1291 		}
1292 
1293 		suboid = strtoul(str, &endptr, 10);
1294 		if (suboid > ASN_MAXID) {
1295 			warnx("Oid too long - %u", var->len);
1296 			return (-1);
1297 		}
1298 
1299 		var->subs[var->len++] = suboid;
1300 		str = endptr + 1;
1301 	} while ( *endptr == '.');
1302 
1303 	if (*endptr != '\0') {
1304 		warnx("Invalid oid string - %s", argv);
1305 		return (-1);
1306 	}
1307 
1308 	return (var->len);
1309 }
1310 
1311 /* Append a length 1 suboid to an asn_oid structure. */
1312 int32_t
1313 snmp_suboid_append(struct asn_oid *var, asn_subid_t suboid)
1314 {
1315 	if (var == NULL)
1316 		return (-1);
1317 
1318 	if (var->len >= ASN_MAXOIDLEN) {
1319 		warnx("Oid too long - %u", var->len);
1320 		return (-1);
1321 	}
1322 
1323 	var->subs[var->len++] = suboid;
1324 
1325 	return (1);
1326 }
1327 
1328 /* Pop the last suboid from an asn_oid structure. */
1329 int32_t
1330 snmp_suboid_pop(struct asn_oid *var)
1331 {
1332 	asn_subid_t suboid;
1333 
1334 	if (var == NULL)
1335 		return (-1);
1336 
1337 	if (var->len < 1)
1338 		return (-1);
1339 
1340 	suboid = var->subs[--(var->len)];
1341 	var->subs[var->len] = 0;
1342 
1343 	return (suboid);
1344 }
1345 
1346 /*
1347  * Parse the command-line provided string into an OID - alocate memory for a new
1348  * snmp object, fill in its fields and insert it in the object list. A
1349  * (snmp_verify_inoid_f) function must be provided to validate the input string.
1350  */
1351 int32_t
1352 snmp_object_add(struct snmp_toolinfo *snmptoolctx, snmp_verify_inoid_f func,
1353     char *string)
1354 {
1355 	struct snmp_object *obj;
1356 
1357 	if (snmptoolctx == NULL)
1358 		return (-1);
1359 
1360 	/* XXX-BZ does that chack make sense? */
1361 	if (snmptoolctx->objects >= SNMP_MAX_BINDINGS) {
1362 		warnx("Too many bindings in PDU - %u", snmptoolctx->objects + 1);
1363 		return (-1);
1364 	}
1365 
1366 	if ((obj = calloc(1, sizeof(struct snmp_object))) == NULL) {
1367 		syslog(LOG_ERR, "malloc() failed: %s", strerror(errno));
1368 		return (-1);
1369 	}
1370 
1371 	if (func(snmptoolctx, obj, string) < 0) {
1372 		warnx("Invalid OID - %s", string);
1373 		free(obj);
1374 		return (-1);
1375 	}
1376 
1377 	snmptoolctx->objects++;
1378 	SLIST_INSERT_HEAD(&snmptoolctx->snmp_objectlist, obj, link);
1379 
1380 	return (1);
1381 }
1382 
1383 /* Given an OID, find it in the object list and remove it. */
1384 int32_t
1385 snmp_object_remove(struct snmp_toolinfo *snmptoolctx, struct asn_oid *oid)
1386 {
1387 	struct snmp_object *temp;
1388 
1389 	if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist)) {
1390 		warnx("Object list already empty");
1391 		return (-1);
1392 	}
1393 
1394 
1395 	SLIST_FOREACH(temp, &snmptoolctx->snmp_objectlist, link)
1396 		if (asn_compare_oid(&(temp->val.var), oid) == 0)
1397 			break;
1398 
1399 	if (temp == NULL) {
1400 		warnx("No such object in list");
1401 		return (-1);
1402 	}
1403 
1404 	SLIST_REMOVE(&snmptoolctx->snmp_objectlist, temp, snmp_object, link);
1405 	if (temp->val.syntax == SNMP_SYNTAX_OCTETSTRING &&
1406 	    temp->val.v.octetstring.octets != NULL)
1407 		free(temp->val.v.octetstring.octets);
1408 	free(temp);
1409 
1410 	return (1);
1411 }
1412 
1413 static void
1414 snmp_object_freeall(struct snmp_toolinfo *snmptoolctx)
1415 {
1416 	struct snmp_object *o;
1417 
1418 	while ((o = SLIST_FIRST(&snmptoolctx->snmp_objectlist)) != NULL) {
1419 		SLIST_REMOVE_HEAD(&snmptoolctx->snmp_objectlist, link);
1420 
1421 		if (o->val.syntax == SNMP_SYNTAX_OCTETSTRING &&
1422 		    o->val.v.octetstring.octets != NULL)
1423 			free(o->val.v.octetstring.octets);
1424 		free(o);
1425 	}
1426 }
1427 
1428 /* Do all possible memory release before exit. */
1429 void
1430 snmp_tool_freeall(struct snmp_toolinfo *snmptoolctx)
1431 {
1432 	if (snmp_client.chost != NULL) {
1433 		free(snmp_client.chost);
1434 		snmp_client.chost = NULL;
1435 	}
1436 
1437 	if (snmp_client.cport != NULL) {
1438 		free(snmp_client.cport);
1439 		snmp_client.cport = NULL;
1440 	}
1441 
1442 	snmp_mapping_free(snmptoolctx);
1443 	free_filelist(snmptoolctx);
1444 	snmp_object_freeall(snmptoolctx);
1445 
1446 	if (snmptoolctx->passwd != NULL) {
1447 		free(snmptoolctx->passwd);
1448 		snmptoolctx->passwd = NULL;
1449 	}
1450 }
1451 
1452 /*
1453  * Fill all variables from the object list into a PDU. (snmp_verify_vbind_f)
1454  * function should check whether the variable is consistent in this PDU
1455  * (e.g do not add non-leaf OIDs to a GET PDU, or OIDs with read access only to
1456  * a SET PDU) - might be NULL though. (snmp_add_vbind_f) function is the
1457  * function actually adds the variable to the PDU and must not be NULL.
1458  */
1459 int32_t
1460 snmp_pdu_add_bindings(struct snmp_toolinfo *snmptoolctx,
1461     snmp_verify_vbind_f vfunc, snmp_add_vbind_f afunc,
1462     struct snmp_pdu *pdu, int32_t maxcount)
1463 {
1464 	int32_t nbindings, abind;
1465 	struct snmp_object *obj;
1466 
1467 	if (pdu == NULL || afunc == NULL)
1468 		return (-1);
1469 
1470 	/* Return 0 in case of no more work todo. */
1471 	if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist))
1472 		return (0);
1473 
1474 	if (maxcount < 0 || maxcount > SNMP_MAX_BINDINGS) {
1475 		warnx("maxcount out of range: <0 || >SNMP_MAX_BINDINGS");
1476 		return (-1);
1477 	}
1478 
1479 	nbindings = 0;
1480 	SLIST_FOREACH(obj, &snmptoolctx->snmp_objectlist, link) {
1481 		if ((vfunc != NULL) && (vfunc(snmptoolctx, pdu, obj) < 0)) {
1482 			nbindings = -1;
1483 			break;
1484 		}
1485 		if ((abind = afunc(pdu, obj)) < 0) {
1486 			nbindings = -1;
1487 			break;
1488 		}
1489 
1490 		if (abind > 0) {
1491 			/* Do not put more varbindings than requested. */
1492 			if (++nbindings >= maxcount)
1493 				break;
1494 		}
1495 	}
1496 
1497 	return (nbindings);
1498 }
1499 
1500 /*
1501  * Locate an object in the object list and set a corresponding error status.
1502  */
1503 int32_t
1504 snmp_object_seterror(struct snmp_toolinfo *snmptoolctx,
1505     struct snmp_value *err_value, int32_t error_status)
1506 {
1507 	struct snmp_object *obj;
1508 
1509 	if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist) || err_value == NULL)
1510 		return (-1);
1511 
1512 	SLIST_FOREACH(obj, &snmptoolctx->snmp_objectlist, link)
1513 		if (asn_compare_oid(&(err_value->var), &(obj->val.var)) == 0) {
1514 			obj->error = error_status;
1515 			return (1);
1516 		}
1517 
1518 	return (0);
1519 }
1520 
1521 /*
1522  * Check a PDU received in response to a SNMP_PDU_GET/SNMP_PDU_GETBULK request
1523  * but don't compare syntaxes - when sending a request PDU they must be null.
1524  * This is a (almost) complete copy of snmp_pdu_check() - with matching syntaxes
1525  * checks and some other checks skipped.
1526  */
1527 int32_t
1528 snmp_parse_get_resp(struct snmp_pdu *resp, struct snmp_pdu *req)
1529 {
1530 	uint32_t i;
1531 
1532 	for (i = 0; i < req->nbindings; i++) {
1533 		if (asn_compare_oid(&req->bindings[i].var,
1534 		    &resp->bindings[i].var) != 0) {
1535 			warnx("Bad OID in response");
1536 			return (-1);
1537 		}
1538 
1539 		if (snmp_client.version != SNMP_V1 && (resp->bindings[i].syntax
1540 		    == SNMP_SYNTAX_NOSUCHOBJECT || resp->bindings[i].syntax ==
1541 		    SNMP_SYNTAX_NOSUCHINSTANCE))
1542 			return (0);
1543 	}
1544 
1545 	return (1);
1546 }
1547 
1548 int32_t
1549 snmp_parse_getbulk_resp(struct snmp_pdu *resp, struct snmp_pdu *req)
1550 {
1551 	int32_t N, R, M, r;
1552 
1553 	if (req->error_status > (int32_t) resp->nbindings) {
1554 		warnx("Bad number of bindings in response");
1555 		return (-1);
1556 	}
1557 
1558 	for (N = 0; N < req->error_status; N++) {
1559 		if (asn_is_suboid(&req->bindings[N].var,
1560 		    &resp->bindings[N].var) == 0)
1561 			return (0);
1562 		if (resp->bindings[N].syntax == SNMP_SYNTAX_ENDOFMIBVIEW)
1563 			return (0);
1564 	}
1565 
1566 	for (R = N , r = N; R  < (int32_t) req->nbindings; R++) {
1567 		for (M = 0; M < req->error_index && (r + M) <
1568 		    (int32_t) resp->nbindings; M++) {
1569 			if (asn_is_suboid(&req->bindings[R].var,
1570 			    &resp->bindings[r + M].var) == 0)
1571 				return (0);
1572 
1573 			if (resp->bindings[r + M].syntax ==
1574 			    SNMP_SYNTAX_ENDOFMIBVIEW) {
1575 				M++;
1576 				break;
1577 			}
1578 		}
1579 		r += M;
1580 	}
1581 
1582 	return (0);
1583 }
1584 
1585 int32_t
1586 snmp_parse_getnext_resp(struct snmp_pdu *resp, struct snmp_pdu *req)
1587 {
1588 	uint32_t i;
1589 
1590 	for (i = 0; i < req->nbindings; i++) {
1591 		if (asn_is_suboid(&req->bindings[i].var, &resp->bindings[i].var)
1592 		    == 0)
1593 			return (0);
1594 
1595 		if (resp->version != SNMP_V1 && resp->bindings[i].syntax ==
1596 		    SNMP_SYNTAX_ENDOFMIBVIEW)
1597 			return (0);
1598 	}
1599 
1600 	return (1);
1601 }
1602 
1603 /*
1604  * Should be called to check a response to get/getnext/getbulk.
1605  */
1606 int32_t
1607 snmp_parse_resp(struct snmp_pdu *resp, struct snmp_pdu *req)
1608 {
1609 	if (resp == NULL || req == NULL)
1610 		return (-2);
1611 
1612 	if (resp->version != req->version) {
1613 		warnx("Response has wrong version");
1614 		return (-1);
1615 	}
1616 
1617 	if (resp->error_status == SNMP_ERR_NOSUCHNAME) {
1618 		warnx("Error - No Such Name");
1619 		return (0);
1620 	}
1621 
1622 	if (resp->error_status != SNMP_ERR_NOERROR) {
1623 		warnx("Error %d in response", resp->error_status);
1624 		return (-1);
1625 	}
1626 
1627 	if (resp->nbindings != req->nbindings && req->type != SNMP_PDU_GETBULK){
1628 		warnx("Bad number of bindings in response");
1629 		return (-1);
1630 	}
1631 
1632 	switch (req->type) {
1633 		case SNMP_PDU_GET:
1634 			return (snmp_parse_get_resp(resp,req));
1635 		case SNMP_PDU_GETBULK:
1636 			return (snmp_parse_getbulk_resp(resp,req));
1637 		case SNMP_PDU_GETNEXT:
1638 			return (snmp_parse_getnext_resp(resp,req));
1639 		default:
1640 			/* NOTREACHED */
1641 			break;
1642 	}
1643 
1644 	return (-2);
1645 }
1646 
1647 static void
1648 snmp_output_octetstring(struct snmp_toolinfo *snmptoolctx, enum snmp_tc tc,
1649     uint32_t len, uint8_t *octets)
1650 {
1651 	char *buf;
1652 
1653 	if (len == 0 || octets == NULL)
1654 		return;
1655 
1656 	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1657 		fprintf(stdout, "%s : ",
1658 		    syntax_strings[SNMP_SYNTAX_OCTETSTRING].str);
1659 
1660 	if ((buf = snmp_oct2tc(tc, len, (char *) octets)) != NULL) {
1661 		fprintf(stdout, "%s", buf);
1662 		free(buf);
1663 	}
1664 }
1665 
1666 static void
1667 snmp_output_octetindex(struct snmp_toolinfo *snmptoolctx, enum snmp_tc tc,
1668     struct asn_oid *oid)
1669 {
1670 	uint32_t i;
1671 	uint8_t *s;
1672 
1673 	if ((s = malloc(oid->subs[0] + 1)) == NULL)
1674 		syslog(LOG_ERR, "malloc failed - %s", strerror(errno));
1675 	else {
1676 		for (i = 0; i < oid->subs[0]; i++)
1677 			s[i] = (u_char) (oid->subs[i + 1]);
1678 
1679 		snmp_output_octetstring(snmptoolctx, tc, oid->subs[0], s);
1680 		free(s);
1681 	}
1682 }
1683 
1684 /*
1685  * Check and output syntax type and value.
1686  */
1687 static void
1688 snmp_output_oid_value(struct snmp_toolinfo *snmptoolctx, struct asn_oid *oid)
1689 {
1690 	char oid_string[ASN_OIDSTRLEN];
1691 	struct snmp_object obj;
1692 
1693 	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1694 		fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_OID].str);
1695 
1696 	if(!ISSET_NUMERIC(snmptoolctx)) {
1697 		memset(&obj, 0, sizeof(struct snmp_object));
1698 		asn_append_oid(&(obj.val.var), oid);
1699 
1700 		if (snmp_lookup_enumstring(snmptoolctx, &obj) > 0)
1701 			fprintf(stdout, "%s" , obj.info->string);
1702 		else if (snmp_lookup_oidstring(snmptoolctx, &obj) > 0)
1703 			fprintf(stdout, "%s" , obj.info->string);
1704 		else if (snmp_lookup_nodestring(snmptoolctx, &obj) > 0)
1705 			fprintf(stdout, "%s" , obj.info->string);
1706 		else {
1707 			(void) asn_oid2str_r(oid, oid_string);
1708 			fprintf(stdout, "%s", oid_string);
1709 		}
1710 	} else {
1711 		(void) asn_oid2str_r(oid, oid_string);
1712 		fprintf(stdout, "%s", oid_string);
1713 	}
1714 }
1715 
1716 static void
1717 snmp_output_int(struct snmp_toolinfo *snmptoolctx, struct enum_pairs *enums,
1718     int32_t int_val)
1719 {
1720 	char *string;
1721 
1722 	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1723 		fprintf(stdout, "%s : ",
1724 		    syntax_strings[SNMP_SYNTAX_INTEGER].str);
1725 
1726 	if (enums != NULL && (string = enum_string_lookup(enums, int_val))
1727 	    != NULL)
1728 		fprintf(stdout, "%s", string);
1729 	else
1730 		fprintf(stdout, "%d", int_val);
1731 }
1732 
1733 static void
1734 snmp_output_ipaddress(struct snmp_toolinfo *snmptoolctx, uint8_t *ip)
1735 {
1736 	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1737 		fprintf(stdout, "%s : ",
1738 		    syntax_strings[SNMP_SYNTAX_IPADDRESS].str);
1739 
1740 	fprintf(stdout, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
1741 }
1742 
1743 static void
1744 snmp_output_counter(struct snmp_toolinfo *snmptoolctx, uint32_t counter)
1745 {
1746 	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1747 		fprintf(stdout, "%s : ",
1748 		    syntax_strings[SNMP_SYNTAX_COUNTER].str);
1749 
1750 	fprintf(stdout, "%u", counter);
1751 }
1752 
1753 static void
1754 snmp_output_gauge(struct snmp_toolinfo *snmptoolctx, uint32_t gauge)
1755 {
1756 	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1757 		fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_GAUGE].str);
1758 
1759 	fprintf(stdout, "%u", gauge);
1760 }
1761 
1762 static void
1763 snmp_output_ticks(struct snmp_toolinfo *snmptoolctx, uint32_t ticks)
1764 {
1765 	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1766 		fprintf(stdout, "%s : ",
1767 		    syntax_strings[SNMP_SYNTAX_TIMETICKS].str);
1768 
1769 	fprintf(stdout, "%u", ticks);
1770 }
1771 
1772 static void
1773 snmp_output_counter64(struct snmp_toolinfo *snmptoolctx, uint64_t counter64)
1774 {
1775 	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1776 		fprintf(stdout, "%s : ",
1777 		    syntax_strings[SNMP_SYNTAX_COUNTER64].str);
1778 
1779 	fprintf(stdout,"%ju", counter64);
1780 }
1781 
1782 int32_t
1783 snmp_output_numval(struct snmp_toolinfo *snmptoolctx, struct snmp_value *val,
1784     struct snmp_oid2str *entry)
1785 {
1786 	if (val == NULL)
1787 		return (-1);
1788 
1789 	if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET)
1790 		fprintf(stdout, " = ");
1791 
1792 	switch (val->syntax) {
1793 	    case SNMP_SYNTAX_INTEGER:
1794 		if (entry != NULL)
1795 			snmp_output_int(snmptoolctx, entry->snmp_enum,
1796 			    val->v.integer);
1797 		else
1798 			snmp_output_int(snmptoolctx, NULL, val->v.integer);
1799 		break;
1800 
1801 	    case SNMP_SYNTAX_OCTETSTRING:
1802 		if (entry != NULL)
1803 			snmp_output_octetstring(snmptoolctx, entry->tc,
1804 			    val->v.octetstring.len, val->v.octetstring.octets);
1805 		else
1806 			snmp_output_octetstring(snmptoolctx, SNMP_STRING,
1807 			    val->v.octetstring.len, val->v.octetstring.octets);
1808 		break;
1809 
1810 	    case SNMP_SYNTAX_OID:
1811 		snmp_output_oid_value(snmptoolctx, &(val->v.oid));
1812 		break;
1813 
1814 	    case SNMP_SYNTAX_IPADDRESS:
1815 		snmp_output_ipaddress(snmptoolctx, val->v.ipaddress);
1816 		break;
1817 
1818 	    case SNMP_SYNTAX_COUNTER:
1819 		snmp_output_counter(snmptoolctx, val->v.uint32);
1820 		break;
1821 
1822 	    case SNMP_SYNTAX_GAUGE:
1823 		snmp_output_gauge(snmptoolctx, val->v.uint32);
1824 		break;
1825 
1826 	    case SNMP_SYNTAX_TIMETICKS:
1827 		snmp_output_ticks(snmptoolctx, val->v.uint32);
1828 		break;
1829 
1830 	    case SNMP_SYNTAX_COUNTER64:
1831 		snmp_output_counter64(snmptoolctx, val->v.counter64);
1832 		break;
1833 
1834 	    case SNMP_SYNTAX_NOSUCHOBJECT:
1835 		fprintf(stdout, "No Such Object\n");
1836 		return (val->syntax);
1837 
1838 	    case SNMP_SYNTAX_NOSUCHINSTANCE:
1839 		fprintf(stdout, "No Such Instance\n");
1840 		return (val->syntax);
1841 
1842 	    case SNMP_SYNTAX_ENDOFMIBVIEW:
1843 		fprintf(stdout, "End of Mib View\n");
1844 		return (val->syntax);
1845 
1846 	    case SNMP_SYNTAX_NULL:
1847 		/* NOTREACHED */
1848 		fprintf(stdout, "agent returned NULL Syntax\n");
1849 		return (val->syntax);
1850 
1851 	    default:
1852 		/* NOTREACHED - If here - then all went completely wrong. */
1853 		fprintf(stdout, "agent returned unknown syntax\n");
1854 		return (-1);
1855 	}
1856 
1857 	fprintf(stdout, "\n");
1858 
1859 	return (0);
1860 }
1861 
1862 static int32_t
1863 snmp_fill_object(struct snmp_toolinfo *snmptoolctx, struct snmp_object *obj,
1864     struct snmp_value *val)
1865 {
1866 	int32_t rc;
1867 	asn_subid_t suboid;
1868 
1869 	if (obj == NULL || val == NULL)
1870 		return (-1);
1871 
1872 	if ((suboid = snmp_suboid_pop(&(val->var))) > ASN_MAXID)
1873 		return (-1);
1874 
1875 	memset(obj, 0, sizeof(struct snmp_object));
1876 	asn_append_oid(&(obj->val.var), &(val->var));
1877 	obj->val.syntax = val->syntax;
1878 
1879 	if (obj->val.syntax > 0)
1880 		rc = snmp_lookup_leafstring(snmptoolctx, obj);
1881 	else
1882 		rc = snmp_lookup_nonleaf_string(snmptoolctx, obj);
1883 
1884 	(void) snmp_suboid_append(&(val->var), suboid);
1885 	(void) snmp_suboid_append(&(obj->val.var), suboid);
1886 
1887 	return (rc);
1888 }
1889 
1890 static int32_t
1891 snmp_output_index(struct snmp_toolinfo *snmptoolctx, struct index *stx,
1892     struct asn_oid *oid)
1893 {
1894 	uint8_t ip[4];
1895 	uint32_t bytes = 1;
1896 	uint64_t cnt64;
1897 	struct asn_oid temp, out;
1898 
1899 	if (oid->len < bytes)
1900 		return (-1);
1901 
1902 	memset(&temp, 0, sizeof(struct asn_oid));
1903 	asn_append_oid(&temp, oid);
1904 
1905 	switch (stx->syntax) {
1906 	    case SNMP_SYNTAX_INTEGER:
1907 		snmp_output_int(snmptoolctx, stx->snmp_enum, temp.subs[0]);
1908 		break;
1909 
1910 	    case SNMP_SYNTAX_OCTETSTRING:
1911 		if ((temp.subs[0] > temp.len -1 ) || (temp.subs[0] >
1912 		    ASN_MAXOCTETSTRING))
1913 			return (-1);
1914 		snmp_output_octetindex(snmptoolctx, stx->tc, &temp);
1915 		bytes += temp.subs[0];
1916 		break;
1917 
1918 	    case SNMP_SYNTAX_OID:
1919 		if ((temp.subs[0] > temp.len -1) || (temp.subs[0] >
1920 		    ASN_MAXOIDLEN))
1921 			return (-1);
1922 
1923 		bytes += temp.subs[0];
1924 		memset(&out, 0, sizeof(struct asn_oid));
1925 		asn_slice_oid(&out, &temp, 1, bytes);
1926 		snmp_output_oid_value(snmptoolctx, &out);
1927 		break;
1928 
1929 	    case SNMP_SYNTAX_IPADDRESS:
1930 		if (temp.len < 4)
1931 			return (-1);
1932 		for (bytes = 0; bytes < 4; bytes++)
1933 			ip[bytes] = temp.subs[bytes];
1934 
1935 		snmp_output_ipaddress(snmptoolctx, ip);
1936 		bytes = 4;
1937 		break;
1938 
1939 	    case SNMP_SYNTAX_COUNTER:
1940 		snmp_output_counter(snmptoolctx, temp.subs[0]);
1941 		break;
1942 
1943 	    case SNMP_SYNTAX_GAUGE:
1944 		snmp_output_gauge(snmptoolctx, temp.subs[0]);
1945 		break;
1946 
1947 	    case SNMP_SYNTAX_TIMETICKS:
1948 		snmp_output_ticks(snmptoolctx, temp.subs[0]);
1949 		break;
1950 
1951 	    case SNMP_SYNTAX_COUNTER64:
1952 		if (oid->len < 2)
1953 			return (-1);
1954 		bytes = 2;
1955 		memcpy(&cnt64, temp.subs, bytes);
1956 		snmp_output_counter64(snmptoolctx, cnt64);
1957 		break;
1958 
1959 	    default:
1960 		return (-1);
1961 	}
1962 
1963 	return (bytes);
1964 }
1965 
1966 static int32_t
1967 snmp_output_object(struct snmp_toolinfo *snmptoolctx, struct snmp_object *o)
1968 {
1969 	int32_t i, first, len;
1970 	struct asn_oid oid;
1971 	struct index *temp;
1972 
1973 	if (ISSET_NUMERIC(snmptoolctx))
1974 		return (-1);
1975 
1976 	if (o->info->table_idx == NULL) {
1977 		fprintf(stdout,"%s.%d", o->info->string,
1978 		    o->val.var.subs[o->val.var.len - 1]);
1979 		return (1);
1980 	}
1981 
1982 	fprintf(stdout,"%s[", o->info->string);
1983 	memset(&oid, 0, sizeof(struct asn_oid));
1984 
1985 	len = 1;
1986 	asn_slice_oid(&oid, &(o->val.var), (o->info->table_idx->var.len + len),
1987 	    o->val.var.len);
1988 
1989 	first = 1;
1990 	STAILQ_FOREACH(temp, &(OBJECT_IDX_LIST(o)), link) {
1991 		if(first)
1992 			first = 0;
1993 		else
1994 			fprintf(stdout, ", ");
1995 		if ((i = snmp_output_index(snmptoolctx, temp, &oid)) < 0)
1996 			break;
1997 		len += i;
1998 		memset(&oid, 0, sizeof(struct asn_oid));
1999 		asn_slice_oid(&oid, &(o->val.var),
2000 		    (o->info->table_idx->var.len + len), o->val.var.len + 1);
2001 	}
2002 
2003 	fprintf(stdout,"]");
2004 	return (1);
2005 }
2006 
2007 void
2008 snmp_output_err_resp(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu)
2009 {
2010 	char buf[ASN_OIDSTRLEN];
2011 	struct snmp_object object;
2012 
2013 	if (pdu == NULL || (pdu->error_index > (int32_t) pdu->nbindings)) {
2014 		fprintf(stdout,"Invalid error index in PDU\n");
2015 		return;
2016 	}
2017 
2018 	fprintf(stdout, "Agent %s:%s returned error \n", snmp_client.chost,
2019 	    snmp_client.cport);
2020 
2021 	if (!ISSET_NUMERIC(snmptoolctx) && (snmp_fill_object(snmptoolctx, &object,
2022 	    &(pdu->bindings[pdu->error_index - 1])) > 0))
2023 		snmp_output_object(snmptoolctx, &object);
2024 	else {
2025 		asn_oid2str_r(&(pdu->bindings[pdu->error_index - 1].var), buf);
2026 		fprintf(stdout,"%s", buf);
2027 	}
2028 
2029 	fprintf(stdout," caused error - ");
2030 	if ((pdu->error_status > 0) && (pdu->error_status <=
2031 	    SNMP_ERR_INCONS_NAME))
2032 		fprintf(stdout, "%s\n", error_strings[pdu->error_status].str);
2033 	else
2034 		fprintf(stdout,"%s\n", error_strings[SNMP_ERR_UNKNOWN].str);
2035 }
2036 
2037 int32_t
2038 snmp_output_resp(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu,
2039     struct asn_oid *root)
2040 {
2041 	int32_t error;
2042 	char p[ASN_OIDSTRLEN];
2043 	uint32_t i;
2044 	struct snmp_object object;
2045 
2046 	i = error = 0;
2047 	while (i < pdu->nbindings) {
2048 		if (root != NULL && !(asn_is_suboid(root,
2049 		    &(pdu->bindings[i].var))))
2050 			break;
2051 
2052 		if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET) {
2053 			if (!ISSET_NUMERIC(snmptoolctx) &&
2054 			    (snmp_fill_object(snmptoolctx, &object,
2055 			    &(pdu->bindings[i])) > 0))
2056 				snmp_output_object(snmptoolctx, &object);
2057 			else {
2058 				asn_oid2str_r(&(pdu->bindings[i].var), p);
2059 				fprintf(stdout, "%s", p);
2060 			}
2061 		}
2062 		error |= snmp_output_numval(snmptoolctx, &(pdu->bindings[i]), object.info);
2063 		i++;
2064 	}
2065 
2066 	if (error)
2067 		return (-1);
2068 
2069 	return (i);
2070 }
2071 
2072 void
2073 snmp_output_engine(void)
2074 {
2075 	uint32_t i;
2076 	char *cptr, engine[2 * SNMP_ENGINE_ID_SIZ + 2];
2077 
2078 	cptr = engine;
2079 	for (i = 0; i < snmp_client.engine.engine_len; i++)
2080 		cptr += sprintf(cptr, "%.2x", snmp_client.engine.engine_id[i]);
2081 	*cptr++ = '\0';
2082 
2083 	fprintf(stdout, "Engine ID 0x%s\n", engine);
2084 	fprintf(stdout, "Boots : %u\t\tTime : %d\n",
2085 	    snmp_client.engine.engine_boots,
2086 	    snmp_client.engine.engine_time);
2087 }
2088 
2089 void
2090 snmp_output_keys(void)
2091 {
2092 	uint32_t i, keylen = 0;
2093 	char *cptr, extkey[2 * SNMP_AUTH_KEY_SIZ + 2];
2094 
2095 	fprintf(stdout, "Localized keys for %s\n", snmp_client.user.sec_name);
2096 	if (snmp_client.user.auth_proto == SNMP_AUTH_HMAC_MD5) {
2097 		fprintf(stdout, "MD5 : 0x");
2098 		keylen = SNMP_AUTH_HMACMD5_KEY_SIZ;
2099 	} else if (snmp_client.user.auth_proto == SNMP_AUTH_HMAC_SHA) {
2100 		fprintf(stdout, "SHA : 0x");
2101 		keylen = SNMP_AUTH_HMACSHA_KEY_SIZ;
2102 	}
2103 	if (snmp_client.user.auth_proto != SNMP_AUTH_NOAUTH) {
2104 		cptr = extkey;
2105 		for (i = 0; i < keylen; i++)
2106 			cptr += sprintf(cptr, "%.2x",
2107 			    snmp_client.user.auth_key[i]);
2108 		*cptr++ = '\0';
2109 		fprintf(stdout, "%s\n", extkey);
2110 	}
2111 
2112 	if (snmp_client.user.priv_proto == SNMP_PRIV_DES) {
2113 		fprintf(stdout, "DES : 0x");
2114 		keylen = SNMP_PRIV_DES_KEY_SIZ;
2115 	} else if (snmp_client.user.priv_proto == SNMP_PRIV_AES) {
2116 		fprintf(stdout, "AES : 0x");
2117 		keylen = SNMP_PRIV_AES_KEY_SIZ;
2118 	}
2119 	if (snmp_client.user.priv_proto != SNMP_PRIV_NOPRIV) {
2120 		cptr = extkey;
2121 		for (i = 0; i < keylen; i++)
2122 			cptr += sprintf(cptr, "%.2x",
2123 			    snmp_client.user.priv_key[i]);
2124 		*cptr++ = '\0';
2125 		fprintf(stdout, "%s\n", extkey);
2126 	}
2127 }
2128