bsnmptools.c revision 300276
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: stable/10/usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.c 300276 2016-05-20 06:47:42Z truckman $
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? */
58int _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 */
68const 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 */
78static const struct {
79	const char *str;
80	int32_t error;
81} error_strings[] = {
82	{ "Unknown", SNMP_ERR_UNKNOWN },
83	{ "Too big ", SNMP_ERR_TOOBIG },
84	{ "No such Name", SNMP_ERR_NOSUCHNAME },
85	{ "Bad Value", SNMP_ERR_BADVALUE },
86	{ "Readonly", SNMP_ERR_READONLY },
87	{ "General error", SNMP_ERR_GENERR },
88	{ "No access", SNMP_ERR_NO_ACCESS },
89	{ "Wrong type", SNMP_ERR_WRONG_TYPE },
90	{ "Wrong length", SNMP_ERR_WRONG_LENGTH },
91	{ "Wrong encoding", SNMP_ERR_WRONG_ENCODING },
92	{ "Wrong value", SNMP_ERR_WRONG_VALUE },
93	{ "No creation", SNMP_ERR_NO_CREATION },
94	{ "Inconsistent value", SNMP_ERR_INCONS_VALUE },
95	{ "Resource unavailable", SNMP_ERR_RES_UNAVAIL },
96	{ "Commit failed", SNMP_ERR_COMMIT_FAILED },
97	{ "Undo failed", SNMP_ERR_UNDO_FAILED },
98	{ "Authorization error", SNMP_ERR_AUTH_ERR },
99	{ "Not writable", SNMP_ERR_NOT_WRITEABLE },
100	{ "Inconsistent name", SNMP_ERR_INCONS_NAME },
101	{ NULL, 0 }
102};
103
104/* This one and any following are exceptions. */
105#define	SNMP_SYNTAX_UNKNOWN	SNMP_SYNTAX_NOSUCHOBJECT
106
107static 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
123int
124snmptool_init(struct snmp_toolinfo *snmptoolctx)
125{
126	char *str;
127	size_t slen;
128
129	memset(snmptoolctx, 0, sizeof(struct snmp_toolinfo));
130	snmptoolctx->objects = 0;
131	snmptoolctx->mappings = NULL;
132	snmptoolctx->flags = SNMP_PDU_GET;	/* XXX */
133	SLIST_INIT(&snmptoolctx->filelist);
134	snmp_client_init(&snmp_client);
135	SET_MAXREP(snmptoolctx, SNMP_MAX_REPETITIONS);
136
137	if (add_filename(snmptoolctx, bsnmpd_defs, &IsoOrgDod_OID, 0) < 0)
138		warnx("Error adding file %s to list", bsnmpd_defs);
139
140	if (add_filename(snmptoolctx, mibII_defs, &IsoOrgDod_OID, 0) < 0)
141		warnx("Error adding file %s to list", mibII_defs);
142
143	/* Read the environment */
144	if ((str = getenv("SNMPAUTH")) != NULL) {
145		slen = strlen(str);
146		if (slen == strlen("md5") && strcasecmp(str, "md5") == 0)
147			snmp_client.user.auth_proto = SNMP_AUTH_HMAC_MD5;
148		else if (slen == strlen("sha")&& strcasecmp(str, "sha") == 0)
149			snmp_client.user.auth_proto = SNMP_AUTH_HMAC_SHA;
150		else if (slen != 0)
151			warnx("Bad authentication type - %s in SNMPAUTH", str);
152	}
153
154	if ((str = getenv("SNMPPRIV")) != NULL) {
155		slen = strlen(str);
156		if (slen == strlen("des") && strcasecmp(str, "des") == 0)
157			snmp_client.user.priv_proto = SNMP_PRIV_DES;
158		else if (slen == strlen("aes")&& strcasecmp(str, "aes") == 0)
159			snmp_client.user.priv_proto = SNMP_PRIV_AES;
160		else if (slen != 0)
161			warnx("Bad privacy type - %s in SNMPPRIV", str);
162	}
163
164	if ((str = getenv("SNMPUSER")) != NULL) {
165		if ((slen = strlen(str)) > sizeof(snmp_client.user.sec_name)) {
166			warnx("Username too long - %s in SNMPUSER", str);
167			return (-1);
168		}
169		if (slen > 0) {
170			strlcpy(snmp_client.user.sec_name, str,
171			    sizeof(snmp_client.user.sec_name));
172			snmp_client.version = SNMP_V3;
173		}
174	}
175
176	if ((str = getenv("SNMPPASSWD")) != NULL) {
177		if ((slen = strlen(str)) > MAXSTR)
178			slen = MAXSTR - 1;
179		if ((snmptoolctx->passwd = malloc(slen + 1)) == NULL) {
180			warnx("malloc() failed - %s", strerror(errno));
181			return (-1);
182		}
183		if (slen > 0)
184			strlcpy(snmptoolctx->passwd, str, slen + 1);
185	}
186
187	return (0);
188}
189
190#define	OBJECT_IDX_LIST(o)	o->info->table_idx->index_list
191
192/*
193 * Walk through the file list and import string<->oid mappings from each file.
194 */
195int32_t
196snmp_import_all(struct snmp_toolinfo *snmptoolctx)
197{
198	int32_t fc;
199	struct fname *tmp;
200
201	if (snmptoolctx == NULL)
202		return (-1);
203
204	if (ISSET_NUMERIC(snmptoolctx))
205		return (0);
206
207	if ((snmptoolctx->mappings = snmp_mapping_init()) == NULL)
208		return (-1);
209
210	fc = 0;
211	if (SLIST_EMPTY(&snmptoolctx->filelist)) {
212		warnx("No files to read OID <-> string conversions from");
213		return (-1);
214	} else {
215		SLIST_FOREACH(tmp, &snmptoolctx->filelist, link) {
216			if (tmp->done)
217				continue;
218			if (snmp_import_file(snmptoolctx, tmp) < 0) {
219				fc = -1;
220				break;
221			}
222			fc++;
223		}
224	}
225
226	snmp_mapping_dump(snmptoolctx);
227	return (fc);
228}
229
230/*
231 * Add a filename to the file list - the initial idea of keeping a list with all
232 * files to read OIDs from was that an application might want to have loaded in
233 * memory the OIDs from a single file only and when done with them read the OIDs
234 * from another file. This is not used yet but might be a good idea at some
235 * point. Size argument is number of bytes in string including trailing '\0',
236 * not string length.
237 */
238int32_t
239add_filename(struct snmp_toolinfo *snmptoolctx, const char *filename,
240    const struct asn_oid *cut, int32_t done)
241{
242	char *fstring;
243	struct fname *entry;
244
245	if (snmptoolctx == NULL)
246		return (-1);
247
248	/* Make sure file was not in list. */
249	SLIST_FOREACH(entry, &snmptoolctx->filelist, link) {
250		if (strncmp(entry->name, filename, strlen(entry->name)) == 0)
251			return (0);
252	}
253
254	if ((fstring = malloc(strlen(filename) + 1)) == NULL) {
255		warnx("malloc() failed - %s", strerror(errno));
256		return (-1);
257	}
258
259	if ((entry = malloc(sizeof(struct fname))) == NULL) {
260		warnx("malloc() failed - %s", strerror(errno));
261		free(fstring);
262		return (-1);
263	}
264
265	memset(entry, 0, sizeof(struct fname));
266
267	if (cut != NULL)
268		asn_append_oid(&(entry->cut), cut);
269	strlcpy(fstring, filename, strlen(filename) + 1);
270	entry->name = fstring;
271	entry->done = done;
272	SLIST_INSERT_HEAD(&snmptoolctx->filelist, entry, link);
273
274	return (1);
275}
276
277void
278free_filelist(struct snmp_toolinfo *snmptoolctx)
279{
280	struct fname *f;
281
282	if (snmptoolctx == NULL)
283		return; /* XXX error handling */
284
285	while ((f = SLIST_FIRST(&snmptoolctx->filelist)) != NULL) {
286		SLIST_REMOVE_HEAD(&snmptoolctx->filelist, link);
287		if (f->name)
288			free(f->name);
289		free(f);
290	}
291}
292
293static char
294isvalid_fchar(char c, int pos)
295{
296	if (isalpha(c)|| c == '/'|| c == '_' || c == '.' || c == '~' ||
297	    (pos != 0 && isdigit(c))){
298		return (c);
299	}
300
301	if (c == '\0')
302		return (0);
303
304	if (!isascii(c) || !isprint(c))
305		warnx("Unexpected character %#2x", (u_int) c);
306	else
307		warnx("Illegal character '%c'", c);
308
309	return (-1);
310}
311
312/*
313 * Re-implement getsubopt from scratch, because the second argument is broken
314 * and will not compile with WARNS=5.
315 * Copied from src/contrib/bsnmp/snmpd/main.c.
316 */
317static int
318getsubopt1(char **arg, const char *const *options, char **valp, char **optp)
319{
320	static const char *const delim = ",\t ";
321	u_int i;
322	char *ptr;
323
324	*optp = NULL;
325
326	/* Skip leading junk. */
327	for (ptr = *arg; *ptr != '\0'; ptr++)
328		if (strchr(delim, *ptr) == NULL)
329			break;
330	if (*ptr == '\0') {
331		*arg = ptr;
332		return (-1);
333	}
334	*optp = ptr;
335
336	/* Find the end of the option. */
337	while (*++ptr != '\0')
338		if (strchr(delim, *ptr) != NULL || *ptr == '=')
339			break;
340
341	if (*ptr != '\0') {
342		if (*ptr == '=') {
343			*ptr++ = '\0';
344			*valp = ptr;
345			while (*ptr != '\0' && strchr(delim, *ptr) == NULL)
346				ptr++;
347			if (*ptr != '\0')
348				*ptr++ = '\0';
349		} else
350			*ptr++ = '\0';
351	}
352
353	*arg = ptr;
354
355	for (i = 0; *options != NULL; options++, i++)
356		if (strcmp(*optp, *options) == 0)
357			return (i);
358	return (-1);
359}
360
361static int32_t
362parse_path(char *value)
363{
364	int32_t i, len;
365
366	if (value == NULL)
367		return (-1);
368
369	for (len = 0; len < MAXPATHLEN; len++) {
370		i = isvalid_fchar(*(value + len), len) ;
371
372		if (i == 0)
373			break;
374		else if (i < 0)
375			return (-1);
376	}
377
378	if (len >= MAXPATHLEN || value[len] != '\0') {
379		warnx("Bad pathname - '%s'", value);
380		return (-1);
381	}
382
383	return (len);
384}
385
386static int32_t
387parse_flist(struct snmp_toolinfo *snmptoolctx, char *value, char *path,
388    const struct asn_oid *cut)
389{
390	int32_t namelen;
391	char filename[MAXPATHLEN + 1];
392
393	if (value == NULL)
394		return (-1);
395
396	do {
397		memset(filename, 0, MAXPATHLEN + 1);
398
399		if (isalpha(*value) && (path == NULL || path[0] == '\0')) {
400			strlcpy(filename, SNMP_DEFS_DIR, MAXPATHLEN + 1);
401			namelen = strlen(SNMP_DEFS_DIR);
402		} else if (path != NULL){
403			strlcpy(filename, path, MAXPATHLEN + 1);
404			namelen = strlen(path);
405		} else
406			namelen = 0;
407
408		for ( ; namelen < MAXPATHLEN; value++) {
409			if (isvalid_fchar(*value, namelen) > 0) {
410				filename[namelen++] = *value;
411				continue;
412			}
413
414			if (*value == ',' )
415				value++;
416			else if (*value == '\0')
417				;
418			else {
419				if (!isascii(*value) || !isprint(*value))
420					warnx("Unexpected character %#2x in"
421					    " filename", (u_int) *value);
422				else
423					warnx("Illegal character '%c' in"
424					    " filename", *value);
425				return (-1);
426			}
427
428			filename[namelen]='\0';
429			break;
430		}
431
432		if ((namelen == MAXPATHLEN) && (filename[MAXPATHLEN] != '\0')) {
433			warnx("Filename %s too long", filename);
434			return (-1);
435		}
436
437		if (add_filename(snmptoolctx, filename, cut, 0) < 0) {
438			warnx("Error adding file %s to list", filename);
439			return (-1);
440		}
441	} while (*value != '\0');
442
443	return(1);
444}
445
446static int32_t
447parse_ascii(char *ascii, uint8_t *binstr, size_t binlen)
448{
449	int32_t alen, count, saved_errno, i;
450	uint32_t val;
451	char dptr[3];
452
453	/* Filter 0x at the beginning */
454	if ((alen = strlen(ascii)) > 2 && ascii[0] == '0' && ascii[1] == 'x')
455		i = 2;
456	else
457		i = 0;
458
459	saved_errno = errno;
460	errno = 0;
461	for (count = 0; i < alen; i += 2) {
462		/* XXX: consider strlen(ascii) % 2 != 0 */
463		dptr[0] = ascii[i];
464		dptr[1] = ascii[i + 1];
465		dptr[2] = '\0';
466		if ((val = strtoul(dptr, NULL, 16)) > 0xFF || errno != 0) {
467			errno = saved_errno;
468			return (-1);
469		}
470		binstr[count] = (uint8_t) val;
471		if (++count >= binlen) {
472			warnx("Key %s too long - truncating to %zu octets",
473			    ascii, binlen);
474			break;
475		}
476	}
477
478	return (count);
479}
480
481/*
482 * Functions to parse common input options for client tools and fill in the
483 * snmp_client structure.
484 */
485int32_t
486parse_authentication(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
487{
488	int32_t count, subopt;
489	char *val, *option;
490	const char *const subopts[] = {
491		"proto",
492		"key",
493		NULL
494	};
495
496	assert(opt_arg != NULL);
497	count = 1;
498	while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) {
499		switch (subopt) {
500		case 0:
501			if (val == NULL) {
502				warnx("Suboption 'proto' requires an argument");
503				return (-1);
504			}
505			if (strlen(val) != 3) {
506				warnx("Unknown auth protocol - %s", val);
507				return (-1);
508			}
509			if (strncasecmp("md5", val, strlen("md5")) == 0)
510				snmp_client.user.auth_proto =
511				    SNMP_AUTH_HMAC_MD5;
512			else if (strncasecmp("sha", val, strlen("sha")) == 0)
513				snmp_client.user.auth_proto =
514				    SNMP_AUTH_HMAC_SHA;
515			else {
516				warnx("Unknown auth protocol - %s", val);
517				return (-1);
518			}
519			break;
520		case 1:
521			if (val == NULL) {
522				warnx("Suboption 'key' requires an argument");
523				return (-1);
524			}
525			if (parse_ascii(val, snmp_client.user.auth_key,
526			    SNMP_AUTH_KEY_SIZ) < 0) {
527				warnx("Bad authentication key- %s", val);
528				return (-1);
529			}
530			break;
531		default:
532			warnx("Unknown suboption - '%s'", suboptarg);
533			return (-1);
534		}
535		count += 1;
536	}
537	return (2/* count */);
538}
539
540int32_t
541parse_privacy(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
542{
543	int32_t count, subopt;
544	char *val, *option;
545	const char *const subopts[] = {
546		"proto",
547		"key",
548		NULL
549	};
550
551	assert(opt_arg != NULL);
552	count = 1;
553	while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) {
554		switch (subopt) {
555		case 0:
556			if (val == NULL) {
557				warnx("Suboption 'proto' requires an argument");
558				return (-1);
559			}
560			if (strlen(val) != 3) {
561				warnx("Unknown privacy protocol - %s", val);
562				return (-1);
563			}
564			if (strncasecmp("aes", val, strlen("aes")) == 0)
565				snmp_client.user.priv_proto = SNMP_PRIV_AES;
566			else if (strncasecmp("des", val, strlen("des")) == 0)
567				snmp_client.user.priv_proto = SNMP_PRIV_DES;
568			else {
569				warnx("Unknown privacy protocol - %s", val);
570				return (-1);
571			}
572			break;
573		case 1:
574			if (val == NULL) {
575				warnx("Suboption 'key' requires an argument");
576				return (-1);
577			}
578			if (parse_ascii(val, snmp_client.user.priv_key,
579			    SNMP_PRIV_KEY_SIZ) < 0) {
580				warnx("Bad privacy key- %s", val);
581				return (-1);
582			}
583			break;
584		default:
585			warnx("Unknown suboption - '%s'", suboptarg);
586			return (-1);
587		}
588		count += 1;
589	}
590	return (2/* count */);
591}
592
593int32_t
594parse_context(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
595{
596	int32_t count, subopt;
597	char *val, *option;
598	const char *const subopts[] = {
599		"context",
600		"context-engine",
601		NULL
602	};
603
604	assert(opt_arg != NULL);
605	count = 1;
606	while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) {
607		switch (subopt) {
608		case 0:
609			if (val == NULL) {
610				warnx("Suboption 'context' - no argument");
611				return (-1);
612			}
613			strlcpy(snmp_client.cname, val, SNMP_CONTEXT_NAME_SIZ);
614			break;
615		case 1:
616			if (val == NULL) {
617				warnx("Suboption 'context-engine' - no argument");
618				return (-1);
619			}
620			if ((snmp_client.clen = parse_ascii(val,
621			    snmp_client.cengine, SNMP_ENGINE_ID_SIZ)) < 0) {
622				warnx("Bad EngineID - %s", val);
623				return (-1);
624			}
625			break;
626		default:
627			warnx("Unknown suboption - '%s'", suboptarg);
628			return (-1);
629		}
630		count += 1;
631	}
632	return (2/* count */);
633}
634
635int32_t
636parse_user_security(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
637{
638	int32_t count, subopt, saved_errno;
639	char *val, *option;
640	const char *const subopts[] = {
641		"engine",
642		"engine-boots",
643		"engine-time",
644		"name",
645		NULL
646	};
647
648	assert(opt_arg != NULL);
649	count = 1;
650	while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) {
651		switch (subopt) {
652		case 0:
653			if (val == NULL) {
654				warnx("Suboption 'engine' - no argument");
655				return (-1);
656			}
657			snmp_client.engine.engine_len = parse_ascii(val,
658			    snmp_client.engine.engine_id, SNMP_ENGINE_ID_SIZ);
659			if (snmp_client.engine.engine_len < 0) {
660				warnx("Bad EngineID - %s", val);
661				return (-1);
662			}
663			break;
664		case 1:
665			if (val == NULL) {
666				warnx("Suboption 'engine-boots' - no argument");
667				return (-1);
668			}
669			saved_errno = errno;
670			errno = 0;
671			snmp_client.engine.engine_boots = strtoul(val, NULL, 10);
672			if (errno != 0) {
673				warnx("Bad 'engine-boots' value %s - %s", val,
674				    strerror(errno));
675				errno = saved_errno;
676				return (-1);
677			}
678			errno = saved_errno;
679			break;
680		case 2:
681			if (val == NULL) {
682				warnx("Suboption 'engine-time' - no argument");
683				return (-1);
684			}
685			saved_errno = errno;
686			errno = 0;
687			snmp_client.engine.engine_time = strtoul(val, NULL, 10);
688			if (errno != 0) {
689				warnx("Bad 'engine-time' value %s - %s", val,
690				    strerror(errno));
691				errno = saved_errno;
692				return (-1);
693			}
694			errno = saved_errno;
695			break;
696		case 3:
697			strlcpy(snmp_client.user.sec_name, val,
698			    SNMP_ADM_STR32_SIZ);
699			break;
700		default:
701			warnx("Unknown suboption - '%s'", suboptarg);
702			return (-1);
703		}
704		count += 1;
705	}
706	return (2/* count */);
707}
708
709int32_t
710parse_file(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
711{
712	assert(opt_arg != NULL);
713
714	if (parse_flist(snmptoolctx, opt_arg, NULL, &IsoOrgDod_OID) < 0)
715		return (-1);
716
717	return (2);
718}
719
720int32_t
721parse_include(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
722{
723	char path[MAXPATHLEN + 1];
724	int32_t cut_dflt, len, subopt;
725	struct asn_oid cut;
726	char *val, *option;
727	const char *const subopts[] = {
728		"cut",
729		"path",
730		"file",
731		NULL
732	};
733
734#define	INC_CUT		0
735#define	INC_PATH	1
736#define	INC_LIST	2
737
738	assert(opt_arg != NULL);
739
740	/* if (opt == 'i')
741		free_filelist(snmptoolctx, ); */
742	/*
743	 * This function should be called only after getopt(3) - otherwise if
744	 * no previous validation of opt_arg strlen() may not return what is
745	 * expected.
746	 */
747
748	path[0] = '\0';
749	memset(&cut, 0, sizeof(struct asn_oid));
750	cut_dflt = -1;
751
752	while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) {
753		switch (subopt) {
754		    case INC_CUT:
755			if (val == NULL) {
756				warnx("Suboption 'cut' requires an argument");
757				return (-1);
758			} else {
759				if (snmp_parse_numoid(val, &cut) < 0)
760					return (-1);
761			}
762			cut_dflt = 1;
763			break;
764
765		    case INC_PATH:
766			if ((len = parse_path(val)) < 0)
767				return (-1);
768			strlcpy(path, val, len + 1);
769			break;
770
771		    case INC_LIST:
772			if (val == NULL)
773				return (-1);
774			if (cut_dflt == -1)
775				len = parse_flist(snmptoolctx, val, path, &IsoOrgDod_OID);
776			else
777				len = parse_flist(snmptoolctx, val, path, &cut);
778			if (len < 0)
779				return (-1);
780			break;
781
782		    default:
783			warnx("Unknown suboption - '%s'", suboptarg);
784			return (-1);
785		}
786	}
787
788	/* XXX: Fix me - returning two is wrong here */
789	return (2);
790}
791
792int32_t
793parse_server(char *opt_arg)
794{
795	assert(opt_arg != NULL);
796
797	if (snmp_parse_server(&snmp_client, opt_arg) < 0)
798		return (-1);
799
800	if (snmp_client.trans > SNMP_TRANS_UDP && snmp_client.chost == NULL) {
801		if ((snmp_client.chost = malloc(strlen(SNMP_DEFAULT_LOCAL) + 1))
802		    == NULL) {
803			syslog(LOG_ERR, "malloc() failed: %s", strerror(errno));
804			return (-1);
805		}
806		strcpy(snmp_client.chost, SNMP_DEFAULT_LOCAL);
807	}
808
809	return (2);
810}
811
812int32_t
813parse_timeout(char *opt_arg)
814{
815	int32_t v, saved_errno;
816
817	assert(opt_arg != NULL);
818
819	saved_errno = errno;
820	errno = 0;
821
822	v = strtol(opt_arg, NULL, 10);
823	if (errno != 0) {
824		warnx( "Error parsing timeout value - %s", strerror(errno));
825		errno = saved_errno;
826		return (-1);
827	}
828
829	snmp_client.timeout.tv_sec = v;
830	errno = saved_errno;
831	return (2);
832}
833
834int32_t
835parse_retry(char *opt_arg)
836{
837	uint32_t v;
838	int32_t saved_errno;
839
840	assert(opt_arg != NULL);
841
842	saved_errno = errno;
843	errno = 0;
844
845	v = strtoul(opt_arg, NULL, 10);
846	if (errno != 0) {
847		warnx("Error parsing retries count - %s", strerror(errno));
848		errno = saved_errno;
849		return (-1);
850	}
851
852	snmp_client.retries = v;
853	errno = saved_errno;
854	return (2);
855}
856
857int32_t
858parse_version(char *opt_arg)
859{
860	uint32_t v;
861	int32_t saved_errno;
862
863	assert(opt_arg != NULL);
864
865	saved_errno = errno;
866	errno = 0;
867
868	v = strtoul(opt_arg, NULL, 10);
869	if (errno != 0) {
870		warnx("Error parsing version - %s", strerror(errno));
871		errno = saved_errno;
872		return (-1);
873	}
874
875	switch (v) {
876		case 1:
877			snmp_client.version = SNMP_V1;
878			break;
879		case 2:
880			snmp_client.version = SNMP_V2c;
881			break;
882		case 3:
883			snmp_client.version = SNMP_V3;
884			break;
885		default:
886			warnx("Unsupported SNMP version - %u", v);
887			errno = saved_errno;
888			return (-1);
889	}
890
891	errno = saved_errno;
892	return (2);
893}
894
895int32_t
896parse_local_path(char *opt_arg)
897{
898	assert(opt_arg != NULL);
899
900	if (sizeof(opt_arg) > sizeof(SNMP_LOCAL_PATH)) {
901		warnx("Filename too long - %s", opt_arg);
902		return (-1);
903	}
904
905	strlcpy(snmp_client.local_path, opt_arg, sizeof(SNMP_LOCAL_PATH));
906	return (2);
907}
908
909int32_t
910parse_buflen(char *opt_arg)
911{
912	uint32_t size;
913	int32_t saved_errno;
914
915	assert(opt_arg != NULL);
916
917	saved_errno = errno;
918	errno = 0;
919
920	size = strtoul(opt_arg, NULL, 10);
921	if (errno != 0) {
922		warnx("Error parsing buffer size - %s", strerror(errno));
923		errno = saved_errno;
924		return (-1);
925	}
926
927	if (size > MAX_BUFF_SIZE) {
928		warnx("Buffer size too big - %d max allowed", MAX_BUFF_SIZE);
929		errno = saved_errno;
930		return (-1);
931	}
932
933	snmp_client.txbuflen = snmp_client.rxbuflen = size;
934	errno = saved_errno;
935	return (2);
936}
937
938int32_t
939parse_debug(void)
940{
941	snmp_client.dump_pdus = 1;
942	return (1);
943}
944
945int32_t
946parse_discovery(struct snmp_toolinfo *snmptoolctx)
947{
948	SET_EDISCOVER(snmptoolctx);
949	snmp_client.version = SNMP_V3;
950	return (1);
951}
952
953int32_t
954parse_local_key(struct snmp_toolinfo *snmptoolctx)
955{
956	SET_LOCALKEY(snmptoolctx);
957	snmp_client.version = SNMP_V3;
958	return (1);
959}
960
961int32_t
962parse_num_oids(struct snmp_toolinfo *snmptoolctx)
963{
964	SET_NUMERIC(snmptoolctx);
965	return (1);
966}
967
968int32_t
969parse_output(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
970{
971	assert(opt_arg != NULL);
972
973	if (strlen(opt_arg) > strlen("verbose")) {
974		warnx( "Invalid output option - %s",opt_arg);
975		return (-1);
976	}
977
978	if (strncasecmp(opt_arg, "short", strlen(opt_arg)) == 0)
979		SET_OUTPUT(snmptoolctx, OUTPUT_SHORT);
980	else if (strncasecmp(opt_arg, "verbose", strlen(opt_arg)) == 0)
981		SET_OUTPUT(snmptoolctx, OUTPUT_VERBOSE);
982	else if (strncasecmp(opt_arg,"tabular", strlen(opt_arg)) == 0)
983		SET_OUTPUT(snmptoolctx, OUTPUT_TABULAR);
984	else if (strncasecmp(opt_arg, "quiet", strlen(opt_arg)) == 0)
985		SET_OUTPUT(snmptoolctx, OUTPUT_QUIET);
986	else {
987		warnx( "Invalid output option - %s", opt_arg);
988		return (-1);
989	}
990
991	return (2);
992}
993
994int32_t
995parse_errors(struct snmp_toolinfo *snmptoolctx)
996{
997	SET_RETRY(snmptoolctx);
998	return (1);
999}
1000
1001int32_t
1002parse_skip_access(struct snmp_toolinfo *snmptoolctx)
1003{
1004	SET_ERRIGNORE(snmptoolctx);
1005	return (1);
1006}
1007
1008char *
1009snmp_parse_suboid(char *str, struct asn_oid *oid)
1010{
1011	char *endptr;
1012	asn_subid_t suboid;
1013
1014	if (*str == '.')
1015		str++;
1016
1017	if (*str < '0' || *str > '9')
1018		return (str);
1019
1020	do {
1021		suboid = strtoul(str, &endptr, 10);
1022		if ((asn_subid_t) suboid > ASN_MAXID) {
1023			warnx("Suboid %u > ASN_MAXID", suboid);
1024			return (NULL);
1025		}
1026		if (snmp_suboid_append(oid, suboid) < 0)
1027			return (NULL);
1028		str = endptr + 1;
1029	} while (*endptr == '.');
1030
1031	return (endptr);
1032}
1033
1034static char *
1035snmp_int2asn_oid(char *str, struct asn_oid *oid)
1036{
1037	char *endptr;
1038	int32_t v, saved_errno;
1039
1040	saved_errno = errno;
1041	errno = 0;
1042
1043	v = strtol(str, &endptr, 10);
1044	if (errno != 0) {
1045		warnx("Integer value %s not supported - %s", str,
1046		    strerror(errno));
1047		errno = saved_errno;
1048		return (NULL);
1049	}
1050	errno = saved_errno;
1051
1052	if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
1053		return (NULL);
1054
1055	return (endptr);
1056}
1057
1058/* It is a bit weird to have a table indexed by OID but still... */
1059static char *
1060snmp_oid2asn_oid(struct snmp_toolinfo *snmptoolctx, char *str,
1061    struct asn_oid *oid)
1062{
1063	int32_t i;
1064	char string[MAXSTR], *endptr;
1065	struct snmp_object obj;
1066
1067	for (i = 0; i < MAXSTR; i++)
1068		if (isalpha (*(str + i)) == 0)
1069			break;
1070
1071	endptr = str + i;
1072	memset(&obj, 0, sizeof(struct snmp_object));
1073	if (i == 0) {
1074		if ((endptr = snmp_parse_suboid(str, &(obj.val.var))) == NULL)
1075			return (NULL);
1076		if (snmp_suboid_append(oid, (asn_subid_t) obj.val.var.len) < 0)
1077			return (NULL);
1078	} else {
1079		strlcpy(string, str, i + 1);
1080		string[i] = '\0';
1081		if (snmp_lookup_enumoid(snmptoolctx, &obj, string) < 0) {
1082			warnx("Unknown string - %s", string);
1083			return (NULL);
1084		}
1085	}
1086
1087	asn_append_oid(oid, &(obj.val.var));
1088	return (endptr);
1089}
1090
1091static char *
1092snmp_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. */
1114static char *
1115snmp_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
1138static char *
1139snmp_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
1166enum snmp_syntax
1167parse_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
1180static char *
1181snmp_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
1245char *
1246snmp_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 */
1279int32_t
1280snmp_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. */
1315int32_t
1316snmp_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. */
1332int32_t
1333snmp_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 */
1354int32_t
1355snmp_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. */
1388int32_t
1389snmp_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
1417static void
1418snmp_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. */
1433void
1434snmp_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 */
1463int32_t
1464snmp_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 */
1507int32_t
1508snmp_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 response 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 skipped.
1530 */
1531int32_t
1532snmp_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
1552int32_t
1553snmp_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
1589int32_t
1590snmp_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 response to get/getnext/getbulk.
1609 */
1610int32_t
1611snmp_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 response", 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
1651static void
1652snmp_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
1670static void
1671snmp_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 */
1691static void
1692snmp_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
1720static void
1721snmp_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
1737static void
1738snmp_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
1747static void
1748snmp_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
1757static void
1758snmp_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
1766static void
1767snmp_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
1776static void
1777snmp_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
1786int32_t
1787snmp_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
1866static int32_t
1867snmp_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
1894static int32_t
1895snmp_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
1970static int32_t
1971snmp_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
2011void
2012snmp_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
2041int32_t
2042snmp_output_resp(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu,
2043    struct asn_oid *root)
2044{
2045	int32_t error;
2046	char p[ASN_OIDSTRLEN];
2047	uint32_t i;
2048	struct snmp_object object;
2049
2050	i = error = 0;
2051	while (i < pdu->nbindings) {
2052		if (root != NULL && !(asn_is_suboid(root,
2053		    &(pdu->bindings[i].var))))
2054			break;
2055
2056		if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET) {
2057			if (!ISSET_NUMERIC(snmptoolctx) &&
2058			    (snmp_fill_object(snmptoolctx, &object,
2059			    &(pdu->bindings[i])) > 0))
2060				snmp_output_object(snmptoolctx, &object);
2061			else {
2062				asn_oid2str_r(&(pdu->bindings[i].var), p);
2063				fprintf(stdout, "%s", p);
2064			}
2065		}
2066		error |= snmp_output_numval(snmptoolctx, &(pdu->bindings[i]), object.info);
2067		i++;
2068	}
2069
2070	if (error)
2071		return (-1);
2072
2073	return (i);
2074}
2075
2076void
2077snmp_output_engine(void)
2078{
2079	uint32_t i;
2080	char *cptr, engine[2 * SNMP_ENGINE_ID_SIZ + 2];
2081
2082	cptr = engine;
2083	for (i = 0; i < snmp_client.engine.engine_len; i++)
2084		cptr += sprintf(cptr, "%.2x", snmp_client.engine.engine_id[i]);
2085	*cptr++ = '\0';
2086
2087	fprintf(stdout, "Engine ID 0x%s\n", engine);
2088	fprintf(stdout, "Boots : %u\t\tTime : %d\n",
2089	    snmp_client.engine.engine_boots,
2090	    snmp_client.engine.engine_time);
2091}
2092
2093void
2094snmp_output_keys(void)
2095{
2096	uint32_t i, keylen = 0;
2097	char *cptr, extkey[2 * SNMP_AUTH_KEY_SIZ + 2];
2098
2099	fprintf(stdout, "Localized keys for %s\n", snmp_client.user.sec_name);
2100	if (snmp_client.user.auth_proto == SNMP_AUTH_HMAC_MD5) {
2101		fprintf(stdout, "MD5 : 0x");
2102		keylen = SNMP_AUTH_HMACMD5_KEY_SIZ;
2103	} else if (snmp_client.user.auth_proto == SNMP_AUTH_HMAC_SHA) {
2104		fprintf(stdout, "SHA : 0x");
2105		keylen = SNMP_AUTH_HMACSHA_KEY_SIZ;
2106	}
2107	if (snmp_client.user.auth_proto != SNMP_AUTH_NOAUTH) {
2108		cptr = extkey;
2109		for (i = 0; i < keylen; i++)
2110			cptr += sprintf(cptr, "%.2x",
2111			    snmp_client.user.auth_key[i]);
2112		*cptr++ = '\0';
2113		fprintf(stdout, "%s\n", extkey);
2114	}
2115
2116	if (snmp_client.user.priv_proto == SNMP_PRIV_DES) {
2117		fprintf(stdout, "DES : 0x");
2118		keylen = SNMP_PRIV_DES_KEY_SIZ;
2119	} else if (snmp_client.user.priv_proto == SNMP_PRIV_AES) {
2120		fprintf(stdout, "AES : 0x");
2121		keylen = SNMP_PRIV_AES_KEY_SIZ;
2122	}
2123	if (snmp_client.user.priv_proto != SNMP_PRIV_NOPRIV) {
2124		cptr = extkey;
2125		for (i = 0; i < keylen; i++)
2126			cptr += sprintf(cptr, "%.2x",
2127			    snmp_client.user.priv_key[i]);
2128		*cptr++ = '\0';
2129		fprintf(stdout, "%s\n", extkey);
2130	}
2131}
2132