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