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