bsnmpget.c revision 310562
1215990Sjmallett/*-
2232812Sjmallett * Copyright (c) 2005-2006 The FreeBSD Project
3215990Sjmallett * All rights reserved.
4215990Sjmallett *
5215990Sjmallett * Author: Shteryana Shopova <syrinx@FreeBSD.org>
6215990Sjmallett *
7215990Sjmallett * Redistribution of this software and documentation and use in source and
8215990Sjmallett * binary forms, with or without modification, are permitted provided that
9215990Sjmallett * the following conditions are met:
10215990Sjmallett *
11215990Sjmallett * 1. Redistributions of source code or documentation must retain the above
12215990Sjmallett *    copyright notice, this list of conditions and the following disclaimer.
13215990Sjmallett * 2. Redistributions in binary form must reproduce the above copyright
14215990Sjmallett *    notice, this list of conditions and the following disclaimer in the
15215990Sjmallett *    documentation and/or other materials provided with the distribution.
16215990Sjmallett *
17215990Sjmallett * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18232812Sjmallett * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19215990Sjmallett * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20215990Sjmallett * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21215990Sjmallett * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22215990Sjmallett * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23215990Sjmallett * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24232812Sjmallett * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25215990Sjmallett * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26215990Sjmallett * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27215990Sjmallett * SUCH DAMAGE.
28215990Sjmallett *
29215990Sjmallett * Bsnmpget and bsnmpwalk are simple tools for querying SNMP agents,
30215990Sjmallett * bsnmpset can be used to set MIB objects in an agent.
31215990Sjmallett *
32215990Sjmallett * $FreeBSD: stable/10/usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.c 310562 2016-12-26 06:04:10Z ngie $
33215990Sjmallett */
34215990Sjmallett
35215990Sjmallett#include <sys/queue.h>
36215990Sjmallett#include <sys/types.h>
37210284Sjmallett
38210284Sjmallett#include <assert.h>
39210284Sjmallett#include <ctype.h>
40210284Sjmallett#include <err.h>
41210284Sjmallett#include <errno.h>
42210284Sjmallett#include <stdarg.h>
43210284Sjmallett#include <stdio.h>
44210284Sjmallett#include <stdlib.h>
45210284Sjmallett#include <string.h>
46210284Sjmallett#include <syslog.h>
47232812Sjmallett#include <unistd.h>
48232812Sjmallett
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			warnx("Snmp dialog - %s", strerror(errno));
398			break;
399		}
400
401		if (snmp_parse_resp(&resp, &req) >= 0) {
402			snmp_output_resp(snmptoolctx, &resp, NULL);
403			break;
404		}
405
406		snmp_output_err_resp(snmptoolctx, &resp);
407		if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK ||
408		    !ISSET_RETRY(snmptoolctx))
409			break;
410
411		/*
412		 * Loop through the object list and set object->error to the
413		 * varbinding that caused the error.
414		 */
415		if (snmp_object_seterror(snmptoolctx,
416		    &(resp.bindings[resp.error_index - 1]),
417		    resp.error_status) <= 0)
418			break;
419
420		fprintf(stderr, "Retrying...\n");
421		snmp_pdu_free(&resp);
422		snmp_pdu_create(&req, GET_PDUTYPE(snmptoolctx));
423	}
424
425	snmp_pdu_free(&resp);
426
427	return (0);
428}
429
430
431/* *****************************************************************************
432 * bsnmpwalk private functions.
433 */
434/* The default tree to walk. */
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				warnx("Snmp dialog - %s", strerror(errno));
525		}
526
527		if (snmp_object_remove(snmptoolctx, &root) < 0) {
528			warnx("snmp_object_remove");
529			break;
530		}
531
532		snmp_pdu_create(&req, op);
533	}
534
535	if (rc == 0)
536		return (0);
537	else
538		return (1);
539}
540
541/* *****************************************************************************
542 * bsnmpset private functions.
543 */
544
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			warnx("Value %s not supported - %s", val,
558			    strerror(errno));
559			errno = saved_errno;
560			return (-1);
561		}
562		errno = saved_errno;
563		if ((asn_subid_t) suboid > ASN_MAXID) {
564			warnx("Suboid %u > ASN_MAXID", suboid);
565			return (-1);
566		}
567		if (snmp_suboid_append(&(value->v.oid), suboid) < 0)
568			return (-1);
569		val = endptr + 1;
570	} while (*endptr == '.');
571
572	if (*endptr != '\0')
573		warnx("OID value %s not supported", val);
574
575	value->syntax = SNMP_SYNTAX_OID;
576	return (0);
577}
578
579/*
580 * Allow OID leaf in both forms:
581 * 1) 1.3.6.1.2... ->  in such case call directly the function reading raw OIDs;
582 * 2) begemotSnmpdAgentFreeBSD -> lookup the ASN OID corresponding to that.
583 */
584static int32_t
585parse_oid_string(struct snmp_toolinfo *snmptoolctx,
586    struct snmp_value *value, char *string)
587{
588	struct snmp_object obj;
589
590	if (isdigit(string[0]))
591		return (parse_oid_numeric(value, string));
592
593	memset(&obj, 0, sizeof(struct snmp_object));
594	if (snmp_lookup_enumoid(snmptoolctx, &obj, string) < 0) {
595		warnx("Unknown OID enum string - %s", string);
596		return (-1);
597	}
598
599	asn_append_oid(&(value->v.oid), &(obj.val.var));
600	return (1);
601}
602
603static int32_t
604parse_ip(struct snmp_value * value, char * val)
605{
606	char *endptr, *str;
607	int32_t i;
608	uint32_t v;
609
610	str = val;
611	for (i = 0; i < 4; i++) {
612		v = strtoul(str, &endptr, 10);
613		if (v > 0xff)
614			return (-1);
615		if (*endptr != '.' && *endptr != '\0' && i != 3)
616			break;
617		str = endptr + 1;
618		value->v.ipaddress[i] = (uint8_t) v;
619	}
620	value->syntax = SNMP_SYNTAX_IPADDRESS;
621
622	return (0);
623}
624
625static int32_t
626parse_int(struct snmp_value *value, char *val)
627{
628	char *endptr;
629	int32_t v, saved_errno;
630
631	saved_errno = errno;
632	errno = 0;
633
634	v = strtol(val, &endptr, 10);
635
636	if (errno != 0) {
637		warnx("Value %s not supported - %s", val, strerror(errno));
638		errno = saved_errno;
639		return (-1);
640	}
641
642	value->syntax = SNMP_SYNTAX_INTEGER;
643	value->v.integer = v;
644	errno = saved_errno;
645
646	return (0);
647}
648
649static int32_t
650parse_int_string(struct snmp_object *object, char *val)
651{
652	int32_t	v;
653
654	if (isdigit(val[0]))
655		return ((parse_int(&(object->val), val)));
656
657	if (object->info == NULL) {
658		warnx("Unknown enumerated integer type - %s", val);
659		return (-1);
660	}
661	if ((v = enum_number_lookup(object->info->snmp_enum, val)) < 0)
662		warnx("Unknown enumerated integer type - %s", val);
663
664	object->val.v.integer = v;
665	return (1);
666}
667
668/*
669 * Here syntax may be one of SNMP_SYNTAX_COUNTER, SNMP_SYNTAX_GAUGE,
670 * SNMP_SYNTAX_TIMETICKS.
671 */
672static int32_t
673parse_uint(struct snmp_value *value, char *val)
674{
675	char *endptr;
676	uint32_t v = 0;
677	int32_t saved_errno;
678
679	saved_errno = errno;
680	errno = 0;
681
682	v = strtoul(val, &endptr, 10);
683
684	if (errno != 0) {
685		warnx("Value %s not supported - %s", val, strerror(errno));
686		errno = saved_errno;
687		return (-1);
688	}
689
690	value->v.uint32 = v;
691	errno = saved_errno;
692
693	return (0);
694}
695
696static int32_t
697parse_ticks(struct snmp_value *value, char *val)
698{
699	if (parse_uint(value, val) < 0)
700		return (-1);
701
702	value->syntax = SNMP_SYNTAX_TIMETICKS;
703	return (0);
704}
705
706static int32_t
707parse_gauge(struct snmp_value *value, char *val)
708{
709	if (parse_uint(value, val) < 0)
710		return (-1);
711
712	value->syntax = SNMP_SYNTAX_GAUGE;
713	return (0);
714}
715
716static int32_t
717parse_counter(struct snmp_value *value, char *val)
718{
719	if (parse_uint(value, val) < 0)
720		return (-1);
721
722	value->syntax = SNMP_SYNTAX_COUNTER;
723	return (0);
724}
725
726static int32_t
727parse_uint64(struct snmp_value *value, char *val)
728{
729	char *endptr;
730	int32_t saved_errno;
731	uint64_t v;
732
733	saved_errno = errno;
734	errno = 0;
735
736	v = strtoull(val, &endptr, 10);
737
738	if (errno != 0) {
739		warnx("Value %s not supported - %s", val, strerror(errno));
740		errno = saved_errno;
741		return (-1);
742	}
743
744	value->syntax = SNMP_SYNTAX_COUNTER64;
745	value->v.counter64 = v;
746	errno = saved_errno;
747
748	return (0);
749}
750
751static int32_t
752parse_syntax_val(struct snmp_value *value, enum snmp_syntax syntax, char *val)
753{
754	switch (syntax) {
755		case SNMP_SYNTAX_INTEGER:
756			return (parse_int(value, val));
757		case SNMP_SYNTAX_IPADDRESS:
758			return (parse_ip(value, val));
759		case SNMP_SYNTAX_COUNTER:
760			return (parse_counter(value, val));
761		case SNMP_SYNTAX_GAUGE:
762			return (parse_gauge(value, val));
763		case SNMP_SYNTAX_TIMETICKS:
764			return (parse_ticks(value, val));
765		case SNMP_SYNTAX_COUNTER64:
766			return (parse_uint64(value, val));
767		case SNMP_SYNTAX_OCTETSTRING:
768			return (snmp_tc2oct(SNMP_STRING, value, val));
769		case SNMP_SYNTAX_OID:
770			return (parse_oid_numeric(value, val));
771		default:
772			/* NOTREACHED */
773			break;
774	}
775
776	return (-1);
777}
778
779/*
780 * Parse a command line argument of type OID=syntax:value and fill in whatever
781 * fields can be derived from the input into snmp_value structure. Reads numeric
782 * OIDs.
783 */
784static int32_t
785parse_pair_numoid_val(char *str, struct snmp_value *snmp_val)
786{
787	int32_t cnt;
788	char *ptr;
789	enum snmp_syntax syntax;
790	char oid_str[ASN_OIDSTRLEN];
791
792	ptr = str;
793	for (cnt = 0; cnt < ASN_OIDSTRLEN; cnt++)
794		if (ptr[cnt] == '=')
795			break;
796
797	if (cnt >= ASN_OIDSTRLEN) {
798		warnx("OID too long - %s", str);
799		return (-1);
800	}
801	strlcpy(oid_str, ptr, (size_t) (cnt + 1));
802
803	ptr = str + cnt + 1;
804	for (cnt = 0; cnt < MAX_CMD_SYNTAX_LEN; cnt++)
805		if(ptr[cnt] == ':')
806			break;
807
808	if (cnt >= MAX_CMD_SYNTAX_LEN) {
809		warnx("Unknown syntax in OID - %s", str);
810		return (-1);
811	}
812
813	if ((syntax = parse_syntax(ptr)) <= SNMP_SYNTAX_NULL) {
814		warnx("Unknown syntax in OID - %s", ptr);
815		return (-1);
816	}
817
818	ptr = ptr + cnt + 1;
819	for (cnt = 0; cnt < MAX_OCTSTRING_LEN; cnt++)
820		if (ptr[cnt] == '\0')
821			break;
822
823	if (ptr[cnt] != '\0') {
824		warnx("Value string too long - %s",ptr);
825		return (-1);
826	}
827
828	/*
829	 * Here try parsing the OIDs and syntaxes and then check values - have
830	 * to know syntax to check value boundaries.
831	 */
832	if (snmp_parse_numoid(oid_str, &(snmp_val->var)) < 0) {
833		warnx("Error parsing OID %s",oid_str);
834		return (-1);
835	}
836
837	if (parse_syntax_val(snmp_val, syntax, ptr) < 0)
838		return (-1);
839
840	return (1);
841}
842
843/* XXX-BZ aruments should be swapped. */
844static int32_t
845parse_syntax_strval(struct snmp_toolinfo *snmptoolctx, char *str,
846    struct snmp_object *object)
847{
848	uint32_t len;
849	enum snmp_syntax syn;
850
851	/*
852	 * Syntax string here not required  - still may be present.
853	 */
854
855	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) {
856		for (len = 0 ; *(str + len) != ':'; len++) {
857			if (*(str + len) == '\0') {
858				warnx("Syntax missing in value - %s", str);
859				return (-1);
860			}
861		}
862		if ((syn = parse_syntax(str)) <= SNMP_SYNTAX_NULL) {
863			warnx("Unknown syntax in - %s", str);
864			return (-1);
865		}
866		if (syn != object->val.syntax) {
867			if (!ISSET_ERRIGNORE(snmptoolctx)) {
868				warnx("Bad syntax in - %s", str);
869				return (-1);
870			} else
871				object->val.syntax = syn;
872		}
873		len++;
874	} else
875		len = 0;
876
877	switch (object->val.syntax) {
878		case SNMP_SYNTAX_INTEGER:
879			return (parse_int_string(object, str + len));
880		case SNMP_SYNTAX_IPADDRESS:
881			return (parse_ip(&(object->val), str + len));
882		case SNMP_SYNTAX_COUNTER:
883			return (parse_counter(&(object->val), str + len));
884		case SNMP_SYNTAX_GAUGE:
885			return (parse_gauge(&(object->val), str + len));
886		case SNMP_SYNTAX_TIMETICKS:
887			return (parse_ticks(&(object->val), str + len));
888		case SNMP_SYNTAX_COUNTER64:
889			return (parse_uint64(&(object->val), str + len));
890		case SNMP_SYNTAX_OCTETSTRING:
891			return (snmp_tc2oct(object->info->tc, &(object->val),
892			    str + len));
893		case SNMP_SYNTAX_OID:
894			return (parse_oid_string(snmptoolctx, &(object->val),
895			    str + len));
896		default:
897			/* NOTREACHED */
898			break;
899	}
900
901	return (-1);
902}
903
904static int32_t
905parse_pair_stroid_val(struct snmp_toolinfo *snmptoolctx,
906    struct snmp_object *obj, char *argv)
907{
908	char *ptr;
909
910	if ((ptr = snmptools_parse_stroid(snmptoolctx, obj, argv)) == NULL)
911		return (-1);
912
913	if (*ptr != '=') {
914		warnx("Value to set expected after OID");
915		return (-1);
916	}
917
918	if (parse_syntax_strval(snmptoolctx, ptr + 1, obj) < 0)
919		return (-1);
920
921	return (1);
922}
923
924
925static int32_t
926snmpset_parse_oid(struct snmp_toolinfo *snmptoolctx,
927    struct snmp_object *obj, char *argv)
928{
929	if (argv == NULL)
930		return (-1);
931
932	if (ISSET_NUMERIC(snmptoolctx)) {
933		if (parse_pair_numoid_val(argv, &(obj->val)) < 0)
934			return (-1);
935	} else {
936		if (parse_pair_stroid_val(snmptoolctx, obj, argv) < 0)
937			return (-1);
938	}
939
940	return (1);
941}
942
943static int32_t
944add_ip_syntax(struct snmp_value *dst, struct snmp_value *src)
945{
946	int8_t i;
947
948	dst->syntax = SNMP_SYNTAX_IPADDRESS;
949	for (i = 0; i < 4; i++)
950		dst->v.ipaddress[i] = src->v.ipaddress[i];
951
952	return (1);
953}
954
955static int32_t
956add_octstring_syntax(struct snmp_value *dst, struct snmp_value *src)
957{
958	if (src->v.octetstring.len > ASN_MAXOCTETSTRING) {
959		warnx("OctetString len too big - %u",src->v.octetstring.len);
960		return (-1);
961	}
962
963	if ((dst->v.octetstring.octets = malloc(src->v.octetstring.len)) ==
964	    NULL) {
965		syslog(LOG_ERR, "malloc() failed - %s", strerror(errno));
966		return (-1);
967	}
968
969	memcpy(dst->v.octetstring.octets, src->v.octetstring.octets,
970	    src->v.octetstring.len);
971	dst->syntax = SNMP_SYNTAX_OCTETSTRING;
972	dst->v.octetstring.len = src->v.octetstring.len;
973
974	return(0);
975}
976
977static int32_t
978add_oid_syntax(struct snmp_value *dst, struct snmp_value *src)
979{
980	asn_append_oid(&(dst->v.oid), &(src->v.oid));
981	dst->syntax = SNMP_SYNTAX_OID;
982	return (0);
983}
984
985/*
986 * Check syntax - if one of SNMP_SYNTAX_NULL, SNMP_SYNTAX_NOSUCHOBJECT,
987 * SNMP_SYNTAX_NOSUCHINSTANCE, SNMP_SYNTAX_ENDOFMIBVIEW or anything not known -
988 * return error.
989 */
990static int32_t
991snmpset_add_value(struct snmp_value *dst, struct snmp_value *src)
992{
993	if (dst == NULL || src == NULL)
994		return (-1);
995
996	switch (src->syntax) {
997		case SNMP_SYNTAX_INTEGER:
998			dst->v.integer = src->v.integer;
999			dst->syntax = SNMP_SYNTAX_INTEGER;
1000			break;
1001		case SNMP_SYNTAX_TIMETICKS:
1002			dst->v.uint32 = src->v.uint32;
1003			dst->syntax = SNMP_SYNTAX_TIMETICKS;
1004			break;
1005		case SNMP_SYNTAX_GAUGE:
1006			dst->v.uint32 = src->v.uint32;
1007			dst->syntax = SNMP_SYNTAX_GAUGE;
1008			break;
1009		case SNMP_SYNTAX_COUNTER:
1010			dst->v.uint32 = src->v.uint32;
1011			dst->syntax = SNMP_SYNTAX_COUNTER;
1012			break;
1013		case SNMP_SYNTAX_COUNTER64:
1014			dst->v.counter64 = src->v.counter64;
1015			dst->syntax = SNMP_SYNTAX_COUNTER64;
1016			break;
1017		case SNMP_SYNTAX_IPADDRESS:
1018			add_ip_syntax(dst, src);
1019			break;
1020		case SNMP_SYNTAX_OCTETSTRING:
1021			add_octstring_syntax(dst, src);
1022			break;
1023		case SNMP_SYNTAX_OID:
1024			add_oid_syntax(dst, src);
1025			break;
1026		default:
1027			warnx("Unknown syntax %d", src->syntax);
1028			return (-1);
1029	}
1030
1031	return (0);
1032}
1033
1034static int32_t
1035snmpset_verify_vbind(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu,
1036    struct snmp_object *obj)
1037{
1038	if (pdu->version == SNMP_V1 && obj->val.syntax ==
1039	    SNMP_SYNTAX_COUNTER64) {
1040		warnx("64-bit counters are not supported in SNMPv1 PDU");
1041		return (-1);
1042	}
1043
1044	if (ISSET_NUMERIC(snmptoolctx) || ISSET_ERRIGNORE(snmptoolctx))
1045		return (1);
1046
1047	if (obj->info->access < SNMP_ACCESS_SET) {
1048		warnx("Object %s not accessible for set - try 'bsnmpset -a'",
1049		    obj->info->string);
1050		return (-1);
1051	}
1052
1053	return (1);
1054}
1055
1056static int32_t
1057snmpset_add_vbind(struct snmp_pdu *pdu, struct snmp_object *obj)
1058{
1059	if (pdu->nbindings > SNMP_MAX_BINDINGS) {
1060		warnx("Too many OIDs for one PDU");
1061		return (-1);
1062	}
1063
1064	if (obj->error > 0)
1065		return (0);
1066
1067	if (snmpset_add_value(&(pdu->bindings[pdu->nbindings]), &(obj->val))
1068	    < 0)
1069		return (-1);
1070
1071	asn_append_oid(&(pdu->bindings[pdu->nbindings].var), &(obj->val.var));
1072	pdu->nbindings++;
1073
1074	return (pdu->nbindings);
1075}
1076
1077static int
1078snmptool_set(struct snmp_toolinfo *snmptoolctx)
1079{
1080	struct snmp_pdu req, resp;
1081
1082	snmp_pdu_create(&req, SNMP_PDU_SET);
1083
1084	while ((snmp_pdu_add_bindings(snmptoolctx, snmpset_verify_vbind,
1085	    snmpset_add_vbind, &req, SNMP_MAX_BINDINGS)) > 0) {
1086		if (snmp_dialog(&req, &resp)) {
1087			warnx("Snmp dialog - %s", strerror(errno));
1088			break;
1089		}
1090
1091		if (snmp_pdu_check(&req, &resp) > 0) {
1092			if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET)
1093				snmp_output_resp(snmptoolctx, &resp, NULL);
1094			break;
1095		}
1096
1097		snmp_output_err_resp(snmptoolctx, &resp);
1098		if (!ISSET_RETRY(snmptoolctx))
1099			break;
1100
1101		if (snmp_object_seterror(snmptoolctx,
1102		    &(resp.bindings[resp.error_index - 1]),
1103		    resp.error_status) <= 0)
1104			break;
1105
1106		fprintf(stderr, "Retrying...\n");
1107		snmp_pdu_free(&req);
1108		snmp_pdu_free(&resp);
1109		snmp_pdu_create(&req, SNMP_PDU_SET);
1110	}
1111
1112	snmp_pdu_free(&resp);
1113
1114	return (0);
1115}
1116
1117/* *****************************************************************************
1118 * main
1119 */
1120/*
1121 * According to command line options prepare SNMP Get | GetNext | GetBulk PDU.
1122 * Wait for a response and print it.
1123 */
1124/*
1125 * Do a 'snmp walk' - according to command line options request for values
1126 * lexicographically subsequent and subrooted at a common node. Send a GetNext
1127 * PDU requesting the value for each next variable and print the response. Stop
1128 * when a Response PDU is received that contains the value of a variable not
1129 * subrooted at the variable the walk started.
1130 */
1131int
1132main(int argc, char ** argv)
1133{
1134	struct snmp_toolinfo snmptoolctx;
1135	int32_t oid_cnt, last_oid, opt_num;
1136	int rc = 0;
1137
1138	/* Make sure program_name is set and valid. */
1139	if (*argv == NULL)
1140		program_name = "snmptool";
1141	else {
1142		program_name = strrchr(*argv, '/');
1143		if (program_name != NULL)
1144			program_name++;
1145		else
1146			program_name = *argv;
1147	}
1148
1149	if (program_name == NULL) {
1150		fprintf(stderr, "Error: No program name?\n");
1151		exit (1);
1152	} else if (strcmp(program_name, "bsnmpget") == 0)
1153		program = BSNMPGET;
1154	else if (strcmp(program_name, "bsnmpwalk") == 0)
1155		program = BSNMPWALK;
1156	else if (strcmp(program_name, "bsnmpset") == 0)
1157		program = BSNMPSET;
1158	else {
1159		fprintf(stderr, "Unknown snmp tool name '%s'.\n", program_name);
1160		exit (1);
1161	}
1162
1163	/* Initialize. */
1164	if (snmptool_init(&snmptoolctx) < 0)
1165		exit (1);
1166
1167	if ((opt_num = snmptool_parse_options(&snmptoolctx, argc, argv)) < 0) {
1168		snmp_tool_freeall(&snmptoolctx);
1169		/* On -h (help) exit without error. */
1170		if (opt_num == -2)
1171			exit(0);
1172		else
1173			exit(1);
1174	}
1175
1176	oid_cnt = argc - opt_num - 1;
1177	if (oid_cnt == 0) {
1178		switch (program) {
1179		case BSNMPGET:
1180			if (!ISSET_EDISCOVER(&snmptoolctx) &&
1181			    !ISSET_LOCALKEY(&snmptoolctx)) {
1182				fprintf(stderr, "No OID given.\n");
1183				usage();
1184				snmp_tool_freeall(&snmptoolctx);
1185				exit(1);
1186			}
1187			break;
1188
1189		case BSNMPWALK:
1190			if (snmp_object_add(&snmptoolctx, snmpwalk_add_default,
1191			    NULL) < 0) {
1192				fprintf(stderr,
1193				    "Error setting default subtree.\n");
1194				snmp_tool_freeall(&snmptoolctx);
1195				exit(1);
1196			}
1197			break;
1198
1199		case BSNMPSET:
1200			fprintf(stderr, "No OID given.\n");
1201			usage();
1202			snmp_tool_freeall(&snmptoolctx);
1203			exit(1);
1204		}
1205	}
1206
1207	if (snmp_import_all(&snmptoolctx) < 0) {
1208		snmp_tool_freeall(&snmptoolctx);
1209		exit(1);
1210	}
1211
1212	/* A simple sanity check - can not send GETBULK when using SNMPv1. */
1213	if (program == BSNMPGET && snmp_client.version == SNMP_V1 &&
1214	    GET_PDUTYPE(&snmptoolctx) == SNMP_PDU_GETBULK) {
1215		fprintf(stderr, "Cannot send GETBULK PDU with SNMPv1.\n");
1216		snmp_tool_freeall(&snmptoolctx);
1217		exit(1);
1218	}
1219
1220	for (last_oid = argc - 1; oid_cnt > 0; last_oid--, oid_cnt--) {
1221		if ((snmp_object_add(&snmptoolctx, (program == BSNMPSET) ?
1222		    snmpset_parse_oid : snmptools_parse_oid,
1223		    argv[last_oid])) < 0) {
1224			fprintf(stderr, "Error parsing OID string '%s'.\n",
1225			    argv[last_oid]);
1226			snmp_tool_freeall(&snmptoolctx);
1227			exit(1);
1228		}
1229	}
1230
1231	if (snmp_open(NULL, NULL, NULL, NULL)) {
1232		warnx("Failed to open snmp session: %s.", strerror(errno));
1233		snmp_tool_freeall(&snmptoolctx);
1234		exit(1);
1235	}
1236
1237	if (snmp_client.version == SNMP_V3 && snmp_client.engine.engine_len == 0)
1238		SET_EDISCOVER(&snmptoolctx);
1239
1240	if (ISSET_EDISCOVER(&snmptoolctx) &&
1241	    snmp_discover_engine(snmptoolctx.passwd) < 0) {
1242		warnx("Unknown SNMP Engine ID: %s.", strerror(errno));
1243		rc = 1;
1244		goto cleanup;
1245	}
1246
1247	if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE ||
1248	    ISSET_EDISCOVER(&snmptoolctx))
1249		snmp_output_engine();
1250
1251	if (snmp_client.version == SNMP_V3 && ISSET_LOCALKEY(&snmptoolctx) &&
1252	    !ISSET_EDISCOVER(&snmptoolctx)) {
1253		if (snmp_passwd_to_keys(&snmp_client.user,
1254		    snmptoolctx.passwd) != SNMP_CODE_OK ||
1255		    snmp_get_local_keys(&snmp_client.user,
1256		    snmp_client.engine.engine_id,
1257		    snmp_client.engine.engine_len) != SNMP_CODE_OK) {
1258		    	warnx("Failed to get keys: %s.", strerror(errno));
1259			rc = 1;
1260			goto cleanup;
1261		}
1262	}
1263
1264	if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE ||
1265	    ISSET_EDISCOVER(&snmptoolctx))
1266		snmp_output_keys();
1267
1268	if (ISSET_EDISCOVER(&snmptoolctx) && snmptoolctx.objects == 0)
1269		goto cleanup;
1270
1271	switch (program) {
1272	case BSNMPGET:
1273		rc = snmptool_get(&snmptoolctx);
1274		break;
1275	case BSNMPWALK:
1276		rc = snmptool_walk(&snmptoolctx);
1277		break;
1278	case BSNMPSET:
1279		rc = snmptool_set(&snmptoolctx);
1280		break;
1281	}
1282
1283
1284cleanup:
1285	snmp_tool_freeall(&snmptoolctx);
1286	snmp_close();
1287
1288	exit(rc);
1289}
1290