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