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