xref: /freebsd/usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.c (revision 8ef24a0d4b28fe230e20637f56869cc4148cd2ca)
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 			warnx("Snmp dialog - %s", strerror(errno));
398 			break;
399 		}
400 
401 		if (snmp_parse_resp(&resp, &req) >= 0) {
402 			snmp_output_resp(snmptoolctx, &resp, NULL);
403 			break;
404 		}
405 
406 		snmp_output_err_resp(snmptoolctx, &resp);
407 		if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK ||
408 		    !ISSET_RETRY(snmptoolctx))
409 			break;
410 
411 		/*
412 		 * Loop through the object list and set object->error to the
413 		 * varbinding that caused the error.
414 		 */
415 		if (snmp_object_seterror(snmptoolctx,
416 		    &(resp.bindings[resp.error_index - 1]),
417 		    resp.error_status) <= 0)
418 			break;
419 
420 		fprintf(stderr, "Retrying...\n");
421 		snmp_pdu_free(&resp);
422 		snmp_pdu_create(&req, GET_PDUTYPE(snmptoolctx));
423 	}
424 
425 	snmp_pdu_free(&resp);
426 
427 	return (0);
428 }
429 
430 
431 /* *****************************************************************************
432  * bsnmpwalk private functions.
433  */
434 /* The default tree to walk. */
435 static const struct asn_oid snmp_mibII_OID = {
436 	6 , { 1, 3, 6, 1, 2, 1 }
437 };
438 
439 static int32_t
440 snmpwalk_add_default(struct snmp_toolinfo *snmptoolctx __unused,
441     struct snmp_object *obj, char *string __unused)
442 {
443 	asn_append_oid(&(obj->val.var), &snmp_mibII_OID);
444 	return (1);
445 }
446 
447 /*
448  * Prepare the next GetNext/Get PDU to send.
449  */
450 static void
451 snmpwalk_nextpdu_create(uint32_t op, struct asn_oid *var, struct snmp_pdu *pdu)
452 {
453 	snmp_pdu_create(pdu, op);
454 	asn_append_oid(&(pdu->bindings[0].var), var);
455 	pdu->nbindings = 1;
456 }
457 
458 static int
459 snmptool_walk(struct snmp_toolinfo *snmptoolctx)
460 {
461 	struct snmp_pdu req, resp;
462 	struct asn_oid root;	/* Keep the initial oid. */
463 	int32_t outputs, rc;
464 	uint32_t op;
465 
466 	if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK)
467 		op = SNMP_PDU_GETBULK;
468 	else
469 		op = SNMP_PDU_GETNEXT;
470 
471 	snmp_pdu_create(&req, op);
472 
473 	while ((rc = snmp_pdu_add_bindings(snmptoolctx, NULL,
474 	    snmptool_add_vbind, &req, 1)) > 0) {
475 
476 		/* Remember the root where the walk started from. */
477 		memset(&root, 0, sizeof(struct asn_oid));
478 		asn_append_oid(&root, &(req.bindings[0].var));
479 
480 		if (op == SNMP_PDU_GETBULK)
481 			snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx),
482 			    GET_NONREP(snmptoolctx));
483 
484 		outputs = 0;
485 		while (snmp_dialog(&req, &resp) >= 0) {
486 			if ((snmp_parse_resp(&resp, &req)) < 0) {
487 				snmp_output_err_resp(snmptoolctx, &resp);
488 				snmp_pdu_free(&resp);
489 				outputs = -1;
490 				break;
491 			}
492 
493 			rc = snmp_output_resp(snmptoolctx, &resp, &root);
494 			if (rc < 0) {
495 				snmp_pdu_free(&resp);
496 				outputs = -1;
497 				break;
498 			}
499 
500 			outputs += rc;
501 			snmp_pdu_free(&resp);
502 
503 			if ((u_int)rc < resp.nbindings)
504 				break;
505 
506 			snmpwalk_nextpdu_create(op,
507 			    &(resp.bindings[resp.nbindings - 1].var), &req);
508 			if (op == SNMP_PDU_GETBULK)
509 				snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx),
510 				    GET_NONREP(snmptoolctx));
511 		}
512 
513 		/* Just in case our root was a leaf. */
514 		if (outputs == 0) {
515 			snmpwalk_nextpdu_create(SNMP_PDU_GET, &root, &req);
516 			if (snmp_dialog(&req, &resp) == SNMP_CODE_OK) {
517 				if (snmp_parse_resp(&resp,&req) < 0)
518 					snmp_output_err_resp(snmptoolctx, &resp);
519 				else
520 					snmp_output_resp(snmptoolctx, &(resp), NULL);
521 
522 				snmp_pdu_free(&resp);
523 			} else
524 				warnx("Snmp dialog - %s", strerror(errno));
525 		}
526 
527 		if (snmp_object_remove(snmptoolctx, &root) < 0) {
528 			warnx("snmp_object_remove");
529 			break;
530 		}
531 
532 		snmp_pdu_create(&req, op);
533 	}
534 
535 	if (rc == 0)
536 		return (0);
537 	else
538 		return (1);
539 }
540 
541 /* *****************************************************************************
542  * bsnmpset private functions.
543  */
544 
545 static int32_t
546 parse_oid_numeric(struct snmp_value *value, char *val)
547 {
548 	char *endptr;
549 	int32_t saved_errno;
550 	asn_subid_t suboid;
551 
552 	do {
553 		saved_errno = errno;
554 		errno = 0;
555 		suboid = strtoul(val, &endptr, 10);
556 		if (errno != 0) {
557 			warnx("Value %s not supported - %s", val,
558 			    strerror(errno));
559 			errno = saved_errno;
560 			return (-1);
561 		}
562 		errno = saved_errno;
563 		if ((asn_subid_t) suboid > ASN_MAXID) {
564 			warnx("Suboid %u > ASN_MAXID", suboid);
565 			return (-1);
566 		}
567 		if (snmp_suboid_append(&(value->v.oid), suboid) < 0)
568 			return (-1);
569 		val = endptr + 1;
570 	} while (*endptr == '.');
571 
572 	if (*endptr != '\0')
573 		warnx("OID value %s not supported", val);
574 
575 	value->syntax = SNMP_SYNTAX_OID;
576 	return (0);
577 }
578 
579 /*
580  * Allow OID leaf in both forms:
581  * 1) 1.3.6.1.2... ->  in such case call directly the function reading raw OIDs;
582  * 2) begemotSnmpdAgentFreeBSD -> lookup the ASN OID corresponding to that.
583  */
584 static int32_t
585 parse_oid_string(struct snmp_toolinfo *snmptoolctx,
586     struct snmp_value *value, char *string)
587 {
588 	struct snmp_object obj;
589 
590 	if (isdigit(string[0]))
591 		return (parse_oid_numeric(value, string));
592 
593 	memset(&obj, 0, sizeof(struct snmp_object));
594 	if (snmp_lookup_enumoid(snmptoolctx, &obj, string) < 0) {
595 		warnx("Unknown OID enum string - %s", string);
596 		return (-1);
597 	}
598 
599 	asn_append_oid(&(value->v.oid), &(obj.val.var));
600 	return (1);
601 }
602 
603 static int32_t
604 parse_ip(struct snmp_value * value, char * val)
605 {
606 	uint32_t v;
607 	int32_t i;
608 	char *endptr, *str;
609 
610 	str = val;
611 	for (i = 0; i < 4; i++) {
612 		v = strtoul(str, &endptr, 10);
613 		if (v > 0xff)
614 			return (-1);
615 		if (*endptr != '.' && *endptr != '\0' && i != 3)
616 			break;
617 		str = endptr + 1;
618 		value->v.ipaddress[i] = (uint8_t) v;
619 	}
620 
621 	value->syntax = SNMP_SYNTAX_IPADDRESS;
622 	return (0);
623 }
624 
625 static int32_t
626 parse_int(struct snmp_value *value, char *val)
627 {
628 	char *endptr;
629 	int32_t v, saved_errno;
630 
631 	saved_errno = errno;
632 	errno = 0;
633 
634 	v = strtol(val, &endptr, 10);
635 
636 	if (errno != 0) {
637 		warnx("Value %s not supported - %s", val, strerror(errno));
638 		errno = saved_errno;
639 		return (-1);
640 	}
641 
642 	value->syntax = SNMP_SYNTAX_INTEGER;
643 	value->v.integer = v;
644 	errno = saved_errno;
645 
646 	return (0);
647 }
648 
649 static int32_t
650 parse_int_string(struct snmp_object *object, char *val)
651 {
652 	int32_t	v;
653 
654 	if (isdigit(val[0]))
655 		return ((parse_int(&(object->val), val)));
656 
657 	if (object->info == NULL) {
658 		warnx("Unknown enumerated integer type - %s", val);
659 		return (-1);
660 	}
661 	if ((v = enum_number_lookup(object->info->snmp_enum, val)) < 0)
662 		warnx("Unknown enumerated integer type - %s", val);
663 
664 	object->val.v.integer = v;
665 	return (1);
666 }
667 
668 /*
669  * Here syntax may be one of SNMP_SYNTAX_COUNTER, SNMP_SYNTAX_GAUGE,
670  * SNMP_SYNTAX_TIMETICKS.
671  */
672 static int32_t
673 parse_uint(struct snmp_value *value, char *val)
674 {
675 	char *endptr;
676 	uint32_t v = 0;
677 	int32_t saved_errno;
678 
679 	saved_errno = errno;
680 	errno = 0;
681 
682 	v = strtoul(val, &endptr, 10);
683 
684 	if (errno != 0) {
685 		warnx("Value %s not supported - %s", val, strerror(errno));
686 		errno = saved_errno;
687 		return (-1);
688 	}
689 
690 	value->v.uint32 = v;
691 	errno = saved_errno;
692 
693 	return (0);
694 }
695 
696 static int32_t
697 parse_ticks(struct snmp_value *value, char *val)
698 {
699 	if (parse_uint(value, val) < 0)
700 		return (-1);
701 
702 	value->syntax = SNMP_SYNTAX_TIMETICKS;
703 	return (0);
704 }
705 
706 static int32_t
707 parse_gauge(struct snmp_value *value, char *val)
708 {
709 	if (parse_uint(value, val) < 0)
710 		return (-1);
711 
712 	value->syntax = SNMP_SYNTAX_GAUGE;
713 	return (0);
714 }
715 
716 static int32_t
717 parse_counter(struct snmp_value *value, char *val)
718 {
719 	if (parse_uint(value, val) < 0)
720 		return (-1);
721 
722 	value->syntax = SNMP_SYNTAX_COUNTER;
723 	return (0);
724 }
725 
726 static int32_t
727 parse_uint64(struct snmp_value *value, char *val)
728 {
729 	char *endptr;
730 	int32_t saved_errno;
731 	uint64_t v;
732 
733 	saved_errno = errno;
734 	errno = 0;
735 
736 	v = strtoull(val, &endptr, 10);
737 
738 	if (errno != 0) {
739 		warnx("Value %s not supported - %s", val, strerror(errno));
740 		errno = saved_errno;
741 		return (-1);
742 	}
743 
744 	value->syntax = SNMP_SYNTAX_COUNTER64;
745 	value->v.counter64 = v;
746 	errno = saved_errno;
747 
748 	return (0);
749 }
750 
751 static int32_t
752 parse_syntax_val(struct snmp_value *value, enum snmp_syntax syntax, char *val)
753 {
754 	switch (syntax) {
755 		case SNMP_SYNTAX_INTEGER:
756 			return (parse_int(value, val));
757 		case SNMP_SYNTAX_IPADDRESS:
758 			return (parse_ip(value, val));
759 		case SNMP_SYNTAX_COUNTER:
760 			return (parse_counter(value, val));
761 		case SNMP_SYNTAX_GAUGE:
762 			return (parse_gauge(value, val));
763 		case SNMP_SYNTAX_TIMETICKS:
764 			return (parse_ticks(value, val));
765 		case SNMP_SYNTAX_COUNTER64:
766 			return (parse_uint64(value, val));
767 		case SNMP_SYNTAX_OCTETSTRING:
768 			return (snmp_tc2oct(SNMP_STRING, value, val));
769 		case SNMP_SYNTAX_OID:
770 			return (parse_oid_numeric(value, val));
771 		default:
772 			/* NOTREACHED */
773 			break;
774 	}
775 
776 	return (-1);
777 }
778 
779 /*
780  * Parse a command line argument of type OID=syntax:value and fill in whatever
781  * fields can be derived from the input into snmp_value structure. Reads numeric
782  * OIDs.
783  */
784 static int32_t
785 parse_pair_numoid_val(char *str, struct snmp_value *snmp_val)
786 {
787 	int32_t cnt;
788 	char *ptr;
789 	enum snmp_syntax syntax;
790 	char oid_str[ASN_OIDSTRLEN];
791 
792 	ptr = str;
793 	for (cnt = 0; cnt < ASN_OIDSTRLEN; cnt++)
794 		if (ptr[cnt] == '=')
795 			break;
796 
797 	if (cnt >= ASN_OIDSTRLEN) {
798 		warnx("OID too long - %s", str);
799 		return (-1);
800 	}
801 	strlcpy(oid_str, ptr, (size_t) (cnt + 1));
802 
803 	ptr = str + cnt + 1;
804 	for (cnt = 0; cnt < MAX_CMD_SYNTAX_LEN; cnt++)
805 		if(ptr[cnt] == ':')
806 			break;
807 
808 	if (cnt >= MAX_CMD_SYNTAX_LEN) {
809 		warnx("Unknown syntax in OID - %s", str);
810 		return (-1);
811 	}
812 
813 	if ((syntax = parse_syntax(ptr)) <= SNMP_SYNTAX_NULL) {
814 		warnx("Unknown syntax in OID - %s", ptr);
815 		return (-1);
816 	}
817 
818 	ptr = ptr + cnt + 1;
819 	for (cnt = 0; cnt < MAX_OCTSTRING_LEN; cnt++)
820 		if (ptr[cnt] == '\0')
821 			break;
822 
823 	if (ptr[cnt] != '\0') {
824 		warnx("Value string too long - %s",ptr);
825 		return (-1);
826 	}
827 
828 	/*
829 	 * Here try parsing the OIDs and syntaxes and then check values - have
830 	 * to know syntax to check value boundaries.
831 	 */
832 	if (snmp_parse_numoid(oid_str, &(snmp_val->var)) < 0) {
833 		warnx("Error parsing OID %s",oid_str);
834 		return (-1);
835 	}
836 
837 	if (parse_syntax_val(snmp_val, syntax, ptr) < 0)
838 		return (-1);
839 
840 	return (1);
841 }
842 
843 /* XXX-BZ aruments should be swapped. */
844 static int32_t
845 parse_syntax_strval(struct snmp_toolinfo *snmptoolctx, char *str,
846     struct snmp_object *object)
847 {
848 	uint32_t len;
849 	enum snmp_syntax syn;
850 
851 	/*
852 	 * Syntax string here not required  - still may be present.
853 	 */
854 
855 	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) {
856 		for (len = 0 ; *(str + len) != ':'; len++) {
857 			if (*(str + len) == '\0') {
858 				warnx("Syntax missing in value - %s", str);
859 				return (-1);
860 			}
861 		}
862 		if ((syn = parse_syntax(str)) <= SNMP_SYNTAX_NULL) {
863 			warnx("Unknown syntax in - %s", str);
864 			return (-1);
865 		}
866 		if (syn != object->val.syntax) {
867 			if (!ISSET_ERRIGNORE(snmptoolctx)) {
868 				warnx("Bad syntax in - %s", str);
869 				return (-1);
870 			} else
871 				object->val.syntax = syn;
872 		}
873 		len++;
874 	} else
875 		len = 0;
876 
877 	switch (object->val.syntax) {
878 		case SNMP_SYNTAX_INTEGER:
879 			return (parse_int_string(object, str + len));
880 		case SNMP_SYNTAX_IPADDRESS:
881 			return (parse_ip(&(object->val), str + len));
882 		case SNMP_SYNTAX_COUNTER:
883 			return (parse_counter(&(object->val), str + len));
884 		case SNMP_SYNTAX_GAUGE:
885 			return (parse_gauge(&(object->val), str + len));
886 		case SNMP_SYNTAX_TIMETICKS:
887 			return (parse_ticks(&(object->val), str + len));
888 		case SNMP_SYNTAX_COUNTER64:
889 			return (parse_uint64(&(object->val), str + len));
890 		case SNMP_SYNTAX_OCTETSTRING:
891 			return (snmp_tc2oct(object->info->tc, &(object->val),
892 			    str + len));
893 		case SNMP_SYNTAX_OID:
894 			return (parse_oid_string(snmptoolctx, &(object->val),
895 			    str + len));
896 		default:
897 			/* NOTREACHED */
898 			break;
899 	}
900 
901 	return (-1);
902 }
903 
904 static int32_t
905 parse_pair_stroid_val(struct snmp_toolinfo *snmptoolctx,
906     struct snmp_object *obj, char *argv)
907 {
908 	char *ptr;
909 
910 	if ((ptr = snmptools_parse_stroid(snmptoolctx, obj, argv)) == NULL)
911 		return (-1);
912 
913 	if (*ptr != '=') {
914 		warnx("Value to set expected after OID");
915 		return (-1);
916 	}
917 
918 	if (parse_syntax_strval(snmptoolctx, ptr + 1, obj) < 0)
919 		return (-1);
920 
921 	return (1);
922 }
923 
924 
925 static int32_t
926 snmpset_parse_oid(struct snmp_toolinfo *snmptoolctx,
927     struct snmp_object *obj, char *argv)
928 {
929 	if (argv == NULL)
930 		return (-1);
931 
932 	if (ISSET_NUMERIC(snmptoolctx)) {
933 		if (parse_pair_numoid_val(argv, &(obj->val)) < 0)
934 			return (-1);
935 	} else {
936 		if (parse_pair_stroid_val(snmptoolctx, obj, argv) < 0)
937 			return (-1);
938 	}
939 
940 	return (1);
941 }
942 
943 static int32_t
944 add_ip_syntax(struct snmp_value *dst, struct snmp_value *src)
945 {
946 	int8_t i;
947 
948 	dst->syntax = SNMP_SYNTAX_IPADDRESS;
949 	for (i = 0; i < 4; i++)
950 		dst->v.ipaddress[i] = src->v.ipaddress[i];
951 
952 	return (1);
953 }
954 
955 static int32_t
956 add_octstring_syntax(struct snmp_value *dst, struct snmp_value *src)
957 {
958 	if (src->v.octetstring.len > ASN_MAXOCTETSTRING) {
959 		warnx("OctetString len too big - %u",src->v.octetstring.len);
960 		return (-1);
961 	}
962 
963 	if ((dst->v.octetstring.octets = malloc(src->v.octetstring.len)) ==
964 	    NULL) {
965 		syslog(LOG_ERR, "malloc() failed - %s", strerror(errno));
966 		return (-1);
967 	}
968 
969 	memcpy(dst->v.octetstring.octets, src->v.octetstring.octets,
970 	    src->v.octetstring.len);
971 	dst->syntax = SNMP_SYNTAX_OCTETSTRING;
972 	dst->v.octetstring.len = src->v.octetstring.len;
973 
974 	return(0);
975 }
976 
977 static int32_t
978 add_oid_syntax(struct snmp_value *dst, struct snmp_value *src)
979 {
980 	asn_append_oid(&(dst->v.oid), &(src->v.oid));
981 	dst->syntax = SNMP_SYNTAX_OID;
982 	return (0);
983 }
984 
985 /*
986  * Check syntax - if one of SNMP_SYNTAX_NULL, SNMP_SYNTAX_NOSUCHOBJECT,
987  * SNMP_SYNTAX_NOSUCHINSTANCE, SNMP_SYNTAX_ENDOFMIBVIEW or anything not known -
988  * return error.
989  */
990 static int32_t
991 snmpset_add_value(struct snmp_value *dst, struct snmp_value *src)
992 {
993 	if (dst == NULL || src == NULL)
994 		return (-1);
995 
996 	switch (src->syntax) {
997 		case SNMP_SYNTAX_INTEGER:
998 			dst->v.integer = src->v.integer;
999 			dst->syntax = SNMP_SYNTAX_INTEGER;
1000 			break;
1001 		case SNMP_SYNTAX_TIMETICKS:
1002 			dst->v.uint32 = src->v.uint32;
1003 			dst->syntax = SNMP_SYNTAX_TIMETICKS;
1004 			break;
1005 		case SNMP_SYNTAX_GAUGE:
1006 			dst->v.uint32 = src->v.uint32;
1007 			dst->syntax = SNMP_SYNTAX_GAUGE;
1008 			break;
1009 		case SNMP_SYNTAX_COUNTER:
1010 			dst->v.uint32 = src->v.uint32;
1011 			dst->syntax = SNMP_SYNTAX_COUNTER;
1012 			break;
1013 		case SNMP_SYNTAX_COUNTER64:
1014 			dst->v.counter64 = src->v.counter64;
1015 			dst->syntax = SNMP_SYNTAX_COUNTER64;
1016 			break;
1017 		case SNMP_SYNTAX_IPADDRESS:
1018 			add_ip_syntax(dst, src);
1019 			break;
1020 		case SNMP_SYNTAX_OCTETSTRING:
1021 			add_octstring_syntax(dst, src);
1022 			break;
1023 		case SNMP_SYNTAX_OID:
1024 			add_oid_syntax(dst, src);
1025 			break;
1026 		default:
1027 			warnx("Unknown syntax %d", src->syntax);
1028 			return (-1);
1029 	}
1030 
1031 	return (0);
1032 }
1033 
1034 static int32_t
1035 snmpset_verify_vbind(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu,
1036     struct snmp_object *obj)
1037 {
1038 	if (pdu->version == SNMP_V1 && obj->val.syntax ==
1039 	    SNMP_SYNTAX_COUNTER64) {
1040 		warnx("64-bit counters are not supported in SNMPv1 PDU");
1041 		return (-1);
1042 	}
1043 
1044 	if (ISSET_NUMERIC(snmptoolctx) || ISSET_ERRIGNORE(snmptoolctx))
1045 		return (1);
1046 
1047 	if (obj->info->access < SNMP_ACCESS_SET) {
1048 		warnx("Object %s not accessible for set - try 'bsnmpset -a'",
1049 		    obj->info->string);
1050 		return (-1);
1051 	}
1052 
1053 	return (1);
1054 }
1055 
1056 static int32_t
1057 snmpset_add_vbind(struct snmp_pdu *pdu, struct snmp_object *obj)
1058 {
1059 	if (pdu->nbindings > SNMP_MAX_BINDINGS) {
1060 		warnx("Too many OIDs for one PDU");
1061 		return (-1);
1062 	}
1063 
1064 	if (obj->error > 0)
1065 		return (0);
1066 
1067 	if (snmpset_add_value(&(pdu->bindings[pdu->nbindings]), &(obj->val))
1068 	    < 0)
1069 		return (-1);
1070 
1071 	asn_append_oid(&(pdu->bindings[pdu->nbindings].var), &(obj->val.var));
1072 	pdu->nbindings++;
1073 
1074 	return (pdu->nbindings);
1075 }
1076 
1077 static int
1078 snmptool_set(struct snmp_toolinfo *snmptoolctx)
1079 {
1080 	struct snmp_pdu req, resp;
1081 
1082 	snmp_pdu_create(&req, SNMP_PDU_SET);
1083 
1084 	while ((snmp_pdu_add_bindings(snmptoolctx, snmpset_verify_vbind,
1085 	    snmpset_add_vbind, &req, SNMP_MAX_BINDINGS)) > 0) {
1086 		if (snmp_dialog(&req, &resp)) {
1087 			warnx("Snmp dialog - %s", strerror(errno));
1088 			break;
1089 		}
1090 
1091 		if (snmp_pdu_check(&req, &resp) > 0) {
1092 			if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET)
1093 				snmp_output_resp(snmptoolctx, &resp, NULL);
1094 			break;
1095 		}
1096 
1097 		snmp_output_err_resp(snmptoolctx, &resp);
1098 		if (!ISSET_RETRY(snmptoolctx))
1099 			break;
1100 
1101 		if (snmp_object_seterror(snmptoolctx,
1102 		    &(resp.bindings[resp.error_index - 1]),
1103 		    resp.error_status) <= 0)
1104 			break;
1105 
1106 		fprintf(stderr, "Retrying...\n");
1107 		snmp_pdu_free(&req);
1108 		snmp_pdu_free(&resp);
1109 		snmp_pdu_create(&req, SNMP_PDU_SET);
1110 	}
1111 
1112 	snmp_pdu_free(&resp);
1113 
1114 	return (0);
1115 }
1116 
1117 /* *****************************************************************************
1118  * main
1119  */
1120 /*
1121  * According to command line options prepare SNMP Get | GetNext | GetBulk PDU.
1122  * Wait for a response and print it.
1123  */
1124 /*
1125  * Do a 'snmp walk' - according to command line options request for values
1126  * lexicographically subsequent and subrooted at a common node. Send a GetNext
1127  * PDU requesting the value for each next variable and print the response. Stop
1128  * when a Response PDU is received that contains the value of a variable not
1129  * subrooted at the variable the walk started.
1130  */
1131 int
1132 main(int argc, char ** argv)
1133 {
1134 	struct snmp_toolinfo snmptoolctx;
1135 	int32_t oid_cnt, last_oid, opt_num;
1136 	int rc = 0;
1137 
1138 	/* Make sure program_name is set and valid. */
1139 	if (*argv == NULL)
1140 		program_name = "snmptool";
1141 	else {
1142 		program_name = strrchr(*argv, '/');
1143 		if (program_name != NULL)
1144 			program_name++;
1145 		else
1146 			program_name = *argv;
1147 	}
1148 
1149 	if (program_name == NULL) {
1150 		fprintf(stderr, "Error: No program name?\n");
1151 		exit (1);
1152 	} else if (strcmp(program_name, "bsnmpget") == 0)
1153 		program = BSNMPGET;
1154 	else if (strcmp(program_name, "bsnmpwalk") == 0)
1155 		program = BSNMPWALK;
1156 	else if (strcmp(program_name, "bsnmpset") == 0)
1157 		program = BSNMPSET;
1158 	else {
1159 		fprintf(stderr, "Unknown snmp tool name '%s'.\n", program_name);
1160 		exit (1);
1161 	}
1162 
1163 	/* Initialize. */
1164 	if (snmptool_init(&snmptoolctx) < 0)
1165 		exit (1);
1166 
1167 	if ((opt_num = snmptool_parse_options(&snmptoolctx, argc, argv)) < 0) {
1168 		snmp_tool_freeall(&snmptoolctx);
1169 		/* On -h (help) exit without error. */
1170 		if (opt_num == -2)
1171 			exit(0);
1172 		else
1173 			exit(1);
1174 	}
1175 
1176 	oid_cnt = argc - opt_num - 1;
1177 	if (oid_cnt == 0) {
1178 		switch (program) {
1179 		case BSNMPGET:
1180 			if (!ISSET_EDISCOVER(&snmptoolctx) &&
1181 			    !ISSET_LOCALKEY(&snmptoolctx)) {
1182 				fprintf(stderr, "No OID given.\n");
1183 				usage();
1184 				snmp_tool_freeall(&snmptoolctx);
1185 				exit(1);
1186 			}
1187 			break;
1188 
1189 		case BSNMPWALK:
1190 			if (snmp_object_add(&snmptoolctx, snmpwalk_add_default,
1191 			    NULL) < 0) {
1192 				fprintf(stderr,
1193 				    "Error setting default subtree.\n");
1194 				snmp_tool_freeall(&snmptoolctx);
1195 				exit(1);
1196 			}
1197 			break;
1198 
1199 		case BSNMPSET:
1200 			fprintf(stderr, "No OID given.\n");
1201 			usage();
1202 			snmp_tool_freeall(&snmptoolctx);
1203 			exit(1);
1204 		}
1205 	}
1206 
1207 	if (snmp_import_all(&snmptoolctx) < 0) {
1208 		snmp_tool_freeall(&snmptoolctx);
1209 		exit(1);
1210 	}
1211 
1212 	/* A simple sanity check - can not send GETBULK when using SNMPv1. */
1213 	if (program == BSNMPGET && snmp_client.version == SNMP_V1 &&
1214 	    GET_PDUTYPE(&snmptoolctx) == SNMP_PDU_GETBULK) {
1215 		fprintf(stderr, "Cannot send GETBULK PDU with SNMPv1.\n");
1216 		snmp_tool_freeall(&snmptoolctx);
1217 		exit(1);
1218 	}
1219 
1220 	for (last_oid = argc - 1; oid_cnt > 0; last_oid--, oid_cnt--) {
1221 		if ((snmp_object_add(&snmptoolctx, (program == BSNMPSET) ?
1222 		    snmpset_parse_oid : snmptools_parse_oid,
1223 		    argv[last_oid])) < 0) {
1224 			fprintf(stderr, "Error parsing OID string '%s'.\n",
1225 			    argv[last_oid]);
1226 			snmp_tool_freeall(&snmptoolctx);
1227 			exit(1);
1228 		}
1229 	}
1230 
1231 	if (snmp_open(NULL, NULL, NULL, NULL)) {
1232 		warnx("Failed to open snmp session: %s.", strerror(errno));
1233 		snmp_tool_freeall(&snmptoolctx);
1234 		exit(1);
1235 	}
1236 
1237 	if (snmp_client.version == SNMP_V3 && snmp_client.engine.engine_len == 0)
1238 		SET_EDISCOVER(&snmptoolctx);
1239 
1240 	if (ISSET_EDISCOVER(&snmptoolctx) &&
1241 	    snmp_discover_engine(snmptoolctx.passwd) < 0) {
1242 		warnx("Unknown SNMP Engine ID: %s.", strerror(errno));
1243 		rc = 1;
1244 		goto cleanup;
1245 	}
1246 
1247 	if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE ||
1248 	    ISSET_EDISCOVER(&snmptoolctx))
1249 		snmp_output_engine();
1250 
1251 	if (snmp_client.version == SNMP_V3 && ISSET_LOCALKEY(&snmptoolctx) &&
1252 	    !ISSET_EDISCOVER(&snmptoolctx)) {
1253 		if (snmp_passwd_to_keys(&snmp_client.user,
1254 		    snmptoolctx.passwd) != SNMP_CODE_OK ||
1255 		    snmp_get_local_keys(&snmp_client.user,
1256 		    snmp_client.engine.engine_id,
1257 		    snmp_client.engine.engine_len) != SNMP_CODE_OK) {
1258 		    	warnx("Failed to get keys: %s.", strerror(errno));
1259 			rc = 1;
1260 			goto cleanup;
1261 		}
1262 	}
1263 
1264 	if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE ||
1265 	    ISSET_EDISCOVER(&snmptoolctx))
1266 		snmp_output_keys();
1267 
1268 	if (ISSET_EDISCOVER(&snmptoolctx) && snmptoolctx.objects == 0)
1269 		goto cleanup;
1270 
1271 	switch (program) {
1272 	case BSNMPGET:
1273 		rc = snmptool_get(&snmptoolctx);
1274 		break;
1275 	case BSNMPWALK:
1276 		rc = snmptool_walk(&snmptoolctx);
1277 		break;
1278 	case BSNMPSET:
1279 		rc = snmptool_set(&snmptoolctx);
1280 		break;
1281 	}
1282 
1283 
1284 cleanup:
1285 	snmp_tool_freeall(&snmptoolctx);
1286 	snmp_close();
1287 
1288 	exit(rc);
1289 }
1290