bsnmptools.c revision 300471
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 300471 2016-05-23 05:41:53Z ngie $
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 = calloc(1, sizeof(struct fname))) == NULL) {
260		warnx("malloc() failed - %s", strerror(errno));
261		free(fstring);
262		return (-1);
263	}
264
265	if (cut != NULL)
266		asn_append_oid(&(entry->cut), cut);
267	strlcpy(fstring, filename, strlen(filename) + 1);
268	entry->name = fstring;
269	entry->done = done;
270	SLIST_INSERT_HEAD(&snmptoolctx->filelist, entry, link);
271
272	return (1);
273}
274
275void
276free_filelist(struct snmp_toolinfo *snmptoolctx)
277{
278	struct fname *f;
279
280	if (snmptoolctx == NULL)
281		return; /* XXX error handling */
282
283	while ((f = SLIST_FIRST(&snmptoolctx->filelist)) != NULL) {
284		SLIST_REMOVE_HEAD(&snmptoolctx->filelist, link);
285		if (f->name)
286			free(f->name);
287		free(f);
288	}
289}
290
291static char
292isvalid_fchar(char c, int pos)
293{
294	if (isalpha(c)|| c == '/'|| c == '_' || c == '.' || c == '~' ||
295	    (pos != 0 && isdigit(c))){
296		return (c);
297	}
298
299	if (c == '\0')
300		return (0);
301
302	if (!isascii(c) || !isprint(c))
303		warnx("Unexpected character %#2x", (u_int) c);
304	else
305		warnx("Illegal character '%c'", c);
306
307	return (-1);
308}
309
310/*
311 * Re-implement getsubopt from scratch, because the second argument is broken
312 * and will not compile with WARNS=5.
313 * Copied from src/contrib/bsnmp/snmpd/main.c.
314 */
315static int
316getsubopt1(char **arg, const char *const *options, char **valp, char **optp)
317{
318	static const char *const delim = ",\t ";
319	u_int i;
320	char *ptr;
321
322	*optp = NULL;
323
324	/* Skip leading junk. */
325	for (ptr = *arg; *ptr != '\0'; ptr++)
326		if (strchr(delim, *ptr) == NULL)
327			break;
328	if (*ptr == '\0') {
329		*arg = ptr;
330		return (-1);
331	}
332	*optp = ptr;
333
334	/* Find the end of the option. */
335	while (*++ptr != '\0')
336		if (strchr(delim, *ptr) != NULL || *ptr == '=')
337			break;
338
339	if (*ptr != '\0') {
340		if (*ptr == '=') {
341			*ptr++ = '\0';
342			*valp = ptr;
343			while (*ptr != '\0' && strchr(delim, *ptr) == NULL)
344				ptr++;
345			if (*ptr != '\0')
346				*ptr++ = '\0';
347		} else
348			*ptr++ = '\0';
349	}
350
351	*arg = ptr;
352
353	for (i = 0; *options != NULL; options++, i++)
354		if (strcmp(*optp, *options) == 0)
355			return (i);
356	return (-1);
357}
358
359static int32_t
360parse_path(char *value)
361{
362	int32_t i, len;
363
364	if (value == NULL)
365		return (-1);
366
367	for (len = 0; len < MAXPATHLEN; len++) {
368		i = isvalid_fchar(*(value + len), len) ;
369
370		if (i == 0)
371			break;
372		else if (i < 0)
373			return (-1);
374	}
375
376	if (len >= MAXPATHLEN || value[len] != '\0') {
377		warnx("Bad pathname - '%s'", value);
378		return (-1);
379	}
380
381	return (len);
382}
383
384static int32_t
385parse_flist(struct snmp_toolinfo *snmptoolctx, char *value, char *path,
386    const struct asn_oid *cut)
387{
388	int32_t namelen;
389	char filename[MAXPATHLEN + 1];
390
391	if (value == NULL)
392		return (-1);
393
394	do {
395		memset(filename, 0, MAXPATHLEN + 1);
396
397		if (isalpha(*value) && (path == NULL || path[0] == '\0')) {
398			strlcpy(filename, SNMP_DEFS_DIR, MAXPATHLEN + 1);
399			namelen = strlen(SNMP_DEFS_DIR);
400		} else if (path != NULL){
401			strlcpy(filename, path, MAXPATHLEN + 1);
402			namelen = strlen(path);
403		} else
404			namelen = 0;
405
406		for ( ; namelen < MAXPATHLEN; value++) {
407			if (isvalid_fchar(*value, namelen) > 0) {
408				filename[namelen++] = *value;
409				continue;
410			}
411
412			if (*value == ',' )
413				value++;
414			else if (*value == '\0')
415				;
416			else {
417				if (!isascii(*value) || !isprint(*value))
418					warnx("Unexpected character %#2x in"
419					    " filename", (u_int) *value);
420				else
421					warnx("Illegal character '%c' in"
422					    " filename", *value);
423				return (-1);
424			}
425
426			filename[namelen]='\0';
427			break;
428		}
429
430		if ((namelen == MAXPATHLEN) && (filename[MAXPATHLEN] != '\0')) {
431			warnx("Filename %s too long", filename);
432			return (-1);
433		}
434
435		if (add_filename(snmptoolctx, filename, cut, 0) < 0) {
436			warnx("Error adding file %s to list", filename);
437			return (-1);
438		}
439	} while (*value != '\0');
440
441	return(1);
442}
443
444static int32_t
445parse_ascii(char *ascii, uint8_t *binstr, size_t binlen)
446{
447	char dptr[3];
448	size_t count;
449	int32_t alen, i, saved_errno;
450	uint32_t val;
451
452	/* Filter 0x at the beginning */
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 octets",
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 */
484int32_t
485parse_authentication(struct snmp_toolinfo *snmptoolctx __unused, 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
539int32_t
540parse_privacy(struct snmp_toolinfo *snmptoolctx __unused, 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
592int32_t
593parse_context(struct snmp_toolinfo *snmptoolctx __unused, 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
634int32_t
635parse_user_security(struct snmp_toolinfo *snmptoolctx __unused, 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
708int32_t
709parse_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
719int32_t
720parse_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
791int32_t
792parse_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
811int32_t
812parse_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
833int32_t
834parse_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
856int32_t
857parse_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
894int32_t
895parse_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
908int32_t
909parse_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
937int32_t
938parse_debug(void)
939{
940	snmp_client.dump_pdus = 1;
941	return (1);
942}
943
944int32_t
945parse_discovery(struct snmp_toolinfo *snmptoolctx)
946{
947	SET_EDISCOVER(snmptoolctx);
948	snmp_client.version = SNMP_V3;
949	return (1);
950}
951
952int32_t
953parse_local_key(struct snmp_toolinfo *snmptoolctx)
954{
955	SET_LOCALKEY(snmptoolctx);
956	snmp_client.version = SNMP_V3;
957	return (1);
958}
959
960int32_t
961parse_num_oids(struct snmp_toolinfo *snmptoolctx)
962{
963	SET_NUMERIC(snmptoolctx);
964	return (1);
965}
966
967int32_t
968parse_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
993int32_t
994parse_errors(struct snmp_toolinfo *snmptoolctx)
995{
996	SET_RETRY(snmptoolctx);
997	return (1);
998}
999
1000int32_t
1001parse_skip_access(struct snmp_toolinfo *snmptoolctx)
1002{
1003	SET_ERRIGNORE(snmptoolctx);
1004	return (1);
1005}
1006
1007char *
1008snmp_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
1033static char *
1034snmp_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... */
1058static char *
1059snmp_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	}
1085
1086	asn_append_oid(oid, &(obj.val.var));
1087	return (endptr);
1088}
1089
1090static char *
1091snmp_ip2asn_oid(char *str, struct asn_oid *oid)
1092{
1093	uint32_t v;
1094	int32_t i;
1095	char *endptr, *ptr;
1096
1097	ptr = str;
1098	for (i = 0; i < 4; i++) {
1099		v = strtoul(ptr, &endptr, 10);
1100		if (v > 0xff)
1101			return (NULL);
1102		if (*endptr != '.' && strchr("],\0", *endptr) == NULL && i != 3)
1103			return (NULL);
1104		if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
1105			return (NULL);
1106		ptr = endptr + 1;
1107	}
1108
1109	return (endptr);
1110}
1111
1112/* 32-bit counter, gauge, timeticks. */
1113static char *
1114snmp_uint2asn_oid(char *str, struct asn_oid *oid)
1115{
1116	char *endptr;
1117	uint32_t v;
1118	int32_t saved_errno;
1119
1120	saved_errno = errno;
1121	errno = 0;
1122
1123	v = strtoul(str, &endptr, 10);
1124	if (errno != 0) {
1125		warnx("Integer value %s not supported - %s\n", str,
1126		    strerror(errno));
1127		errno = saved_errno;
1128		return (NULL);
1129	}
1130	errno = saved_errno;
1131	if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
1132		return (NULL);
1133
1134	return (endptr);
1135}
1136
1137static char *
1138snmp_cnt64_2asn_oid(char *str, struct asn_oid *oid)
1139{
1140	char *endptr;
1141	uint64_t v;
1142	int32_t saved_errno;
1143
1144	saved_errno = errno;
1145	errno = 0;
1146
1147	v = strtoull(str, &endptr, 10);
1148
1149	if (errno != 0) {
1150		warnx("Integer value %s not supported - %s", str,
1151		    strerror(errno));
1152		errno = saved_errno;
1153		return (NULL);
1154	}
1155	errno = saved_errno;
1156	if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xffffffff)) < 0)
1157		return (NULL);
1158
1159	if (snmp_suboid_append(oid, (asn_subid_t) (v >> 32)) < 0)
1160		return (NULL);
1161
1162	return (endptr);
1163}
1164
1165enum snmp_syntax
1166parse_syntax(char *str)
1167{
1168	int32_t i;
1169
1170	for (i = 0; i < SNMP_SYNTAX_UNKNOWN; i++) {
1171		if (strncmp(syntax_strings[i].str, str,
1172		    strlen(syntax_strings[i].str)) == 0)
1173			return (syntax_strings[i].stx);
1174	}
1175
1176	return (SNMP_SYNTAX_NULL);
1177}
1178
1179static char *
1180snmp_parse_subindex(struct snmp_toolinfo *snmptoolctx, char *str,
1181    struct index *idx, struct snmp_object *object)
1182{
1183	char *ptr;
1184	int32_t i;
1185	enum snmp_syntax stx;
1186	char syntax[MAX_CMD_SYNTAX_LEN];
1187
1188	ptr = str;
1189	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) {
1190		for (i = 0; i < MAX_CMD_SYNTAX_LEN ; i++) {
1191			if (*(ptr + i) == ':')
1192				break;
1193		}
1194
1195		if (i >= MAX_CMD_SYNTAX_LEN) {
1196			warnx("Unknown syntax in OID - %s", str);
1197			return (NULL);
1198		}
1199		/* Expect a syntax string here. */
1200		if ((stx = parse_syntax(str)) <= SNMP_SYNTAX_NULL) {
1201			warnx("Invalid  syntax - %s",syntax);
1202			return (NULL);
1203		}
1204
1205		if (stx != idx->syntax && !ISSET_ERRIGNORE(snmptoolctx)) {
1206			warnx("Syntax mismatch - %d expected, %d given",
1207			    idx->syntax, stx);
1208			return (NULL);
1209		}
1210		/*
1211		 * That is where the suboid started + the syntax length + one
1212		 * character for ':'.
1213		 */
1214		ptr = str + i + 1;
1215	} else
1216		stx = idx->syntax;
1217
1218	switch (stx) {
1219		case SNMP_SYNTAX_INTEGER:
1220			return (snmp_int2asn_oid(ptr, &(object->val.var)));
1221		case SNMP_SYNTAX_OID:
1222			return (snmp_oid2asn_oid(snmptoolctx, ptr,
1223			    &(object->val.var)));
1224		case SNMP_SYNTAX_IPADDRESS:
1225			return (snmp_ip2asn_oid(ptr, &(object->val.var)));
1226		case SNMP_SYNTAX_COUNTER:
1227			/* FALLTHROUGH */
1228		case SNMP_SYNTAX_GAUGE:
1229			/* FALLTHROUGH */
1230		case SNMP_SYNTAX_TIMETICKS:
1231			return (snmp_uint2asn_oid(ptr, &(object->val.var)));
1232		case SNMP_SYNTAX_COUNTER64:
1233			return (snmp_cnt64_2asn_oid(ptr, &(object->val.var)));
1234		case SNMP_SYNTAX_OCTETSTRING:
1235			return (snmp_tc2oid(idx->tc, ptr, &(object->val.var)));
1236		default:
1237			/* NOTREACHED */
1238			break;
1239	}
1240
1241	return (NULL);
1242}
1243
1244char *
1245snmp_parse_index(struct snmp_toolinfo *snmptoolctx, char *str,
1246    struct snmp_object *object)
1247{
1248	char *ptr;
1249	struct index *temp;
1250
1251	if (object->info->table_idx == NULL)
1252		return (NULL);
1253
1254	ptr = NULL;
1255	STAILQ_FOREACH(temp, &(OBJECT_IDX_LIST(object)), link) {
1256		if ((ptr = snmp_parse_subindex(snmptoolctx, str, temp, object))
1257		    == NULL)
1258			return (NULL);
1259
1260		if (*ptr != ',' && *ptr != ']')
1261			return (NULL);
1262		str = ptr + 1;
1263	}
1264
1265	if (ptr == NULL || *ptr != ']') {
1266		warnx("Mismatching index - %s", str);
1267		return (NULL);
1268	}
1269
1270	return (ptr + 1);
1271}
1272
1273/*
1274 * Fill in the struct asn_oid member of snmp_value with suboids from input.
1275 * If an error occurs - print message on stderr and return (-1).
1276 * If all is ok - return the length of the oid.
1277 */
1278int32_t
1279snmp_parse_numoid(char *argv, struct asn_oid *var)
1280{
1281	char *endptr, *str;
1282	asn_subid_t suboid;
1283
1284	str = argv;
1285
1286	if (*str == '.')
1287		str++;
1288
1289	do {
1290		if (var->len == ASN_MAXOIDLEN) {
1291			warnx("Oid too long - %u", var->len);
1292			return (-1);
1293		}
1294
1295		suboid = strtoul(str, &endptr, 10);
1296		if (suboid > ASN_MAXID) {
1297			warnx("Oid too long - %u", var->len);
1298			return (-1);
1299		}
1300
1301		var->subs[var->len++] = suboid;
1302		str = endptr + 1;
1303	} while ( *endptr == '.');
1304
1305	if (*endptr != '\0') {
1306		warnx("Invalid oid string - %s", argv);
1307		return (-1);
1308	}
1309
1310	return (var->len);
1311}
1312
1313/* Append a length 1 suboid to an asn_oid structure. */
1314int32_t
1315snmp_suboid_append(struct asn_oid *var, asn_subid_t suboid)
1316{
1317	if (var == NULL)
1318		return (-1);
1319
1320	if (var->len >= ASN_MAXOIDLEN) {
1321		warnx("Oid too long - %u", var->len);
1322		return (-1);
1323	}
1324
1325	var->subs[var->len++] = suboid;
1326
1327	return (1);
1328}
1329
1330/* Pop the last suboid from an asn_oid structure. */
1331int32_t
1332snmp_suboid_pop(struct asn_oid *var)
1333{
1334	asn_subid_t suboid;
1335
1336	if (var == NULL)
1337		return (-1);
1338
1339	if (var->len < 1)
1340		return (-1);
1341
1342	suboid = var->subs[--(var->len)];
1343	var->subs[var->len] = 0;
1344
1345	return (suboid);
1346}
1347
1348/*
1349 * Parse the command-line provided string into an OID - alocate memory for a new
1350 * snmp object, fill in its fields and insert it in the object list. A
1351 * (snmp_verify_inoid_f) function must be provided to validate the input string.
1352 */
1353int32_t
1354snmp_object_add(struct snmp_toolinfo *snmptoolctx, snmp_verify_inoid_f func,
1355    char *string)
1356{
1357	struct snmp_object *obj;
1358
1359	if (snmptoolctx == NULL)
1360		return (-1);
1361
1362	/* XXX-BZ does that chack make sense? */
1363	if (snmptoolctx->objects >= SNMP_MAX_BINDINGS) {
1364		warnx("Too many bindings in PDU - %u", snmptoolctx->objects + 1);
1365		return (-1);
1366	}
1367
1368	if ((obj = calloc(1, sizeof(struct snmp_object))) == NULL) {
1369		syslog(LOG_ERR, "malloc() failed: %s", strerror(errno));
1370		return (-1);
1371	}
1372
1373	if (func(snmptoolctx, obj, string) < 0) {
1374		warnx("Invalid OID - %s", string);
1375		free(obj);
1376		return (-1);
1377	}
1378
1379	snmptoolctx->objects++;
1380	SLIST_INSERT_HEAD(&snmptoolctx->snmp_objectlist, obj, link);
1381
1382	return (1);
1383}
1384
1385/* Given an OID, find it in the object list and remove it. */
1386int32_t
1387snmp_object_remove(struct snmp_toolinfo *snmptoolctx, struct asn_oid *oid)
1388{
1389	struct snmp_object *temp;
1390
1391	if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist)) {
1392		warnx("Object list already empty");
1393		return (-1);
1394	}
1395
1396
1397	SLIST_FOREACH(temp, &snmptoolctx->snmp_objectlist, link)
1398		if (asn_compare_oid(&(temp->val.var), oid) == 0)
1399			break;
1400
1401	if (temp == NULL) {
1402		warnx("No such object in list");
1403		return (-1);
1404	}
1405
1406	SLIST_REMOVE(&snmptoolctx->snmp_objectlist, temp, snmp_object, link);
1407	if (temp->val.syntax == SNMP_SYNTAX_OCTETSTRING &&
1408	    temp->val.v.octetstring.octets != NULL)
1409		free(temp->val.v.octetstring.octets);
1410	free(temp);
1411
1412	return (1);
1413}
1414
1415static void
1416snmp_object_freeall(struct snmp_toolinfo *snmptoolctx)
1417{
1418	struct snmp_object *o;
1419
1420	while ((o = SLIST_FIRST(&snmptoolctx->snmp_objectlist)) != NULL) {
1421		SLIST_REMOVE_HEAD(&snmptoolctx->snmp_objectlist, link);
1422
1423		if (o->val.syntax == SNMP_SYNTAX_OCTETSTRING &&
1424		    o->val.v.octetstring.octets != NULL)
1425			free(o->val.v.octetstring.octets);
1426		free(o);
1427	}
1428}
1429
1430/* Do all possible memory release before exit. */
1431void
1432snmp_tool_freeall(struct snmp_toolinfo *snmptoolctx)
1433{
1434	if (snmp_client.chost != NULL) {
1435		free(snmp_client.chost);
1436		snmp_client.chost = NULL;
1437	}
1438
1439	if (snmp_client.cport != NULL) {
1440		free(snmp_client.cport);
1441		snmp_client.cport = NULL;
1442	}
1443
1444	snmp_mapping_free(snmptoolctx);
1445	free_filelist(snmptoolctx);
1446	snmp_object_freeall(snmptoolctx);
1447
1448	if (snmptoolctx->passwd != NULL) {
1449		free(snmptoolctx->passwd);
1450		snmptoolctx->passwd = NULL;
1451	}
1452}
1453
1454/*
1455 * Fill all variables from the object list into a PDU. (snmp_verify_vbind_f)
1456 * function should check whether the variable is consistent in this PDU
1457 * (e.g do not add non-leaf OIDs to a GET PDU, or OIDs with read access only to
1458 * a SET PDU) - might be NULL though. (snmp_add_vbind_f) function is the
1459 * function actually adds the variable to the PDU and must not be NULL.
1460 */
1461int32_t
1462snmp_pdu_add_bindings(struct snmp_toolinfo *snmptoolctx,
1463    snmp_verify_vbind_f vfunc, snmp_add_vbind_f afunc,
1464    struct snmp_pdu *pdu, int32_t maxcount)
1465{
1466	int32_t nbindings, abind;
1467	struct snmp_object *obj;
1468
1469	if (pdu == NULL || afunc == NULL)
1470		return (-1);
1471
1472	/* Return 0 in case of no more work todo. */
1473	if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist))
1474		return (0);
1475
1476	if (maxcount < 0 || maxcount > SNMP_MAX_BINDINGS) {
1477		warnx("maxcount out of range: <0 || >SNMP_MAX_BINDINGS");
1478		return (-1);
1479	}
1480
1481	nbindings = 0;
1482	SLIST_FOREACH(obj, &snmptoolctx->snmp_objectlist, link) {
1483		if ((vfunc != NULL) && (vfunc(snmptoolctx, pdu, obj) < 0)) {
1484			nbindings = -1;
1485			break;
1486		}
1487		if ((abind = afunc(pdu, obj)) < 0) {
1488			nbindings = -1;
1489			break;
1490		}
1491
1492		if (abind > 0) {
1493			/* Do not put more varbindings than requested. */
1494			if (++nbindings >= maxcount)
1495				break;
1496		}
1497	}
1498
1499	return (nbindings);
1500}
1501
1502/*
1503 * Locate an object in the object list and set a corresponding error status.
1504 */
1505int32_t
1506snmp_object_seterror(struct snmp_toolinfo *snmptoolctx,
1507    struct snmp_value *err_value, int32_t error_status)
1508{
1509	struct snmp_object *obj;
1510
1511	if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist) || err_value == NULL)
1512		return (-1);
1513
1514	SLIST_FOREACH(obj, &snmptoolctx->snmp_objectlist, link)
1515		if (asn_compare_oid(&(err_value->var), &(obj->val.var)) == 0) {
1516			obj->error = error_status;
1517			return (1);
1518		}
1519
1520	return (0);
1521}
1522
1523/*
1524 * Check a PDU received in response to a SNMP_PDU_GET/SNMP_PDU_GETBULK request
1525 * but don't compare syntaxes - when sending a request PDU they must be null.
1526 * This is a (almost) complete copy of snmp_pdu_check() - with matching syntaxes
1527 * checks and some other checks skipped.
1528 */
1529int32_t
1530snmp_parse_get_resp(struct snmp_pdu *resp, struct snmp_pdu *req)
1531{
1532	uint32_t i;
1533
1534	for (i = 0; i < req->nbindings; i++) {
1535		if (asn_compare_oid(&req->bindings[i].var,
1536		    &resp->bindings[i].var) != 0) {
1537			warnx("Bad OID in response");
1538			return (-1);
1539		}
1540
1541		if (snmp_client.version != SNMP_V1 && (resp->bindings[i].syntax
1542		    == SNMP_SYNTAX_NOSUCHOBJECT || resp->bindings[i].syntax ==
1543		    SNMP_SYNTAX_NOSUCHINSTANCE))
1544			return (0);
1545	}
1546
1547	return (1);
1548}
1549
1550int32_t
1551snmp_parse_getbulk_resp(struct snmp_pdu *resp, struct snmp_pdu *req)
1552{
1553	int32_t N, R, M, r;
1554
1555	if (req->error_status > (int32_t) resp->nbindings) {
1556		warnx("Bad number of bindings in response");
1557		return (-1);
1558	}
1559
1560	for (N = 0; N < req->error_status; N++) {
1561		if (asn_is_suboid(&req->bindings[N].var,
1562		    &resp->bindings[N].var) == 0)
1563			return (0);
1564		if (resp->bindings[N].syntax == SNMP_SYNTAX_ENDOFMIBVIEW)
1565			return (0);
1566	}
1567
1568	for (R = N , r = N; R  < (int32_t) req->nbindings; R++) {
1569		for (M = 0; M < req->error_index && (r + M) <
1570		    (int32_t) resp->nbindings; M++) {
1571			if (asn_is_suboid(&req->bindings[R].var,
1572			    &resp->bindings[r + M].var) == 0)
1573				return (0);
1574
1575			if (resp->bindings[r + M].syntax ==
1576			    SNMP_SYNTAX_ENDOFMIBVIEW) {
1577				M++;
1578				break;
1579			}
1580		}
1581		r += M;
1582	}
1583
1584	return (0);
1585}
1586
1587int32_t
1588snmp_parse_getnext_resp(struct snmp_pdu *resp, struct snmp_pdu *req)
1589{
1590	uint32_t i;
1591
1592	for (i = 0; i < req->nbindings; i++) {
1593		if (asn_is_suboid(&req->bindings[i].var, &resp->bindings[i].var)
1594		    == 0)
1595			return (0);
1596
1597		if (resp->version != SNMP_V1 && resp->bindings[i].syntax ==
1598		    SNMP_SYNTAX_ENDOFMIBVIEW)
1599			return (0);
1600	}
1601
1602	return (1);
1603}
1604
1605/*
1606 * Should be called to check a response to get/getnext/getbulk.
1607 */
1608int32_t
1609snmp_parse_resp(struct snmp_pdu *resp, struct snmp_pdu *req)
1610{
1611	if (resp == NULL || req == NULL)
1612		return (-2);
1613
1614	if (resp->version != req->version) {
1615		warnx("Response has wrong version");
1616		return (-1);
1617	}
1618
1619	if (resp->error_status == SNMP_ERR_NOSUCHNAME) {
1620		warnx("Error - No Such Name");
1621		return (0);
1622	}
1623
1624	if (resp->error_status != SNMP_ERR_NOERROR) {
1625		warnx("Error %d in response", resp->error_status);
1626		return (-1);
1627	}
1628
1629	if (resp->nbindings != req->nbindings && req->type != SNMP_PDU_GETBULK){
1630		warnx("Bad number of bindings in response");
1631		return (-1);
1632	}
1633
1634	switch (req->type) {
1635		case SNMP_PDU_GET:
1636			return (snmp_parse_get_resp(resp,req));
1637		case SNMP_PDU_GETBULK:
1638			return (snmp_parse_getbulk_resp(resp,req));
1639		case SNMP_PDU_GETNEXT:
1640			return (snmp_parse_getnext_resp(resp,req));
1641		default:
1642			/* NOTREACHED */
1643			break;
1644	}
1645
1646	return (-2);
1647}
1648
1649static void
1650snmp_output_octetstring(struct snmp_toolinfo *snmptoolctx, enum snmp_tc tc,
1651    uint32_t len, uint8_t *octets)
1652{
1653	char *buf;
1654
1655	if (len == 0 || octets == NULL)
1656		return;
1657
1658	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1659		fprintf(stdout, "%s : ",
1660		    syntax_strings[SNMP_SYNTAX_OCTETSTRING].str);
1661
1662	if ((buf = snmp_oct2tc(tc, len, (char *) octets)) != NULL) {
1663		fprintf(stdout, "%s", buf);
1664		free(buf);
1665	}
1666}
1667
1668static void
1669snmp_output_octetindex(struct snmp_toolinfo *snmptoolctx, enum snmp_tc tc,
1670    struct asn_oid *oid)
1671{
1672	uint32_t i;
1673	uint8_t *s;
1674
1675	if ((s = malloc(oid->subs[0] + 1)) == NULL)
1676		syslog(LOG_ERR, "malloc failed - %s", strerror(errno));
1677	else {
1678		for (i = 0; i < oid->subs[0]; i++)
1679			s[i] = (u_char) (oid->subs[i + 1]);
1680
1681		snmp_output_octetstring(snmptoolctx, tc, oid->subs[0], s);
1682		free(s);
1683	}
1684}
1685
1686/*
1687 * Check and output syntax type and value.
1688 */
1689static void
1690snmp_output_oid_value(struct snmp_toolinfo *snmptoolctx, struct asn_oid *oid)
1691{
1692	char oid_string[ASN_OIDSTRLEN];
1693	struct snmp_object obj;
1694
1695	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1696		fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_OID].str);
1697
1698	if(!ISSET_NUMERIC(snmptoolctx)) {
1699		memset(&obj, 0, sizeof(struct snmp_object));
1700		asn_append_oid(&(obj.val.var), oid);
1701
1702		if (snmp_lookup_enumstring(snmptoolctx, &obj) > 0)
1703			fprintf(stdout, "%s" , obj.info->string);
1704		else if (snmp_lookup_oidstring(snmptoolctx, &obj) > 0)
1705			fprintf(stdout, "%s" , obj.info->string);
1706		else if (snmp_lookup_nodestring(snmptoolctx, &obj) > 0)
1707			fprintf(stdout, "%s" , obj.info->string);
1708		else {
1709			(void) asn_oid2str_r(oid, oid_string);
1710			fprintf(stdout, "%s", oid_string);
1711		}
1712	} else {
1713		(void) asn_oid2str_r(oid, oid_string);
1714		fprintf(stdout, "%s", oid_string);
1715	}
1716}
1717
1718static void
1719snmp_output_int(struct snmp_toolinfo *snmptoolctx, struct enum_pairs *enums,
1720    int32_t int_val)
1721{
1722	char *string;
1723
1724	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1725		fprintf(stdout, "%s : ",
1726		    syntax_strings[SNMP_SYNTAX_INTEGER].str);
1727
1728	if (enums != NULL && (string = enum_string_lookup(enums, int_val))
1729	    != NULL)
1730		fprintf(stdout, "%s", string);
1731	else
1732		fprintf(stdout, "%d", int_val);
1733}
1734
1735static void
1736snmp_output_ipaddress(struct snmp_toolinfo *snmptoolctx, uint8_t *ip)
1737{
1738	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1739		fprintf(stdout, "%s : ",
1740		    syntax_strings[SNMP_SYNTAX_IPADDRESS].str);
1741
1742	fprintf(stdout, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
1743}
1744
1745static void
1746snmp_output_counter(struct snmp_toolinfo *snmptoolctx, uint32_t counter)
1747{
1748	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1749		fprintf(stdout, "%s : ",
1750		    syntax_strings[SNMP_SYNTAX_COUNTER].str);
1751
1752	fprintf(stdout, "%u", counter);
1753}
1754
1755static void
1756snmp_output_gauge(struct snmp_toolinfo *snmptoolctx, uint32_t gauge)
1757{
1758	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1759		fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_GAUGE].str);
1760
1761	fprintf(stdout, "%u", gauge);
1762}
1763
1764static void
1765snmp_output_ticks(struct snmp_toolinfo *snmptoolctx, uint32_t ticks)
1766{
1767	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1768		fprintf(stdout, "%s : ",
1769		    syntax_strings[SNMP_SYNTAX_TIMETICKS].str);
1770
1771	fprintf(stdout, "%u", ticks);
1772}
1773
1774static void
1775snmp_output_counter64(struct snmp_toolinfo *snmptoolctx, uint64_t counter64)
1776{
1777	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1778		fprintf(stdout, "%s : ",
1779		    syntax_strings[SNMP_SYNTAX_COUNTER64].str);
1780
1781	fprintf(stdout,"%ju", counter64);
1782}
1783
1784int32_t
1785snmp_output_numval(struct snmp_toolinfo *snmptoolctx, struct snmp_value *val,
1786    struct snmp_oid2str *entry)
1787{
1788	if (val == NULL)
1789		return (-1);
1790
1791	if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET)
1792		fprintf(stdout, " = ");
1793
1794	switch (val->syntax) {
1795	    case SNMP_SYNTAX_INTEGER:
1796		if (entry != NULL)
1797			snmp_output_int(snmptoolctx, entry->snmp_enum,
1798			    val->v.integer);
1799		else
1800			snmp_output_int(snmptoolctx, NULL, val->v.integer);
1801		break;
1802
1803	    case SNMP_SYNTAX_OCTETSTRING:
1804		if (entry != NULL)
1805			snmp_output_octetstring(snmptoolctx, entry->tc,
1806			    val->v.octetstring.len, val->v.octetstring.octets);
1807		else
1808			snmp_output_octetstring(snmptoolctx, SNMP_STRING,
1809			    val->v.octetstring.len, val->v.octetstring.octets);
1810		break;
1811
1812	    case SNMP_SYNTAX_OID:
1813		snmp_output_oid_value(snmptoolctx, &(val->v.oid));
1814		break;
1815
1816	    case SNMP_SYNTAX_IPADDRESS:
1817		snmp_output_ipaddress(snmptoolctx, val->v.ipaddress);
1818		break;
1819
1820	    case SNMP_SYNTAX_COUNTER:
1821		snmp_output_counter(snmptoolctx, val->v.uint32);
1822		break;
1823
1824	    case SNMP_SYNTAX_GAUGE:
1825		snmp_output_gauge(snmptoolctx, val->v.uint32);
1826		break;
1827
1828	    case SNMP_SYNTAX_TIMETICKS:
1829		snmp_output_ticks(snmptoolctx, val->v.uint32);
1830		break;
1831
1832	    case SNMP_SYNTAX_COUNTER64:
1833		snmp_output_counter64(snmptoolctx, val->v.counter64);
1834		break;
1835
1836	    case SNMP_SYNTAX_NOSUCHOBJECT:
1837		fprintf(stdout, "No Such Object\n");
1838		return (val->syntax);
1839
1840	    case SNMP_SYNTAX_NOSUCHINSTANCE:
1841		fprintf(stdout, "No Such Instance\n");
1842		return (val->syntax);
1843
1844	    case SNMP_SYNTAX_ENDOFMIBVIEW:
1845		fprintf(stdout, "End of Mib View\n");
1846		return (val->syntax);
1847
1848	    case SNMP_SYNTAX_NULL:
1849		/* NOTREACHED */
1850		fprintf(stdout, "agent returned NULL Syntax\n");
1851		return (val->syntax);
1852
1853	    default:
1854		/* NOTREACHED - If here - then all went completely wrong. */
1855		fprintf(stdout, "agent returned unknown syntax\n");
1856		return (-1);
1857	}
1858
1859	fprintf(stdout, "\n");
1860
1861	return (0);
1862}
1863
1864static int32_t
1865snmp_fill_object(struct snmp_toolinfo *snmptoolctx, struct snmp_object *obj,
1866    struct snmp_value *val)
1867{
1868	int32_t rc;
1869	asn_subid_t suboid;
1870
1871	if (obj == NULL || val == NULL)
1872		return (-1);
1873
1874	if ((suboid = snmp_suboid_pop(&(val->var))) > ASN_MAXID)
1875		return (-1);
1876
1877	memset(obj, 0, sizeof(struct snmp_object));
1878	asn_append_oid(&(obj->val.var), &(val->var));
1879	obj->val.syntax = val->syntax;
1880
1881	if (obj->val.syntax > 0)
1882		rc = snmp_lookup_leafstring(snmptoolctx, obj);
1883	else
1884		rc = snmp_lookup_nonleaf_string(snmptoolctx, obj);
1885
1886	(void) snmp_suboid_append(&(val->var), suboid);
1887	(void) snmp_suboid_append(&(obj->val.var), suboid);
1888
1889	return (rc);
1890}
1891
1892static int32_t
1893snmp_output_index(struct snmp_toolinfo *snmptoolctx, struct index *stx,
1894    struct asn_oid *oid)
1895{
1896	uint8_t ip[4];
1897	uint32_t bytes = 1;
1898	uint64_t cnt64;
1899	struct asn_oid temp, out;
1900
1901	if (oid->len < bytes)
1902		return (-1);
1903
1904	memset(&temp, 0, sizeof(struct asn_oid));
1905	asn_append_oid(&temp, oid);
1906
1907	switch (stx->syntax) {
1908	    case SNMP_SYNTAX_INTEGER:
1909		snmp_output_int(snmptoolctx, stx->snmp_enum, temp.subs[0]);
1910		break;
1911
1912	    case SNMP_SYNTAX_OCTETSTRING:
1913		if ((temp.subs[0] > temp.len -1 ) || (temp.subs[0] >
1914		    ASN_MAXOCTETSTRING))
1915			return (-1);
1916		snmp_output_octetindex(snmptoolctx, stx->tc, &temp);
1917		bytes += temp.subs[0];
1918		break;
1919
1920	    case SNMP_SYNTAX_OID:
1921		if ((temp.subs[0] > temp.len -1) || (temp.subs[0] >
1922		    ASN_MAXOIDLEN))
1923			return (-1);
1924
1925		bytes += temp.subs[0];
1926		memset(&out, 0, sizeof(struct asn_oid));
1927		asn_slice_oid(&out, &temp, 1, bytes);
1928		snmp_output_oid_value(snmptoolctx, &out);
1929		break;
1930
1931	    case SNMP_SYNTAX_IPADDRESS:
1932		if (temp.len < 4)
1933			return (-1);
1934		for (bytes = 0; bytes < 4; bytes++)
1935			ip[bytes] = temp.subs[bytes];
1936
1937		snmp_output_ipaddress(snmptoolctx, ip);
1938		bytes = 4;
1939		break;
1940
1941	    case SNMP_SYNTAX_COUNTER:
1942		snmp_output_counter(snmptoolctx, temp.subs[0]);
1943		break;
1944
1945	    case SNMP_SYNTAX_GAUGE:
1946		snmp_output_gauge(snmptoolctx, temp.subs[0]);
1947		break;
1948
1949	    case SNMP_SYNTAX_TIMETICKS:
1950		snmp_output_ticks(snmptoolctx, temp.subs[0]);
1951		break;
1952
1953	    case SNMP_SYNTAX_COUNTER64:
1954		if (oid->len < 2)
1955			return (-1);
1956		bytes = 2;
1957		memcpy(&cnt64, temp.subs, bytes);
1958		snmp_output_counter64(snmptoolctx, cnt64);
1959		break;
1960
1961	    default:
1962		return (-1);
1963	}
1964
1965	return (bytes);
1966}
1967
1968static int32_t
1969snmp_output_object(struct snmp_toolinfo *snmptoolctx, struct snmp_object *o)
1970{
1971	int32_t i, first, len;
1972	struct asn_oid oid;
1973	struct index *temp;
1974
1975	if (ISSET_NUMERIC(snmptoolctx))
1976		return (-1);
1977
1978	if (o->info->table_idx == NULL) {
1979		fprintf(stdout,"%s.%d", o->info->string,
1980		    o->val.var.subs[o->val.var.len - 1]);
1981		return (1);
1982	}
1983
1984	fprintf(stdout,"%s[", o->info->string);
1985	memset(&oid, 0, sizeof(struct asn_oid));
1986
1987	len = 1;
1988	asn_slice_oid(&oid, &(o->val.var), (o->info->table_idx->var.len + len),
1989	    o->val.var.len);
1990
1991	first = 1;
1992	STAILQ_FOREACH(temp, &(OBJECT_IDX_LIST(o)), link) {
1993		if(first)
1994			first = 0;
1995		else
1996			fprintf(stdout, ", ");
1997		if ((i = snmp_output_index(snmptoolctx, temp, &oid)) < 0)
1998			break;
1999		len += i;
2000		memset(&oid, 0, sizeof(struct asn_oid));
2001		asn_slice_oid(&oid, &(o->val.var),
2002		    (o->info->table_idx->var.len + len), o->val.var.len + 1);
2003	}
2004
2005	fprintf(stdout,"]");
2006	return (1);
2007}
2008
2009void
2010snmp_output_err_resp(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu)
2011{
2012	char buf[ASN_OIDSTRLEN];
2013	struct snmp_object object;
2014
2015	if (pdu == NULL || (pdu->error_index > (int32_t) pdu->nbindings)) {
2016		fprintf(stdout,"Invalid error index in PDU\n");
2017		return;
2018	}
2019
2020	fprintf(stdout, "Agent %s:%s returned error \n", snmp_client.chost,
2021	    snmp_client.cport);
2022
2023	if (!ISSET_NUMERIC(snmptoolctx) && (snmp_fill_object(snmptoolctx, &object,
2024	    &(pdu->bindings[pdu->error_index - 1])) > 0))
2025		snmp_output_object(snmptoolctx, &object);
2026	else {
2027		asn_oid2str_r(&(pdu->bindings[pdu->error_index - 1].var), buf);
2028		fprintf(stdout,"%s", buf);
2029	}
2030
2031	fprintf(stdout," caused error - ");
2032	if ((pdu->error_status > 0) && (pdu->error_status <=
2033	    SNMP_ERR_INCONS_NAME))
2034		fprintf(stdout, "%s\n", error_strings[pdu->error_status].str);
2035	else
2036		fprintf(stdout,"%s\n", error_strings[SNMP_ERR_UNKNOWN].str);
2037}
2038
2039int32_t
2040snmp_output_resp(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu,
2041    struct asn_oid *root)
2042{
2043	int32_t error;
2044	char p[ASN_OIDSTRLEN];
2045	uint32_t i;
2046	struct snmp_object object;
2047
2048	i = error = 0;
2049	while (i < pdu->nbindings) {
2050		if (root != NULL && !(asn_is_suboid(root,
2051		    &(pdu->bindings[i].var))))
2052			break;
2053
2054		if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET) {
2055			if (!ISSET_NUMERIC(snmptoolctx) &&
2056			    (snmp_fill_object(snmptoolctx, &object,
2057			    &(pdu->bindings[i])) > 0))
2058				snmp_output_object(snmptoolctx, &object);
2059			else {
2060				asn_oid2str_r(&(pdu->bindings[i].var), p);
2061				fprintf(stdout, "%s", p);
2062			}
2063		}
2064		error |= snmp_output_numval(snmptoolctx, &(pdu->bindings[i]), object.info);
2065		i++;
2066	}
2067
2068	if (error)
2069		return (-1);
2070
2071	return (i);
2072}
2073
2074void
2075snmp_output_engine(void)
2076{
2077	uint32_t i;
2078	char *cptr, engine[2 * SNMP_ENGINE_ID_SIZ + 2];
2079
2080	cptr = engine;
2081	for (i = 0; i < snmp_client.engine.engine_len; i++)
2082		cptr += sprintf(cptr, "%.2x", snmp_client.engine.engine_id[i]);
2083	*cptr++ = '\0';
2084
2085	fprintf(stdout, "Engine ID 0x%s\n", engine);
2086	fprintf(stdout, "Boots : %u\t\tTime : %d\n",
2087	    snmp_client.engine.engine_boots,
2088	    snmp_client.engine.engine_time);
2089}
2090
2091void
2092snmp_output_keys(void)
2093{
2094	uint32_t i, keylen = 0;
2095	char *cptr, extkey[2 * SNMP_AUTH_KEY_SIZ + 2];
2096
2097	fprintf(stdout, "Localized keys for %s\n", snmp_client.user.sec_name);
2098	if (snmp_client.user.auth_proto == SNMP_AUTH_HMAC_MD5) {
2099		fprintf(stdout, "MD5 : 0x");
2100		keylen = SNMP_AUTH_HMACMD5_KEY_SIZ;
2101	} else if (snmp_client.user.auth_proto == SNMP_AUTH_HMAC_SHA) {
2102		fprintf(stdout, "SHA : 0x");
2103		keylen = SNMP_AUTH_HMACSHA_KEY_SIZ;
2104	}
2105	if (snmp_client.user.auth_proto != SNMP_AUTH_NOAUTH) {
2106		cptr = extkey;
2107		for (i = 0; i < keylen; i++)
2108			cptr += sprintf(cptr, "%.2x",
2109			    snmp_client.user.auth_key[i]);
2110		*cptr++ = '\0';
2111		fprintf(stdout, "%s\n", extkey);
2112	}
2113
2114	if (snmp_client.user.priv_proto == SNMP_PRIV_DES) {
2115		fprintf(stdout, "DES : 0x");
2116		keylen = SNMP_PRIV_DES_KEY_SIZ;
2117	} else if (snmp_client.user.priv_proto == SNMP_PRIV_AES) {
2118		fprintf(stdout, "AES : 0x");
2119		keylen = SNMP_PRIV_AES_KEY_SIZ;
2120	}
2121	if (snmp_client.user.priv_proto != SNMP_PRIV_NOPRIV) {
2122		cptr = extkey;
2123		for (i = 0; i < keylen; i++)
2124			cptr += sprintf(cptr, "%.2x",
2125			    snmp_client.user.priv_key[i]);
2126		*cptr++ = '\0';
2127		fprintf(stdout, "%s\n", extkey);
2128	}
2129}
2130