]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - usr.sbin/bsnmpd/tools/libbsnmptools/bsnmptools.c
MFC r298451,r298452,r298507,r298750:
[FreeBSD/stable/10.git] / usr.sbin / bsnmpd / tools / libbsnmptools / bsnmptools.c
1 /*-
2  * Copyright (c) 2005-2006 The FreeBSD Project
3  * All rights reserved.
4  *
5  * Author: Shteryana Shopova <syrinx@FreeBSD.org>
6  *
7  * Redistribution of this software and documentation and use in source and
8  * binary forms, with or without modification, are permitted provided that
9  * the following conditions are met:
10  *
11  * 1. Redistributions of source code or documentation must retain the above
12  *    copyright notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * Helper functions for snmp client tools
30  *
31  * $FreeBSD$
32  */
33
34 #include <sys/param.h> 
35 #include <sys/queue.h>
36 #include <sys/uio.h>
37
38 #include <assert.h>
39 #include <ctype.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <syslog.h>
47 #include <unistd.h>
48
49 #include <bsnmp/asn1.h>
50 #include <bsnmp/snmp.h>
51 #include <bsnmp/snmpclient.h>
52 #include "bsnmptc.h"
53 #include "bsnmptools.h"
54
55 /* Internal varibale to turn on library debugging for testing and to
56  * find bugs. It is not exported via the header file.
57  * XXX should we cover it by some #ifdef BSNMPTOOLS_DEBUG? */
58 int _bsnmptools_debug = 0;
59
60 /* Default files to import mapping from if none explicitly provided. */
61 #define bsnmpd_defs             "/usr/share/snmp/defs/tree.def"
62 #define mibII_defs              "/usr/share/snmp/defs/mibII_tree.def"
63
64 /*
65  * The .iso.org.dod oid that has to be prepended to every OID when requesting
66  * a value.
67  */
68 const struct asn_oid IsoOrgDod_OID = {
69         3, { 1, 3, 6 }
70 };
71
72
73 #define SNMP_ERR_UNKNOWN        0
74
75 /*
76  * An array of error strings corresponding to error definitions from libbsnmp.
77  */
78 static const struct {
79         const char *str;
80         int32_t error;
81 } error_strings[] = {
82         { "Unknown", SNMP_ERR_UNKNOWN },
83         { "Too big ", SNMP_ERR_TOOBIG },
84         { "No such Name", SNMP_ERR_NOSUCHNAME },
85         { "Bad Value", SNMP_ERR_BADVALUE },
86         { "Readonly", SNMP_ERR_READONLY },
87         { "General error", SNMP_ERR_GENERR },
88         { "No access", SNMP_ERR_NO_ACCESS },
89         { "Wrong type", SNMP_ERR_WRONG_TYPE },
90         { "Wrong length", SNMP_ERR_WRONG_LENGTH },
91         { "Wrong encoding", SNMP_ERR_WRONG_ENCODING },
92         { "Wrong value", SNMP_ERR_WRONG_VALUE },
93         { "No creation", SNMP_ERR_NO_CREATION },
94         { "Inconsistent value", SNMP_ERR_INCONS_VALUE },
95         { "Resource unavailable", SNMP_ERR_RES_UNAVAIL },
96         { "Commit failed", SNMP_ERR_COMMIT_FAILED },
97         { "Undo failed", SNMP_ERR_UNDO_FAILED },
98         { "Authorization error", SNMP_ERR_AUTH_ERR },
99         { "Not writable", SNMP_ERR_NOT_WRITEABLE },
100         { "Inconsistent name", SNMP_ERR_INCONS_NAME },
101         { NULL, 0 }
102 };
103
104 /* This one and any following are exceptions. */
105 #define SNMP_SYNTAX_UNKNOWN     SNMP_SYNTAX_NOSUCHOBJECT
106
107 static const struct {
108         const char *str;
109         enum snmp_syntax stx;
110 } syntax_strings[] = {
111         { "Null", SNMP_SYNTAX_NULL },
112         { "Integer", SNMP_SYNTAX_INTEGER },
113         { "OctetString", SNMP_SYNTAX_OCTETSTRING },
114         { "OID", SNMP_SYNTAX_OID },
115         { "IpAddress", SNMP_SYNTAX_IPADDRESS },
116         { "Counter32", SNMP_SYNTAX_COUNTER },
117         { "Gauge", SNMP_SYNTAX_GAUGE },
118         { "TimeTicks", SNMP_SYNTAX_TIMETICKS },
119         { "Counter64", SNMP_SYNTAX_COUNTER64 },
120         { "Unknown", SNMP_SYNTAX_UNKNOWN }, 
121 };
122
123 int
124 snmptool_init(struct snmp_toolinfo *snmptoolctx)
125 {
126         char *str;
127         size_t slen;
128
129         memset(snmptoolctx, 0, sizeof(struct snmp_toolinfo));
130         snmptoolctx->objects = 0;
131         snmptoolctx->mappings = NULL;
132         snmptoolctx->flags = SNMP_PDU_GET;      /* XXX */
133         SLIST_INIT(&snmptoolctx->filelist);
134         snmp_client_init(&snmp_client);
135         SET_MAXREP(snmptoolctx, SNMP_MAX_REPETITIONS);
136
137         if (add_filename(snmptoolctx, bsnmpd_defs, &IsoOrgDod_OID, 0) < 0)
138                 warnx("Error adding file %s to list", bsnmpd_defs);
139
140         if (add_filename(snmptoolctx, mibII_defs, &IsoOrgDod_OID, 0) < 0)
141                 warnx("Error adding file %s to list", mibII_defs);
142
143         /* Read the environment */
144         if ((str = getenv("SNMPAUTH")) != NULL) {
145                 slen = strlen(str);
146                 if (slen == strlen("md5") && strcasecmp(str, "md5") == 0)
147                         snmp_client.user.auth_proto = SNMP_AUTH_HMAC_MD5;
148                 else if (slen == strlen("sha")&& strcasecmp(str, "sha") == 0)
149                         snmp_client.user.auth_proto = SNMP_AUTH_HMAC_SHA;
150                 else if (slen != 0)
151                         warnx("Bad authentication type - %s in SNMPAUTH", str);
152         }
153
154         if ((str = getenv("SNMPPRIV")) != NULL) {
155                 slen = strlen(str);
156                 if (slen == strlen("des") && strcasecmp(str, "des") == 0)
157                         snmp_client.user.priv_proto = SNMP_PRIV_DES;
158                 else if (slen == strlen("aes")&& strcasecmp(str, "aes") == 0)
159                         snmp_client.user.priv_proto = SNMP_PRIV_AES;
160                 else if (slen != 0)
161                         warnx("Bad privacy type - %s in SNMPPRIV", str);
162         }
163
164         if ((str = getenv("SNMPUSER")) != NULL) {
165                 if ((slen = strlen(str)) > sizeof(snmp_client.user.sec_name)) {
166                         warnx("Username too long - %s in SNMPUSER", str);
167                         return (-1);
168                 }
169                 if (slen > 0) {
170                         strlcpy(snmp_client.user.sec_name, str,
171                             sizeof(snmp_client.user.sec_name));
172                         snmp_client.version = SNMP_V3;
173                 }
174         }
175
176         if ((str = getenv("SNMPPASSWD")) != NULL) {
177                 if ((slen = strlen(str)) > MAXSTR)
178                         slen = MAXSTR - 1;
179                 if ((snmptoolctx->passwd = malloc(slen + 1)) == NULL) {
180                         warnx("malloc() failed - %s", strerror(errno));
181                         return (-1);
182                 }
183                 if (slen > 0)
184                         strlcpy(snmptoolctx->passwd, str, slen + 1);
185         }
186
187         return (0);
188 }
189
190 #define OBJECT_IDX_LIST(o)      o->info->table_idx->index_list
191
192 /* 
193  * Walk through the file list and import string<->oid mappings from each file.
194  */
195 int32_t
196 snmp_import_all(struct snmp_toolinfo *snmptoolctx)
197 {
198         int32_t fc;
199         struct fname *tmp;
200
201         if (snmptoolctx == NULL)
202                 return (-1);
203
204         if (ISSET_NUMERIC(snmptoolctx))
205                 return (0);
206
207         if ((snmptoolctx->mappings = snmp_mapping_init()) == NULL)
208                 return (-1);
209
210         fc = 0;
211         if (SLIST_EMPTY(&snmptoolctx->filelist)) {
212                 warnx("No files to read OID <-> string conversions from");
213                 return (-1);
214         } else {
215                 SLIST_FOREACH(tmp, &snmptoolctx->filelist, link) {
216                         if (tmp->done)
217                                 continue;
218                         if (snmp_import_file(snmptoolctx, tmp) < 0) {
219                                 fc = -1;
220                                 break;
221                         }
222                         fc++;
223                 }
224         }
225
226         snmp_mapping_dump(snmptoolctx);
227         return (fc);
228 }
229
230 /*
231  * Add a filename to the file list - the initial idea of keeping a list with all
232  * files to read OIDs from was that an application might want to have loaded in
233  * memory the OIDs from a single file only and when done with them read the OIDs
234  * from another file. This is not used yet but might be a good idea at some
235  * point. Size argument is number of bytes in string including trailing '\0',
236  * not string length.
237  */
238 int32_t
239 add_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
277 void
278 free_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
293 static char 
294 isvalid_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  */
317 static int
318 getsubopt1(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
361 static int32_t
362 parse_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
386 static int32_t
387 parse_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
446 static int32_t
447 parse_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  */
485 int32_t
486 parse_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
540 int32_t
541 parse_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
593 int32_t
594 parse_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
635 int32_t
636 parse_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
709 int32_t
710 parse_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
720 int32_t
721 parse_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
792 int32_t
793 parse_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
812 int32_t
813 parse_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
834 int32_t
835 parse_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
857 int32_t
858 parse_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
895 int32_t
896 parse_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
909 int32_t
910 parse_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
938 int32_t
939 parse_debug(void)
940 {
941         snmp_client.dump_pdus = 1;
942         return (1);
943 }
944
945 int32_t
946 parse_discovery(struct snmp_toolinfo *snmptoolctx)
947 {
948         SET_EDISCOVER(snmptoolctx);
949         snmp_client.version = SNMP_V3;
950         return (1);
951 }
952
953 int32_t
954 parse_local_key(struct snmp_toolinfo *snmptoolctx)
955 {
956         SET_LOCALKEY(snmptoolctx);
957         snmp_client.version = SNMP_V3;
958         return (1);
959 }
960
961 int32_t
962 parse_num_oids(struct snmp_toolinfo *snmptoolctx)
963 {
964         SET_NUMERIC(snmptoolctx);
965         return (1);
966 }
967
968 int32_t
969 parse_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
994 int32_t
995 parse_errors(struct snmp_toolinfo *snmptoolctx)
996 {
997         SET_RETRY(snmptoolctx);
998         return (1);
999 }
1000
1001 int32_t
1002 parse_skip_access(struct snmp_toolinfo *snmptoolctx)
1003 {
1004         SET_ERRIGNORE(snmptoolctx);
1005         return (1);
1006 }
1007
1008 char *
1009 snmp_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
1034 static char *
1035 snmp_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... */
1059 static char *
1060 snmp_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         }
1086
1087         asn_append_oid(oid, &(obj.val.var));
1088         return (endptr);
1089 }
1090
1091 static char *
1092 snmp_ip2asn_oid(char *str, struct asn_oid *oid)
1093 {
1094         uint32_t v;
1095         int32_t i;
1096         char *endptr, *ptr;
1097
1098         ptr = str;
1099         for (i = 0; i < 4; i++) {
1100                 v = strtoul(ptr, &endptr, 10);
1101                 if (v > 0xff)
1102                         return (NULL);
1103                 if (*endptr != '.' && strchr("],\0", *endptr) == NULL && i != 3)
1104                         return (NULL);
1105                 if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
1106                         return (NULL);
1107                 ptr = endptr + 1;
1108         }
1109
1110         return (endptr);
1111 }
1112
1113 /* 32-bit counter, gauge, timeticks. */
1114 static char *
1115 snmp_uint2asn_oid(char *str, struct asn_oid *oid)
1116 {
1117         char *endptr;
1118         uint32_t v;
1119         int32_t saved_errno;
1120
1121         saved_errno = errno;
1122         errno = 0;
1123
1124         v = strtoul(str, &endptr, 10);
1125         if (errno != 0) {
1126                 warnx("Integer value %s not supported - %s\n", str,
1127                     strerror(errno));
1128                 errno = saved_errno;
1129                 return (NULL);
1130         }
1131         errno = saved_errno;
1132         if (snmp_suboid_append(oid, (asn_subid_t) v) < 0)
1133                 return (NULL);
1134
1135         return (endptr);
1136 }
1137
1138 static char *
1139 snmp_cnt64_2asn_oid(char *str, struct asn_oid *oid)
1140 {
1141         char *endptr;
1142         uint64_t v;
1143         int32_t saved_errno;
1144
1145         saved_errno = errno;
1146         errno = 0;
1147
1148         v = strtoull(str, &endptr, 10);
1149
1150         if (errno != 0) {
1151                 warnx("Integer value %s not supported - %s", str,
1152                     strerror(errno));
1153                 errno = saved_errno;
1154                 return (NULL);
1155         }
1156         errno = saved_errno;
1157         if (snmp_suboid_append(oid, (asn_subid_t) (v & 0xffffffff)) < 0)
1158                 return (NULL);
1159
1160         if (snmp_suboid_append(oid, (asn_subid_t) (v >> 32)) < 0)
1161                 return (NULL);
1162
1163         return (endptr);
1164 }
1165
1166 enum snmp_syntax
1167 parse_syntax(char *str)
1168 {
1169         int32_t i;
1170
1171         for (i = 0; i < SNMP_SYNTAX_UNKNOWN; i++) {
1172                 if (strncmp(syntax_strings[i].str, str,
1173                     strlen(syntax_strings[i].str)) == 0)
1174                         return (syntax_strings[i].stx);
1175         }
1176
1177         return (SNMP_SYNTAX_NULL);
1178 }
1179
1180 static char *
1181 snmp_parse_subindex(struct snmp_toolinfo *snmptoolctx, char *str,
1182     struct index *idx, struct snmp_object *object)
1183 {
1184         char *ptr;
1185         int32_t i;
1186         enum snmp_syntax stx;
1187         char syntax[MAX_CMD_SYNTAX_LEN];
1188
1189         ptr = str;
1190         if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) {
1191                 for (i = 0; i < MAX_CMD_SYNTAX_LEN ; i++) {
1192                         if (*(ptr + i) == ':')
1193                                 break;
1194                 }
1195
1196                 if (i >= MAX_CMD_SYNTAX_LEN) {
1197                         warnx("Unknown syntax in OID - %s", str);
1198                         return (NULL);
1199                 }
1200                 /* Expect a syntax string here. */
1201                 if ((stx = parse_syntax(str)) <= SNMP_SYNTAX_NULL) {
1202                         warnx("Invalid  syntax - %s",syntax);
1203                         return (NULL);
1204                 }
1205
1206                 if (stx != idx->syntax && !ISSET_ERRIGNORE(snmptoolctx)) {
1207                         warnx("Syntax mismatch - %d expected, %d given",
1208                             idx->syntax, stx);
1209                         return (NULL);
1210                 }
1211                 /*
1212                  * That is where the suboid started + the syntax length + one
1213                  * character for ':'.
1214                  */
1215                 ptr = str + i + 1;
1216         } else
1217                 stx = idx->syntax;
1218
1219         switch (stx) {
1220                 case SNMP_SYNTAX_INTEGER:
1221                         return (snmp_int2asn_oid(ptr, &(object->val.var)));
1222                 case SNMP_SYNTAX_OID:
1223                         return (snmp_oid2asn_oid(snmptoolctx, ptr,
1224                             &(object->val.var)));
1225                 case SNMP_SYNTAX_IPADDRESS:
1226                         return (snmp_ip2asn_oid(ptr, &(object->val.var)));
1227                 case SNMP_SYNTAX_COUNTER:
1228                         /* FALLTHROUGH */
1229                 case SNMP_SYNTAX_GAUGE:
1230                         /* FALLTHROUGH */
1231                 case SNMP_SYNTAX_TIMETICKS:
1232                         return (snmp_uint2asn_oid(ptr, &(object->val.var)));
1233                 case SNMP_SYNTAX_COUNTER64:
1234                         return (snmp_cnt64_2asn_oid(ptr, &(object->val.var)));
1235                 case SNMP_SYNTAX_OCTETSTRING:
1236                         return (snmp_tc2oid(idx->tc, ptr, &(object->val.var)));
1237                 default:
1238                         /* NOTREACHED */
1239                         break;
1240         }
1241
1242         return (NULL);
1243 }
1244
1245 char *
1246 snmp_parse_index(struct snmp_toolinfo *snmptoolctx, char *str,
1247     struct snmp_object *object)
1248 {
1249         char *ptr;
1250         struct index *temp;
1251
1252         if (object->info->table_idx == NULL)
1253                 return (NULL);
1254
1255         ptr = NULL;
1256         STAILQ_FOREACH(temp, &(OBJECT_IDX_LIST(object)), link) {
1257                 if ((ptr = snmp_parse_subindex(snmptoolctx, str, temp, object))
1258                     == NULL)
1259                         return (NULL);
1260
1261                 if (*ptr != ',' && *ptr != ']')
1262                         return (NULL);
1263                 str = ptr + 1;
1264         }
1265
1266         if (ptr == NULL || *ptr != ']') {
1267                 warnx("Mismatching index - %s", str);
1268                 return (NULL);
1269         }
1270
1271         return (ptr + 1);
1272 }
1273
1274 /*
1275  * Fill in the struct asn_oid member of snmp_value with suboids from input.
1276  * If an error occurs - print message on stderr and return (-1).
1277  * If all is ok - return the length of the oid.
1278  */
1279 int32_t
1280 snmp_parse_numoid(char *argv, struct asn_oid *var)
1281 {
1282         char *endptr, *str;
1283         asn_subid_t suboid;
1284
1285         str = argv;
1286
1287         if (*str == '.')
1288                 str++;
1289
1290         do {
1291                 if (var->len == ASN_MAXOIDLEN) {
1292                         warnx("Oid too long - %u", var->len);
1293                         return (-1);
1294                 }
1295
1296                 suboid = strtoul(str, &endptr, 10);
1297                 if (suboid > ASN_MAXID) {
1298                         warnx("Oid too long - %u", var->len);
1299                         return (-1);
1300                 }
1301
1302                 var->subs[var->len++] = suboid;
1303                 str = endptr + 1;
1304         } while ( *endptr == '.');
1305
1306         if (*endptr != '\0') {
1307                 warnx("Invalid oid string - %s", argv);
1308                 return (-1);
1309         }
1310
1311         return (var->len);
1312 }
1313
1314 /* Append a length 1 suboid to an asn_oid structure. */
1315 int32_t
1316 snmp_suboid_append(struct asn_oid *var, asn_subid_t suboid)
1317 {
1318         if (var == NULL)
1319                 return (-1);
1320
1321         if (var->len >= ASN_MAXOIDLEN) {
1322                 warnx("Oid too long - %u", var->len);
1323                 return (-1);
1324         }
1325
1326         var->subs[var->len++] = suboid;
1327
1328         return (1);
1329 }
1330
1331 /* Pop the last suboid from an asn_oid structure. */
1332 int32_t
1333 snmp_suboid_pop(struct asn_oid *var)
1334 {
1335         asn_subid_t suboid;
1336
1337         if (var == NULL)
1338                 return (-1);
1339
1340         if (var->len < 1)
1341                 return (-1);
1342
1343         suboid = var->subs[--(var->len)];
1344         var->subs[var->len] = 0;
1345
1346         return (suboid);
1347 }
1348
1349 /*
1350  * Parse the command-line provided string into an OID - alocate memory for a new
1351  * snmp object, fill in its fields and insert it in the object list. A
1352  * (snmp_verify_inoid_f) function must be provided to validate the input string.
1353  */
1354 int32_t
1355 snmp_object_add(struct snmp_toolinfo *snmptoolctx, snmp_verify_inoid_f func,
1356     char *string)
1357 {
1358         struct snmp_object *obj;
1359
1360         if (snmptoolctx == NULL)
1361                 return (-1);
1362
1363         /* XXX-BZ does that chack make sense? */
1364         if (snmptoolctx->objects >= SNMP_MAX_BINDINGS) {
1365                 warnx("Too many bindings in PDU - %u", snmptoolctx->objects + 1);
1366                 return (-1);
1367         }
1368
1369         if ((obj = malloc(sizeof(struct snmp_object))) == NULL) {
1370                 syslog(LOG_ERR, "malloc() failed: %s", strerror(errno));
1371                 return (-1);
1372         }
1373
1374         memset(obj, 0, sizeof(struct snmp_object));
1375         if (func(snmptoolctx, obj, string) < 0) {
1376                 warnx("Invalid OID - %s", string);
1377                 free(obj);
1378                 return (-1);
1379         }
1380
1381         snmptoolctx->objects++;
1382         SLIST_INSERT_HEAD(&snmptoolctx->snmp_objectlist, obj, link);
1383
1384         return (1);
1385 }
1386
1387 /* Given an OID, find it in the object list and remove it. */
1388 int32_t
1389 snmp_object_remove(struct snmp_toolinfo *snmptoolctx, struct asn_oid *oid)
1390 {
1391         struct snmp_object *temp;
1392
1393         if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist)) {
1394                 warnx("Object list already empty");
1395                 return (-1);
1396         }
1397
1398
1399         SLIST_FOREACH(temp, &snmptoolctx->snmp_objectlist, link)
1400                 if (asn_compare_oid(&(temp->val.var), oid) == 0)
1401                         break;
1402
1403         if (temp == NULL) {
1404                 warnx("No such object in list");
1405                 return (-1);
1406         }
1407
1408         SLIST_REMOVE(&snmptoolctx->snmp_objectlist, temp, snmp_object, link);
1409         if (temp->val.syntax == SNMP_SYNTAX_OCTETSTRING &&
1410             temp->val.v.octetstring.octets != NULL)
1411                 free(temp->val.v.octetstring.octets);
1412         free(temp);
1413
1414         return (1);
1415 }
1416
1417 static void
1418 snmp_object_freeall(struct snmp_toolinfo *snmptoolctx)
1419 {
1420         struct snmp_object *o;
1421
1422         while ((o = SLIST_FIRST(&snmptoolctx->snmp_objectlist)) != NULL) {
1423                 SLIST_REMOVE_HEAD(&snmptoolctx->snmp_objectlist, link);
1424
1425                 if (o->val.syntax == SNMP_SYNTAX_OCTETSTRING &&
1426                     o->val.v.octetstring.octets != NULL)
1427                         free(o->val.v.octetstring.octets);
1428                 free(o);
1429         }
1430 }
1431
1432 /* Do all possible memory release before exit. */
1433 void
1434 snmp_tool_freeall(struct snmp_toolinfo *snmptoolctx)
1435 {
1436         if (snmp_client.chost != NULL) {
1437                 free(snmp_client.chost);
1438                 snmp_client.chost = NULL;
1439         }
1440
1441         if (snmp_client.cport != NULL) {
1442                 free(snmp_client.cport);
1443                 snmp_client.cport = NULL;
1444         }
1445
1446         snmp_mapping_free(snmptoolctx);
1447         free_filelist(snmptoolctx);
1448         snmp_object_freeall(snmptoolctx);
1449
1450         if (snmptoolctx->passwd != NULL) {
1451                 free(snmptoolctx->passwd);
1452                 snmptoolctx->passwd = NULL;
1453         }
1454 }
1455
1456 /*
1457  * Fill all variables from the object list into a PDU. (snmp_verify_vbind_f)
1458  * function should check whether the variable is consistent in this PDU
1459  * (e.g do not add non-leaf OIDs to a GET PDU, or OIDs with read access only to
1460  * a SET PDU) - might be NULL though. (snmp_add_vbind_f) function is the
1461  * function actually adds the variable to the PDU and must not be NULL.
1462  */
1463 int32_t
1464 snmp_pdu_add_bindings(struct snmp_toolinfo *snmptoolctx,
1465     snmp_verify_vbind_f vfunc, snmp_add_vbind_f afunc,
1466     struct snmp_pdu *pdu, int32_t maxcount)
1467 {
1468         int32_t nbindings, abind;
1469         struct snmp_object *obj;
1470
1471         if (pdu == NULL || afunc == NULL)
1472                 return (-1);
1473
1474         /* Return 0 in case of no more work todo. */
1475         if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist))
1476                 return (0);
1477         
1478         if (maxcount < 0 || maxcount > SNMP_MAX_BINDINGS) {
1479                 warnx("maxcount out of range: <0 || >SNMP_MAX_BINDINGS");
1480                 return (-1);
1481         }
1482
1483         nbindings = 0;
1484         SLIST_FOREACH(obj, &snmptoolctx->snmp_objectlist, link) {
1485                 if ((vfunc != NULL) && (vfunc(snmptoolctx, pdu, obj) < 0)) {
1486                         nbindings = -1;
1487                         break;
1488                 }
1489                 if ((abind = afunc(pdu, obj)) < 0) {
1490                         nbindings = -1;
1491                         break;
1492                 }
1493
1494                 if (abind > 0) {
1495                         /* Do not put more varbindings than requested. */
1496                         if (++nbindings >= maxcount)
1497                                 break;
1498                 }
1499         }
1500
1501         return (nbindings);
1502 }
1503
1504 /*
1505  * Locate an object in the object list and set a corresponding error status.
1506  */
1507 int32_t
1508 snmp_object_seterror(struct snmp_toolinfo *snmptoolctx,
1509     struct snmp_value *err_value, int32_t error_status)
1510 {
1511         struct snmp_object *obj;
1512
1513         if (SLIST_EMPTY(&snmptoolctx->snmp_objectlist) || err_value == NULL)
1514                 return (-1);
1515
1516         SLIST_FOREACH(obj, &snmptoolctx->snmp_objectlist, link)
1517                 if (asn_compare_oid(&(err_value->var), &(obj->val.var)) == 0) {
1518                         obj->error = error_status;
1519                         return (1);
1520                 }
1521
1522         return (0);
1523 }
1524
1525 /*
1526  * Check a PDU received in response to a SNMP_PDU_GET/SNMP_PDU_GETBULK request
1527  * but don't compare syntaxes - when sending a request PDU they must be null.
1528  * This is a (almost) complete copy of snmp_pdu_check() - with matching syntaxes
1529  * checks and some other checks skipped.
1530  */
1531 int32_t
1532 snmp_parse_get_resp(struct snmp_pdu *resp, struct snmp_pdu *req)
1533 {
1534         uint32_t i;
1535
1536         for (i = 0; i < req->nbindings; i++) {
1537                 if (asn_compare_oid(&req->bindings[i].var,
1538                     &resp->bindings[i].var) != 0) {
1539                         warnx("Bad OID in response");
1540                         return (-1);
1541                 }
1542
1543                 if (snmp_client.version != SNMP_V1 && (resp->bindings[i].syntax
1544                     == SNMP_SYNTAX_NOSUCHOBJECT || resp->bindings[i].syntax ==
1545                     SNMP_SYNTAX_NOSUCHINSTANCE))
1546                         return (0);
1547         }
1548
1549         return (1);
1550 }
1551
1552 int32_t
1553 snmp_parse_getbulk_resp(struct snmp_pdu *resp, struct snmp_pdu *req)
1554 {
1555         int32_t N, R, M, r;
1556
1557         if (req->error_status > (int32_t) resp->nbindings) {
1558                 warnx("Bad number of bindings in response");
1559                 return (-1);
1560         }
1561
1562         for (N = 0; N < req->error_status; N++) {
1563                 if (asn_is_suboid(&req->bindings[N].var,
1564                     &resp->bindings[N].var) == 0)
1565                         return (0);
1566                 if (resp->bindings[N].syntax == SNMP_SYNTAX_ENDOFMIBVIEW)
1567                         return (0);
1568         }
1569
1570         for (R = N , r = N; R  < (int32_t) req->nbindings; R++) {
1571                 for (M = 0; M < req->error_index && (r + M) <
1572                     (int32_t) resp->nbindings; M++) {
1573                         if (asn_is_suboid(&req->bindings[R].var,
1574                             &resp->bindings[r + M].var) == 0)
1575                                 return (0);
1576
1577                         if (resp->bindings[r + M].syntax ==
1578                             SNMP_SYNTAX_ENDOFMIBVIEW) {
1579                                 M++;
1580                                 break;
1581                         }
1582                 }
1583                 r += M;
1584         }
1585
1586         return (0);
1587 }
1588
1589 int32_t
1590 snmp_parse_getnext_resp(struct snmp_pdu *resp, struct snmp_pdu *req)
1591 {
1592         uint32_t i;
1593
1594         for (i = 0; i < req->nbindings; i++) {
1595                 if (asn_is_suboid(&req->bindings[i].var, &resp->bindings[i].var)
1596                     == 0)
1597                         return (0);
1598
1599                 if (resp->version != SNMP_V1 && resp->bindings[i].syntax ==
1600                     SNMP_SYNTAX_ENDOFMIBVIEW)
1601                         return (0);
1602         }
1603
1604         return (1);
1605 }
1606
1607 /*
1608  * Should be called to check a response to get/getnext/getbulk.
1609  */
1610 int32_t
1611 snmp_parse_resp(struct snmp_pdu *resp, struct snmp_pdu *req)
1612 {
1613         if (resp == NULL || req == NULL)
1614                 return (-2);
1615
1616         if (resp->version != req->version) {
1617                 warnx("Response has wrong version");
1618                 return (-1);
1619         }
1620
1621         if (resp->error_status == SNMP_ERR_NOSUCHNAME) {
1622                 warnx("Error - No Such Name");
1623                 return (0);
1624         }
1625
1626         if (resp->error_status != SNMP_ERR_NOERROR) {
1627                 warnx("Error %d in response", resp->error_status);
1628                 return (-1);
1629         }
1630
1631         if (resp->nbindings != req->nbindings && req->type != SNMP_PDU_GETBULK){
1632                 warnx("Bad number of bindings in response");
1633                 return (-1);
1634         }
1635
1636         switch (req->type) {
1637                 case SNMP_PDU_GET:
1638                         return (snmp_parse_get_resp(resp,req));
1639                 case SNMP_PDU_GETBULK:
1640                         return (snmp_parse_getbulk_resp(resp,req));
1641                 case SNMP_PDU_GETNEXT:
1642                         return (snmp_parse_getnext_resp(resp,req));
1643                 default:
1644                         /* NOTREACHED */
1645                         break;
1646         }
1647
1648         return (-2);
1649 }
1650
1651 static void
1652 snmp_output_octetstring(struct snmp_toolinfo *snmptoolctx, enum snmp_tc tc,
1653     uint32_t len, uint8_t *octets)
1654 {
1655         char *buf;
1656
1657         if (len == 0 || octets == NULL)
1658                 return;
1659
1660         if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1661                 fprintf(stdout, "%s : ",
1662                     syntax_strings[SNMP_SYNTAX_OCTETSTRING].str);
1663
1664         if ((buf = snmp_oct2tc(tc, len, (char *) octets)) != NULL) {
1665                 fprintf(stdout, "%s", buf);
1666                 free(buf);
1667         }
1668 }
1669
1670 static void
1671 snmp_output_octetindex(struct snmp_toolinfo *snmptoolctx, enum snmp_tc tc,
1672     struct asn_oid *oid)
1673 {
1674         uint32_t i;
1675         uint8_t *s;
1676
1677         if ((s = malloc(oid->subs[0] + 1)) == NULL)
1678                 syslog(LOG_ERR, "malloc failed - %s", strerror(errno));
1679         else {
1680                 for (i = 0; i < oid->subs[0]; i++)
1681                         s[i] = (u_char) (oid->subs[i + 1]);
1682
1683                 snmp_output_octetstring(snmptoolctx, tc, oid->subs[0], s);
1684                 free(s);
1685         }
1686 }
1687
1688 /*
1689  * Check and output syntax type and value.
1690  */
1691 static void
1692 snmp_output_oid_value(struct snmp_toolinfo *snmptoolctx, struct asn_oid *oid)
1693 {
1694         char oid_string[ASN_OIDSTRLEN];
1695         struct snmp_object obj;
1696
1697         if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1698                 fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_OID].str);
1699
1700         if(!ISSET_NUMERIC(snmptoolctx)) {
1701                 memset(&obj, 0, sizeof(struct snmp_object));
1702                 asn_append_oid(&(obj.val.var), oid);
1703
1704                 if (snmp_lookup_enumstring(snmptoolctx, &obj) > 0)
1705                         fprintf(stdout, "%s" , obj.info->string);
1706                 else if (snmp_lookup_oidstring(snmptoolctx, &obj) > 0)
1707                         fprintf(stdout, "%s" , obj.info->string);
1708                 else if (snmp_lookup_nodestring(snmptoolctx, &obj) > 0)
1709                         fprintf(stdout, "%s" , obj.info->string);
1710                 else {
1711                         (void) asn_oid2str_r(oid, oid_string);
1712                         fprintf(stdout, "%s", oid_string);
1713                 }
1714         } else {
1715                 (void) asn_oid2str_r(oid, oid_string);
1716                 fprintf(stdout, "%s", oid_string);
1717         }
1718 }
1719
1720 static void
1721 snmp_output_int(struct snmp_toolinfo *snmptoolctx, struct enum_pairs *enums,
1722     int32_t int_val)
1723 {
1724         char *string;
1725
1726         if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1727                 fprintf(stdout, "%s : ",
1728                     syntax_strings[SNMP_SYNTAX_INTEGER].str);
1729
1730         if (enums != NULL && (string = enum_string_lookup(enums, int_val))
1731             != NULL)
1732                 fprintf(stdout, "%s", string);
1733         else
1734                 fprintf(stdout, "%d", int_val);
1735 }
1736
1737 static void
1738 snmp_output_ipaddress(struct snmp_toolinfo *snmptoolctx, uint8_t *ip)
1739 {
1740         if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1741                 fprintf(stdout, "%s : ",
1742                     syntax_strings[SNMP_SYNTAX_IPADDRESS].str);
1743
1744         fprintf(stdout, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
1745 }
1746
1747 static void
1748 snmp_output_counter(struct snmp_toolinfo *snmptoolctx, uint32_t counter)
1749 {
1750         if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1751                 fprintf(stdout, "%s : ",
1752                     syntax_strings[SNMP_SYNTAX_COUNTER].str);
1753
1754         fprintf(stdout, "%u", counter);
1755 }
1756
1757 static void
1758 snmp_output_gauge(struct snmp_toolinfo *snmptoolctx, uint32_t gauge)
1759 {
1760         if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1761                 fprintf(stdout, "%s : ", syntax_strings[SNMP_SYNTAX_GAUGE].str);
1762
1763         fprintf(stdout, "%u", gauge);
1764 }
1765
1766 static void
1767 snmp_output_ticks(struct snmp_toolinfo *snmptoolctx, uint32_t ticks)
1768 {
1769         if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1770                 fprintf(stdout, "%s : ",
1771                     syntax_strings[SNMP_SYNTAX_TIMETICKS].str);
1772
1773         fprintf(stdout, "%u", ticks);
1774 }
1775
1776 static void
1777 snmp_output_counter64(struct snmp_toolinfo *snmptoolctx, uint64_t counter64)
1778 {
1779         if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE)
1780                 fprintf(stdout, "%s : ",
1781                     syntax_strings[SNMP_SYNTAX_COUNTER64].str);
1782
1783         fprintf(stdout,"%ju", counter64);
1784 }
1785
1786 int32_t
1787 snmp_output_numval(struct snmp_toolinfo *snmptoolctx, struct snmp_value *val,
1788     struct snmp_oid2str *entry)
1789 {
1790         if (val == NULL)
1791                 return (-1);
1792
1793         if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET)
1794                 fprintf(stdout, " = ");
1795
1796         switch (val->syntax) {
1797             case SNMP_SYNTAX_INTEGER:
1798                 if (entry != NULL)
1799                         snmp_output_int(snmptoolctx, entry->snmp_enum,
1800                             val->v.integer);
1801                 else
1802                         snmp_output_int(snmptoolctx, NULL, val->v.integer);
1803                 break;
1804
1805             case SNMP_SYNTAX_OCTETSTRING:
1806                 if (entry != NULL)
1807                         snmp_output_octetstring(snmptoolctx, entry->tc,
1808                             val->v.octetstring.len, val->v.octetstring.octets);
1809                 else
1810                         snmp_output_octetstring(snmptoolctx, SNMP_STRING,
1811                             val->v.octetstring.len, val->v.octetstring.octets);
1812                 break;
1813
1814             case SNMP_SYNTAX_OID:
1815                 snmp_output_oid_value(snmptoolctx, &(val->v.oid));
1816                 break;
1817
1818             case SNMP_SYNTAX_IPADDRESS:
1819                 snmp_output_ipaddress(snmptoolctx, val->v.ipaddress);
1820                 break;
1821
1822             case SNMP_SYNTAX_COUNTER:
1823                 snmp_output_counter(snmptoolctx, val->v.uint32);
1824                 break;
1825
1826             case SNMP_SYNTAX_GAUGE:
1827                 snmp_output_gauge(snmptoolctx, val->v.uint32);
1828                 break;
1829
1830             case SNMP_SYNTAX_TIMETICKS:
1831                 snmp_output_ticks(snmptoolctx, val->v.uint32);
1832                 break;
1833
1834             case SNMP_SYNTAX_COUNTER64:
1835                 snmp_output_counter64(snmptoolctx, val->v.counter64);
1836                 break;
1837
1838             case SNMP_SYNTAX_NOSUCHOBJECT:
1839                 fprintf(stdout, "No Such Object\n");
1840                 return (val->syntax);
1841
1842             case SNMP_SYNTAX_NOSUCHINSTANCE:
1843                 fprintf(stdout, "No Such Instance\n");
1844                 return (val->syntax);
1845
1846             case SNMP_SYNTAX_ENDOFMIBVIEW:
1847                 fprintf(stdout, "End of Mib View\n");
1848                 return (val->syntax);
1849
1850             case SNMP_SYNTAX_NULL:
1851                 /* NOTREACHED */
1852                 fprintf(stdout, "agent returned NULL Syntax\n");
1853                 return (val->syntax);
1854
1855             default:
1856                 /* NOTREACHED - If here - then all went completely wrong. */
1857                 fprintf(stdout, "agent returned unknown syntax\n");
1858                 return (-1);
1859         }
1860
1861         fprintf(stdout, "\n");
1862
1863         return (0);
1864 }
1865
1866 static int32_t
1867 snmp_fill_object(struct snmp_toolinfo *snmptoolctx, struct snmp_object *obj,
1868     struct snmp_value *val)
1869 {
1870         int32_t rc;
1871         asn_subid_t suboid;
1872
1873         if (obj == NULL || val == NULL)
1874                 return (-1);
1875
1876         if ((suboid = snmp_suboid_pop(&(val->var))) > ASN_MAXID)
1877                 return (-1);
1878
1879         memset(obj, 0, sizeof(struct snmp_object));
1880         asn_append_oid(&(obj->val.var), &(val->var));
1881         obj->val.syntax = val->syntax;
1882
1883         if (obj->val.syntax > 0)
1884                 rc = snmp_lookup_leafstring(snmptoolctx, obj);
1885         else
1886                 rc = snmp_lookup_nonleaf_string(snmptoolctx, obj);
1887
1888         (void) snmp_suboid_append(&(val->var), suboid);
1889         (void) snmp_suboid_append(&(obj->val.var), suboid);
1890
1891         return (rc);
1892 }
1893
1894 static int32_t
1895 snmp_output_index(struct snmp_toolinfo *snmptoolctx, struct index *stx,
1896     struct asn_oid *oid)
1897 {
1898         uint8_t ip[4];
1899         uint32_t bytes = 1;
1900         uint64_t cnt64;
1901         struct asn_oid temp, out;
1902
1903         if (oid->len < bytes)
1904                 return (-1);
1905
1906         memset(&temp, 0, sizeof(struct asn_oid));
1907         asn_append_oid(&temp, oid);
1908
1909         switch (stx->syntax) {
1910             case SNMP_SYNTAX_INTEGER:
1911                 snmp_output_int(snmptoolctx, stx->snmp_enum, temp.subs[0]);
1912                 break;
1913
1914             case SNMP_SYNTAX_OCTETSTRING:
1915                 if ((temp.subs[0] > temp.len -1 ) || (temp.subs[0] >
1916                     ASN_MAXOCTETSTRING))
1917                         return (-1);
1918                 snmp_output_octetindex(snmptoolctx, stx->tc, &temp);
1919                 bytes += temp.subs[0];
1920                 break;
1921
1922             case SNMP_SYNTAX_OID:
1923                 if ((temp.subs[0] > temp.len -1) || (temp.subs[0] >
1924                     ASN_MAXOIDLEN))
1925                         return (-1);
1926
1927                 bytes += temp.subs[0];
1928                 memset(&out, 0, sizeof(struct asn_oid));
1929                 asn_slice_oid(&out, &temp, 1, bytes);
1930                 snmp_output_oid_value(snmptoolctx, &out);
1931                 break;
1932
1933             case SNMP_SYNTAX_IPADDRESS:
1934                 if (temp.len < 4)
1935                         return (-1);
1936                 for (bytes = 0; bytes < 4; bytes++)
1937                         ip[bytes] = temp.subs[bytes];
1938
1939                 snmp_output_ipaddress(snmptoolctx, ip);
1940                 bytes = 4;
1941                 break;
1942
1943             case SNMP_SYNTAX_COUNTER:
1944                 snmp_output_counter(snmptoolctx, temp.subs[0]);
1945                 break;
1946
1947             case SNMP_SYNTAX_GAUGE:
1948                 snmp_output_gauge(snmptoolctx, temp.subs[0]);
1949                 break;
1950
1951             case SNMP_SYNTAX_TIMETICKS:
1952                 snmp_output_ticks(snmptoolctx, temp.subs[0]);
1953                 break;
1954
1955             case SNMP_SYNTAX_COUNTER64:
1956                 if (oid->len < 2)
1957                         return (-1);
1958                 bytes = 2;
1959                 memcpy(&cnt64, temp.subs, bytes);
1960                 snmp_output_counter64(snmptoolctx, cnt64);
1961                 break;
1962
1963             default:
1964                 return (-1);
1965         }
1966
1967         return (bytes);
1968 }
1969
1970 static int32_t
1971 snmp_output_object(struct snmp_toolinfo *snmptoolctx, struct snmp_object *o)
1972 {
1973         int32_t i, first, len;
1974         struct asn_oid oid;
1975         struct index *temp;
1976
1977         if (ISSET_NUMERIC(snmptoolctx))
1978                 return (-1);
1979
1980         if (o->info->table_idx == NULL) {
1981                 fprintf(stdout,"%s.%d", o->info->string,
1982                     o->val.var.subs[o->val.var.len - 1]);
1983                 return (1);
1984         }
1985
1986         fprintf(stdout,"%s[", o->info->string);
1987         memset(&oid, 0, sizeof(struct asn_oid));
1988
1989         len = 1;
1990         asn_slice_oid(&oid, &(o->val.var), (o->info->table_idx->var.len + len),
1991             o->val.var.len);
1992
1993         first = 1;
1994         STAILQ_FOREACH(temp, &(OBJECT_IDX_LIST(o)), link) {
1995                 if(first)
1996                         first = 0;
1997                 else
1998                         fprintf(stdout, ", ");
1999                 if ((i = snmp_output_index(snmptoolctx, temp, &oid)) < 0)
2000                         break;
2001                 len += i;
2002                 memset(&oid, 0, sizeof(struct asn_oid));
2003                 asn_slice_oid(&oid, &(o->val.var),
2004                     (o->info->table_idx->var.len + len), o->val.var.len + 1);
2005         }
2006
2007         fprintf(stdout,"]");
2008         return (1);
2009 }
2010
2011 void
2012 snmp_output_err_resp(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu)
2013 {
2014         char buf[ASN_OIDSTRLEN];
2015         struct snmp_object object;
2016
2017         if (pdu == NULL || (pdu->error_index > (int32_t) pdu->nbindings)) {
2018                 fprintf(stdout,"Invalid error index in PDU\n");
2019                 return;
2020         }
2021
2022         fprintf(stdout, "Agent %s:%s returned error \n", snmp_client.chost,
2023             snmp_client.cport);
2024
2025         if (!ISSET_NUMERIC(snmptoolctx) && (snmp_fill_object(snmptoolctx, &object,
2026             &(pdu->bindings[pdu->error_index - 1])) > 0))
2027                 snmp_output_object(snmptoolctx, &object);
2028         else {
2029                 asn_oid2str_r(&(pdu->bindings[pdu->error_index - 1].var), buf);
2030                 fprintf(stdout,"%s", buf);
2031         }
2032
2033         fprintf(stdout," caused error - ");
2034         if ((pdu->error_status > 0) && (pdu->error_status <=
2035             SNMP_ERR_INCONS_NAME))
2036                 fprintf(stdout, "%s\n", error_strings[pdu->error_status].str);
2037         else
2038                 fprintf(stdout,"%s\n", error_strings[SNMP_ERR_UNKNOWN].str);
2039 }
2040
2041 int32_t
2042 snmp_output_resp(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu,
2043     struct asn_oid *root)
2044 {
2045         int32_t error;
2046         char p[ASN_OIDSTRLEN];
2047         uint32_t i;
2048         struct snmp_object object;
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]), object.info);
2067                 i++;
2068         }
2069
2070         if (error)
2071                 return (-1);
2072
2073         return (i);
2074 }
2075
2076 void
2077 snmp_output_engine(void)
2078 {
2079         uint32_t i;
2080         char *cptr, engine[2 * SNMP_ENGINE_ID_SIZ + 2];
2081
2082         cptr = engine;
2083         for (i = 0; i < snmp_client.engine.engine_len; i++)
2084                 cptr += sprintf(cptr, "%.2x", snmp_client.engine.engine_id[i]);
2085         *cptr++ = '\0';
2086
2087         fprintf(stdout, "Engine ID 0x%s\n", engine);
2088         fprintf(stdout, "Boots : %u\t\tTime : %d\n",
2089             snmp_client.engine.engine_boots,
2090             snmp_client.engine.engine_time);
2091 }
2092
2093 void
2094 snmp_output_keys(void)
2095 {
2096         uint32_t i, keylen = 0;
2097         char *cptr, extkey[2 * SNMP_AUTH_KEY_SIZ + 2];
2098
2099         fprintf(stdout, "Localized keys for %s\n", snmp_client.user.sec_name);
2100         if (snmp_client.user.auth_proto == SNMP_AUTH_HMAC_MD5) {
2101                 fprintf(stdout, "MD5 : 0x");
2102                 keylen = SNMP_AUTH_HMACMD5_KEY_SIZ;
2103         } else if (snmp_client.user.auth_proto == SNMP_AUTH_HMAC_SHA) {
2104                 fprintf(stdout, "SHA : 0x");
2105                 keylen = SNMP_AUTH_HMACSHA_KEY_SIZ;
2106         }
2107         if (snmp_client.user.auth_proto != SNMP_AUTH_NOAUTH) {
2108                 cptr = extkey;
2109                 for (i = 0; i < keylen; i++)
2110                         cptr += sprintf(cptr, "%.2x",
2111                             snmp_client.user.auth_key[i]);
2112                 *cptr++ = '\0';
2113                 fprintf(stdout, "%s\n", extkey);
2114         }
2115
2116         if (snmp_client.user.priv_proto == SNMP_PRIV_DES) {
2117                 fprintf(stdout, "DES : 0x");
2118                 keylen = SNMP_PRIV_DES_KEY_SIZ;
2119         } else if (snmp_client.user.priv_proto == SNMP_PRIV_AES) {
2120                 fprintf(stdout, "AES : 0x");
2121                 keylen = SNMP_PRIV_AES_KEY_SIZ;
2122         }
2123         if (snmp_client.user.priv_proto != SNMP_PRIV_NOPRIV) {
2124                 cptr = extkey;
2125                 for (i = 0; i < keylen; i++)
2126                         cptr += sprintf(cptr, "%.2x",
2127                             snmp_client.user.priv_key[i]);
2128                 *cptr++ = '\0';
2129                 fprintf(stdout, "%s\n", extkey);
2130         }
2131 }