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