]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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 (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         uint32_t v;
607         int32_t i;
608         char *endptr, *str;
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
621         value->syntax = SNMP_SYNTAX_IPADDRESS;
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 /* XXX-BZ aruments should be swapped. */
844 static int32_t
845 parse_syntax_strval(struct snmp_toolinfo *snmptoolctx, char *str,
846     struct snmp_object *object)
847 {
848         uint32_t len;
849         enum snmp_syntax syn;
850
851         /*
852          * Syntax string here not required  - still may be present.
853          */
854
855         if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) {
856                 for (len = 0 ; *(str + len) != ':'; len++) {
857                         if (*(str + len) == '\0') {
858                                 warnx("Syntax missing in value - %s", str);
859                                 return (-1);
860                         }
861                 }
862                 if ((syn = parse_syntax(str)) <= SNMP_SYNTAX_NULL) {
863                         warnx("Unknown syntax in - %s", str);
864                         return (-1);
865                 }
866                 if (syn != object->val.syntax) {
867                         if (!ISSET_ERRIGNORE(snmptoolctx)) {
868                                 warnx("Bad syntax in - %s", str);
869                                 return (-1);
870                         } else
871                                 object->val.syntax = syn;
872                 }
873                 len++;
874         } else
875                 len = 0;
876
877         switch (object->val.syntax) {
878                 case SNMP_SYNTAX_INTEGER:
879                         return (parse_int_string(object, str + len));
880                 case SNMP_SYNTAX_IPADDRESS:
881                         return (parse_ip(&(object->val), str + len));
882                 case SNMP_SYNTAX_COUNTER:
883                         return (parse_counter(&(object->val), str + len));
884                 case SNMP_SYNTAX_GAUGE:
885                         return (parse_gauge(&(object->val), str + len));
886                 case SNMP_SYNTAX_TIMETICKS:
887                         return (parse_ticks(&(object->val), str + len));
888                 case SNMP_SYNTAX_COUNTER64:
889                         return (parse_uint64(&(object->val), str + len));
890                 case SNMP_SYNTAX_OCTETSTRING:
891                         return (snmp_tc2oct(object->info->tc, &(object->val),
892                             str + len));
893                 case SNMP_SYNTAX_OID:
894                         return (parse_oid_string(snmptoolctx, &(object->val),
895                             str + len));
896                 default:
897                         /* NOTREACHED */
898                         break;
899         }
900
901         return (-1);
902 }
903
904 static int32_t
905 parse_pair_stroid_val(struct snmp_toolinfo *snmptoolctx,
906     struct snmp_object *obj, char *argv)
907 {
908         char *ptr;
909
910         if ((ptr = snmptools_parse_stroid(snmptoolctx, obj, argv)) == NULL)
911                 return (-1);
912  
913         if (*ptr != '=') {
914                 warnx("Value to set expected after OID");
915                 return (-1);
916         }
917
918         if (parse_syntax_strval(snmptoolctx, ptr + 1, obj) < 0)
919                 return (-1);
920
921         return (1);
922 }
923
924
925 static int32_t
926 snmpset_parse_oid(struct snmp_toolinfo *snmptoolctx,
927     struct snmp_object *obj, char *argv)
928 {
929         if (argv == NULL)
930                 return (-1);
931
932         if (ISSET_NUMERIC(snmptoolctx)) {
933                 if (parse_pair_numoid_val(argv, &(obj->val)) < 0)
934                         return (-1);
935         } else {
936                 if (parse_pair_stroid_val(snmptoolctx, obj, argv) < 0)
937                         return (-1);
938         }
939
940         return (1);
941 }
942
943 static int32_t
944 add_ip_syntax(struct snmp_value *dst, struct snmp_value *src)
945 {
946         int8_t i;
947
948         dst->syntax = SNMP_SYNTAX_IPADDRESS;
949         for (i = 0; i < 4; i++)
950                 dst->v.ipaddress[i] = src->v.ipaddress[i];
951
952         return (1);
953 }
954
955 static int32_t
956 add_octstring_syntax(struct snmp_value *dst, struct snmp_value *src)
957 {
958         if (src->v.octetstring.len > ASN_MAXOCTETSTRING) {
959                 warnx("OctetString len too big - %u",src->v.octetstring.len);
960                 return (-1);
961         }
962
963         if ((dst->v.octetstring.octets = malloc(src->v.octetstring.len)) ==
964             NULL) {
965                 syslog(LOG_ERR, "malloc() failed - %s", strerror(errno));
966                 return (-1);
967         }
968
969         memcpy(dst->v.octetstring.octets, src->v.octetstring.octets,
970             src->v.octetstring.len);
971         dst->syntax = SNMP_SYNTAX_OCTETSTRING;
972         dst->v.octetstring.len = src->v.octetstring.len;
973
974         return(0);
975 }
976
977 static int32_t
978 add_oid_syntax(struct snmp_value *dst, struct snmp_value *src)
979 {
980         asn_append_oid(&(dst->v.oid), &(src->v.oid));
981         dst->syntax = SNMP_SYNTAX_OID;
982         return (0);
983 }
984
985 /*
986  * Check syntax - if one of SNMP_SYNTAX_NULL, SNMP_SYNTAX_NOSUCHOBJECT,
987  * SNMP_SYNTAX_NOSUCHINSTANCE, SNMP_SYNTAX_ENDOFMIBVIEW or anything not known -
988  * return error.
989  */
990 static int32_t
991 snmpset_add_value(struct snmp_value *dst, struct snmp_value *src)
992 {
993         if (dst == NULL || src == NULL)
994                 return (-1);
995
996         switch (src->syntax) {
997                 case SNMP_SYNTAX_INTEGER:
998                         dst->v.integer = src->v.integer;
999                         dst->syntax = SNMP_SYNTAX_INTEGER;
1000                         break;
1001                 case SNMP_SYNTAX_TIMETICKS:
1002                         dst->v.uint32 = src->v.uint32;
1003                         dst->syntax = SNMP_SYNTAX_TIMETICKS;
1004                         break;
1005                 case SNMP_SYNTAX_GAUGE:
1006                         dst->v.uint32 = src->v.uint32;
1007                         dst->syntax = SNMP_SYNTAX_GAUGE;
1008                         break;
1009                 case SNMP_SYNTAX_COUNTER:
1010                         dst->v.uint32 = src->v.uint32;
1011                         dst->syntax = SNMP_SYNTAX_COUNTER;
1012                         break;
1013                 case SNMP_SYNTAX_COUNTER64:
1014                         dst->v.counter64 = src->v.counter64;
1015                         dst->syntax = SNMP_SYNTAX_COUNTER64;
1016                         break;
1017                 case SNMP_SYNTAX_IPADDRESS:
1018                         add_ip_syntax(dst, src);
1019                         break;
1020                 case SNMP_SYNTAX_OCTETSTRING:
1021                         add_octstring_syntax(dst, src);
1022                         break;
1023                 case SNMP_SYNTAX_OID:
1024                         add_oid_syntax(dst, src);
1025                         break;
1026                 default:
1027                         warnx("Unknown syntax %d", src->syntax);
1028                         return (-1);
1029         }
1030
1031         return (0);
1032 }
1033
1034 static int32_t
1035 snmpset_verify_vbind(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu,
1036     struct snmp_object *obj)
1037 {
1038         if (pdu->version == SNMP_V1 && obj->val.syntax ==
1039             SNMP_SYNTAX_COUNTER64) {
1040                 warnx("64-bit counters are not supported in SNMPv1 PDU");
1041                 return (-1);
1042         }
1043
1044         if (ISSET_NUMERIC(snmptoolctx) || ISSET_ERRIGNORE(snmptoolctx))
1045                 return (1);
1046
1047         if (obj->info->access < SNMP_ACCESS_SET) {
1048                 warnx("Object %s not accessible for set - try 'bsnmpset -a'",
1049                     obj->info->string);
1050                 return (-1);
1051         }
1052
1053         return (1);
1054 }
1055
1056 static int32_t
1057 snmpset_add_vbind(struct snmp_pdu *pdu, struct snmp_object *obj)
1058 {
1059         if (pdu->nbindings > SNMP_MAX_BINDINGS) {
1060                 warnx("Too many OIDs for one PDU");
1061                 return (-1);
1062         }
1063
1064         if (obj->error > 0)
1065                 return (0);
1066
1067         if (snmpset_add_value(&(pdu->bindings[pdu->nbindings]), &(obj->val))
1068             < 0)
1069                 return (-1);
1070
1071         asn_append_oid(&(pdu->bindings[pdu->nbindings].var), &(obj->val.var));
1072         pdu->nbindings++;
1073
1074         return (pdu->nbindings);
1075 }
1076
1077 static int
1078 snmptool_set(struct snmp_toolinfo *snmptoolctx)
1079 {
1080         struct snmp_pdu req, resp;
1081
1082         snmp_pdu_create(&req, SNMP_PDU_SET);
1083
1084         while ((snmp_pdu_add_bindings(snmptoolctx, snmpset_verify_vbind,
1085             snmpset_add_vbind, &req, SNMP_MAX_BINDINGS)) > 0) {
1086                 if (snmp_dialog(&req, &resp)) {
1087                         warnx("Snmp dialog - %s", strerror(errno));
1088                         break;
1089                 }
1090
1091                 if (snmp_pdu_check(&req, &resp) > 0) {
1092                         if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET)
1093                                 snmp_output_resp(snmptoolctx, &resp, NULL);
1094                         break;
1095                 }
1096
1097                 snmp_output_err_resp(snmptoolctx, &resp);
1098                 if (!ISSET_RETRY(snmptoolctx))
1099                         break;
1100
1101                 if (snmp_object_seterror(snmptoolctx,
1102                     &(resp.bindings[resp.error_index - 1]),
1103                     resp.error_status) <= 0)
1104                         break;
1105
1106                 fprintf(stderr, "Retrying...\n");
1107                 snmp_pdu_free(&req);
1108                 snmp_pdu_free(&resp);
1109                 snmp_pdu_create(&req, SNMP_PDU_SET);
1110         }
1111
1112         snmp_pdu_free(&resp);
1113
1114         return (0);
1115 }
1116
1117 /* *****************************************************************************
1118  * main
1119  */
1120 /*
1121  * According to command line options prepare SNMP Get | GetNext | GetBulk PDU.
1122  * Wait for a response and print it.
1123  */
1124 /*
1125  * Do a 'snmp walk' - according to command line options request for values
1126  * lexicographically subsequent and subrooted at a common node. Send a GetNext
1127  * PDU requesting the value for each next variable and print the response. Stop
1128  * when a Response PDU is received that contains the value of a variable not
1129  * subrooted at the variable the walk started.
1130  */
1131 int
1132 main(int argc, char ** argv)
1133 {
1134         struct snmp_toolinfo snmptoolctx;
1135         int32_t oid_cnt, last_oid, opt_num;
1136         int rc = 0;
1137
1138         /* Make sure program_name is set and valid. */
1139         if (*argv == NULL)
1140                 program_name = "snmptool";
1141         else {
1142                 program_name = strrchr(*argv, '/');
1143                 if (program_name != NULL)
1144                         program_name++;
1145                 else
1146                         program_name = *argv;
1147         }
1148
1149         if (program_name == NULL) {
1150                 fprintf(stderr, "Error: No program name?\n");
1151                 exit (1);
1152         } else if (strcmp(program_name, "bsnmpget") == 0)
1153                 program = BSNMPGET;
1154         else if (strcmp(program_name, "bsnmpwalk") == 0)
1155                 program = BSNMPWALK;
1156         else if (strcmp(program_name, "bsnmpset") == 0)
1157                 program = BSNMPSET;
1158         else {
1159                 fprintf(stderr, "Unknown snmp tool name '%s'.\n", program_name);
1160                 exit (1);
1161         }
1162
1163         /* Initialize. */
1164         if (snmptool_init(&snmptoolctx) < 0)
1165                 exit (1);
1166
1167         if ((opt_num = snmptool_parse_options(&snmptoolctx, argc, argv)) < 0) {
1168                 snmp_tool_freeall(&snmptoolctx);
1169                 /* On -h (help) exit without error. */
1170                 if (opt_num == -2)
1171                         exit(0);
1172                 else
1173                         exit(1);
1174         }
1175
1176         oid_cnt = argc - opt_num - 1;
1177         if (oid_cnt == 0) {
1178                 switch (program) {
1179                 case BSNMPGET:
1180                         if (!ISSET_EDISCOVER(&snmptoolctx) &&
1181                             !ISSET_LOCALKEY(&snmptoolctx)) {
1182                                 fprintf(stderr, "No OID given.\n");
1183                                 usage();
1184                                 snmp_tool_freeall(&snmptoolctx);
1185                                 exit(1);
1186                         }
1187                         break;
1188
1189                 case BSNMPWALK:
1190                         if (snmp_object_add(&snmptoolctx, snmpwalk_add_default,
1191                             NULL) < 0) {
1192                                 fprintf(stderr,
1193                                     "Error setting default subtree.\n");
1194                                 snmp_tool_freeall(&snmptoolctx);
1195                                 exit(1);
1196                         }
1197                         break;
1198
1199                 case BSNMPSET:
1200                         fprintf(stderr, "No OID given.\n");
1201                         usage();
1202                         snmp_tool_freeall(&snmptoolctx);
1203                         exit(1);
1204                 }
1205         }
1206
1207         if (snmp_import_all(&snmptoolctx) < 0) {
1208                 snmp_tool_freeall(&snmptoolctx);
1209                 exit(1);
1210         }
1211
1212         /* A simple sanity check - can not send GETBULK when using SNMPv1. */
1213         if (program == BSNMPGET && snmp_client.version == SNMP_V1 &&
1214             GET_PDUTYPE(&snmptoolctx) == SNMP_PDU_GETBULK) {
1215                 fprintf(stderr, "Cannot send GETBULK PDU with SNMPv1.\n");
1216                 snmp_tool_freeall(&snmptoolctx);
1217                 exit(1);
1218         }
1219
1220         for (last_oid = argc - 1; oid_cnt > 0; last_oid--, oid_cnt--) {
1221                 if ((snmp_object_add(&snmptoolctx, (program == BSNMPSET) ?
1222                     snmpset_parse_oid : snmptools_parse_oid,
1223                     argv[last_oid])) < 0) {
1224                         fprintf(stderr, "Error parsing OID string '%s'.\n",
1225                             argv[last_oid]);
1226                         snmp_tool_freeall(&snmptoolctx);
1227                         exit(1);
1228                 }
1229         }
1230
1231         if (snmp_open(NULL, NULL, NULL, NULL)) {
1232                 warnx("Failed to open snmp session: %s.", strerror(errno));
1233                 snmp_tool_freeall(&snmptoolctx);
1234                 exit(1);
1235         }
1236
1237         if (snmp_client.version == SNMP_V3 && snmp_client.engine.engine_len == 0)
1238                 SET_EDISCOVER(&snmptoolctx);
1239
1240         if (ISSET_EDISCOVER(&snmptoolctx) &&
1241             snmp_discover_engine(snmptoolctx.passwd) < 0) {
1242                 warnx("Unknown SNMP Engine ID: %s.", strerror(errno));
1243                 rc = 1;
1244                 goto cleanup;
1245         }
1246
1247         if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE ||
1248             ISSET_EDISCOVER(&snmptoolctx))
1249                 snmp_output_engine();
1250
1251         if (snmp_client.version == SNMP_V3 && ISSET_LOCALKEY(&snmptoolctx) &&
1252             !ISSET_EDISCOVER(&snmptoolctx)) {
1253                 if (snmp_passwd_to_keys(&snmp_client.user,
1254                     snmptoolctx.passwd) != SNMP_CODE_OK ||
1255                     snmp_get_local_keys(&snmp_client.user,
1256                     snmp_client.engine.engine_id,
1257                     snmp_client.engine.engine_len) != SNMP_CODE_OK) {
1258                         warnx("Failed to get keys: %s.", strerror(errno));
1259                         rc = 1;
1260                         goto cleanup;
1261                 }
1262         }
1263
1264         if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE ||
1265             ISSET_EDISCOVER(&snmptoolctx))
1266                 snmp_output_keys();
1267
1268         if (ISSET_EDISCOVER(&snmptoolctx) && snmptoolctx.objects == 0)
1269                 goto cleanup;
1270
1271         switch (program) {
1272         case BSNMPGET:
1273                 rc = snmptool_get(&snmptoolctx);
1274                 break;
1275         case BSNMPWALK:
1276                 rc = snmptool_walk(&snmptoolctx);
1277                 break;
1278         case BSNMPSET:
1279                 rc = snmptool_set(&snmptoolctx);
1280                 break;
1281         }
1282
1283
1284 cleanup:
1285         snmp_tool_freeall(&snmptoolctx);
1286         snmp_close();
1287
1288         exit(rc);
1289 }