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