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