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