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