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: stable/9/usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.c 216295 2010-12-08 14:30:25Z syrinx $
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
usage(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
parse_max_repetitions(struct snmp_toolinfo * snmptoolctx,char * opt_arg)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
parse_non_repeaters(struct snmp_toolinfo * snmptoolctx,char * opt_arg)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
parse_pdu_type(struct snmp_toolinfo * snmptoolctx,char * opt_arg)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
snmptool_parse_options(struct snmp_toolinfo * snmptoolctx,int argc,char ** argv)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 *
snmptools_parse_stroid(struct snmp_toolinfo * snmptoolctx,struct snmp_object * obj,char * argv)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
snmptools_parse_oid(struct snmp_toolinfo * snmptoolctx,struct snmp_object * obj,char * argv)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
snmptool_add_vbind(struct snmp_pdu * pdu,struct snmp_object * obj)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
snmpget_verify_vbind(struct snmp_toolinfo * snmptoolctx,struct snmp_pdu * pdu,struct snmp_object * obj)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
snmpget_fix_getbulk(struct snmp_pdu * pdu,uint32_t max_rep,uint32_t non_rep)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
snmptool_get(struct snmp_toolinfo * snmptoolctx)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
snmpwalk_add_default(struct snmp_toolinfo * snmptoolctx __unused,struct snmp_object * obj,char * string __unused)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
snmpwalk_nextpdu_create(uint32_t op,struct asn_oid * var,struct snmp_pdu * pdu)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
snmptool_walk(struct snmp_toolinfo * snmptoolctx)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
parse_oid_numeric(struct snmp_value * value,char * val)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
parse_oid_string(struct snmp_toolinfo * snmptoolctx,struct snmp_value * value,char * string)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
parse_ip(struct snmp_value * value,char * val)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
parse_int(struct snmp_value * value,char * val)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
parse_int_string(struct snmp_object * object,char * val)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
parse_uint(struct snmp_value * value,char * val)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
parse_ticks(struct snmp_value * value,char * val)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
parse_gauge(struct snmp_value * value,char * val)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
parse_counter(struct snmp_value * value,char * val)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
parse_uint64(struct snmp_value * value,char * val)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
parse_syntax_val(struct snmp_value * value,enum snmp_syntax syntax,char * val)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
parse_pair_numoid_val(char * str,struct snmp_value * snmp_val)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
parse_syntax_strval(struct snmp_toolinfo * snmptoolctx,char * str,struct snmp_object * object)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
parse_pair_stroid_val(struct snmp_toolinfo * snmptoolctx,struct snmp_object * obj,char * argv)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
snmpset_parse_oid(struct snmp_toolinfo * snmptoolctx,struct snmp_object * obj,char * argv)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
add_ip_syntax(struct snmp_value * dst,struct snmp_value * src)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
add_octstring_syntax(struct snmp_value * dst,struct snmp_value * src)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
add_oid_syntax(struct snmp_value * dst,struct snmp_value * src)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
snmpset_add_value(struct snmp_value * dst,struct snmp_value * src)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
snmpset_verify_vbind(struct snmp_toolinfo * snmptoolctx,struct snmp_pdu * pdu,struct snmp_object * obj)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
snmpset_add_vbind(struct snmp_pdu * pdu,struct snmp_object * obj)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
snmptool_set(struct snmp_toolinfo * snmptoolctx)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
main(int argc,char ** argv)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 }
1276