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