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