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