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