]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.c
MFC r310497:
[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                         break;
404                 }
405
406                 snmp_output_err_resp(snmptoolctx, &resp);
407                 if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK ||
408                     !ISSET_RETRY(snmptoolctx))
409                         break;
410
411                 /*
412                  * Loop through the object list and set object->error to the
413                  * varbinding that caused the error.
414                  */
415                 if (snmp_object_seterror(snmptoolctx,
416                     &(resp.bindings[resp.error_index - 1]),
417                     resp.error_status) <= 0)
418                         break;
419
420                 fprintf(stderr, "Retrying...\n");
421                 snmp_pdu_free(&resp);
422                 snmp_pdu_create(&req, GET_PDUTYPE(snmptoolctx));
423         }
424
425         snmp_pdu_free(&resp);
426
427         return (0);
428 }
429
430
431 /* *****************************************************************************
432  * bsnmpwalk private functions.
433  */
434 /* The default tree to walk. */
435 static const struct asn_oid snmp_mibII_OID = {
436         6 , { 1, 3, 6, 1, 2, 1 }
437 };
438
439 static int32_t
440 snmpwalk_add_default(struct snmp_toolinfo *snmptoolctx __unused,
441     struct snmp_object *obj, char *string __unused)
442 {
443         asn_append_oid(&(obj->val.var), &snmp_mibII_OID);
444         return (1);
445 }
446
447 /*
448  * Prepare the next GetNext/Get PDU to send.
449  */
450 static void
451 snmpwalk_nextpdu_create(uint32_t op, struct asn_oid *var, struct snmp_pdu *pdu)
452 {
453         snmp_pdu_create(pdu, op);
454         asn_append_oid(&(pdu->bindings[0].var), var);
455         pdu->nbindings = 1;
456 }
457
458 static int
459 snmptool_walk(struct snmp_toolinfo *snmptoolctx)
460 {
461         struct snmp_pdu req, resp;
462         struct asn_oid root;    /* Keep the initial oid. */
463         int32_t outputs, rc;
464         uint32_t op;
465
466         if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK)
467                 op = SNMP_PDU_GETBULK;
468         else
469                 op = SNMP_PDU_GETNEXT;
470
471         snmp_pdu_create(&req, op);
472
473         while ((rc = snmp_pdu_add_bindings(snmptoolctx, NULL,
474             snmptool_add_vbind, &req, 1)) > 0) {
475
476                 /* Remember the root where the walk started from. */
477                 memset(&root, 0, sizeof(struct asn_oid));
478                 asn_append_oid(&root, &(req.bindings[0].var));
479
480                 if (op == SNMP_PDU_GETBULK)
481                         snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx),
482                             GET_NONREP(snmptoolctx));
483
484                 outputs = 0;
485                 while (snmp_dialog(&req, &resp) >= 0) {
486                         if ((snmp_parse_resp(&resp, &req)) < 0) {
487                                 snmp_output_err_resp(snmptoolctx, &resp);
488                                 snmp_pdu_free(&resp);
489                                 outputs = -1;
490                                 break;
491                         }
492
493                         rc = snmp_output_resp(snmptoolctx, &resp, &root);
494                         if (rc < 0) {
495                                 snmp_pdu_free(&resp);
496                                 outputs = -1;
497                                 break;
498                         }
499
500                         outputs += rc;
501                         snmp_pdu_free(&resp);
502
503                         if ((u_int)rc < resp.nbindings)
504                                 break;
505
506                         snmpwalk_nextpdu_create(op,
507                             &(resp.bindings[resp.nbindings - 1].var), &req);
508                         if (op == SNMP_PDU_GETBULK)
509                                 snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx),
510                                     GET_NONREP(snmptoolctx));
511                 }
512
513                 /* Just in case our root was a leaf. */
514                 if (outputs == 0) {
515                         snmpwalk_nextpdu_create(SNMP_PDU_GET, &root, &req);
516                         if (snmp_dialog(&req, &resp) == SNMP_CODE_OK) {
517                                 if (snmp_parse_resp(&resp,&req) < 0)
518                                         snmp_output_err_resp(snmptoolctx, &resp);
519                                 else
520                                         snmp_output_resp(snmptoolctx, &(resp), NULL);
521
522                                 snmp_pdu_free(&resp);
523                         } else
524                                 warn("Snmp dialog");
525                 }
526
527                 if (snmp_object_remove(snmptoolctx, &root) < 0) {
528                         warnx("snmp_object_remove");
529                         break;
530                 }
531
532                 snmp_pdu_create(&req, op);
533         }
534
535         if (rc == 0)
536                 return (0);
537         else
538                 return (1);
539 }
540
541 /* *****************************************************************************
542  * bsnmpset private functions.
543  */
544
545 static int32_t
546 parse_oid_numeric(struct snmp_value *value, char *val)
547 {
548         char *endptr;
549         int32_t saved_errno;
550         asn_subid_t suboid;
551
552         do {
553                 saved_errno = errno;
554                 errno = 0;
555                 suboid = strtoul(val, &endptr, 10);
556                 if (errno != 0) {
557                         warn("Value %s not supported", val);
558                         errno = saved_errno;
559                         return (-1);
560                 }
561                 errno = saved_errno;
562                 if ((asn_subid_t) suboid > ASN_MAXID) {
563                         warnx("Suboid %u > ASN_MAXID", suboid);
564                         return (-1);
565                 }
566                 if (snmp_suboid_append(&(value->v.oid), suboid) < 0)
567                         return (-1);
568                 val = endptr + 1;
569         } while (*endptr == '.');
570
571         if (*endptr != '\0')
572                 warnx("OID value %s not supported", val);
573
574         value->syntax = SNMP_SYNTAX_OID;
575         return (0);
576 }
577
578 /*
579  * Allow OID leaf in both forms:
580  * 1) 1.3.6.1.2... ->  in such case call directly the function reading raw OIDs;
581  * 2) begemotSnmpdAgentFreeBSD -> lookup the ASN OID corresponding to that.
582  */
583 static int32_t
584 parse_oid_string(struct snmp_toolinfo *snmptoolctx,
585     struct snmp_value *value, char *string)
586 {
587         struct snmp_object obj;
588
589         if (isdigit(string[0]))
590                 return (parse_oid_numeric(value, string));
591
592         memset(&obj, 0, sizeof(struct snmp_object));
593         if (snmp_lookup_enumoid(snmptoolctx, &obj, string) < 0) {
594                 warnx("Unknown OID enum string - %s", string);
595                 return (-1);
596         }
597
598         asn_append_oid(&(value->v.oid), &(obj.val.var));
599         return (1);
600 }
601
602 static int32_t
603 parse_ip(struct snmp_value * value, char * val)
604 {
605         char *endptr, *str;
606         int32_t i;
607         uint32_t v;
608
609         str = val;
610         for (i = 0; i < 4; i++) {
611                 v = strtoul(str, &endptr, 10);
612                 if (v > 0xff)
613                         return (-1);
614                 if (*endptr != '.' && *endptr != '\0' && i != 3)
615                         break;
616                 str = endptr + 1;
617                 value->v.ipaddress[i] = (uint8_t) v;
618         }
619         value->syntax = SNMP_SYNTAX_IPADDRESS;
620
621         return (0);
622 }
623
624 static int32_t
625 parse_int(struct snmp_value *value, char *val)
626 {
627         char *endptr;
628         int32_t v, saved_errno;
629
630         saved_errno = errno;
631         errno = 0;
632
633         v = strtol(val, &endptr, 10);
634
635         if (errno != 0) {
636                 warn("Value %s not supported", val);
637                 errno = saved_errno;
638                 return (-1);
639         }
640
641         value->syntax = SNMP_SYNTAX_INTEGER;
642         value->v.integer = v;
643         errno = saved_errno;
644
645         return (0);
646 }
647
648 static int32_t
649 parse_int_string(struct snmp_object *object, char *val)
650 {
651         int32_t v;
652
653         if (isdigit(val[0]))
654                 return ((parse_int(&(object->val), val)));
655
656         if (object->info == NULL) {
657                 warnx("Unknown enumerated integer type - %s", val);
658                 return (-1);
659         }
660         if ((v = enum_number_lookup(object->info->snmp_enum, val)) < 0)
661                 warnx("Unknown enumerated integer type - %s", val);
662
663         object->val.v.integer = v;
664         return (1);
665 }
666
667 /*
668  * Here syntax may be one of SNMP_SYNTAX_COUNTER, SNMP_SYNTAX_GAUGE,
669  * SNMP_SYNTAX_TIMETICKS.
670  */
671 static int32_t
672 parse_uint(struct snmp_value *value, char *val)
673 {
674         char *endptr;
675         uint32_t v = 0;
676         int32_t saved_errno;
677
678         saved_errno = errno;
679         errno = 0;
680
681         v = strtoul(val, &endptr, 10);
682
683         if (errno != 0) {
684                 warn("Value %s not supported", val);
685                 errno = saved_errno;
686                 return (-1);
687         }
688
689         value->v.uint32 = v;
690         errno = saved_errno;
691
692         return (0);
693 }
694
695 static int32_t
696 parse_ticks(struct snmp_value *value, char *val)
697 {
698         if (parse_uint(value, val) < 0)
699                 return (-1);
700
701         value->syntax = SNMP_SYNTAX_TIMETICKS;
702         return (0);
703 }
704
705 static int32_t
706 parse_gauge(struct snmp_value *value, char *val)
707 {
708         if (parse_uint(value, val) < 0)
709                 return (-1);
710
711         value->syntax = SNMP_SYNTAX_GAUGE;
712         return (0);
713 }
714
715 static int32_t
716 parse_counter(struct snmp_value *value, char *val)
717 {
718         if (parse_uint(value, val) < 0)
719                 return (-1);
720
721         value->syntax = SNMP_SYNTAX_COUNTER;
722         return (0);
723 }
724
725 static int32_t
726 parse_uint64(struct snmp_value *value, char *val)
727 {
728         char *endptr;
729         int32_t saved_errno;
730         uint64_t v;
731
732         saved_errno = errno;
733         errno = 0;
734
735         v = strtoull(val, &endptr, 10);
736
737         if (errno != 0) {
738                 warnx("Value %s not supported", val);
739                 errno = saved_errno;
740                 return (-1);
741         }
742
743         value->syntax = SNMP_SYNTAX_COUNTER64;
744         value->v.counter64 = v;
745         errno = saved_errno;
746
747         return (0);
748 }
749
750 static int32_t
751 parse_syntax_val(struct snmp_value *value, enum snmp_syntax syntax, char *val)
752 {
753         switch (syntax) {
754                 case SNMP_SYNTAX_INTEGER:
755                         return (parse_int(value, val));
756                 case SNMP_SYNTAX_IPADDRESS:
757                         return (parse_ip(value, val));
758                 case SNMP_SYNTAX_COUNTER:
759                         return (parse_counter(value, val));
760                 case SNMP_SYNTAX_GAUGE:
761                         return (parse_gauge(value, val));
762                 case SNMP_SYNTAX_TIMETICKS:
763                         return (parse_ticks(value, val));
764                 case SNMP_SYNTAX_COUNTER64:
765                         return (parse_uint64(value, val));
766                 case SNMP_SYNTAX_OCTETSTRING:
767                         return (snmp_tc2oct(SNMP_STRING, value, val));
768                 case SNMP_SYNTAX_OID:
769                         return (parse_oid_numeric(value, val));
770                 default:
771                         /* NOTREACHED */
772                         break;
773         }
774
775         return (-1);
776 }
777
778 /*
779  * Parse a command line argument of type OID=syntax:value and fill in whatever
780  * fields can be derived from the input into snmp_value structure. Reads numeric
781  * OIDs.
782  */
783 static int32_t
784 parse_pair_numoid_val(char *str, struct snmp_value *snmp_val)
785 {
786         int32_t cnt;
787         char *ptr;
788         enum snmp_syntax syntax;
789         char oid_str[ASN_OIDSTRLEN];
790
791         ptr = str;
792         for (cnt = 0; cnt < ASN_OIDSTRLEN; cnt++)
793                 if (ptr[cnt] == '=')
794                         break;
795
796         if (cnt >= ASN_OIDSTRLEN) {
797                 warnx("OID too long - %s", str);
798                 return (-1);
799         }
800         strlcpy(oid_str, ptr, (size_t) (cnt + 1));
801
802         ptr = str + cnt + 1;
803         for (cnt = 0; cnt < MAX_CMD_SYNTAX_LEN; cnt++)
804                 if(ptr[cnt] == ':')
805                         break;
806
807         if (cnt >= MAX_CMD_SYNTAX_LEN) {
808                 warnx("Unknown syntax in OID - %s", str);
809                 return (-1);
810         }
811
812         if ((syntax = parse_syntax(ptr)) <= SNMP_SYNTAX_NULL) {
813                 warnx("Unknown syntax in OID - %s", ptr);
814                 return (-1);
815         }
816
817         ptr = ptr + cnt + 1;
818         for (cnt = 0; cnt < MAX_OCTSTRING_LEN; cnt++)
819                 if (ptr[cnt] == '\0')
820                         break;
821
822         if (ptr[cnt] != '\0') {
823                 warnx("Value string too long - %s", ptr);
824                 return (-1);
825         }
826
827         /*
828          * Here try parsing the OIDs and syntaxes and then check values - have
829          * to know syntax to check value boundaries.
830          */
831         if (snmp_parse_numoid(oid_str, &(snmp_val->var)) < 0) {
832                 warnx("Error parsing OID %s", oid_str);
833                 return (-1);
834         }
835
836         if (parse_syntax_val(snmp_val, syntax, ptr) < 0)
837                 return (-1);
838
839         return (1);
840 }
841
842 static int32_t
843 parse_syntax_strval(struct snmp_toolinfo *snmptoolctx,
844     struct snmp_object *object, char *str)
845 {
846         uint32_t len;
847         enum snmp_syntax syn;
848
849         /*
850          * Syntax string here not required  - still may be present.
851          */
852
853         if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) {
854                 for (len = 0 ; *(str + len) != ':'; len++) {
855                         if (*(str + len) == '\0') {
856                                 warnx("Syntax missing in value - %s", str);
857                                 return (-1);
858                         }
859                 }
860                 if ((syn = parse_syntax(str)) <= SNMP_SYNTAX_NULL) {
861                         warnx("Unknown syntax in - %s", str);
862                         return (-1);
863                 }
864                 if (syn != object->val.syntax) {
865                         if (!ISSET_ERRIGNORE(snmptoolctx)) {
866                                 warnx("Bad syntax in - %s", str);
867                                 return (-1);
868                         } else
869                                 object->val.syntax = syn;
870                 }
871                 len++;
872         } else
873                 len = 0;
874
875         switch (object->val.syntax) {
876                 case SNMP_SYNTAX_INTEGER:
877                         return (parse_int_string(object, str + len));
878                 case SNMP_SYNTAX_IPADDRESS:
879                         return (parse_ip(&(object->val), str + len));
880                 case SNMP_SYNTAX_COUNTER:
881                         return (parse_counter(&(object->val), str + len));
882                 case SNMP_SYNTAX_GAUGE:
883                         return (parse_gauge(&(object->val), str + len));
884                 case SNMP_SYNTAX_TIMETICKS:
885                         return (parse_ticks(&(object->val), str + len));
886                 case SNMP_SYNTAX_COUNTER64:
887                         return (parse_uint64(&(object->val), str + len));
888                 case SNMP_SYNTAX_OCTETSTRING:
889                         return (snmp_tc2oct(object->info->tc, &(object->val),
890                             str + len));
891                 case SNMP_SYNTAX_OID:
892                         return (parse_oid_string(snmptoolctx, &(object->val),
893                             str + len));
894                 default:
895                         /* NOTREACHED */
896                         break;
897         }
898
899         return (-1);
900 }
901
902 static int32_t
903 parse_pair_stroid_val(struct snmp_toolinfo *snmptoolctx,
904     struct snmp_object *obj, char *argv)
905 {
906         char *ptr;
907
908         if ((ptr = snmptools_parse_stroid(snmptoolctx, obj, argv)) == NULL)
909                 return (-1);
910
911         if (*ptr != '=') {
912                 warnx("Value to set expected after OID");
913                 return (-1);
914         }
915
916         if (parse_syntax_strval(snmptoolctx, obj, ptr + 1) < 0)
917                 return (-1);
918
919         return (1);
920 }
921
922
923 static int32_t
924 snmpset_parse_oid(struct snmp_toolinfo *snmptoolctx,
925     struct snmp_object *obj, char *argv)
926 {
927         if (argv == NULL)
928                 return (-1);
929
930         if (ISSET_NUMERIC(snmptoolctx)) {
931                 if (parse_pair_numoid_val(argv, &(obj->val)) < 0)
932                         return (-1);
933         } else {
934                 if (parse_pair_stroid_val(snmptoolctx, obj, argv) < 0)
935                         return (-1);
936         }
937
938         return (1);
939 }
940
941 static int32_t
942 add_ip_syntax(struct snmp_value *dst, struct snmp_value *src)
943 {
944         int8_t i;
945
946         dst->syntax = SNMP_SYNTAX_IPADDRESS;
947         for (i = 0; i < 4; i++)
948                 dst->v.ipaddress[i] = src->v.ipaddress[i];
949
950         return (1);
951 }
952
953 static int32_t
954 add_octstring_syntax(struct snmp_value *dst, struct snmp_value *src)
955 {
956         if (src->v.octetstring.len > ASN_MAXOCTETSTRING) {
957                 warnx("OctetString len too big - %u", src->v.octetstring.len);
958                 return (-1);
959         }
960
961         if ((dst->v.octetstring.octets = malloc(src->v.octetstring.len)) ==
962             NULL) {
963                 syslog(LOG_ERR, "malloc() failed - %s", strerror(errno));
964                 return (-1);
965         }
966
967         memcpy(dst->v.octetstring.octets, src->v.octetstring.octets,
968             src->v.octetstring.len);
969         dst->syntax = SNMP_SYNTAX_OCTETSTRING;
970         dst->v.octetstring.len = src->v.octetstring.len;
971
972         return(0);
973 }
974
975 static int32_t
976 add_oid_syntax(struct snmp_value *dst, struct snmp_value *src)
977 {
978         asn_append_oid(&(dst->v.oid), &(src->v.oid));
979         dst->syntax = SNMP_SYNTAX_OID;
980         return (0);
981 }
982
983 /*
984  * Check syntax - if one of SNMP_SYNTAX_NULL, SNMP_SYNTAX_NOSUCHOBJECT,
985  * SNMP_SYNTAX_NOSUCHINSTANCE, SNMP_SYNTAX_ENDOFMIBVIEW or anything not known -
986  * return error.
987  */
988 static int32_t
989 snmpset_add_value(struct snmp_value *dst, struct snmp_value *src)
990 {
991         if (dst == NULL || src == NULL)
992                 return (-1);
993
994         switch (src->syntax) {
995                 case SNMP_SYNTAX_INTEGER:
996                         dst->v.integer = src->v.integer;
997                         dst->syntax = SNMP_SYNTAX_INTEGER;
998                         break;
999                 case SNMP_SYNTAX_TIMETICKS:
1000                         dst->v.uint32 = src->v.uint32;
1001                         dst->syntax = SNMP_SYNTAX_TIMETICKS;
1002                         break;
1003                 case SNMP_SYNTAX_GAUGE:
1004                         dst->v.uint32 = src->v.uint32;
1005                         dst->syntax = SNMP_SYNTAX_GAUGE;
1006                         break;
1007                 case SNMP_SYNTAX_COUNTER:
1008                         dst->v.uint32 = src->v.uint32;
1009                         dst->syntax = SNMP_SYNTAX_COUNTER;
1010                         break;
1011                 case SNMP_SYNTAX_COUNTER64:
1012                         dst->v.counter64 = src->v.counter64;
1013                         dst->syntax = SNMP_SYNTAX_COUNTER64;
1014                         break;
1015                 case SNMP_SYNTAX_IPADDRESS:
1016                         add_ip_syntax(dst, src);
1017                         break;
1018                 case SNMP_SYNTAX_OCTETSTRING:
1019                         add_octstring_syntax(dst, src);
1020                         break;
1021                 case SNMP_SYNTAX_OID:
1022                         add_oid_syntax(dst, src);
1023                         break;
1024                 default:
1025                         warnx("Unknown syntax %d", src->syntax);
1026                         return (-1);
1027         }
1028
1029         return (0);
1030 }
1031
1032 static int32_t
1033 snmpset_verify_vbind(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu,
1034     struct snmp_object *obj)
1035 {
1036         if (pdu->version == SNMP_V1 && obj->val.syntax ==
1037             SNMP_SYNTAX_COUNTER64) {
1038                 warnx("64-bit counters are not supported in SNMPv1 PDU");
1039                 return (-1);
1040         }
1041
1042         if (ISSET_NUMERIC(snmptoolctx) || ISSET_ERRIGNORE(snmptoolctx))
1043                 return (1);
1044
1045         if (obj->info->access < SNMP_ACCESS_SET) {
1046                 warnx("Object %s not accessible for set - try 'bsnmpset -a'",
1047                     obj->info->string);
1048                 return (-1);
1049         }
1050
1051         return (1);
1052 }
1053
1054 static int32_t
1055 snmpset_add_vbind(struct snmp_pdu *pdu, struct snmp_object *obj)
1056 {
1057         if (pdu->nbindings > SNMP_MAX_BINDINGS) {
1058                 warnx("Too many OIDs for one PDU");
1059                 return (-1);
1060         }
1061
1062         if (obj->error > 0)
1063                 return (0);
1064
1065         if (snmpset_add_value(&(pdu->bindings[pdu->nbindings]), &(obj->val))
1066             < 0)
1067                 return (-1);
1068
1069         asn_append_oid(&(pdu->bindings[pdu->nbindings].var), &(obj->val.var));
1070         pdu->nbindings++;
1071
1072         return (pdu->nbindings);
1073 }
1074
1075 static int
1076 snmptool_set(struct snmp_toolinfo *snmptoolctx)
1077 {
1078         struct snmp_pdu req, resp;
1079
1080         snmp_pdu_create(&req, SNMP_PDU_SET);
1081
1082         while ((snmp_pdu_add_bindings(snmptoolctx, snmpset_verify_vbind,
1083             snmpset_add_vbind, &req, SNMP_MAX_BINDINGS)) > 0) {
1084                 if (snmp_dialog(&req, &resp)) {
1085                         warn("Snmp dialog");
1086                         break;
1087                 }
1088
1089                 if (snmp_pdu_check(&req, &resp) > 0) {
1090                         if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET)
1091                                 snmp_output_resp(snmptoolctx, &resp, NULL);
1092                         break;
1093                 }
1094
1095                 snmp_output_err_resp(snmptoolctx, &resp);
1096                 if (!ISSET_RETRY(snmptoolctx))
1097                         break;
1098
1099                 if (snmp_object_seterror(snmptoolctx,
1100                     &(resp.bindings[resp.error_index - 1]),
1101                     resp.error_status) <= 0)
1102                         break;
1103
1104                 fprintf(stderr, "Retrying...\n");
1105                 snmp_pdu_free(&req);
1106                 snmp_pdu_free(&resp);
1107                 snmp_pdu_create(&req, SNMP_PDU_SET);
1108         }
1109
1110         snmp_pdu_free(&resp);
1111
1112         return (0);
1113 }
1114
1115 /* *****************************************************************************
1116  * main
1117  */
1118 /*
1119  * According to command line options prepare SNMP Get | GetNext | GetBulk PDU.
1120  * Wait for a response and print it.
1121  */
1122 /*
1123  * Do a 'snmp walk' - according to command line options request for values
1124  * lexicographically subsequent and subrooted at a common node. Send a GetNext
1125  * PDU requesting the value for each next variable and print the response. Stop
1126  * when a Response PDU is received that contains the value of a variable not
1127  * subrooted at the variable the walk started.
1128  */
1129 int
1130 main(int argc, char ** argv)
1131 {
1132         struct snmp_toolinfo snmptoolctx;
1133         int32_t oid_cnt, last_oid, opt_num;
1134         int rc = 0;
1135
1136         /* Make sure program_name is set and valid. */
1137         if (*argv == NULL)
1138                 program_name = "snmptool";
1139         else {
1140                 program_name = strrchr(*argv, '/');
1141                 if (program_name != NULL)
1142                         program_name++;
1143                 else
1144                         program_name = *argv;
1145         }
1146
1147         if (program_name == NULL) {
1148                 fprintf(stderr, "Error: No program name?\n");
1149                 exit (1);
1150         } else if (strcmp(program_name, "bsnmpget") == 0)
1151                 program = BSNMPGET;
1152         else if (strcmp(program_name, "bsnmpwalk") == 0)
1153                 program = BSNMPWALK;
1154         else if (strcmp(program_name, "bsnmpset") == 0)
1155                 program = BSNMPSET;
1156         else {
1157                 fprintf(stderr, "Unknown snmp tool name '%s'.\n", program_name);
1158                 exit (1);
1159         }
1160
1161         /* Initialize. */
1162         if (snmptool_init(&snmptoolctx) < 0)
1163                 exit (1);
1164
1165         if ((opt_num = snmptool_parse_options(&snmptoolctx, argc, argv)) < 0) {
1166                 snmp_tool_freeall(&snmptoolctx);
1167                 /* On -h (help) exit without error. */
1168                 if (opt_num == -2)
1169                         exit(0);
1170                 else
1171                         exit(1);
1172         }
1173
1174         oid_cnt = argc - opt_num - 1;
1175         if (oid_cnt == 0) {
1176                 switch (program) {
1177                 case BSNMPGET:
1178                         if (!ISSET_EDISCOVER(&snmptoolctx) &&
1179                             !ISSET_LOCALKEY(&snmptoolctx)) {
1180                                 fprintf(stderr, "No OID given.\n");
1181                                 usage();
1182                                 snmp_tool_freeall(&snmptoolctx);
1183                                 exit(1);
1184                         }
1185                         break;
1186
1187                 case BSNMPWALK:
1188                         if (snmp_object_add(&snmptoolctx, snmpwalk_add_default,
1189                             NULL) < 0) {
1190                                 fprintf(stderr,
1191                                     "Error setting default subtree.\n");
1192                                 snmp_tool_freeall(&snmptoolctx);
1193                                 exit(1);
1194                         }
1195                         break;
1196
1197                 case BSNMPSET:
1198                         fprintf(stderr, "No OID given.\n");
1199                         usage();
1200                         snmp_tool_freeall(&snmptoolctx);
1201                         exit(1);
1202                 }
1203         }
1204
1205         if (snmp_import_all(&snmptoolctx) < 0) {
1206                 snmp_tool_freeall(&snmptoolctx);
1207                 exit(1);
1208         }
1209
1210         /* A simple sanity check - can not send GETBULK when using SNMPv1. */
1211         if (program == BSNMPGET && snmp_client.version == SNMP_V1 &&
1212             GET_PDUTYPE(&snmptoolctx) == SNMP_PDU_GETBULK) {
1213                 fprintf(stderr, "Cannot send GETBULK PDU with SNMPv1.\n");
1214                 snmp_tool_freeall(&snmptoolctx);
1215                 exit(1);
1216         }
1217
1218         for (last_oid = argc - 1; oid_cnt > 0; last_oid--, oid_cnt--) {
1219                 if ((snmp_object_add(&snmptoolctx, (program == BSNMPSET) ?
1220                     snmpset_parse_oid : snmptools_parse_oid,
1221                     argv[last_oid])) < 0) {
1222                         fprintf(stderr, "Error parsing OID string '%s'.\n",
1223                             argv[last_oid]);
1224                         snmp_tool_freeall(&snmptoolctx);
1225                         exit(1);
1226                 }
1227         }
1228
1229         if (snmp_open(NULL, NULL, NULL, NULL)) {
1230                 warn("Failed to open snmp session");
1231                 snmp_tool_freeall(&snmptoolctx);
1232                 exit(1);
1233         }
1234
1235         if (snmp_client.version == SNMP_V3 && snmp_client.engine.engine_len == 0)
1236                 SET_EDISCOVER(&snmptoolctx);
1237
1238         if (ISSET_EDISCOVER(&snmptoolctx) &&
1239             snmp_discover_engine(snmptoolctx.passwd) < 0) {
1240                 warn("Unknown SNMP Engine ID");
1241                 rc = 1;
1242                 goto cleanup;
1243         }
1244
1245         if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE ||
1246             ISSET_EDISCOVER(&snmptoolctx))
1247                 snmp_output_engine();
1248
1249         if (snmp_client.version == SNMP_V3 && ISSET_LOCALKEY(&snmptoolctx) &&
1250             !ISSET_EDISCOVER(&snmptoolctx)) {
1251                 if (snmp_passwd_to_keys(&snmp_client.user,
1252                     snmptoolctx.passwd) != SNMP_CODE_OK ||
1253                     snmp_get_local_keys(&snmp_client.user,
1254                     snmp_client.engine.engine_id,
1255                     snmp_client.engine.engine_len) != SNMP_CODE_OK) {
1256                         warn("Failed to get keys");
1257                         rc = 1;
1258                         goto cleanup;
1259                 }
1260         }
1261
1262         if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE ||
1263             ISSET_EDISCOVER(&snmptoolctx))
1264                 snmp_output_keys();
1265
1266         if (ISSET_EDISCOVER(&snmptoolctx) && snmptoolctx.objects == 0)
1267                 goto cleanup;
1268
1269         switch (program) {
1270         case BSNMPGET:
1271                 rc = snmptool_get(&snmptoolctx);
1272                 break;
1273         case BSNMPWALK:
1274                 rc = snmptool_walk(&snmptoolctx);
1275                 break;
1276         case BSNMPSET:
1277                 rc = snmptool_set(&snmptoolctx);
1278                 break;
1279         }
1280
1281
1282 cleanup:
1283         snmp_tool_freeall(&snmptoolctx);
1284         snmp_close();
1285
1286         exit(rc);
1287 }