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