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