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
53static const char *program_name = NULL;
54static enum program_e {
55	BSNMPGET,
56	BSNMPWALK,
57	BSNMPSET
58} program;
59
60/* *****************************************************************************
61 * Common bsnmptools functions.
62 */
63static void
64usage(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
86static int32_t
87parse_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
105static int32_t
106parse_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
124static int32_t
125parse_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
143static int32_t
144snmptool_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 */
254static char *
255snmptools_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
302static int32_t
303snmptools_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
321static int32_t
322snmptool_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 */
336static int32_t
337snmpget_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 */
364static void
365snmpget_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
380static int
381snmptool_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. */
438static const struct asn_oid snmp_mibII_OID = {
439	6 , { 1, 3, 6, 1, 2, 1 }
440};
441
442static int32_t
443snmpwalk_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 */
453static void
454snmpwalk_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
461static int
462snmptool_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
553static int32_t
554parse_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 */
591static int32_t
592parse_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
610static int32_t
611parse_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
632static int32_t
633parse_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
656static int32_t
657parse_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 */
679static int32_t
680parse_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
703static int32_t
704parse_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
713static int32_t
714parse_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
723static int32_t
724parse_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
733static int32_t
734parse_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
758static int32_t
759parse_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 */
791static int32_t
792parse_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
850static int32_t
851parse_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
910static int32_t
911parse_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
931static int32_t
932snmpset_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
949static int32_t
950add_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
961static int32_t
962add_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
983static int32_t
984add_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 */
996static int32_t
997snmpset_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
1040static int32_t
1041snmpset_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
1062static int32_t
1063snmpset_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
1083static int
1084snmptool_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 */
1141int
1142main(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
1294cleanup:
1295	snmp_tool_freeall(&snmptoolctx);
1296	snmp_close();
1297
1298	exit(rc);
1299}
1300