bsnmptools.c revision 311590
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 311590 2017-01-07 08:28:41Z 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 = strdup(filename)) == NULL) {
255		warnx("strdup() failed - %s", strerror(errno));
256		return (-1);
257	}
258
259	if ((entry = calloc(1, sizeof(struct fname))) == NULL) {
260		warnx("calloc() failed - %s", strerror(errno));
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				warnx("Bad 'engine-boots' value %s - %s", val,
672				    strerror(errno));
673				errno = saved_errno;
674				return (-1);
675			}
676			errno = saved_errno;
677			break;
678		case 2:
679			if (val == NULL) {
680				warnx("Suboption 'engine-time' - no argument");
681				return (-1);
682			}
683			saved_errno = errno;
684			errno = 0;
685			snmp_client.engine.engine_time = strtoul(val, NULL, 10);
686			if (errno != 0) {
687				warnx("Bad 'engine-time' value %s - %s", val,
688				    strerror(errno));
689				errno = saved_errno;
690				return (-1);
691			}
692			errno = saved_errno;
693			break;
694		case 3:
695			strlcpy(snmp_client.user.sec_name, val,
696			    SNMP_ADM_STR32_SIZ);
697			break;
698		default:
699			warnx("Unknown suboption - '%s'", suboptarg);
700			return (-1);
701		}
702		count += 1;
703	}
704	return (2/* count */);
705}
706
707int32_t
708parse_file(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
709{
710	assert(opt_arg != NULL);
711
712	if (parse_flist(snmptoolctx, opt_arg, NULL, &IsoOrgDod_OID) < 0)
713		return (-1);
714
715	return (2);
716}
717
718int32_t
719parse_include(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
720{
721	char path[MAXPATHLEN + 1];
722	int32_t cut_dflt, len, subopt;
723	struct asn_oid cut;
724	char *val, *option;
725	const char *const subopts[] = {
726		"cut",
727		"path",
728		"file",
729		NULL
730	};
731
732#define	INC_CUT		0
733#define	INC_PATH	1
734#define	INC_LIST	2
735
736	assert(opt_arg != NULL);
737
738	/* if (opt == 'i')
739		free_filelist(snmptoolctx, ); */
740	/*
741	 * This function should be called only after getopt(3) - otherwise if
742	 * no previous validation of opt_arg strlen() may not return what is
743	 * expected.
744	 */
745
746	path[0] = '\0';
747	memset(&cut, 0, sizeof(struct asn_oid));
748	cut_dflt = -1;
749
750	while ((subopt = getsubopt1(&opt_arg, subopts, &val, &option)) != EOF) {
751		switch (subopt) {
752		    case INC_CUT:
753			if (val == NULL) {
754				warnx("Suboption 'cut' requires an argument");
755				return (-1);
756			} else {
757				if (snmp_parse_numoid(val, &cut) < 0)
758					return (-1);
759			}
760			cut_dflt = 1;
761			break;
762
763		    case INC_PATH:
764			if ((len = parse_path(val)) < 0)
765				return (-1);
766			strlcpy(path, val, len + 1);
767			break;
768
769		    case INC_LIST:
770			if (val == NULL)
771				return (-1);
772			if (cut_dflt == -1)
773				len = parse_flist(snmptoolctx, val, path, &IsoOrgDod_OID);
774			else
775				len = parse_flist(snmptoolctx, val, path, &cut);
776			if (len < 0)
777				return (-1);
778			break;
779
780		    default:
781			warnx("Unknown suboption - '%s'", suboptarg);
782			return (-1);
783		}
784	}
785
786	/* XXX: Fix me - returning two is wrong here */
787	return (2);
788}
789
790int32_t
791parse_server(char *opt_arg)
792{
793	assert(opt_arg != NULL);
794
795	if (snmp_parse_server(&snmp_client, opt_arg) < 0)
796		return (-1);
797
798	if (snmp_client.trans > SNMP_TRANS_UDP && snmp_client.chost == NULL) {
799		if ((snmp_client.chost = malloc(strlen(SNMP_DEFAULT_LOCAL) + 1))
800		    == NULL) {
801			syslog(LOG_ERR, "malloc() failed: %s", strerror(errno));
802			return (-1);
803		}
804		strcpy(snmp_client.chost, SNMP_DEFAULT_LOCAL);
805	}
806
807	return (2);
808}
809
810int32_t
811parse_timeout(char *opt_arg)
812{
813	int32_t v, saved_errno;
814
815	assert(opt_arg != NULL);
816
817	saved_errno = errno;
818	errno = 0;
819
820	v = strtol(opt_arg, NULL, 10);
821	if (errno != 0) {
822		warnx( "Error parsing timeout value - %s", strerror(errno));
823		errno = saved_errno;
824		return (-1);
825	}
826
827	snmp_client.timeout.tv_sec = v;
828	errno = saved_errno;
829	return (2);
830}
831
832int32_t
833parse_retry(char *opt_arg)
834{
835	uint32_t v;
836	int32_t saved_errno;
837
838	assert(opt_arg != NULL);
839
840	saved_errno = errno;
841	errno = 0;
842
843	v = strtoul(opt_arg, NULL, 10);
844	if (errno != 0) {
845		warnx("Error parsing retries count - %s", strerror(errno));
846		errno = saved_errno;
847		return (-1);
848	}
849
850	snmp_client.retries = v;
851	errno = saved_errno;
852	return (2);
853}
854
855int32_t
856parse_version(char *opt_arg)
857{
858	uint32_t v;
859	int32_t saved_errno;
860
861	assert(opt_arg != NULL);
862
863	saved_errno = errno;
864	errno = 0;
865
866	v = strtoul(opt_arg, NULL, 10);
867	if (errno != 0) {
868		warnx("Error parsing version - %s", strerror(errno));
869		errno = saved_errno;
870		return (-1);
871	}
872
873	switch (v) {
874		case 1:
875			snmp_client.version = SNMP_V1;
876			break;
877		case 2:
878			snmp_client.version = SNMP_V2c;
879			break;
880		case 3:
881			snmp_client.version = SNMP_V3;
882			break;
883		default:
884			warnx("Unsupported SNMP version - %u", v);
885			errno = saved_errno;
886			return (-1);
887	}
888
889	errno = saved_errno;
890	return (2);
891}
892
893int32_t
894parse_local_path(char *opt_arg)
895{
896	assert(opt_arg != NULL);
897
898	if (sizeof(opt_arg) > sizeof(SNMP_LOCAL_PATH)) {
899		warnx("Filename too long - %s", opt_arg);
900		return (-1);
901	}
902
903	strlcpy(snmp_client.local_path, opt_arg, sizeof(SNMP_LOCAL_PATH));
904	return (2);
905}
906
907int32_t
908parse_buflen(char *opt_arg)
909{
910	uint32_t size;
911	int32_t saved_errno;
912
913	assert(opt_arg != NULL);
914
915	saved_errno = errno;
916	errno = 0;
917
918	size = strtoul(opt_arg, NULL, 10);
919	if (errno != 0) {
920		warnx("Error parsing buffer size - %s", strerror(errno));
921		errno = saved_errno;
922		return (-1);
923	}
924
925	if (size > MAX_BUFF_SIZE) {
926		warnx("Buffer size too big - %d max allowed", MAX_BUFF_SIZE);
927		errno = saved_errno;
928		return (-1);
929	}
930
931	snmp_client.txbuflen = snmp_client.rxbuflen = size;
932	errno = saved_errno;
933	return (2);
934}
935
936int32_t
937parse_debug(void)
938{
939	snmp_client.dump_pdus = 1;
940	return (1);
941}
942
943int32_t
944parse_discovery(struct snmp_toolinfo *snmptoolctx)
945{
946	SET_EDISCOVER(snmptoolctx);
947	snmp_client.version = SNMP_V3;
948	return (1);
949}
950
951int32_t
952parse_local_key(struct snmp_toolinfo *snmptoolctx)
953{
954	SET_LOCALKEY(snmptoolctx);
955	snmp_client.version = SNMP_V3;
956	return (1);
957}
958
959int32_t
960parse_num_oids(struct snmp_toolinfo *snmptoolctx)
961{
962	SET_NUMERIC(snmptoolctx);
963	return (1);
964}
965
966int32_t
967parse_output(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
968{
969	assert(opt_arg != NULL);
970
971	if (strlen(opt_arg) > strlen("verbose")) {
972		warnx( "Invalid output option - %s",opt_arg);
973		return (-1);
974	}
975
976	if (strncasecmp(opt_arg, "short", strlen(opt_arg)) == 0)
977		SET_OUTPUT(snmptoolctx, OUTPUT_SHORT);
978	else if (strncasecmp(opt_arg, "verbose", strlen(opt_arg)) == 0)
979		SET_OUTPUT(snmptoolctx, OUTPUT_VERBOSE);
980	else if (strncasecmp(opt_arg,"tabular", strlen(opt_arg)) == 0)
981		SET_OUTPUT(snmptoolctx, OUTPUT_TABULAR);
982	else if (strncasecmp(opt_arg, "quiet", strlen(opt_arg)) == 0)
983		SET_OUTPUT(snmptoolctx, OUTPUT_QUIET);
984	else {
985		warnx( "Invalid output option - %s", opt_arg);
986		return (-1);
987	}
988
989	return (2);
990}
991
992int32_t
993parse_errors(struct snmp_toolinfo *snmptoolctx)
994{
995	SET_RETRY(snmptoolctx);
996	return (1);
997}
998
999int32_t
1000parse_skip_access(struct snmp_toolinfo *snmptoolctx)
1001{
1002	SET_ERRIGNORE(snmptoolctx);
1003	return (1);
1004}
1005
1006char *
1007snmp_parse_suboid(char *str, struct asn_oid *oid)
1008{
1009	char *endptr;
1010	asn_subid_t suboid;
1011
1012	if (*str == '.')
1013		str++;
1014
1015	if (*str < '0' || *str > '9')
1016		return (str);
1017
1018	do {
1019		suboid = strtoul(str, &endptr, 10);
1020		if ((asn_subid_t) suboid > ASN_MAXID) {
1021			warnx("Suboid %u > ASN_MAXID", suboid);
1022			return (NULL);
1023		}
1024		if (snmp_suboid_append(oid, suboid) < 0)
1025			return (NULL);
1026		str = endptr + 1;
1027	} while (*endptr == '.');
1028
1029	return (endptr);
1030}
1031
1032static char *
1033snmp_int2asn_oid(char *str, struct asn_oid *oid)
1034{
1035	char *endptr;
1036	int32_t v, saved_errno;
1037
1038	saved_errno = errno;
1039	errno = 0;
1040
1041	v = strtol(str, &endptr, 10);
1042	if (errno != 0) {
1043		warnx("Integer value %s not supported - %s", str,
1044		    strerror(errno));
1045		errno = saved_errno;
1046		return (NULL);
1047	}
1048	errno = saved_errno;
1049
1050	if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
1051		return (NULL);
1052
1053	return (endptr);
1054}
1055
1056/* It is a bit weird to have a table indexed by OID but still... */
1057static char *
1058snmp_oid2asn_oid(struct snmp_toolinfo *snmptoolctx, char *str,
1059    struct asn_oid *oid)
1060{
1061	int32_t i;
1062	char string[MAXSTR + 1], *endptr;
1063	struct snmp_object obj;
1064
1065	for (i = 0; i < MAXSTR; i++)
1066		if (isalpha (*(str + i)) == 0)
1067			break;
1068
1069	endptr = str + i;
1070	memset(&obj, 0, sizeof(struct snmp_object));
1071	if (i == 0) {
1072		if ((endptr = snmp_parse_suboid(str, &(obj.val.var))) == NULL)
1073			return (NULL);
1074		if (snmp_suboid_append(oid, (asn_subid_t) obj.val.var.len) < 0)
1075			return (NULL);
1076	} else {
1077		strlcpy(string, str, i + 1);
1078		if (snmp_lookup_enumoid(snmptoolctx, &obj, string) < 0) {
1079			warnx("Unknown string - %s", string);
1080			return (NULL);
1081		}
1082	}
1083
1084	asn_append_oid(oid, &(obj.val.var));
1085	return (endptr);
1086}
1087
1088static char *
1089snmp_ip2asn_oid(char *str, struct asn_oid *oid)
1090{
1091	uint32_t v;
1092	int32_t i;
1093	char *endptr, *ptr;
1094
1095	ptr = str;
1096
1097	for (i = 0; i < 4; i++) {
1098		v = strtoul(ptr, &endptr, 10);
1099		if (v > 0xff)
1100			return (NULL);
1101		if (*endptr != '.' && strchr("],\0", *endptr) == NULL && i != 3)
1102			return (NULL);
1103		if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
1104			return (NULL);
1105		ptr = endptr + 1;
1106	}
1107
1108	return (endptr);
1109}
1110
1111/* 32-bit counter, gauge, timeticks. */
1112static char *
1113snmp_uint2asn_oid(char *str, struct asn_oid *oid)
1114{
1115	char *endptr;
1116	uint32_t v;
1117	int32_t saved_errno;
1118
1119	saved_errno = errno;
1120	errno = 0;
1121
1122	v = strtoul(str, &endptr, 10);
1123	if (errno != 0) {
1124		warnx("Integer value %s not supported - %s\n", str,
1125		    strerror(errno));
1126		errno = saved_errno;
1127		return (NULL);
1128	}
1129	errno = saved_errno;
1130	if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
1131		return (NULL);
1132
1133	return (endptr);
1134}
1135
1136static char *
1137snmp_cnt64_2asn_oid(char *str, struct asn_oid *oid)
1138{
1139	char *endptr;
1140	uint64_t v;
1141	int32_t saved_errno;
1142
1143	saved_errno = errno;
1144	errno = 0;
1145
1146	v = strtoull(str, &endptr, 10);
1147
1148	if (errno != 0) {
1149		warnx("Integer value %s not supported - %s", str,
1150		    strerror(errno));
1151		errno = saved_errno;
1152		return (NULL);
1153	}
1154	errno = saved_errno;
1155	if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xffffffff)) < 0)
1156		return (NULL);
1157
1158	if (snmp_suboid_append(oid, (asn_subid_t) (v >> 32)) < 0)
1159		return (NULL);
1160
1161	return (endptr);
1162}
1163
1164enum snmp_syntax
1165parse_syntax(char *str)
1166{
1167	int32_t i;
1168
1169	for (i = 0; i < SNMP_SYNTAX_UNKNOWN; i++) {
1170		if (strncmp(syntax_strings[i].str, str,
1171		    strlen(syntax_strings[i].str)) == 0)
1172			return (syntax_strings[i].stx);
1173	}
1174
1175	return (SNMP_SYNTAX_NULL);
1176}
1177
1178static char *
1179snmp_parse_subindex(struct snmp_toolinfo *snmptoolctx, char *str,
1180    struct index *idx, struct snmp_object *object)
1181{
1182	char *ptr;
1183	int32_t i;
1184	enum snmp_syntax stx;
1185	char syntax[MAX_CMD_SYNTAX_LEN];
1186
1187	ptr = str;
1188	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) {
1189		for (i = 0; i < MAX_CMD_SYNTAX_LEN ; i++) {
1190			if (*(ptr + i) == ':')
1191				break;
1192		}
1193
1194		if (i >= MAX_CMD_SYNTAX_LEN) {
1195			warnx("Unknown syntax in OID - %s", str);
1196			return (NULL);
1197		}
1198		/* Expect a syntax string here. */
1199		if ((stx = parse_syntax(str)) <= SNMP_SYNTAX_NULL) {
1200			warnx("Invalid  syntax - %s",syntax);
1201			return (NULL);
1202		}
1203
1204		if (stx != idx->syntax && !ISSET_ERRIGNORE(snmptoolctx)) {
1205			warnx("Syntax mismatch - %d expected, %d given",
1206			    idx->syntax, stx);
1207			return (NULL);
1208		}
1209		/*
1210		 * That is where the suboid started + the syntax length + one
1211		 * character for ':'.
1212		 */
1213		ptr = str + i + 1;
1214	} else
1215		stx = idx->syntax;
1216
1217	switch (stx) {
1218		case SNMP_SYNTAX_INTEGER:
1219			return (snmp_int2asn_oid(ptr, &(object->val.var)));
1220		case SNMP_SYNTAX_OID:
1221			return (snmp_oid2asn_oid(snmptoolctx, ptr,
1222			    &(object->val.var)));
1223		case SNMP_SYNTAX_IPADDRESS:
1224			return (snmp_ip2asn_oid(ptr, &(object->val.var)));
1225		case SNMP_SYNTAX_COUNTER:
1226			/* FALLTHROUGH */
1227		case SNMP_SYNTAX_GAUGE:
1228			/* FALLTHROUGH */
1229		case SNMP_SYNTAX_TIMETICKS:
1230			return (snmp_uint2asn_oid(ptr, &(object->val.var)));
1231		case SNMP_SYNTAX_COUNTER64:
1232			return (snmp_cnt64_2asn_oid(ptr, &(object->val.var)));
1233		case SNMP_SYNTAX_OCTETSTRING:
1234			return (snmp_tc2oid(idx->tc, ptr, &(object->val.var)));
1235		default:
1236			/* NOTREACHED */
1237			break;
1238	}
1239
1240	return (NULL);
1241}
1242
1243char *
1244snmp_parse_index(struct snmp_toolinfo *snmptoolctx, char *str,
1245    struct snmp_object *object)
1246{
1247	char *ptr;
1248	struct index *temp;
1249
1250	if (object->info->table_idx == NULL)
1251		return (NULL);
1252
1253	ptr = NULL;
1254	STAILQ_FOREACH(temp, &(OBJECT_IDX_LIST(object)), link) {
1255		if ((ptr = snmp_parse_subindex(snmptoolctx, str, temp, object))
1256		    == NULL)
1257			return (NULL);
1258
1259		if (*ptr != ',' && *ptr != ']')
1260			return (NULL);
1261		str = ptr + 1;
1262	}
1263
1264	if (ptr == NULL || *ptr != ']') {
1265		warnx("Mismatching index - %s", str);
1266		return (NULL);
1267	}
1268
1269	return (ptr + 1);
1270}
1271
1272/*
1273 * Fill in the struct asn_oid member of snmp_value with suboids from input.
1274 * If an error occurs - print message on stderr and return (-1).
1275 * If all is ok - return the length of the oid.
1276 */
1277int32_t
1278snmp_parse_numoid(char *argv, struct asn_oid *var)
1279{
1280	char *endptr, *str;
1281	asn_subid_t suboid;
1282
1283	str = argv;
1284
1285	if (*str == '.')
1286		str++;
1287
1288	do {
1289		if (var->len == ASN_MAXOIDLEN) {
1290			warnx("Oid too long - %u", var->len);
1291			return (-1);
1292		}
1293
1294		suboid = strtoul(str, &endptr, 10);
1295		if (suboid > ASN_MAXID) {
1296			warnx("Oid too long - %u", var->len);
1297			return (-1);
1298		}
1299
1300		var->subs[var->len++] = suboid;
1301		str = endptr + 1;
1302	} while ( *endptr == '.');
1303
1304	if (*endptr != '\0') {
1305		warnx("Invalid oid string - %s", argv);
1306		return (-1);
1307	}
1308
1309	return (var->len);
1310}
1311
1312/* Append a length 1 suboid to an asn_oid structure. */
1313int32_t
1314snmp_suboid_append(struct asn_oid *var, asn_subid_t suboid)
1315{
1316	if (var == NULL)
1317		return (-1);
1318
1319	if (var->len >= ASN_MAXOIDLEN) {
1320		warnx("Oid too long - %u", var->len);
1321		return (-1);
1322	}
1323
1324	var->subs[var->len++] = suboid;
1325
1326	return (1);
1327}
1328
1329/* Pop the last suboid from an asn_oid structure. */
1330int32_t
1331snmp_suboid_pop(struct asn_oid *var)
1332{
1333	asn_subid_t suboid;
1334
1335	if (var == NULL)
1336		return (-1);
1337
1338	if (var->len < 1)
1339		return (-1);
1340
1341	suboid = var->subs[--(var->len)];
1342	var->subs[var->len] = 0;
1343
1344	return (suboid);
1345}
1346
1347/*
1348 * Parse the command-line provided string into an OID - alocate memory for a new
1349 * snmp object, fill in its fields and insert it in the object list. A
1350 * (snmp_verify_inoid_f) function must be provided to validate the input string.
1351 */
1352int32_t
1353snmp_object_add(struct snmp_toolinfo *snmptoolctx, snmp_verify_inoid_f func,
1354    char *string)
1355{
1356	struct snmp_object *obj;
1357
1358	if (snmptoolctx == NULL)
1359		return (-1);
1360
1361	/* XXX-BZ does that chack make sense? */
1362	if (snmptoolctx->objects >= SNMP_MAX_BINDINGS) {
1363		warnx("Too many bindings in PDU - %u", snmptoolctx->objects + 1);
1364		return (-1);
1365	}
1366
1367	if ((obj = calloc(1, sizeof(struct snmp_object))) == NULL) {
1368		syslog(LOG_ERR, "malloc() failed: %s", strerror(errno));
1369		return (-1);
1370	}
1371
1372	if (func(snmptoolctx, obj, string) < 0) {
1373		warnx("Invalid OID - %s", string);
1374		free(obj);
1375		return (-1);
1376	}
1377
1378	snmptoolctx->objects++;
1379	SLIST_INSERT_HEAD(&snmptoolctx->snmp_objectlist, obj, link);
1380
1381	return (1);
1382}
1383
1384/* Given an OID, find it in the object list and remove it. */
1385int32_t
1386snmp_object_remove(struct snmp_toolinfo *snmptoolctx, struct asn_oid *oid)
1387{
1388	struct snmp_object *temp;
1389
1390	if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist)) {
1391		warnx("Object list already empty");
1392		return (-1);
1393	}
1394
1395
1396	SLIST_FOREACH(temp, &snmptoolctx->snmp_objectlist, link)
1397		if (asn_compare_oid(&(temp->val.var), oid) == 0)
1398			break;
1399
1400	if (temp == NULL) {
1401		warnx("No such object in list");
1402		return (-1);
1403	}
1404
1405	SLIST_REMOVE(&snmptoolctx->snmp_objectlist, temp, snmp_object, link);
1406	if (temp->val.syntax == SNMP_SYNTAX_OCTETSTRING &&
1407	    temp->val.v.octetstring.octets != NULL)
1408		free(temp->val.v.octetstring.octets);
1409	free(temp);
1410
1411	return (1);
1412}
1413
1414static void
1415snmp_object_freeall(struct snmp_toolinfo *snmptoolctx)
1416{
1417	struct snmp_object *o;
1418
1419	while ((o = SLIST_FIRST(&snmptoolctx->snmp_objectlist)) != NULL) {
1420		SLIST_REMOVE_HEAD(&snmptoolctx->snmp_objectlist, link);
1421
1422		if (o->val.syntax == SNMP_SYNTAX_OCTETSTRING &&
1423		    o->val.v.octetstring.octets != NULL)
1424			free(o->val.v.octetstring.octets);
1425		free(o);
1426	}
1427}
1428
1429/* Do all possible memory release before exit. */
1430void
1431snmp_tool_freeall(struct snmp_toolinfo *snmptoolctx)
1432{
1433	if (snmp_client.chost != NULL) {
1434		free(snmp_client.chost);
1435		snmp_client.chost = NULL;
1436	}
1437
1438	if (snmp_client.cport != NULL) {
1439		free(snmp_client.cport);
1440		snmp_client.cport = NULL;
1441	}
1442
1443	snmp_mapping_free(snmptoolctx);
1444	free_filelist(snmptoolctx);
1445	snmp_object_freeall(snmptoolctx);
1446
1447	if (snmptoolctx->passwd != NULL) {
1448		free(snmptoolctx->passwd);
1449		snmptoolctx->passwd = NULL;
1450	}
1451}
1452
1453/*
1454 * Fill all variables from the object list into a PDU. (snmp_verify_vbind_f)
1455 * function should check whether the variable is consistent in this PDU
1456 * (e.g do not add non-leaf OIDs to a GET PDU, or OIDs with read access only to
1457 * a SET PDU) - might be NULL though. (snmp_add_vbind_f) function is the
1458 * function actually adds the variable to the PDU and must not be NULL.
1459 */
1460int32_t
1461snmp_pdu_add_bindings(struct snmp_toolinfo *snmptoolctx,
1462    snmp_verify_vbind_f vfunc, snmp_add_vbind_f afunc,
1463    struct snmp_pdu *pdu, int32_t maxcount)
1464{
1465	int32_t nbindings, abind;
1466	struct snmp_object *obj;
1467
1468	if (pdu == NULL || afunc == NULL)
1469		return (-1);
1470
1471	/* Return 0 in case of no more work todo. */
1472	if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist))
1473		return (0);
1474
1475	if (maxcount < 0 || maxcount > SNMP_MAX_BINDINGS) {
1476		warnx("maxcount out of range: <0 || >SNMP_MAX_BINDINGS");
1477		return (-1);
1478	}
1479
1480	nbindings = 0;
1481	SLIST_FOREACH(obj, &snmptoolctx->snmp_objectlist, link) {
1482		if ((vfunc != NULL) && (vfunc(snmptoolctx, pdu, obj) < 0)) {
1483			nbindings = -1;
1484			break;
1485		}
1486		if ((abind = afunc(pdu, obj)) < 0) {
1487			nbindings = -1;
1488			break;
1489		}
1490
1491		if (abind > 0) {
1492			/* Do not put more varbindings than requested. */
1493			if (++nbindings >= maxcount)
1494				break;
1495		}
1496	}
1497
1498	return (nbindings);
1499}
1500
1501/*
1502 * Locate an object in the object list and set a corresponding error status.
1503 */
1504int32_t
1505snmp_object_seterror(struct snmp_toolinfo *snmptoolctx,
1506    struct snmp_value *err_value, int32_t error_status)
1507{
1508	struct snmp_object *obj;
1509
1510	if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist) || err_value == NULL)
1511		return (-1);
1512
1513	SLIST_FOREACH(obj, &snmptoolctx->snmp_objectlist, link)
1514		if (asn_compare_oid(&(err_value->var), &(obj->val.var)) == 0) {
1515			obj->error = error_status;
1516			return (1);
1517		}
1518
1519	return (0);
1520}
1521
1522/*
1523 * Check a PDU received in response to a SNMP_PDU_GET/SNMP_PDU_GETBULK request
1524 * but don't compare syntaxes - when sending a request PDU they must be null.
1525 * This is a (almost) complete copy of snmp_pdu_check() - with matching syntaxes
1526 * checks and some other checks skipped.
1527 */
1528int32_t
1529snmp_parse_get_resp(struct snmp_pdu *resp, struct snmp_pdu *req)
1530{
1531	uint32_t i;
1532
1533	for (i = 0; i < req->nbindings; i++) {
1534		if (asn_compare_oid(&req->bindings[i].var,
1535		    &resp->bindings[i].var) != 0) {
1536			warnx("Bad OID in response");
1537			return (-1);
1538		}
1539
1540		if (snmp_client.version != SNMP_V1 && (resp->bindings[i].syntax
1541		    == SNMP_SYNTAX_NOSUCHOBJECT || resp->bindings[i].syntax ==
1542		    SNMP_SYNTAX_NOSUCHINSTANCE))
1543			return (0);
1544	}
1545
1546	return (1);
1547}
1548
1549int32_t
1550snmp_parse_getbulk_resp(struct snmp_pdu *resp, struct snmp_pdu *req)
1551{
1552	int32_t N, R, M, r;
1553
1554	if (req->error_status > (int32_t) resp->nbindings) {
1555		warnx("Bad number of bindings in response");
1556		return (-1);
1557	}
1558
1559	for (N = 0; N < req->error_status; N++) {
1560		if (asn_is_suboid(&req->bindings[N].var,
1561		    &resp->bindings[N].var) == 0)
1562			return (0);
1563		if (resp->bindings[N].syntax == SNMP_SYNTAX_ENDOFMIBVIEW)
1564			return (0);
1565	}
1566
1567	for (R = N , r = N; R  < (int32_t) req->nbindings; R++) {
1568		for (M = 0; M < req->error_index && (r + M) <
1569		    (int32_t) resp->nbindings; M++) {
1570			if (asn_is_suboid(&req->bindings[R].var,
1571			    &resp->bindings[r + M].var) == 0)
1572				return (0);
1573
1574			if (resp->bindings[r + M].syntax ==
1575			    SNMP_SYNTAX_ENDOFMIBVIEW) {
1576				M++;
1577				break;
1578			}
1579		}
1580		r += M;
1581	}
1582
1583	return (0);
1584}
1585
1586int32_t
1587snmp_parse_getnext_resp(struct snmp_pdu *resp, struct snmp_pdu *req)
1588{
1589	uint32_t i;
1590
1591	for (i = 0; i < req->nbindings; i++) {
1592		if (asn_is_suboid(&req->bindings[i].var, &resp->bindings[i].var)
1593		    == 0)
1594			return (0);
1595
1596		if (resp->version != SNMP_V1 && resp->bindings[i].syntax ==
1597		    SNMP_SYNTAX_ENDOFMIBVIEW)
1598			return (0);
1599	}
1600
1601	return (1);
1602}
1603
1604/*
1605 * Should be called to check a response to get/getnext/getbulk.
1606 */
1607int32_t
1608snmp_parse_resp(struct snmp_pdu *resp, struct snmp_pdu *req)
1609{
1610	if (resp == NULL || req == NULL)
1611		return (-2);
1612
1613	if (resp->version != req->version) {
1614		warnx("Response has wrong version");
1615		return (-1);
1616	}
1617
1618	if (resp->error_status == SNMP_ERR_NOSUCHNAME) {
1619		warnx("Error - No Such Name");
1620		return (0);
1621	}
1622
1623	if (resp->error_status != SNMP_ERR_NOERROR) {
1624		warnx("Error %d in response", resp->error_status);
1625		return (-1);
1626	}
1627
1628	if (resp->nbindings != req->nbindings && req->type != SNMP_PDU_GETBULK){
1629		warnx("Bad number of bindings in response");
1630		return (-1);
1631	}
1632
1633	switch (req->type) {
1634		case SNMP_PDU_GET:
1635			return (snmp_parse_get_resp(resp,req));
1636		case SNMP_PDU_GETBULK:
1637			return (snmp_parse_getbulk_resp(resp,req));
1638		case SNMP_PDU_GETNEXT:
1639			return (snmp_parse_getnext_resp(resp,req));
1640		default:
1641			/* NOTREACHED */
1642			break;
1643	}
1644
1645	return (-2);
1646}
1647
1648static void
1649snmp_output_octetstring(struct snmp_toolinfo *snmptoolctx, enum snmp_tc tc,
1650    uint32_t len, uint8_t *octets)
1651{
1652	char *buf;
1653
1654	if (len == 0 || octets == NULL)
1655		return;
1656
1657	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1658		fprintf(stdout, "%s : ",
1659		    syntax_strings[SNMP_SYNTAX_OCTETSTRING].str);
1660
1661	if ((buf = snmp_oct2tc(tc, len, (char *) octets)) != NULL) {
1662		fprintf(stdout, "%s", buf);
1663		free(buf);
1664	}
1665}
1666
1667static void
1668snmp_output_octetindex(struct snmp_toolinfo *snmptoolctx, enum snmp_tc tc,
1669    struct asn_oid *oid)
1670{
1671	uint32_t i;
1672	uint8_t *s;
1673
1674	if ((s = malloc(oid->subs[0] + 1)) == NULL)
1675		syslog(LOG_ERR, "malloc failed - %s", strerror(errno));
1676	else {
1677		for (i = 0; i < oid->subs[0]; i++)
1678			s[i] = (u_char) (oid->subs[i + 1]);
1679
1680		snmp_output_octetstring(snmptoolctx, tc, oid->subs[0], s);
1681		free(s);
1682	}
1683}
1684
1685/*
1686 * Check and output syntax type and value.
1687 */
1688static void
1689snmp_output_oid_value(struct snmp_toolinfo *snmptoolctx, struct asn_oid *oid)
1690{
1691	char oid_string[ASN_OIDSTRLEN];
1692	struct snmp_object obj;
1693
1694	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1695		fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_OID].str);
1696
1697	if(!ISSET_NUMERIC(snmptoolctx)) {
1698		memset(&obj, 0, sizeof(struct snmp_object));
1699		asn_append_oid(&(obj.val.var), oid);
1700
1701		if (snmp_lookup_enumstring(snmptoolctx, &obj) > 0)
1702			fprintf(stdout, "%s" , obj.info->string);
1703		else if (snmp_lookup_oidstring(snmptoolctx, &obj) > 0)
1704			fprintf(stdout, "%s" , obj.info->string);
1705		else if (snmp_lookup_nodestring(snmptoolctx, &obj) > 0)
1706			fprintf(stdout, "%s" , obj.info->string);
1707		else {
1708			(void) asn_oid2str_r(oid, oid_string);
1709			fprintf(stdout, "%s", oid_string);
1710		}
1711	} else {
1712		(void) asn_oid2str_r(oid, oid_string);
1713		fprintf(stdout, "%s", oid_string);
1714	}
1715}
1716
1717static void
1718snmp_output_int(struct snmp_toolinfo *snmptoolctx, struct enum_pairs *enums,
1719    int32_t int_val)
1720{
1721	char *string;
1722
1723	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1724		fprintf(stdout, "%s : ",
1725		    syntax_strings[SNMP_SYNTAX_INTEGER].str);
1726
1727	if (enums != NULL && (string = enum_string_lookup(enums, int_val))
1728	    != NULL)
1729		fprintf(stdout, "%s", string);
1730	else
1731		fprintf(stdout, "%d", int_val);
1732}
1733
1734static void
1735snmp_output_ipaddress(struct snmp_toolinfo *snmptoolctx, uint8_t *ip)
1736{
1737	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1738		fprintf(stdout, "%s : ",
1739		    syntax_strings[SNMP_SYNTAX_IPADDRESS].str);
1740
1741	fprintf(stdout, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
1742}
1743
1744static void
1745snmp_output_counter(struct snmp_toolinfo *snmptoolctx, uint32_t counter)
1746{
1747	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1748		fprintf(stdout, "%s : ",
1749		    syntax_strings[SNMP_SYNTAX_COUNTER].str);
1750
1751	fprintf(stdout, "%u", counter);
1752}
1753
1754static void
1755snmp_output_gauge(struct snmp_toolinfo *snmptoolctx, uint32_t gauge)
1756{
1757	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1758		fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_GAUGE].str);
1759
1760	fprintf(stdout, "%u", gauge);
1761}
1762
1763static void
1764snmp_output_ticks(struct snmp_toolinfo *snmptoolctx, uint32_t ticks)
1765{
1766	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1767		fprintf(stdout, "%s : ",
1768		    syntax_strings[SNMP_SYNTAX_TIMETICKS].str);
1769
1770	fprintf(stdout, "%u", ticks);
1771}
1772
1773static void
1774snmp_output_counter64(struct snmp_toolinfo *snmptoolctx, uint64_t counter64)
1775{
1776	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1777		fprintf(stdout, "%s : ",
1778		    syntax_strings[SNMP_SYNTAX_COUNTER64].str);
1779
1780	fprintf(stdout,"%ju", counter64);
1781}
1782
1783int32_t
1784snmp_output_numval(struct snmp_toolinfo *snmptoolctx, struct snmp_value *val,
1785    struct snmp_oid2str *entry)
1786{
1787	if (val == NULL)
1788		return (-1);
1789
1790	if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET)
1791		fprintf(stdout, " = ");
1792
1793	switch (val->syntax) {
1794	    case SNMP_SYNTAX_INTEGER:
1795		if (entry != NULL)
1796			snmp_output_int(snmptoolctx, entry->snmp_enum,
1797			    val->v.integer);
1798		else
1799			snmp_output_int(snmptoolctx, NULL, val->v.integer);
1800		break;
1801
1802	    case SNMP_SYNTAX_OCTETSTRING:
1803		if (entry != NULL)
1804			snmp_output_octetstring(snmptoolctx, entry->tc,
1805			    val->v.octetstring.len, val->v.octetstring.octets);
1806		else
1807			snmp_output_octetstring(snmptoolctx, SNMP_STRING,
1808			    val->v.octetstring.len, val->v.octetstring.octets);
1809		break;
1810
1811	    case SNMP_SYNTAX_OID:
1812		snmp_output_oid_value(snmptoolctx, &(val->v.oid));
1813		break;
1814
1815	    case SNMP_SYNTAX_IPADDRESS:
1816		snmp_output_ipaddress(snmptoolctx, val->v.ipaddress);
1817		break;
1818
1819	    case SNMP_SYNTAX_COUNTER:
1820		snmp_output_counter(snmptoolctx, val->v.uint32);
1821		break;
1822
1823	    case SNMP_SYNTAX_GAUGE:
1824		snmp_output_gauge(snmptoolctx, val->v.uint32);
1825		break;
1826
1827	    case SNMP_SYNTAX_TIMETICKS:
1828		snmp_output_ticks(snmptoolctx, val->v.uint32);
1829		break;
1830
1831	    case SNMP_SYNTAX_COUNTER64:
1832		snmp_output_counter64(snmptoolctx, val->v.counter64);
1833		break;
1834
1835	    case SNMP_SYNTAX_NOSUCHOBJECT:
1836		fprintf(stdout, "No Such Object\n");
1837		return (val->syntax);
1838
1839	    case SNMP_SYNTAX_NOSUCHINSTANCE:
1840		fprintf(stdout, "No Such Instance\n");
1841		return (val->syntax);
1842
1843	    case SNMP_SYNTAX_ENDOFMIBVIEW:
1844		fprintf(stdout, "End of Mib View\n");
1845		return (val->syntax);
1846
1847	    case SNMP_SYNTAX_NULL:
1848		/* NOTREACHED */
1849		fprintf(stdout, "agent returned NULL Syntax\n");
1850		return (val->syntax);
1851
1852	    default:
1853		/* NOTREACHED - If here - then all went completely wrong. */
1854		fprintf(stdout, "agent returned unknown syntax\n");
1855		return (-1);
1856	}
1857
1858	fprintf(stdout, "\n");
1859
1860	return (0);
1861}
1862
1863static int32_t
1864snmp_fill_object(struct snmp_toolinfo *snmptoolctx, struct snmp_object *obj,
1865    struct snmp_value *val)
1866{
1867	int32_t rc;
1868	asn_subid_t suboid;
1869
1870	if (obj == NULL || val == NULL)
1871		return (-1);
1872
1873	if ((suboid = snmp_suboid_pop(&(val->var))) > ASN_MAXID)
1874		return (-1);
1875
1876	memset(obj, 0, sizeof(struct snmp_object));
1877	asn_append_oid(&(obj->val.var), &(val->var));
1878	obj->val.syntax = val->syntax;
1879
1880	if (obj->val.syntax > 0)
1881		rc = snmp_lookup_leafstring(snmptoolctx, obj);
1882	else
1883		rc = snmp_lookup_nonleaf_string(snmptoolctx, obj);
1884
1885	(void) snmp_suboid_append(&(val->var), suboid);
1886	(void) snmp_suboid_append(&(obj->val.var), suboid);
1887
1888	return (rc);
1889}
1890
1891static int32_t
1892snmp_output_index(struct snmp_toolinfo *snmptoolctx, struct index *stx,
1893    struct asn_oid *oid)
1894{
1895	uint8_t ip[4];
1896	uint32_t bytes = 1;
1897	uint64_t cnt64;
1898	struct asn_oid temp, out;
1899
1900	if (oid->len < bytes)
1901		return (-1);
1902
1903	memset(&temp, 0, sizeof(struct asn_oid));
1904	asn_append_oid(&temp, oid);
1905
1906	switch (stx->syntax) {
1907	    case SNMP_SYNTAX_INTEGER:
1908		snmp_output_int(snmptoolctx, stx->snmp_enum, temp.subs[0]);
1909		break;
1910
1911	    case SNMP_SYNTAX_OCTETSTRING:
1912		if ((temp.subs[0] > temp.len -1 ) || (temp.subs[0] >
1913		    ASN_MAXOCTETSTRING))
1914			return (-1);
1915		snmp_output_octetindex(snmptoolctx, stx->tc, &temp);
1916		bytes += temp.subs[0];
1917		break;
1918
1919	    case SNMP_SYNTAX_OID:
1920		if ((temp.subs[0] > temp.len -1) || (temp.subs[0] >
1921		    ASN_MAXOIDLEN))
1922			return (-1);
1923
1924		bytes += temp.subs[0];
1925		memset(&out, 0, sizeof(struct asn_oid));
1926		asn_slice_oid(&out, &temp, 1, bytes);
1927		snmp_output_oid_value(snmptoolctx, &out);
1928		break;
1929
1930	    case SNMP_SYNTAX_IPADDRESS:
1931		if (temp.len < 4)
1932			return (-1);
1933		for (bytes = 0; bytes < 4; bytes++)
1934			ip[bytes] = temp.subs[bytes];
1935
1936		snmp_output_ipaddress(snmptoolctx, ip);
1937		bytes = 4;
1938		break;
1939
1940	    case SNMP_SYNTAX_COUNTER:
1941		snmp_output_counter(snmptoolctx, temp.subs[0]);
1942		break;
1943
1944	    case SNMP_SYNTAX_GAUGE:
1945		snmp_output_gauge(snmptoolctx, temp.subs[0]);
1946		break;
1947
1948	    case SNMP_SYNTAX_TIMETICKS:
1949		snmp_output_ticks(snmptoolctx, temp.subs[0]);
1950		break;
1951
1952	    case SNMP_SYNTAX_COUNTER64:
1953		if (oid->len < 2)
1954			return (-1);
1955		bytes = 2;
1956		memcpy(&cnt64, temp.subs, bytes);
1957		snmp_output_counter64(snmptoolctx, cnt64);
1958		break;
1959
1960	    default:
1961		return (-1);
1962	}
1963
1964	return (bytes);
1965}
1966
1967static int32_t
1968snmp_output_object(struct snmp_toolinfo *snmptoolctx, struct snmp_object *o)
1969{
1970	int32_t i, first, len;
1971	struct asn_oid oid;
1972	struct index *temp;
1973
1974	if (ISSET_NUMERIC(snmptoolctx))
1975		return (-1);
1976
1977	if (o->info->table_idx == NULL) {
1978		fprintf(stdout,"%s.%d", o->info->string,
1979		    o->val.var.subs[o->val.var.len - 1]);
1980		return (1);
1981	}
1982
1983	fprintf(stdout,"%s[", o->info->string);
1984	memset(&oid, 0, sizeof(struct asn_oid));
1985
1986	len = 1;
1987	asn_slice_oid(&oid, &(o->val.var), (o->info->table_idx->var.len + len),
1988	    o->val.var.len);
1989
1990	first = 1;
1991	STAILQ_FOREACH(temp, &(OBJECT_IDX_LIST(o)), link) {
1992		if(first)
1993			first = 0;
1994		else
1995			fprintf(stdout, ", ");
1996		if ((i = snmp_output_index(snmptoolctx, temp, &oid)) < 0)
1997			break;
1998		len += i;
1999		memset(&oid, 0, sizeof(struct asn_oid));
2000		asn_slice_oid(&oid, &(o->val.var),
2001		    (o->info->table_idx->var.len + len), o->val.var.len + 1);
2002	}
2003
2004	fprintf(stdout,"]");
2005	return (1);
2006}
2007
2008void
2009snmp_output_err_resp(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu)
2010{
2011	struct snmp_object *object;
2012	char buf[ASN_OIDSTRLEN];
2013
2014	if (pdu == NULL || (pdu->error_index > (int32_t) pdu->nbindings)) {
2015		fprintf(stdout, "Invalid error index in PDU\n");
2016		return;
2017	}
2018
2019	if ((object = calloc(1, sizeof(struct snmp_object))) == NULL) {
2020		fprintf(stdout, "calloc: %s", strerror(errno));
2021		return;
2022	}
2023
2024	fprintf(stdout, "Agent %s:%s returned error \n", snmp_client.chost,
2025	    snmp_client.cport);
2026
2027	if (!ISSET_NUMERIC(snmptoolctx) && (snmp_fill_object(snmptoolctx, object,
2028	    &(pdu->bindings[pdu->error_index - 1])) > 0))
2029		snmp_output_object(snmptoolctx, object);
2030	else {
2031		asn_oid2str_r(&(pdu->bindings[pdu->error_index - 1].var), buf);
2032		fprintf(stdout,"%s", buf);
2033	}
2034
2035	fprintf(stdout," caused error - ");
2036	if ((pdu->error_status > 0) && (pdu->error_status <=
2037	    SNMP_ERR_INCONS_NAME))
2038		fprintf(stdout, "%s\n", error_strings[pdu->error_status].str);
2039	else
2040		fprintf(stdout,"%s\n", error_strings[SNMP_ERR_UNKNOWN].str);
2041
2042	free(object);
2043	object = NULL;
2044}
2045
2046int32_t
2047snmp_output_resp(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu,
2048    struct asn_oid *root)
2049{
2050	struct snmp_object *object;
2051	char p[ASN_OIDSTRLEN];
2052	int32_t error;
2053	uint32_t i;
2054
2055	if ((object = calloc(1, sizeof(struct snmp_object))) == NULL)
2056		return (-1);
2057
2058	i = error = 0;
2059	while (i < pdu->nbindings) {
2060		if (root != NULL && !(asn_is_suboid(root,
2061		    &(pdu->bindings[i].var))))
2062			break;
2063
2064		if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET) {
2065			if (!ISSET_NUMERIC(snmptoolctx) &&
2066			    (snmp_fill_object(snmptoolctx, object,
2067			    &(pdu->bindings[i])) > 0))
2068				snmp_output_object(snmptoolctx, object);
2069			else {
2070				asn_oid2str_r(&(pdu->bindings[i].var), p);
2071				fprintf(stdout, "%s", p);
2072			}
2073		}
2074		error |= snmp_output_numval(snmptoolctx, &(pdu->bindings[i]),
2075		    object->info);
2076		i++;
2077	}
2078
2079	free(object);
2080	object = NULL;
2081
2082	if (error)
2083		return (-1);
2084
2085	return (i);
2086}
2087
2088void
2089snmp_output_engine(void)
2090{
2091	uint32_t i;
2092	char *cptr, engine[2 * SNMP_ENGINE_ID_SIZ + 2];
2093
2094	cptr = engine;
2095	for (i = 0; i < snmp_client.engine.engine_len; i++)
2096		cptr += sprintf(cptr, "%.2x", snmp_client.engine.engine_id[i]);
2097	*cptr++ = '\0';
2098
2099	fprintf(stdout, "Engine ID 0x%s\n", engine);
2100	fprintf(stdout, "Boots : %u\t\tTime : %d\n",
2101	    snmp_client.engine.engine_boots,
2102	    snmp_client.engine.engine_time);
2103}
2104
2105void
2106snmp_output_keys(void)
2107{
2108	uint32_t i, keylen = 0;
2109	char *cptr, extkey[2 * SNMP_AUTH_KEY_SIZ + 2];
2110
2111	fprintf(stdout, "Localized keys for %s\n", snmp_client.user.sec_name);
2112	if (snmp_client.user.auth_proto == SNMP_AUTH_HMAC_MD5) {
2113		fprintf(stdout, "MD5 : 0x");
2114		keylen = SNMP_AUTH_HMACMD5_KEY_SIZ;
2115	} else if (snmp_client.user.auth_proto == SNMP_AUTH_HMAC_SHA) {
2116		fprintf(stdout, "SHA : 0x");
2117		keylen = SNMP_AUTH_HMACSHA_KEY_SIZ;
2118	}
2119	if (snmp_client.user.auth_proto != SNMP_AUTH_NOAUTH) {
2120		cptr = extkey;
2121		for (i = 0; i < keylen; i++)
2122			cptr += sprintf(cptr, "%.2x",
2123			    snmp_client.user.auth_key[i]);
2124		*cptr++ = '\0';
2125		fprintf(stdout, "%s\n", extkey);
2126	}
2127
2128	if (snmp_client.user.priv_proto == SNMP_PRIV_DES) {
2129		fprintf(stdout, "DES : 0x");
2130		keylen = SNMP_PRIV_DES_KEY_SIZ;
2131	} else if (snmp_client.user.priv_proto == SNMP_PRIV_AES) {
2132		fprintf(stdout, "AES : 0x");
2133		keylen = SNMP_PRIV_AES_KEY_SIZ;
2134	}
2135	if (snmp_client.user.priv_proto != SNMP_PRIV_NOPRIV) {
2136		cptr = extkey;
2137		for (i = 0; i < keylen; i++)
2138			cptr += sprintf(cptr, "%.2x",
2139			    snmp_client.user.priv_key[i]);
2140		*cptr++ = '\0';
2141		fprintf(stdout, "%s\n", extkey);
2142	}
2143}
2144