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 || 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
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
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
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
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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