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