]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.c
MFC r310892,r310894,r310989:
[FreeBSD/stable/10.git] / usr.sbin / bsnmpd / tools / bsnmptools / bsnmpget.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  * Bsnmpget and bsnmpwalk are simple tools for querying SNMP agents,
30  * bsnmpset can be used to set MIB objects in an agent.
31  *
32  * $FreeBSD$
33  */
34
35 #include <sys/queue.h>
36 #include <sys/types.h>
37
38 #include <assert.h>
39 #include <ctype.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <stdarg.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 static const char *program_name = NULL;
56 static enum program_e {
57         BSNMPGET,
58         BSNMPWALK,
59         BSNMPSET
60 } program;
61
62 /* *****************************************************************************
63  * Common bsnmptools functions.
64  */
65 static void
66 usage(void)
67 {
68         fprintf(stderr,
69 "Usage:\n"
70 "%s %s [-A options] [-b buffersize] [-C options] [-I options]\n"
71 "\t[-i filelist] [-l filename]%s [-o output] [-P options]\n"
72 "\t%s[-r retries] [-s [trans::][community@][server][:port]]\n"
73 "\t[-t timeout] [-U options] [-v version]%s\n",
74         program_name,
75         (program == BSNMPGET) ? "[-aDdehnK]" :
76             (program == BSNMPWALK) ? "[-dhnK]" :
77             (program == BSNMPSET) ? "[-adehnK]" :
78             "",
79         (program == BSNMPGET || program == BSNMPWALK) ?
80         " [-M max-repetitions] [-N non-repeaters]" : "",
81         (program == BSNMPGET || program == BSNMPWALK) ? "[-p pdu] " : "",
82         (program == BSNMPGET) ? " OID [OID ...]" :
83             (program == BSNMPWALK || program == BSNMPSET) ? " [OID ...]" :
84             ""
85         );
86 }
87
88 static int32_t
89 parse_max_repetitions(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
90 {
91         uint32_t v;
92
93         assert(opt_arg != NULL);
94
95         v = strtoul(opt_arg, (void *) NULL, 10);
96
97         if (v > SNMP_MAX_BINDINGS) {
98                 warnx("Max repetitions value greater than %d maximum allowed.",
99                     SNMP_MAX_BINDINGS);
100                 return (-1);
101         }
102
103         SET_MAXREP(snmptoolctx, v);
104         return (2);
105 }
106
107 static int32_t
108 parse_non_repeaters(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
109 {
110         uint32_t v;
111
112         assert(opt_arg != NULL);
113
114         v = strtoul(opt_arg, (void *) NULL, 10);
115
116         if (v > SNMP_MAX_BINDINGS) {
117                 warnx("Non repeaters value greater than %d maximum allowed.",
118                     SNMP_MAX_BINDINGS);
119                 return (-1);
120         }
121
122         SET_NONREP(snmptoolctx, v);
123         return (2);
124 }
125
126 static int32_t
127 parse_pdu_type(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
128 {
129         assert(opt_arg != NULL);
130
131         if (strcasecmp(opt_arg, "getbulk") == 0)
132                 SET_PDUTYPE(snmptoolctx, SNMP_PDU_GETBULK);
133         else if (strcasecmp(opt_arg, "getnext") == 0)
134                 SET_PDUTYPE(snmptoolctx, SNMP_PDU_GETNEXT);
135         else if (strcasecmp(opt_arg, "get") == 0)
136                 SET_PDUTYPE(snmptoolctx, SNMP_PDU_GET);
137         else {
138                 warnx("PDU type '%s' not supported.", opt_arg);
139                 return (-1);
140         }
141
142         return (2);
143 }
144
145 static int32_t
146 snmptool_parse_options(struct snmp_toolinfo *snmptoolctx, int argc, char **argv)
147 {
148         int32_t count, optnum = 0;
149         int ch;
150         const char *opts;
151
152         switch (program) {
153                 case BSNMPWALK:
154                         opts = "dhnKA:b:C:I:i:l:M:N:o:P:p:r:s:t:U:v:";
155                         break;
156                 case BSNMPGET:
157                         opts = "aDdehnKA:b:C:I:i:l:M:N:o:P:p:r:s:t:U:v:";
158                         break;
159                 case BSNMPSET:
160                         opts = "adehnKA:b:C:I:i:l:o:P:r:s:t:U:v:";
161                         break;
162                 default:
163                         return (-1);
164         }
165
166         while ((ch = getopt(argc, argv, opts)) != EOF) {
167                 switch (ch) {
168                 case 'A':
169                         count = parse_authentication(snmptoolctx, optarg);
170                         break;
171                 case 'a':
172                         count = parse_skip_access(snmptoolctx);
173                         break;
174                 case 'b':
175                         count = parse_buflen(optarg);
176                         break;
177                 case 'D':
178                         count = parse_discovery(snmptoolctx);
179                         break;
180                 case 'd':
181                         count = parse_debug();
182                         break;
183                 case 'e':
184                         count = parse_errors(snmptoolctx);
185                         break;
186                 case 'h':
187                         usage();
188                         return (-2);
189                 case 'C':
190                         count = parse_context(snmptoolctx, optarg);
191                         break;
192                 case 'I':
193                         count = parse_include(snmptoolctx, optarg);
194                         break;
195                 case 'i':
196                         count = parse_file(snmptoolctx, optarg);
197                         break;
198                 case 'K':
199                         count = parse_local_key(snmptoolctx);
200                         break;
201                 case 'l':
202                         count = parse_local_path(optarg);
203                         break;
204                 case 'M':
205                         count = parse_max_repetitions(snmptoolctx, optarg);
206                         break;
207                 case 'N':
208                         count = parse_non_repeaters(snmptoolctx, optarg);
209                         break;
210                 case 'n':
211                         count = parse_num_oids(snmptoolctx);
212                         break;
213                 case 'o':
214                         count = parse_output(snmptoolctx, optarg);
215                         break;
216                 case 'P':
217                         count = parse_privacy(snmptoolctx, optarg);
218                         break;
219                 case 'p':
220                         count = parse_pdu_type(snmptoolctx, optarg);
221                         break;
222                 case 'r':
223                         count = parse_retry(optarg);
224                         break;
225                 case 's':
226                         count = parse_server(optarg);
227                         break;
228                 case 't':
229                         count = parse_timeout(optarg);
230                         break;
231                 case 'U':
232                         count = parse_user_security(snmptoolctx, optarg);
233                         break;
234                 case 'v':
235                         count = parse_version(optarg);
236                         break;
237                 case '?':
238                 default:
239                         usage();
240                         return (-1);
241                 }
242                 if (count < 0)
243                         return (-1);
244             optnum += count;
245         }
246
247         return (optnum);
248 }
249
250 /*
251  * Read user input OID - one of following formats:
252  * 1) 1.2.1.1.2.1.0 - that is if option numeric was given;
253  * 2) string - in such case append .0 to the asn_oid subs;
254  * 3) string.1 - no additional processing required in such case.
255  */
256 static char *
257 snmptools_parse_stroid(struct snmp_toolinfo *snmptoolctx,
258     struct snmp_object *obj, char *argv)
259 {
260         char string[MAXSTR], *str;
261         int32_t i = 0;
262         struct asn_oid in_oid;
263
264         str = argv;
265
266         if (*str == '.')
267                 str++;
268
269         while (isalpha(*str) || *str == '_' || (i != 0 && isdigit(*str))) {
270                 str++;
271                 i++;
272         }
273
274         if (i <= 0 || i >= MAXSTR)
275                 return (NULL);
276
277         memset(&in_oid, 0, sizeof(struct asn_oid));
278         if ((str = snmp_parse_suboid((argv + i), &in_oid)) == NULL) {
279                 warnx("Invalid OID - %s", argv);
280                 return (NULL);
281         }
282
283         strlcpy(string, argv, i + 1);
284         if (snmp_lookup_oidall(snmptoolctx, obj, string) < 0) {
285                 warnx("No entry for %s in mapping lists", string);
286                 return (NULL);
287         }
288
289         /* If OID given on command line append it. */
290         if (in_oid.len > 0)
291                 asn_append_oid(&(obj->val.var), &in_oid);
292         else if (*str == '[') {
293                 if ((str = snmp_parse_index(snmptoolctx, str + 1, obj)) == NULL)
294                         return (NULL);
295         } else if (obj->val.syntax > 0 && GET_PDUTYPE(snmptoolctx) ==
296             SNMP_PDU_GET) {
297                 if (snmp_suboid_append(&(obj->val.var), (asn_subid_t) 0) < 0)
298                         return (NULL);
299         }
300
301         return (str);
302 }
303
304 static int32_t
305 snmptools_parse_oid(struct snmp_toolinfo *snmptoolctx,
306     struct snmp_object *obj, char *argv)
307 {
308         if (argv == NULL)
309                 return (-1);
310
311         if (ISSET_NUMERIC(snmptoolctx)) {
312                 if (snmp_parse_numoid(argv, &(obj->val.var)) < 0)
313                         return (-1);
314         } else {
315                 if (snmptools_parse_stroid(snmptoolctx, obj, argv) == NULL &&
316                     snmp_parse_numoid(argv, &(obj->val.var)) < 0)
317                         return (-1);
318         }
319
320         return (1);
321 }
322
323 static int32_t
324 snmptool_add_vbind(struct snmp_pdu *pdu, struct snmp_object *obj)
325 {
326         if (obj->error > 0)
327                 return (0);
328
329         asn_append_oid(&(pdu->bindings[pdu->nbindings].var), &(obj->val.var));
330         pdu->nbindings++;
331
332         return (pdu->nbindings);
333 }
334
335 /* *****************************************************************************
336  * bsnmpget private functions.
337  */
338 static int32_t
339 snmpget_verify_vbind(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu,
340     struct snmp_object *obj)
341 {
342         if (pdu->version == SNMP_V1 && obj->val.syntax ==
343             SNMP_SYNTAX_COUNTER64) {
344                 warnx("64-bit counters are not supported in SNMPv1 PDU");
345                 return (-1);
346         }
347
348         if (ISSET_NUMERIC(snmptoolctx) || pdu->type == SNMP_PDU_GETNEXT ||
349             pdu->type == SNMP_PDU_GETBULK)
350                 return (1);
351
352         if (pdu->type == SNMP_PDU_GET && obj->val.syntax == SNMP_SYNTAX_NULL) {
353                 warnx("Only leaf object values can be added to GET PDU");
354                 return (-1);
355         }
356
357         return (1);
358 }
359
360 /*
361  * In case of a getbulk PDU, the error_status and error_index fields are used by
362  * libbsnmp to hold the values of the non-repeaters and max-repetitions fields
363  * that are present only in the getbulk - so before sending the PDU make sure
364  * these have correct values as well.
365  */
366 static void
367 snmpget_fix_getbulk(struct snmp_pdu *pdu, uint32_t max_rep, uint32_t non_rep)
368 {
369         assert(pdu != NULL);
370
371         if (pdu->nbindings < non_rep)
372                 pdu->error_status = pdu->nbindings;
373         else
374                 pdu->error_status = non_rep;
375
376         if (max_rep > 0)
377                 pdu->error_index = max_rep;
378         else
379                 pdu->error_index = 1;
380 }
381
382 static int
383 snmptool_get(struct snmp_toolinfo *snmptoolctx)
384 {
385         struct snmp_pdu req, resp;
386
387         snmp_pdu_create(&req, GET_PDUTYPE(snmptoolctx));
388
389         while ((snmp_pdu_add_bindings(snmptoolctx, snmpget_verify_vbind,
390              snmptool_add_vbind, &req, SNMP_MAX_BINDINGS)) > 0) {
391
392                 if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK)
393                         snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx),
394                             GET_NONREP(snmptoolctx));
395
396                 if (snmp_dialog(&req, &resp) == -1) {
397                         warn("Snmp dialog");
398                         break;
399                 }
400
401                 if (snmp_parse_resp(&resp, &req) >= 0) {
402                         snmp_output_resp(snmptoolctx, &resp, NULL);
403                         snmp_pdu_free(&resp);
404                         break;
405                 }
406
407                 snmp_output_err_resp(snmptoolctx, &resp);
408                 if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK ||
409                     !ISSET_RETRY(snmptoolctx)) {
410                         snmp_pdu_free(&resp);
411                         break;
412                 }
413
414                 /*
415                  * Loop through the object list and set object->error to the
416                  * varbinding that caused the error.
417                  */
418                 if (snmp_object_seterror(snmptoolctx,
419                     &(resp.bindings[resp.error_index - 1]),
420                     resp.error_status) <= 0) {
421                         snmp_pdu_free(&resp);
422                         break;
423                 }
424
425                 fprintf(stderr, "Retrying...\n");
426                 snmp_pdu_free(&resp);
427                 snmp_pdu_create(&req, GET_PDUTYPE(snmptoolctx));
428         }
429
430         snmp_pdu_free(&req);
431
432         return (0);
433 }
434
435
436 /* *****************************************************************************
437  * bsnmpwalk private functions.
438  */
439 /* The default tree to walk. */
440 static const struct asn_oid snmp_mibII_OID = {
441         6 , { 1, 3, 6, 1, 2, 1 }
442 };
443
444 static int32_t
445 snmpwalk_add_default(struct snmp_toolinfo *snmptoolctx __unused,
446     struct snmp_object *obj, char *string __unused)
447 {
448         asn_append_oid(&(obj->val.var), &snmp_mibII_OID);
449         return (1);
450 }
451
452 /*
453  * Prepare the next GetNext/Get PDU to send.
454  */
455 static void
456 snmpwalk_nextpdu_create(uint32_t op, struct asn_oid *var, struct snmp_pdu *pdu)
457 {
458         snmp_pdu_create(pdu, op);
459         asn_append_oid(&(pdu->bindings[0].var), var);
460         pdu->nbindings = 1;
461 }
462
463 static int
464 snmptool_walk(struct snmp_toolinfo *snmptoolctx)
465 {
466         struct snmp_pdu req, resp;
467         struct asn_oid root;    /* Keep the initial oid. */
468         int32_t outputs, rc;
469         uint32_t op;
470
471         if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK)
472                 op = SNMP_PDU_GETBULK;
473         else
474                 op = SNMP_PDU_GETNEXT;
475
476         snmp_pdu_create(&req, op);
477
478         while ((rc = snmp_pdu_add_bindings(snmptoolctx, NULL,
479             snmptool_add_vbind, &req, 1)) > 0) {
480
481                 /* Remember the root where the walk started from. */
482                 memset(&root, 0, sizeof(struct asn_oid));
483                 asn_append_oid(&root, &(req.bindings[0].var));
484
485                 if (op == SNMP_PDU_GETBULK)
486                         snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx),
487                             GET_NONREP(snmptoolctx));
488
489                 outputs = 0;
490                 while (snmp_dialog(&req, &resp) >= 0) {
491                         if ((snmp_parse_resp(&resp, &req)) < 0) {
492                                 snmp_output_err_resp(snmptoolctx, &resp);
493                                 snmp_pdu_free(&resp);
494                                 outputs = -1;
495                                 break;
496                         }
497
498                         rc = snmp_output_resp(snmptoolctx, &resp, &root);
499                         if (rc < 0) {
500                                 snmp_pdu_free(&resp);
501                                 outputs = -1;
502                                 break;
503                         }
504
505                         outputs += rc;
506
507                         if ((u_int)rc < resp.nbindings) {
508                                 snmp_pdu_free(&resp);
509                                 break;
510                         }
511
512                         snmpwalk_nextpdu_create(op,
513                             &(resp.bindings[resp.nbindings - 1].var), &req);
514                         if (op == SNMP_PDU_GETBULK)
515                                 snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx),
516                                     GET_NONREP(snmptoolctx));
517                         snmp_pdu_free(&resp);
518                 }
519
520                 /* Just in case our root was a leaf. */
521                 if (outputs == 0) {
522                         snmpwalk_nextpdu_create(SNMP_PDU_GET, &root, &req);
523                         if (snmp_dialog(&req, &resp) == SNMP_CODE_OK) {
524                                 if (snmp_parse_resp(&resp, &req) < 0)
525                                         snmp_output_err_resp(snmptoolctx, &resp);
526                                 else
527                                         snmp_output_resp(snmptoolctx, &resp,
528                                             NULL);
529                                 snmp_pdu_free(&resp);
530                         } else
531                                 warn("Snmp dialog");
532                 }
533
534                 if (snmp_object_remove(snmptoolctx, &root) < 0) {
535                         warnx("snmp_object_remove");
536                         break;
537                 }
538
539                 snmp_pdu_free(&req);
540                 snmp_pdu_create(&req, op);
541         }
542
543         snmp_pdu_free(&req);
544
545         if (rc == 0)
546                 return (0);
547         else
548                 return (1);
549 }
550
551 /* *****************************************************************************
552  * bsnmpset private functions.
553  */
554
555 static int32_t
556 parse_oid_numeric(struct snmp_value *value, char *val)
557 {
558         char *endptr;
559         int32_t saved_errno;
560         asn_subid_t suboid;
561
562         do {
563                 saved_errno = errno;
564                 errno = 0;
565                 suboid = strtoul(val, &endptr, 10);
566                 if (errno != 0) {
567                         warn("Value %s not supported", val);
568                         errno = saved_errno;
569                         return (-1);
570                 }
571                 errno = saved_errno;
572                 if ((asn_subid_t) suboid > ASN_MAXID) {
573                         warnx("Suboid %u > ASN_MAXID", suboid);
574                         return (-1);
575                 }
576                 if (snmp_suboid_append(&(value->v.oid), suboid) < 0)
577                         return (-1);
578                 val = endptr + 1;
579         } while (*endptr == '.');
580
581         if (*endptr != '\0')
582                 warnx("OID value %s not supported", val);
583
584         value->syntax = SNMP_SYNTAX_OID;
585         return (0);
586 }
587
588 /*
589  * Allow OID leaf in both forms:
590  * 1) 1.3.6.1.2... ->  in such case call directly the function reading raw OIDs;
591  * 2) begemotSnmpdAgentFreeBSD -> lookup the ASN OID corresponding to that.
592  */
593 static int32_t
594 parse_oid_string(struct snmp_toolinfo *snmptoolctx,
595     struct snmp_value *value, char *string)
596 {
597         struct snmp_object obj;
598
599         if (isdigit(string[0]))
600                 return (parse_oid_numeric(value, string));
601
602         memset(&obj, 0, sizeof(struct snmp_object));
603         if (snmp_lookup_enumoid(snmptoolctx, &obj, string) < 0) {
604                 warnx("Unknown OID enum string - %s", string);
605                 return (-1);
606         }
607
608         asn_append_oid(&(value->v.oid), &(obj.val.var));
609         return (1);
610 }
611
612 static int32_t
613 parse_ip(struct snmp_value * value, char * val)
614 {
615         char *endptr, *str;
616         int32_t i;
617         uint32_t v;
618
619         str = val;
620         for (i = 0; i < 4; i++) {
621                 v = strtoul(str, &endptr, 10);
622                 if (v > 0xff)
623                         return (-1);
624                 if (*endptr != '.' && *endptr != '\0' && i != 3)
625                         break;
626                 str = endptr + 1;
627                 value->v.ipaddress[i] = (uint8_t) v;
628         }
629         value->syntax = SNMP_SYNTAX_IPADDRESS;
630
631         return (0);
632 }
633
634 static int32_t
635 parse_int(struct snmp_value *value, char *val)
636 {
637         char *endptr;
638         int32_t v, saved_errno;
639
640         saved_errno = errno;
641         errno = 0;
642
643         v = strtol(val, &endptr, 10);
644
645         if (errno != 0) {
646                 warn("Value %s not supported", val);
647                 errno = saved_errno;
648                 return (-1);
649         }
650
651         value->syntax = SNMP_SYNTAX_INTEGER;
652         value->v.integer = v;
653         errno = saved_errno;
654
655         return (0);
656 }
657
658 static int32_t
659 parse_int_string(struct snmp_object *object, char *val)
660 {
661         int32_t v;
662
663         if (isdigit(val[0]))
664                 return ((parse_int(&(object->val), val)));
665
666         if (object->info == NULL) {
667                 warnx("Unknown enumerated integer type - %s", val);
668                 return (-1);
669         }
670         if ((v = enum_number_lookup(object->info->snmp_enum, val)) < 0)
671                 warnx("Unknown enumerated integer type - %s", val);
672
673         object->val.v.integer = v;
674         return (1);
675 }
676
677 /*
678  * Here syntax may be one of SNMP_SYNTAX_COUNTER, SNMP_SYNTAX_GAUGE,
679  * SNMP_SYNTAX_TIMETICKS.
680  */
681 static int32_t
682 parse_uint(struct snmp_value *value, char *val)
683 {
684         char *endptr;
685         uint32_t v = 0;
686         int32_t saved_errno;
687
688         saved_errno = errno;
689         errno = 0;
690
691         v = strtoul(val, &endptr, 10);
692
693         if (errno != 0) {
694                 warn("Value %s not supported", val);
695                 errno = saved_errno;
696                 return (-1);
697         }
698
699         value->v.uint32 = v;
700         errno = saved_errno;
701
702         return (0);
703 }
704
705 static int32_t
706 parse_ticks(struct snmp_value *value, char *val)
707 {
708         if (parse_uint(value, val) < 0)
709                 return (-1);
710
711         value->syntax = SNMP_SYNTAX_TIMETICKS;
712         return (0);
713 }
714
715 static int32_t
716 parse_gauge(struct snmp_value *value, char *val)
717 {
718         if (parse_uint(value, val) < 0)
719                 return (-1);
720
721         value->syntax = SNMP_SYNTAX_GAUGE;
722         return (0);
723 }
724
725 static int32_t
726 parse_counter(struct snmp_value *value, char *val)
727 {
728         if (parse_uint(value, val) < 0)
729                 return (-1);
730
731         value->syntax = SNMP_SYNTAX_COUNTER;
732         return (0);
733 }
734
735 static int32_t
736 parse_uint64(struct snmp_value *value, char *val)
737 {
738         char *endptr;
739         int32_t saved_errno;
740         uint64_t v;
741
742         saved_errno = errno;
743         errno = 0;
744
745         v = strtoull(val, &endptr, 10);
746
747         if (errno != 0) {
748                 warnx("Value %s not supported", val);
749                 errno = saved_errno;
750                 return (-1);
751         }
752
753         value->syntax = SNMP_SYNTAX_COUNTER64;
754         value->v.counter64 = v;
755         errno = saved_errno;
756
757         return (0);
758 }
759
760 static int32_t
761 parse_syntax_val(struct snmp_value *value, enum snmp_syntax syntax, char *val)
762 {
763         switch (syntax) {
764                 case SNMP_SYNTAX_INTEGER:
765                         return (parse_int(value, val));
766                 case SNMP_SYNTAX_IPADDRESS:
767                         return (parse_ip(value, val));
768                 case SNMP_SYNTAX_COUNTER:
769                         return (parse_counter(value, val));
770                 case SNMP_SYNTAX_GAUGE:
771                         return (parse_gauge(value, val));
772                 case SNMP_SYNTAX_TIMETICKS:
773                         return (parse_ticks(value, val));
774                 case SNMP_SYNTAX_COUNTER64:
775                         return (parse_uint64(value, val));
776                 case SNMP_SYNTAX_OCTETSTRING:
777                         return (snmp_tc2oct(SNMP_STRING, value, val));
778                 case SNMP_SYNTAX_OID:
779                         return (parse_oid_numeric(value, val));
780                 default:
781                         /* NOTREACHED */
782                         break;
783         }
784
785         return (-1);
786 }
787
788 /*
789  * Parse a command line argument of type OID=syntax:value and fill in whatever
790  * fields can be derived from the input into snmp_value structure. Reads numeric
791  * OIDs.
792  */
793 static int32_t
794 parse_pair_numoid_val(char *str, struct snmp_value *snmp_val)
795 {
796         int32_t cnt;
797         char *ptr;
798         enum snmp_syntax syntax;
799         char oid_str[ASN_OIDSTRLEN];
800
801         ptr = str;
802         for (cnt = 0; cnt < ASN_OIDSTRLEN; cnt++)
803                 if (ptr[cnt] == '=')
804                         break;
805
806         if (cnt >= ASN_OIDSTRLEN) {
807                 warnx("OID too long - %s", str);
808                 return (-1);
809         }
810         strlcpy(oid_str, ptr, (size_t) (cnt + 1));
811
812         ptr = str + cnt + 1;
813         for (cnt = 0; cnt < MAX_CMD_SYNTAX_LEN; cnt++)
814                 if(ptr[cnt] == ':')
815                         break;
816
817         if (cnt >= MAX_CMD_SYNTAX_LEN) {
818                 warnx("Unknown syntax in OID - %s", str);
819                 return (-1);
820         }
821
822         if ((syntax = parse_syntax(ptr)) <= SNMP_SYNTAX_NULL) {
823                 warnx("Unknown syntax in OID - %s", ptr);
824                 return (-1);
825         }
826
827         ptr = ptr + cnt + 1;
828         for (cnt = 0; cnt < MAX_OCTSTRING_LEN; cnt++)
829                 if (ptr[cnt] == '\0')
830                         break;
831
832         if (ptr[cnt] != '\0') {
833                 warnx("Value string too long - %s", ptr);
834                 return (-1);
835         }
836
837         /*
838          * Here try parsing the OIDs and syntaxes and then check values - have
839          * to know syntax to check value boundaries.
840          */
841         if (snmp_parse_numoid(oid_str, &(snmp_val->var)) < 0) {
842                 warnx("Error parsing OID %s", oid_str);
843                 return (-1);
844         }
845
846         if (parse_syntax_val(snmp_val, syntax, ptr) < 0)
847                 return (-1);
848
849         return (1);
850 }
851
852 static int32_t
853 parse_syntax_strval(struct snmp_toolinfo *snmptoolctx,
854     struct snmp_object *object, char *str)
855 {
856         uint32_t len;
857         enum snmp_syntax syn;
858
859         /*
860          * Syntax string here not required  - still may be present.
861          */
862
863         if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) {
864                 for (len = 0 ; *(str + len) != ':'; len++) {
865                         if (*(str + len) == '\0') {
866                                 warnx("Syntax missing in value - %s", str);
867                                 return (-1);
868                         }
869                 }
870                 if ((syn = parse_syntax(str)) <= SNMP_SYNTAX_NULL) {
871                         warnx("Unknown syntax in - %s", str);
872                         return (-1);
873                 }
874                 if (syn != object->val.syntax) {
875                         if (!ISSET_ERRIGNORE(snmptoolctx)) {
876                                 warnx("Bad syntax in - %s", str);
877                                 return (-1);
878                         } else
879                                 object->val.syntax = syn;
880                 }
881                 len++;
882         } else
883                 len = 0;
884
885         switch (object->val.syntax) {
886                 case SNMP_SYNTAX_INTEGER:
887                         return (parse_int_string(object, str + len));
888                 case SNMP_SYNTAX_IPADDRESS:
889                         return (parse_ip(&(object->val), str + len));
890                 case SNMP_SYNTAX_COUNTER:
891                         return (parse_counter(&(object->val), str + len));
892                 case SNMP_SYNTAX_GAUGE:
893                         return (parse_gauge(&(object->val), str + len));
894                 case SNMP_SYNTAX_TIMETICKS:
895                         return (parse_ticks(&(object->val), str + len));
896                 case SNMP_SYNTAX_COUNTER64:
897                         return (parse_uint64(&(object->val), str + len));
898                 case SNMP_SYNTAX_OCTETSTRING:
899                         return (snmp_tc2oct(object->info->tc, &(object->val),
900                             str + len));
901                 case SNMP_SYNTAX_OID:
902                         return (parse_oid_string(snmptoolctx, &(object->val),
903                             str + len));
904                 default:
905                         /* NOTREACHED */
906                         break;
907         }
908
909         return (-1);
910 }
911
912 static int32_t
913 parse_pair_stroid_val(struct snmp_toolinfo *snmptoolctx,
914     struct snmp_object *obj, char *argv)
915 {
916         char *ptr;
917
918         if ((ptr = snmptools_parse_stroid(snmptoolctx, obj, argv)) == NULL)
919                 return (-1);
920
921         if (*ptr != '=') {
922                 warnx("Value to set expected after OID");
923                 return (-1);
924         }
925
926         if (parse_syntax_strval(snmptoolctx, obj, ptr + 1) < 0)
927                 return (-1);
928
929         return (1);
930 }
931
932
933 static int32_t
934 snmpset_parse_oid(struct snmp_toolinfo *snmptoolctx,
935     struct snmp_object *obj, char *argv)
936 {
937         if (argv == NULL)
938                 return (-1);
939
940         if (ISSET_NUMERIC(snmptoolctx)) {
941                 if (parse_pair_numoid_val(argv, &(obj->val)) < 0)
942                         return (-1);
943         } else {
944                 if (parse_pair_stroid_val(snmptoolctx, obj, argv) < 0)
945                         return (-1);
946         }
947
948         return (1);
949 }
950
951 static int32_t
952 add_ip_syntax(struct snmp_value *dst, struct snmp_value *src)
953 {
954         int8_t i;
955
956         dst->syntax = SNMP_SYNTAX_IPADDRESS;
957         for (i = 0; i < 4; i++)
958                 dst->v.ipaddress[i] = src->v.ipaddress[i];
959
960         return (1);
961 }
962
963 static int32_t
964 add_octstring_syntax(struct snmp_value *dst, struct snmp_value *src)
965 {
966         if (src->v.octetstring.len > ASN_MAXOCTETSTRING) {
967                 warnx("OctetString len too big - %u", src->v.octetstring.len);
968                 return (-1);
969         }
970
971         if ((dst->v.octetstring.octets = malloc(src->v.octetstring.len)) ==
972             NULL) {
973                 syslog(LOG_ERR, "malloc() failed - %s", strerror(errno));
974                 return (-1);
975         }
976
977         memcpy(dst->v.octetstring.octets, src->v.octetstring.octets,
978             src->v.octetstring.len);
979         dst->syntax = SNMP_SYNTAX_OCTETSTRING;
980         dst->v.octetstring.len = src->v.octetstring.len;
981
982         return(0);
983 }
984
985 static int32_t
986 add_oid_syntax(struct snmp_value *dst, struct snmp_value *src)
987 {
988         asn_append_oid(&(dst->v.oid), &(src->v.oid));
989         dst->syntax = SNMP_SYNTAX_OID;
990         return (0);
991 }
992
993 /*
994  * Check syntax - if one of SNMP_SYNTAX_NULL, SNMP_SYNTAX_NOSUCHOBJECT,
995  * SNMP_SYNTAX_NOSUCHINSTANCE, SNMP_SYNTAX_ENDOFMIBVIEW or anything not known -
996  * return error.
997  */
998 static int32_t
999 snmpset_add_value(struct snmp_value *dst, struct snmp_value *src)
1000 {
1001         if (dst == NULL || src == NULL)
1002                 return (-1);
1003
1004         switch (src->syntax) {
1005                 case SNMP_SYNTAX_INTEGER:
1006                         dst->v.integer = src->v.integer;
1007                         dst->syntax = SNMP_SYNTAX_INTEGER;
1008                         break;
1009                 case SNMP_SYNTAX_TIMETICKS:
1010                         dst->v.uint32 = src->v.uint32;
1011                         dst->syntax = SNMP_SYNTAX_TIMETICKS;
1012                         break;
1013                 case SNMP_SYNTAX_GAUGE:
1014                         dst->v.uint32 = src->v.uint32;
1015                         dst->syntax = SNMP_SYNTAX_GAUGE;
1016                         break;
1017                 case SNMP_SYNTAX_COUNTER:
1018                         dst->v.uint32 = src->v.uint32;
1019                         dst->syntax = SNMP_SYNTAX_COUNTER;
1020                         break;
1021                 case SNMP_SYNTAX_COUNTER64:
1022                         dst->v.counter64 = src->v.counter64;
1023                         dst->syntax = SNMP_SYNTAX_COUNTER64;
1024                         break;
1025                 case SNMP_SYNTAX_IPADDRESS:
1026                         add_ip_syntax(dst, src);
1027                         break;
1028                 case SNMP_SYNTAX_OCTETSTRING:
1029                         add_octstring_syntax(dst, src);
1030                         break;
1031                 case SNMP_SYNTAX_OID:
1032                         add_oid_syntax(dst, src);
1033                         break;
1034                 default:
1035                         warnx("Unknown syntax %d", src->syntax);
1036                         return (-1);
1037         }
1038
1039         return (0);
1040 }
1041
1042 static int32_t
1043 snmpset_verify_vbind(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu,
1044     struct snmp_object *obj)
1045 {
1046         if (pdu->version == SNMP_V1 && obj->val.syntax ==
1047             SNMP_SYNTAX_COUNTER64) {
1048                 warnx("64-bit counters are not supported in SNMPv1 PDU");
1049                 return (-1);
1050         }
1051
1052         if (ISSET_NUMERIC(snmptoolctx) || ISSET_ERRIGNORE(snmptoolctx))
1053                 return (1);
1054
1055         if (obj->info->access < SNMP_ACCESS_SET) {
1056                 warnx("Object %s not accessible for set - try 'bsnmpset -a'",
1057                     obj->info->string);
1058                 return (-1);
1059         }
1060
1061         return (1);
1062 }
1063
1064 static int32_t
1065 snmpset_add_vbind(struct snmp_pdu *pdu, struct snmp_object *obj)
1066 {
1067         if (pdu->nbindings > SNMP_MAX_BINDINGS) {
1068                 warnx("Too many OIDs for one PDU");
1069                 return (-1);
1070         }
1071
1072         if (obj->error > 0)
1073                 return (0);
1074
1075         if (snmpset_add_value(&(pdu->bindings[pdu->nbindings]), &(obj->val))
1076             < 0)
1077                 return (-1);
1078
1079         asn_append_oid(&(pdu->bindings[pdu->nbindings].var), &(obj->val.var));
1080         pdu->nbindings++;
1081
1082         return (pdu->nbindings);
1083 }
1084
1085 static int
1086 snmptool_set(struct snmp_toolinfo *snmptoolctx)
1087 {
1088         struct snmp_pdu req, resp;
1089
1090         snmp_pdu_create(&req, SNMP_PDU_SET);
1091
1092         while ((snmp_pdu_add_bindings(snmptoolctx, snmpset_verify_vbind,
1093             snmpset_add_vbind, &req, SNMP_MAX_BINDINGS)) > 0) {
1094                 if (snmp_dialog(&req, &resp)) {
1095                         warn("Snmp dialog");
1096                         break;
1097                 }
1098
1099                 if (snmp_pdu_check(&req, &resp) > 0) {
1100                         if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET)
1101                                 snmp_output_resp(snmptoolctx, &resp, NULL);
1102                         snmp_pdu_free(&resp);
1103                         break;
1104                 }
1105
1106                 snmp_output_err_resp(snmptoolctx, &resp);
1107                 if (!ISSET_RETRY(snmptoolctx)) {
1108                         snmp_pdu_free(&resp);
1109                         break;
1110                 }
1111
1112                 if (snmp_object_seterror(snmptoolctx,
1113                     &(resp.bindings[resp.error_index - 1]),
1114                     resp.error_status) <= 0) {
1115                         snmp_pdu_free(&resp);
1116                         break;
1117                 }
1118
1119                 fprintf(stderr, "Retrying...\n");
1120                 snmp_pdu_free(&req);
1121                 snmp_pdu_create(&req, SNMP_PDU_SET);
1122         }
1123
1124         snmp_pdu_free(&req);
1125
1126         return (0);
1127 }
1128
1129 /* *****************************************************************************
1130  * main
1131  */
1132 /*
1133  * According to command line options prepare SNMP Get | GetNext | GetBulk PDU.
1134  * Wait for a response and print it.
1135  */
1136 /*
1137  * Do a 'snmp walk' - according to command line options request for values
1138  * lexicographically subsequent and subrooted at a common node. Send a GetNext
1139  * PDU requesting the value for each next variable and print the response. Stop
1140  * when a Response PDU is received that contains the value of a variable not
1141  * subrooted at the variable the walk started.
1142  */
1143 int
1144 main(int argc, char ** argv)
1145 {
1146         struct snmp_toolinfo snmptoolctx;
1147         int32_t oid_cnt, last_oid, opt_num;
1148         int rc = 0;
1149
1150         /* Make sure program_name is set and valid. */
1151         if (*argv == NULL)
1152                 program_name = "snmptool";
1153         else {
1154                 program_name = strrchr(*argv, '/');
1155                 if (program_name != NULL)
1156                         program_name++;
1157                 else
1158                         program_name = *argv;
1159         }
1160
1161         if (program_name == NULL) {
1162                 fprintf(stderr, "Error: No program name?\n");
1163                 exit (1);
1164         } else if (strcmp(program_name, "bsnmpget") == 0)
1165                 program = BSNMPGET;
1166         else if (strcmp(program_name, "bsnmpwalk") == 0)
1167                 program = BSNMPWALK;
1168         else if (strcmp(program_name, "bsnmpset") == 0)
1169                 program = BSNMPSET;
1170         else {
1171                 fprintf(stderr, "Unknown snmp tool name '%s'.\n", program_name);
1172                 exit (1);
1173         }
1174
1175         /* Initialize. */
1176         if (snmptool_init(&snmptoolctx) < 0)
1177                 exit (1);
1178
1179         if ((opt_num = snmptool_parse_options(&snmptoolctx, argc, argv)) < 0) {
1180                 snmp_tool_freeall(&snmptoolctx);
1181                 /* On -h (help) exit without error. */
1182                 if (opt_num == -2)
1183                         exit(0);
1184                 else
1185                         exit(1);
1186         }
1187
1188         oid_cnt = argc - opt_num - 1;
1189         if (oid_cnt == 0) {
1190                 switch (program) {
1191                 case BSNMPGET:
1192                         if (!ISSET_EDISCOVER(&snmptoolctx) &&
1193                             !ISSET_LOCALKEY(&snmptoolctx)) {
1194                                 fprintf(stderr, "No OID given.\n");
1195                                 usage();
1196                                 snmp_tool_freeall(&snmptoolctx);
1197                                 exit(1);
1198                         }
1199                         break;
1200
1201                 case BSNMPWALK:
1202                         if (snmp_object_add(&snmptoolctx, snmpwalk_add_default,
1203                             NULL) < 0) {
1204                                 fprintf(stderr,
1205                                     "Error setting default subtree.\n");
1206                                 snmp_tool_freeall(&snmptoolctx);
1207                                 exit(1);
1208                         }
1209                         break;
1210
1211                 case BSNMPSET:
1212                         fprintf(stderr, "No OID given.\n");
1213                         usage();
1214                         snmp_tool_freeall(&snmptoolctx);
1215                         exit(1);
1216                 }
1217         }
1218
1219         if (snmp_import_all(&snmptoolctx) < 0) {
1220                 snmp_tool_freeall(&snmptoolctx);
1221                 exit(1);
1222         }
1223
1224         /* A simple sanity check - can not send GETBULK when using SNMPv1. */
1225         if (program == BSNMPGET && snmp_client.version == SNMP_V1 &&
1226             GET_PDUTYPE(&snmptoolctx) == SNMP_PDU_GETBULK) {
1227                 fprintf(stderr, "Cannot send GETBULK PDU with SNMPv1.\n");
1228                 snmp_tool_freeall(&snmptoolctx);
1229                 exit(1);
1230         }
1231
1232         for (last_oid = argc - 1; oid_cnt > 0; last_oid--, oid_cnt--) {
1233                 if ((snmp_object_add(&snmptoolctx, (program == BSNMPSET) ?
1234                     snmpset_parse_oid : snmptools_parse_oid,
1235                     argv[last_oid])) < 0) {
1236                         fprintf(stderr, "Error parsing OID string '%s'.\n",
1237                             argv[last_oid]);
1238                         snmp_tool_freeall(&snmptoolctx);
1239                         exit(1);
1240                 }
1241         }
1242
1243         if (snmp_open(NULL, NULL, NULL, NULL)) {
1244                 warn("Failed to open snmp session");
1245                 snmp_tool_freeall(&snmptoolctx);
1246                 exit(1);
1247         }
1248
1249         if (snmp_client.version == SNMP_V3 && snmp_client.engine.engine_len == 0)
1250                 SET_EDISCOVER(&snmptoolctx);
1251
1252         if (ISSET_EDISCOVER(&snmptoolctx) &&
1253             snmp_discover_engine(snmptoolctx.passwd) < 0) {
1254                 warn("Unknown SNMP Engine ID");
1255                 rc = 1;
1256                 goto cleanup;
1257         }
1258
1259         if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE ||
1260             ISSET_EDISCOVER(&snmptoolctx))
1261                 snmp_output_engine();
1262
1263         if (snmp_client.version == SNMP_V3 && ISSET_LOCALKEY(&snmptoolctx) &&
1264             !ISSET_EDISCOVER(&snmptoolctx)) {
1265                 if (snmp_passwd_to_keys(&snmp_client.user,
1266                     snmptoolctx.passwd) != SNMP_CODE_OK ||
1267                     snmp_get_local_keys(&snmp_client.user,
1268                     snmp_client.engine.engine_id,
1269                     snmp_client.engine.engine_len) != SNMP_CODE_OK) {
1270                         warn("Failed to get keys");
1271                         rc = 1;
1272                         goto cleanup;
1273                 }
1274         }
1275
1276         if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE ||
1277             ISSET_EDISCOVER(&snmptoolctx))
1278                 snmp_output_keys();
1279
1280         if (ISSET_EDISCOVER(&snmptoolctx) && snmptoolctx.objects == 0)
1281                 goto cleanup;
1282
1283         switch (program) {
1284         case BSNMPGET:
1285                 rc = snmptool_get(&snmptoolctx);
1286                 break;
1287         case BSNMPWALK:
1288                 rc = snmptool_walk(&snmptoolctx);
1289                 break;
1290         case BSNMPSET:
1291                 rc = snmptool_set(&snmptoolctx);
1292                 break;
1293         }
1294
1295
1296 cleanup:
1297         snmp_tool_freeall(&snmptoolctx);
1298         snmp_close();
1299
1300         exit(rc);
1301 }