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