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