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