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