1216295Ssyrinx/*-
2216295Ssyrinx * Copyright (c) 2005-2006 The FreeBSD Project
3216295Ssyrinx * All rights reserved.
4216295Ssyrinx *
5216295Ssyrinx * Author: Shteryana Shopova <syrinx@FreeBSD.org>
6216295Ssyrinx *
7216295Ssyrinx * Redistribution of this software and documentation and use in source and
8216295Ssyrinx * binary forms, with or without modification, are permitted provided that
9216295Ssyrinx * the following conditions are met:
10216295Ssyrinx *
11216295Ssyrinx * 1. Redistributions of source code or documentation must retain the above
12216295Ssyrinx *    copyright notice, this list of conditions and the following disclaimer.
13216295Ssyrinx * 2. Redistributions in binary form must reproduce the above copyright
14216295Ssyrinx *    notice, this list of conditions and the following disclaimer in the
15216295Ssyrinx *    documentation and/or other materials provided with the distribution.
16216295Ssyrinx *
17216295Ssyrinx * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18216295Ssyrinx * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19216295Ssyrinx * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20216295Ssyrinx * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21216295Ssyrinx * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22216295Ssyrinx * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23216295Ssyrinx * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24216295Ssyrinx * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25216295Ssyrinx * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26216295Ssyrinx * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27216295Ssyrinx * SUCH DAMAGE.
28216295Ssyrinx *
29216295Ssyrinx * Bsnmpget and bsnmpwalk are simple tools for querying SNMP agents,
30216295Ssyrinx * bsnmpset can be used to set MIB objects in an agent.
31216295Ssyrinx *
32216295Ssyrinx * $FreeBSD: stable/10/usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.c 312050 2017-01-13 08:59:22Z ngie $
33216295Ssyrinx */
34216295Ssyrinx
35216295Ssyrinx#include <sys/queue.h>
36216295Ssyrinx#include <sys/types.h>
37216295Ssyrinx
38216295Ssyrinx#include <assert.h>
39216295Ssyrinx#include <ctype.h>
40216295Ssyrinx#include <err.h>
41216295Ssyrinx#include <errno.h>
42216295Ssyrinx#include <stdarg.h>
43216295Ssyrinx#include <stdio.h>
44216295Ssyrinx#include <stdlib.h>
45216295Ssyrinx#include <string.h>
46216295Ssyrinx#include <syslog.h>
47216295Ssyrinx#include <unistd.h>
48216295Ssyrinx
49216295Ssyrinx#include <bsnmp/asn1.h>
50216295Ssyrinx#include <bsnmp/snmp.h>
51216295Ssyrinx#include <bsnmp/snmpclient.h>
52216295Ssyrinx#include "bsnmptc.h"
53216295Ssyrinx#include "bsnmptools.h"
54216295Ssyrinx
55216295Ssyrinxstatic const char *program_name = NULL;
56216295Ssyrinxstatic enum program_e {
57216295Ssyrinx	BSNMPGET,
58216295Ssyrinx	BSNMPWALK,
59216295Ssyrinx	BSNMPSET
60216295Ssyrinx} program;
61216295Ssyrinx
62216295Ssyrinx/* *****************************************************************************
63216295Ssyrinx * Common bsnmptools functions.
64216295Ssyrinx */
65216295Ssyrinxstatic void
66216295Ssyrinxusage(void)
67216295Ssyrinx{
68216295Ssyrinx	fprintf(stderr,
69216295Ssyrinx"Usage:\n"
70216295Ssyrinx"%s %s [-A options] [-b buffersize] [-C options] [-I options]\n"
71216295Ssyrinx"\t[-i filelist] [-l filename]%s [-o output] [-P options]\n"
72216295Ssyrinx"\t%s[-r retries] [-s [trans::][community@][server][:port]]\n"
73216295Ssyrinx"\t[-t timeout] [-U options] [-v version]%s\n",
74216295Ssyrinx	program_name,
75216295Ssyrinx	(program == BSNMPGET) ? "[-aDdehnK]" :
76216295Ssyrinx	    (program == BSNMPWALK) ? "[-dhnK]" :
77216295Ssyrinx	    (program == BSNMPSET) ? "[-adehnK]" :
78216295Ssyrinx	    "",
79229933Ssyrinx	(program == BSNMPGET || program == BSNMPWALK) ?
80229933Ssyrinx	" [-M max-repetitions] [-N non-repeaters]" : "",
81229933Ssyrinx	(program == BSNMPGET || program == BSNMPWALK) ? "[-p pdu] " : "",
82216295Ssyrinx	(program == BSNMPGET) ? " OID [OID ...]" :
83216295Ssyrinx	    (program == BSNMPWALK || program == BSNMPSET) ? " [OID ...]" :
84216295Ssyrinx	    ""
85216295Ssyrinx	);
86216295Ssyrinx}
87216295Ssyrinx
88216295Ssyrinxstatic int32_t
89216295Ssyrinxparse_max_repetitions(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
90216295Ssyrinx{
91216295Ssyrinx	uint32_t v;
92216295Ssyrinx
93216295Ssyrinx	assert(opt_arg != NULL);
94216295Ssyrinx
95216295Ssyrinx	v = strtoul(opt_arg, (void *) NULL, 10);
96216295Ssyrinx
97216295Ssyrinx	if (v > SNMP_MAX_BINDINGS) {
98216295Ssyrinx		warnx("Max repetitions value greater than %d maximum allowed.",
99216295Ssyrinx		    SNMP_MAX_BINDINGS);
100216295Ssyrinx		return (-1);
101216295Ssyrinx	}
102216295Ssyrinx
103216295Ssyrinx	SET_MAXREP(snmptoolctx, v);
104216295Ssyrinx	return (2);
105216295Ssyrinx}
106216295Ssyrinx
107216295Ssyrinxstatic int32_t
108216295Ssyrinxparse_non_repeaters(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
109216295Ssyrinx{
110216295Ssyrinx	uint32_t v;
111216295Ssyrinx
112216295Ssyrinx	assert(opt_arg != NULL);
113216295Ssyrinx
114216295Ssyrinx	v = strtoul(opt_arg, (void *) NULL, 10);
115216295Ssyrinx
116216295Ssyrinx	if (v > SNMP_MAX_BINDINGS) {
117216295Ssyrinx		warnx("Non repeaters value greater than %d maximum allowed.",
118216295Ssyrinx		    SNMP_MAX_BINDINGS);
119216295Ssyrinx		return (-1);
120216295Ssyrinx	}
121216295Ssyrinx
122216295Ssyrinx	SET_NONREP(snmptoolctx, v);
123216295Ssyrinx	return (2);
124216295Ssyrinx}
125216295Ssyrinx
126216295Ssyrinxstatic int32_t
127216295Ssyrinxparse_pdu_type(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
128216295Ssyrinx{
129216295Ssyrinx	assert(opt_arg != NULL);
130216295Ssyrinx
131216295Ssyrinx	if (strcasecmp(opt_arg, "getbulk") == 0)
132216295Ssyrinx		SET_PDUTYPE(snmptoolctx, SNMP_PDU_GETBULK);
133216295Ssyrinx	else if (strcasecmp(opt_arg, "getnext") == 0)
134216295Ssyrinx		SET_PDUTYPE(snmptoolctx, SNMP_PDU_GETNEXT);
135216295Ssyrinx	else if (strcasecmp(opt_arg, "get") == 0)
136216295Ssyrinx		SET_PDUTYPE(snmptoolctx, SNMP_PDU_GET);
137216295Ssyrinx	else {
138216295Ssyrinx		warnx("PDU type '%s' not supported.", opt_arg);
139216295Ssyrinx		return (-1);
140216295Ssyrinx	}
141216295Ssyrinx
142216295Ssyrinx	return (2);
143216295Ssyrinx}
144216295Ssyrinx
145216295Ssyrinxstatic int32_t
146216295Ssyrinxsnmptool_parse_options(struct snmp_toolinfo *snmptoolctx, int argc, char **argv)
147216295Ssyrinx{
148216295Ssyrinx	int32_t count, optnum = 0;
149216295Ssyrinx	int ch;
150216295Ssyrinx	const char *opts;
151216295Ssyrinx
152216295Ssyrinx	switch (program) {
153216295Ssyrinx		case BSNMPWALK:
154229933Ssyrinx			opts = "dhnKA:b:C:I:i:l:M:N:o:P:p:r:s:t:U:v:";
155216295Ssyrinx			break;
156216295Ssyrinx		case BSNMPGET:
157216295Ssyrinx			opts = "aDdehnKA:b:C:I:i:l:M:N:o:P:p:r:s:t:U:v:";
158216295Ssyrinx			break;
159216295Ssyrinx		case BSNMPSET:
160216295Ssyrinx			opts = "adehnKA:b:C:I:i:l:o:P:r:s:t:U:v:";
161216295Ssyrinx			break;
162216295Ssyrinx		default:
163216295Ssyrinx			return (-1);
164216295Ssyrinx	}
165216295Ssyrinx
166216295Ssyrinx	while ((ch = getopt(argc, argv, opts)) != EOF) {
167216295Ssyrinx		switch (ch) {
168216295Ssyrinx		case 'A':
169216295Ssyrinx			count = parse_authentication(snmptoolctx, optarg);
170216295Ssyrinx			break;
171216295Ssyrinx		case 'a':
172216295Ssyrinx			count = parse_skip_access(snmptoolctx);
173216295Ssyrinx			break;
174216295Ssyrinx		case 'b':
175216295Ssyrinx			count = parse_buflen(optarg);
176216295Ssyrinx			break;
177216295Ssyrinx		case 'D':
178216295Ssyrinx			count = parse_discovery(snmptoolctx);
179216295Ssyrinx			break;
180216295Ssyrinx		case 'd':
181216295Ssyrinx			count = parse_debug();
182216295Ssyrinx			break;
183216295Ssyrinx		case 'e':
184216295Ssyrinx			count = parse_errors(snmptoolctx);
185216295Ssyrinx			break;
186216295Ssyrinx		case 'h':
187216295Ssyrinx			usage();
188216295Ssyrinx			return (-2);
189216295Ssyrinx		case 'C':
190216295Ssyrinx			count = parse_context(snmptoolctx, optarg);
191216295Ssyrinx			break;
192216295Ssyrinx		case 'I':
193216295Ssyrinx			count = parse_include(snmptoolctx, optarg);
194216295Ssyrinx			break;
195216295Ssyrinx		case 'i':
196216295Ssyrinx			count = parse_file(snmptoolctx, optarg);
197216295Ssyrinx			break;
198216295Ssyrinx		case 'K':
199216295Ssyrinx			count = parse_local_key(snmptoolctx);
200216295Ssyrinx			break;
201216295Ssyrinx		case 'l':
202216295Ssyrinx			count = parse_local_path(optarg);
203216295Ssyrinx			break;
204216295Ssyrinx		case 'M':
205216295Ssyrinx			count = parse_max_repetitions(snmptoolctx, optarg);
206216295Ssyrinx			break;
207216295Ssyrinx		case 'N':
208216295Ssyrinx			count = parse_non_repeaters(snmptoolctx, optarg);
209216295Ssyrinx			break;
210216295Ssyrinx		case 'n':
211216295Ssyrinx			count = parse_num_oids(snmptoolctx);
212216295Ssyrinx			break;
213216295Ssyrinx		case 'o':
214216295Ssyrinx			count = parse_output(snmptoolctx, optarg);
215216295Ssyrinx			break;
216216295Ssyrinx		case 'P':
217216295Ssyrinx			count = parse_privacy(snmptoolctx, optarg);
218216295Ssyrinx			break;
219216295Ssyrinx		case 'p':
220216295Ssyrinx			count = parse_pdu_type(snmptoolctx, optarg);
221216295Ssyrinx			break;
222216295Ssyrinx		case 'r':
223216295Ssyrinx			count = parse_retry(optarg);
224216295Ssyrinx			break;
225216295Ssyrinx		case 's':
226216295Ssyrinx			count = parse_server(optarg);
227216295Ssyrinx			break;
228216295Ssyrinx		case 't':
229216295Ssyrinx			count = parse_timeout(optarg);
230216295Ssyrinx			break;
231216295Ssyrinx		case 'U':
232216295Ssyrinx			count = parse_user_security(snmptoolctx, optarg);
233216295Ssyrinx			break;
234216295Ssyrinx		case 'v':
235216295Ssyrinx			count = parse_version(optarg);
236216295Ssyrinx			break;
237216295Ssyrinx		case '?':
238216295Ssyrinx		default:
239216295Ssyrinx			usage();
240216295Ssyrinx			return (-1);
241216295Ssyrinx		}
242216295Ssyrinx		if (count < 0)
243216295Ssyrinx			return (-1);
244216295Ssyrinx	    optnum += count;
245216295Ssyrinx	}
246216295Ssyrinx
247216295Ssyrinx	return (optnum);
248216295Ssyrinx}
249216295Ssyrinx
250216295Ssyrinx/*
251216295Ssyrinx * Read user input OID - one of following formats:
252228990Suqs * 1) 1.2.1.1.2.1.0 - that is if option numeric was given;
253216295Ssyrinx * 2) string - in such case append .0 to the asn_oid subs;
254228990Suqs * 3) string.1 - no additional processing required in such case.
255216295Ssyrinx */
256216295Ssyrinxstatic char *
257216295Ssyrinxsnmptools_parse_stroid(struct snmp_toolinfo *snmptoolctx,
258216295Ssyrinx    struct snmp_object *obj, char *argv)
259216295Ssyrinx{
260216295Ssyrinx	char string[MAXSTR], *str;
261216295Ssyrinx	int32_t i = 0;
262216295Ssyrinx	struct asn_oid in_oid;
263216295Ssyrinx
264216295Ssyrinx	str = argv;
265216295Ssyrinx
266216295Ssyrinx	if (*str == '.')
267216295Ssyrinx		str++;
268216295Ssyrinx
269216295Ssyrinx	while (isalpha(*str) || *str == '_' || (i != 0 && isdigit(*str))) {
270216295Ssyrinx		str++;
271216295Ssyrinx		i++;
272216295Ssyrinx	}
273216295Ssyrinx
274216295Ssyrinx	if (i <= 0 || i >= MAXSTR)
275216295Ssyrinx		return (NULL);
276216295Ssyrinx
277216295Ssyrinx	memset(&in_oid, 0, sizeof(struct asn_oid));
278216295Ssyrinx	if ((str = snmp_parse_suboid((argv + i), &in_oid)) == NULL) {
279216295Ssyrinx		warnx("Invalid OID - %s", argv);
280216295Ssyrinx		return (NULL);
281216295Ssyrinx	}
282216295Ssyrinx
283216295Ssyrinx	strlcpy(string, argv, i + 1);
284216295Ssyrinx	if (snmp_lookup_oidall(snmptoolctx, obj, string) < 0) {
285216295Ssyrinx		warnx("No entry for %s in mapping lists", string);
286216295Ssyrinx		return (NULL);
287216295Ssyrinx	}
288216295Ssyrinx
289216295Ssyrinx	/* If OID given on command line append it. */
290216295Ssyrinx	if (in_oid.len > 0)
291216295Ssyrinx		asn_append_oid(&(obj->val.var), &in_oid);
292216295Ssyrinx	else if (*str == '[') {
293216295Ssyrinx		if ((str = snmp_parse_index(snmptoolctx, str + 1, obj)) == NULL)
294216295Ssyrinx			return (NULL);
295216295Ssyrinx	} else if (obj->val.syntax > 0 && GET_PDUTYPE(snmptoolctx) ==
296216295Ssyrinx	    SNMP_PDU_GET) {
297216295Ssyrinx		if (snmp_suboid_append(&(obj->val.var), (asn_subid_t) 0) < 0)
298216295Ssyrinx			return (NULL);
299216295Ssyrinx	}
300216295Ssyrinx
301216295Ssyrinx	return (str);
302216295Ssyrinx}
303216295Ssyrinx
304216295Ssyrinxstatic int32_t
305216295Ssyrinxsnmptools_parse_oid(struct snmp_toolinfo *snmptoolctx,
306216295Ssyrinx    struct snmp_object *obj, char *argv)
307216295Ssyrinx{
308216295Ssyrinx	if (argv == NULL)
309216295Ssyrinx		return (-1);
310216295Ssyrinx
311216295Ssyrinx	if (ISSET_NUMERIC(snmptoolctx)) {
312216295Ssyrinx		if (snmp_parse_numoid(argv, &(obj->val.var)) < 0)
313216295Ssyrinx			return (-1);
314216295Ssyrinx	} else {
315216295Ssyrinx		if (snmptools_parse_stroid(snmptoolctx, obj, argv) == NULL &&
316216295Ssyrinx		    snmp_parse_numoid(argv, &(obj->val.var)) < 0)
317216295Ssyrinx			return (-1);
318216295Ssyrinx	}
319216295Ssyrinx
320216295Ssyrinx	return (1);
321216295Ssyrinx}
322216295Ssyrinx
323216295Ssyrinxstatic int32_t
324216295Ssyrinxsnmptool_add_vbind(struct snmp_pdu *pdu, struct snmp_object *obj)
325216295Ssyrinx{
326216295Ssyrinx	if (obj->error > 0)
327216295Ssyrinx		return (0);
328216295Ssyrinx
329216295Ssyrinx	asn_append_oid(&(pdu->bindings[pdu->nbindings].var), &(obj->val.var));
330216295Ssyrinx	pdu->nbindings++;
331216295Ssyrinx
332216295Ssyrinx	return (pdu->nbindings);
333216295Ssyrinx}
334216295Ssyrinx
335216295Ssyrinx/* *****************************************************************************
336216295Ssyrinx * bsnmpget private functions.
337216295Ssyrinx */
338216295Ssyrinxstatic int32_t
339216295Ssyrinxsnmpget_verify_vbind(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu,
340216295Ssyrinx    struct snmp_object *obj)
341216295Ssyrinx{
342216295Ssyrinx	if (pdu->version == SNMP_V1 && obj->val.syntax ==
343216295Ssyrinx	    SNMP_SYNTAX_COUNTER64) {
344216295Ssyrinx		warnx("64-bit counters are not supported in SNMPv1 PDU");
345216295Ssyrinx		return (-1);
346216295Ssyrinx	}
347216295Ssyrinx
348216295Ssyrinx	if (ISSET_NUMERIC(snmptoolctx) || pdu->type == SNMP_PDU_GETNEXT ||
349216295Ssyrinx	    pdu->type == SNMP_PDU_GETBULK)
350216295Ssyrinx		return (1);
351216295Ssyrinx
352216295Ssyrinx	if (pdu->type == SNMP_PDU_GET && obj->val.syntax == SNMP_SYNTAX_NULL) {
353216295Ssyrinx		warnx("Only leaf object values can be added to GET PDU");
354216295Ssyrinx		return (-1);
355216295Ssyrinx	}
356216295Ssyrinx
357216295Ssyrinx	return (1);
358216295Ssyrinx}
359216295Ssyrinx
360216295Ssyrinx/*
361216295Ssyrinx * In case of a getbulk PDU, the error_status and error_index fields are used by
362216295Ssyrinx * libbsnmp to hold the values of the non-repeaters and max-repetitions fields
363216295Ssyrinx * that are present only in the getbulk - so before sending the PDU make sure
364216295Ssyrinx * these have correct values as well.
365216295Ssyrinx */
366216295Ssyrinxstatic void
367216295Ssyrinxsnmpget_fix_getbulk(struct snmp_pdu *pdu, uint32_t max_rep, uint32_t non_rep)
368216295Ssyrinx{
369216295Ssyrinx	assert(pdu != NULL);
370216295Ssyrinx
371216295Ssyrinx	if (pdu->nbindings < non_rep)
372216295Ssyrinx		pdu->error_status = pdu->nbindings;
373216295Ssyrinx	else
374216295Ssyrinx		pdu->error_status = non_rep;
375216295Ssyrinx
376216295Ssyrinx	if (max_rep > 0)
377216295Ssyrinx		pdu->error_index = max_rep;
378216295Ssyrinx	else
379216295Ssyrinx		pdu->error_index = 1;
380216295Ssyrinx}
381216295Ssyrinx
382216295Ssyrinxstatic int
383216295Ssyrinxsnmptool_get(struct snmp_toolinfo *snmptoolctx)
384216295Ssyrinx{
385216295Ssyrinx	struct snmp_pdu req, resp;
386216295Ssyrinx
387216295Ssyrinx	snmp_pdu_create(&req, GET_PDUTYPE(snmptoolctx));
388216295Ssyrinx
389216295Ssyrinx	while ((snmp_pdu_add_bindings(snmptoolctx, snmpget_verify_vbind,
390216295Ssyrinx	     snmptool_add_vbind, &req, SNMP_MAX_BINDINGS)) > 0) {
391216295Ssyrinx
392216295Ssyrinx		if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK)
393216295Ssyrinx			snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx),
394216295Ssyrinx			    GET_NONREP(snmptoolctx));
395216295Ssyrinx
396216295Ssyrinx		if (snmp_dialog(&req, &resp) == -1) {
397311595Sngie			warn("Snmp dialog");
398216295Ssyrinx			break;
399216295Ssyrinx		}
400216295Ssyrinx
401216295Ssyrinx		if (snmp_parse_resp(&resp, &req) >= 0) {
402229933Ssyrinx			snmp_output_resp(snmptoolctx, &resp, NULL);
403312050Sngie			snmp_pdu_free(&resp);
404216295Ssyrinx			break;
405216295Ssyrinx		}
406216295Ssyrinx
407216295Ssyrinx		snmp_output_err_resp(snmptoolctx, &resp);
408216295Ssyrinx		if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK ||
409312050Sngie		    !ISSET_RETRY(snmptoolctx)) {
410312050Sngie			snmp_pdu_free(&resp);
411216295Ssyrinx			break;
412312050Sngie		}
413216295Ssyrinx
414216295Ssyrinx		/*
415216295Ssyrinx		 * Loop through the object list and set object->error to the
416216295Ssyrinx		 * varbinding that caused the error.
417216295Ssyrinx		 */
418216295Ssyrinx		if (snmp_object_seterror(snmptoolctx,
419216295Ssyrinx		    &(resp.bindings[resp.error_index - 1]),
420312050Sngie		    resp.error_status) <= 0) {
421312050Sngie			snmp_pdu_free(&resp);
422216295Ssyrinx			break;
423312050Sngie		}
424216295Ssyrinx
425216295Ssyrinx		fprintf(stderr, "Retrying...\n");
426216295Ssyrinx		snmp_pdu_free(&resp);
427216295Ssyrinx		snmp_pdu_create(&req, GET_PDUTYPE(snmptoolctx));
428216295Ssyrinx	}
429216295Ssyrinx
430312050Sngie	snmp_pdu_free(&req);
431216295Ssyrinx
432216295Ssyrinx	return (0);
433216295Ssyrinx}
434216295Ssyrinx
435216295Ssyrinx
436216295Ssyrinx/* *****************************************************************************
437216295Ssyrinx * bsnmpwalk private functions.
438216295Ssyrinx */
439216295Ssyrinx/* The default tree to walk. */
440216295Ssyrinxstatic const struct asn_oid snmp_mibII_OID = {
441216295Ssyrinx	6 , { 1, 3, 6, 1, 2, 1 }
442216295Ssyrinx};
443216295Ssyrinx
444216295Ssyrinxstatic int32_t
445216295Ssyrinxsnmpwalk_add_default(struct snmp_toolinfo *snmptoolctx __unused,
446216295Ssyrinx    struct snmp_object *obj, char *string __unused)
447216295Ssyrinx{
448216295Ssyrinx	asn_append_oid(&(obj->val.var), &snmp_mibII_OID);
449216295Ssyrinx	return (1);
450216295Ssyrinx}
451216295Ssyrinx
452216295Ssyrinx/*
453216295Ssyrinx * Prepare the next GetNext/Get PDU to send.
454216295Ssyrinx */
455216295Ssyrinxstatic void
456216295Ssyrinxsnmpwalk_nextpdu_create(uint32_t op, struct asn_oid *var, struct snmp_pdu *pdu)
457216295Ssyrinx{
458216295Ssyrinx	snmp_pdu_create(pdu, op);
459216295Ssyrinx	asn_append_oid(&(pdu->bindings[0].var), var);
460216295Ssyrinx	pdu->nbindings = 1;
461216295Ssyrinx}
462216295Ssyrinx
463216295Ssyrinxstatic int
464216295Ssyrinxsnmptool_walk(struct snmp_toolinfo *snmptoolctx)
465216295Ssyrinx{
466216295Ssyrinx	struct snmp_pdu req, resp;
467228990Suqs	struct asn_oid root;	/* Keep the initial oid. */
468216295Ssyrinx	int32_t outputs, rc;
469229933Ssyrinx	uint32_t op;
470216295Ssyrinx
471229933Ssyrinx	if (GET_PDUTYPE(snmptoolctx) == SNMP_PDU_GETBULK)
472229933Ssyrinx		op = SNMP_PDU_GETBULK;
473229933Ssyrinx	else
474229933Ssyrinx		op = SNMP_PDU_GETNEXT;
475216295Ssyrinx
476229933Ssyrinx	snmp_pdu_create(&req, op);
477229933Ssyrinx
478216295Ssyrinx	while ((rc = snmp_pdu_add_bindings(snmptoolctx, NULL,
479216295Ssyrinx	    snmptool_add_vbind, &req, 1)) > 0) {
480216295Ssyrinx
481216295Ssyrinx		/* Remember the root where the walk started from. */
482216295Ssyrinx		memset(&root, 0, sizeof(struct asn_oid));
483216295Ssyrinx		asn_append_oid(&root, &(req.bindings[0].var));
484216295Ssyrinx
485229933Ssyrinx		if (op == SNMP_PDU_GETBULK)
486229933Ssyrinx			snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx),
487229933Ssyrinx			    GET_NONREP(snmptoolctx));
488229933Ssyrinx
489216295Ssyrinx		outputs = 0;
490216295Ssyrinx		while (snmp_dialog(&req, &resp) >= 0) {
491216295Ssyrinx			if ((snmp_parse_resp(&resp, &req)) < 0) {
492216295Ssyrinx				snmp_output_err_resp(snmptoolctx, &resp);
493216295Ssyrinx				snmp_pdu_free(&resp);
494216295Ssyrinx				outputs = -1;
495216295Ssyrinx				break;
496216295Ssyrinx			}
497216295Ssyrinx
498229933Ssyrinx			rc = snmp_output_resp(snmptoolctx, &resp, &root);
499229933Ssyrinx			if (rc < 0) {
500216295Ssyrinx				snmp_pdu_free(&resp);
501229933Ssyrinx				outputs = -1;
502216295Ssyrinx				break;
503216295Ssyrinx			}
504216295Ssyrinx
505229933Ssyrinx			outputs += rc;
506216295Ssyrinx
507312050Sngie			if ((u_int)rc < resp.nbindings) {
508312050Sngie				snmp_pdu_free(&resp);
509229933Ssyrinx				break;
510312050Sngie			}
511229933Ssyrinx
512229933Ssyrinx			snmpwalk_nextpdu_create(op,
513229933Ssyrinx			    &(resp.bindings[resp.nbindings - 1].var), &req);
514229933Ssyrinx			if (op == SNMP_PDU_GETBULK)
515229933Ssyrinx				snmpget_fix_getbulk(&req, GET_MAXREP(snmptoolctx),
516229933Ssyrinx				    GET_NONREP(snmptoolctx));
517312050Sngie			snmp_pdu_free(&resp);
518216295Ssyrinx		}
519216295Ssyrinx
520216295Ssyrinx		/* Just in case our root was a leaf. */
521216295Ssyrinx		if (outputs == 0) {
522216295Ssyrinx			snmpwalk_nextpdu_create(SNMP_PDU_GET, &root, &req);
523216295Ssyrinx			if (snmp_dialog(&req, &resp) == SNMP_CODE_OK) {
524312050Sngie				if (snmp_parse_resp(&resp, &req) < 0)
525216295Ssyrinx					snmp_output_err_resp(snmptoolctx, &resp);
526216295Ssyrinx				else
527312050Sngie					snmp_output_resp(snmptoolctx, &resp,
528312050Sngie					    NULL);
529216295Ssyrinx				snmp_pdu_free(&resp);
530216295Ssyrinx			} else
531311595Sngie				warn("Snmp dialog");
532216295Ssyrinx		}
533216295Ssyrinx
534216295Ssyrinx		if (snmp_object_remove(snmptoolctx, &root) < 0) {
535216295Ssyrinx			warnx("snmp_object_remove");
536216295Ssyrinx			break;
537216295Ssyrinx		}
538216295Ssyrinx
539312050Sngie		snmp_pdu_free(&req);
540229933Ssyrinx		snmp_pdu_create(&req, op);
541216295Ssyrinx	}
542216295Ssyrinx
543312050Sngie	snmp_pdu_free(&req);
544312050Sngie
545216295Ssyrinx	if (rc == 0)
546216295Ssyrinx		return (0);
547216295Ssyrinx	else
548216295Ssyrinx		return (1);
549216295Ssyrinx}
550216295Ssyrinx
551216295Ssyrinx/* *****************************************************************************
552216295Ssyrinx * bsnmpset private functions.
553216295Ssyrinx */
554216295Ssyrinx
555216295Ssyrinxstatic int32_t
556216295Ssyrinxparse_oid_numeric(struct snmp_value *value, char *val)
557216295Ssyrinx{
558216295Ssyrinx	char *endptr;
559216295Ssyrinx	int32_t saved_errno;
560216295Ssyrinx	asn_subid_t suboid;
561216295Ssyrinx
562216295Ssyrinx	do {
563216295Ssyrinx		saved_errno = errno;
564216295Ssyrinx		errno = 0;
565216295Ssyrinx		suboid = strtoul(val, &endptr, 10);
566216295Ssyrinx		if (errno != 0) {
567311595Sngie			warn("Value %s not supported", val);
568216295Ssyrinx			errno = saved_errno;
569216295Ssyrinx			return (-1);
570216295Ssyrinx		}
571216295Ssyrinx		errno = saved_errno;
572216295Ssyrinx		if ((asn_subid_t) suboid > ASN_MAXID) {
573216295Ssyrinx			warnx("Suboid %u > ASN_MAXID", suboid);
574216295Ssyrinx			return (-1);
575216295Ssyrinx		}
576216295Ssyrinx		if (snmp_suboid_append(&(value->v.oid), suboid) < 0)
577216295Ssyrinx			return (-1);
578216295Ssyrinx		val = endptr + 1;
579216295Ssyrinx	} while (*endptr == '.');
580216295Ssyrinx
581216295Ssyrinx	if (*endptr != '\0')
582216295Ssyrinx		warnx("OID value %s not supported", val);
583216295Ssyrinx
584216295Ssyrinx	value->syntax = SNMP_SYNTAX_OID;
585216295Ssyrinx	return (0);
586216295Ssyrinx}
587216295Ssyrinx
588216295Ssyrinx/*
589216295Ssyrinx * Allow OID leaf in both forms:
590216295Ssyrinx * 1) 1.3.6.1.2... ->  in such case call directly the function reading raw OIDs;
591216295Ssyrinx * 2) begemotSnmpdAgentFreeBSD -> lookup the ASN OID corresponding to that.
592216295Ssyrinx */
593216295Ssyrinxstatic int32_t
594216295Ssyrinxparse_oid_string(struct snmp_toolinfo *snmptoolctx,
595216295Ssyrinx    struct snmp_value *value, char *string)
596216295Ssyrinx{
597216295Ssyrinx	struct snmp_object obj;
598216295Ssyrinx
599216295Ssyrinx	if (isdigit(string[0]))
600216295Ssyrinx		return (parse_oid_numeric(value, string));
601216295Ssyrinx
602216295Ssyrinx	memset(&obj, 0, sizeof(struct snmp_object));
603216295Ssyrinx	if (snmp_lookup_enumoid(snmptoolctx, &obj, string) < 0) {
604216295Ssyrinx		warnx("Unknown OID enum string - %s", string);
605216295Ssyrinx		return (-1);
606216295Ssyrinx	}
607216295Ssyrinx
608216295Ssyrinx	asn_append_oid(&(value->v.oid), &(obj.val.var));
609216295Ssyrinx	return (1);
610216295Ssyrinx}
611216295Ssyrinx
612216295Ssyrinxstatic int32_t
613216295Ssyrinxparse_ip(struct snmp_value * value, char * val)
614216295Ssyrinx{
615310562Sngie	char *endptr, *str;
616310562Sngie	int32_t i;
617216295Ssyrinx	uint32_t v;
618216295Ssyrinx
619216295Ssyrinx	str = val;
620216295Ssyrinx	for (i = 0; i < 4; i++) {
621216295Ssyrinx		v = strtoul(str, &endptr, 10);
622216295Ssyrinx		if (v > 0xff)
623216295Ssyrinx			return (-1);
624216295Ssyrinx		if (*endptr != '.' && *endptr != '\0' && i != 3)
625216295Ssyrinx			break;
626216295Ssyrinx		str = endptr + 1;
627216295Ssyrinx		value->v.ipaddress[i] = (uint8_t) v;
628216295Ssyrinx	}
629310562Sngie	value->syntax = SNMP_SYNTAX_IPADDRESS;
630216295Ssyrinx
631216295Ssyrinx	return (0);
632216295Ssyrinx}
633216295Ssyrinx
634216295Ssyrinxstatic int32_t
635216295Ssyrinxparse_int(struct snmp_value *value, char *val)
636216295Ssyrinx{
637216295Ssyrinx	char *endptr;
638216295Ssyrinx	int32_t v, saved_errno;
639216295Ssyrinx
640216295Ssyrinx	saved_errno = errno;
641216295Ssyrinx	errno = 0;
642216295Ssyrinx
643216295Ssyrinx	v = strtol(val, &endptr, 10);
644216295Ssyrinx
645216295Ssyrinx	if (errno != 0) {
646311595Sngie		warn("Value %s not supported", val);
647216295Ssyrinx		errno = saved_errno;
648216295Ssyrinx		return (-1);
649216295Ssyrinx	}
650216295Ssyrinx
651216295Ssyrinx	value->syntax = SNMP_SYNTAX_INTEGER;
652216295Ssyrinx	value->v.integer = v;
653216295Ssyrinx	errno = saved_errno;
654216295Ssyrinx
655216295Ssyrinx	return (0);
656216295Ssyrinx}
657216295Ssyrinx
658216295Ssyrinxstatic int32_t
659216295Ssyrinxparse_int_string(struct snmp_object *object, char *val)
660216295Ssyrinx{
661216295Ssyrinx	int32_t	v;
662216295Ssyrinx
663216295Ssyrinx	if (isdigit(val[0]))
664216295Ssyrinx		return ((parse_int(&(object->val), val)));
665216295Ssyrinx
666216295Ssyrinx	if (object->info == NULL) {
667216295Ssyrinx		warnx("Unknown enumerated integer type - %s", val);
668216295Ssyrinx		return (-1);
669216295Ssyrinx	}
670216295Ssyrinx	if ((v = enum_number_lookup(object->info->snmp_enum, val)) < 0)
671216295Ssyrinx		warnx("Unknown enumerated integer type - %s", val);
672216295Ssyrinx
673216295Ssyrinx	object->val.v.integer = v;
674216295Ssyrinx	return (1);
675216295Ssyrinx}
676216295Ssyrinx
677216295Ssyrinx/*
678216295Ssyrinx * Here syntax may be one of SNMP_SYNTAX_COUNTER, SNMP_SYNTAX_GAUGE,
679216295Ssyrinx * SNMP_SYNTAX_TIMETICKS.
680216295Ssyrinx */
681216295Ssyrinxstatic int32_t
682216295Ssyrinxparse_uint(struct snmp_value *value, char *val)
683216295Ssyrinx{
684216295Ssyrinx	char *endptr;
685216295Ssyrinx	uint32_t v = 0;
686216295Ssyrinx	int32_t saved_errno;
687216295Ssyrinx
688216295Ssyrinx	saved_errno = errno;
689216295Ssyrinx	errno = 0;
690216295Ssyrinx
691216295Ssyrinx	v = strtoul(val, &endptr, 10);
692216295Ssyrinx
693216295Ssyrinx	if (errno != 0) {
694311595Sngie		warn("Value %s not supported", val);
695216295Ssyrinx		errno = saved_errno;
696216295Ssyrinx		return (-1);
697216295Ssyrinx	}
698216295Ssyrinx
699216295Ssyrinx	value->v.uint32 = v;
700216295Ssyrinx	errno = saved_errno;
701216295Ssyrinx
702216295Ssyrinx	return (0);
703216295Ssyrinx}
704216295Ssyrinx
705216295Ssyrinxstatic int32_t
706216295Ssyrinxparse_ticks(struct snmp_value *value, char *val)
707216295Ssyrinx{
708216295Ssyrinx	if (parse_uint(value, val) < 0)
709216295Ssyrinx		return (-1);
710216295Ssyrinx
711216295Ssyrinx	value->syntax = SNMP_SYNTAX_TIMETICKS;
712216295Ssyrinx	return (0);
713216295Ssyrinx}
714216295Ssyrinx
715216295Ssyrinxstatic int32_t
716216295Ssyrinxparse_gauge(struct snmp_value *value, char *val)
717216295Ssyrinx{
718216295Ssyrinx	if (parse_uint(value, val) < 0)
719216295Ssyrinx		return (-1);
720216295Ssyrinx
721216295Ssyrinx	value->syntax = SNMP_SYNTAX_GAUGE;
722216295Ssyrinx	return (0);
723216295Ssyrinx}
724216295Ssyrinx
725216295Ssyrinxstatic int32_t
726216295Ssyrinxparse_counter(struct snmp_value *value, char *val)
727216295Ssyrinx{
728216295Ssyrinx	if (parse_uint(value, val) < 0)
729216295Ssyrinx		return (-1);
730216295Ssyrinx
731216295Ssyrinx	value->syntax = SNMP_SYNTAX_COUNTER;
732216295Ssyrinx	return (0);
733216295Ssyrinx}
734216295Ssyrinx
735216295Ssyrinxstatic int32_t
736216295Ssyrinxparse_uint64(struct snmp_value *value, char *val)
737216295Ssyrinx{
738216295Ssyrinx	char *endptr;
739216295Ssyrinx	int32_t saved_errno;
740216295Ssyrinx	uint64_t v;
741216295Ssyrinx
742216295Ssyrinx	saved_errno = errno;
743216295Ssyrinx	errno = 0;
744216295Ssyrinx
745216295Ssyrinx	v = strtoull(val, &endptr, 10);
746216295Ssyrinx
747216295Ssyrinx	if (errno != 0) {
748311595Sngie		warnx("Value %s not supported", val);
749216295Ssyrinx		errno = saved_errno;
750216295Ssyrinx		return (-1);
751216295Ssyrinx	}
752216295Ssyrinx
753216295Ssyrinx	value->syntax = SNMP_SYNTAX_COUNTER64;
754216295Ssyrinx	value->v.counter64 = v;
755216295Ssyrinx	errno = saved_errno;
756216295Ssyrinx
757216295Ssyrinx	return (0);
758216295Ssyrinx}
759216295Ssyrinx
760216295Ssyrinxstatic int32_t
761216295Ssyrinxparse_syntax_val(struct snmp_value *value, enum snmp_syntax syntax, char *val)
762216295Ssyrinx{
763216295Ssyrinx	switch (syntax) {
764216295Ssyrinx		case SNMP_SYNTAX_INTEGER:
765216295Ssyrinx			return (parse_int(value, val));
766216295Ssyrinx		case SNMP_SYNTAX_IPADDRESS:
767216295Ssyrinx			return (parse_ip(value, val));
768216295Ssyrinx		case SNMP_SYNTAX_COUNTER:
769216295Ssyrinx			return (parse_counter(value, val));
770216295Ssyrinx		case SNMP_SYNTAX_GAUGE:
771216295Ssyrinx			return (parse_gauge(value, val));
772216295Ssyrinx		case SNMP_SYNTAX_TIMETICKS:
773216295Ssyrinx			return (parse_ticks(value, val));
774216295Ssyrinx		case SNMP_SYNTAX_COUNTER64:
775216295Ssyrinx			return (parse_uint64(value, val));
776216295Ssyrinx		case SNMP_SYNTAX_OCTETSTRING:
777216295Ssyrinx			return (snmp_tc2oct(SNMP_STRING, value, val));
778216295Ssyrinx		case SNMP_SYNTAX_OID:
779216295Ssyrinx			return (parse_oid_numeric(value, val));
780216295Ssyrinx		default:
781216295Ssyrinx			/* NOTREACHED */
782216295Ssyrinx			break;
783216295Ssyrinx	}
784216295Ssyrinx
785216295Ssyrinx	return (-1);
786216295Ssyrinx}
787216295Ssyrinx
788216295Ssyrinx/*
789310903Sngie * Parse a command line argument of type OID=syntax:value and fill in whatever
790216295Ssyrinx * fields can be derived from the input into snmp_value structure. Reads numeric
791216295Ssyrinx * OIDs.
792216295Ssyrinx */
793216295Ssyrinxstatic int32_t
794216295Ssyrinxparse_pair_numoid_val(char *str, struct snmp_value *snmp_val)
795216295Ssyrinx{
796216295Ssyrinx	int32_t cnt;
797216295Ssyrinx	char *ptr;
798216295Ssyrinx	enum snmp_syntax syntax;
799216295Ssyrinx	char oid_str[ASN_OIDSTRLEN];
800216295Ssyrinx
801216295Ssyrinx	ptr = str;
802216295Ssyrinx	for (cnt = 0; cnt < ASN_OIDSTRLEN; cnt++)
803216295Ssyrinx		if (ptr[cnt] == '=')
804216295Ssyrinx			break;
805216295Ssyrinx
806216295Ssyrinx	if (cnt >= ASN_OIDSTRLEN) {
807216295Ssyrinx		warnx("OID too long - %s", str);
808216295Ssyrinx		return (-1);
809216295Ssyrinx	}
810216295Ssyrinx	strlcpy(oid_str, ptr, (size_t) (cnt + 1));
811216295Ssyrinx
812216295Ssyrinx	ptr = str + cnt + 1;
813216295Ssyrinx	for (cnt = 0; cnt < MAX_CMD_SYNTAX_LEN; cnt++)
814216295Ssyrinx		if(ptr[cnt] == ':')
815216295Ssyrinx			break;
816216295Ssyrinx
817216295Ssyrinx	if (cnt >= MAX_CMD_SYNTAX_LEN) {
818216295Ssyrinx		warnx("Unknown syntax in OID - %s", str);
819216295Ssyrinx		return (-1);
820216295Ssyrinx	}
821216295Ssyrinx
822216295Ssyrinx	if ((syntax = parse_syntax(ptr)) <= SNMP_SYNTAX_NULL) {
823216295Ssyrinx		warnx("Unknown syntax in OID - %s", ptr);
824216295Ssyrinx		return (-1);
825216295Ssyrinx	}
826216295Ssyrinx
827216295Ssyrinx	ptr = ptr + cnt + 1;
828216295Ssyrinx	for (cnt = 0; cnt < MAX_OCTSTRING_LEN; cnt++)
829216295Ssyrinx		if (ptr[cnt] == '\0')
830216295Ssyrinx			break;
831216295Ssyrinx
832216295Ssyrinx	if (ptr[cnt] != '\0') {
833311595Sngie		warnx("Value string too long - %s", ptr);
834216295Ssyrinx		return (-1);
835216295Ssyrinx	}
836216295Ssyrinx
837216295Ssyrinx	/*
838216295Ssyrinx	 * Here try parsing the OIDs and syntaxes and then check values - have
839216295Ssyrinx	 * to know syntax to check value boundaries.
840216295Ssyrinx	 */
841216295Ssyrinx	if (snmp_parse_numoid(oid_str, &(snmp_val->var)) < 0) {
842311595Sngie		warnx("Error parsing OID %s", oid_str);
843216295Ssyrinx		return (-1);
844216295Ssyrinx	}
845216295Ssyrinx
846216295Ssyrinx	if (parse_syntax_val(snmp_val, syntax, ptr) < 0)
847216295Ssyrinx		return (-1);
848216295Ssyrinx
849216295Ssyrinx	return (1);
850216295Ssyrinx}
851216295Ssyrinx
852216295Ssyrinxstatic int32_t
853311466Sngieparse_syntax_strval(struct snmp_toolinfo *snmptoolctx,
854311466Sngie    struct snmp_object *object, char *str)
855216295Ssyrinx{
856216295Ssyrinx	uint32_t len;
857216295Ssyrinx	enum snmp_syntax syn;
858216295Ssyrinx
859216295Ssyrinx	/*
860216295Ssyrinx	 * Syntax string here not required  - still may be present.
861216295Ssyrinx	 */
862216295Ssyrinx
863216295Ssyrinx	if (GET_OUTPUT(snmptoolctx) == OUTPUT_VERBOSE) {
864216295Ssyrinx		for (len = 0 ; *(str + len) != ':'; len++) {
865216295Ssyrinx			if (*(str + len) == '\0') {
866216295Ssyrinx				warnx("Syntax missing in value - %s", str);
867216295Ssyrinx				return (-1);
868216295Ssyrinx			}
869216295Ssyrinx		}
870216295Ssyrinx		if ((syn = parse_syntax(str)) <= SNMP_SYNTAX_NULL) {
871216295Ssyrinx			warnx("Unknown syntax in - %s", str);
872216295Ssyrinx			return (-1);
873216295Ssyrinx		}
874216295Ssyrinx		if (syn != object->val.syntax) {
875216295Ssyrinx			if (!ISSET_ERRIGNORE(snmptoolctx)) {
876216295Ssyrinx				warnx("Bad syntax in - %s", str);
877216295Ssyrinx				return (-1);
878216295Ssyrinx			} else
879216295Ssyrinx				object->val.syntax = syn;
880216295Ssyrinx		}
881216295Ssyrinx		len++;
882216295Ssyrinx	} else
883216295Ssyrinx		len = 0;
884216295Ssyrinx
885216295Ssyrinx	switch (object->val.syntax) {
886216295Ssyrinx		case SNMP_SYNTAX_INTEGER:
887216295Ssyrinx			return (parse_int_string(object, str + len));
888216295Ssyrinx		case SNMP_SYNTAX_IPADDRESS:
889216295Ssyrinx			return (parse_ip(&(object->val), str + len));
890216295Ssyrinx		case SNMP_SYNTAX_COUNTER:
891216295Ssyrinx			return (parse_counter(&(object->val), str + len));
892216295Ssyrinx		case SNMP_SYNTAX_GAUGE:
893216295Ssyrinx			return (parse_gauge(&(object->val), str + len));
894216295Ssyrinx		case SNMP_SYNTAX_TIMETICKS:
895216295Ssyrinx			return (parse_ticks(&(object->val), str + len));
896216295Ssyrinx		case SNMP_SYNTAX_COUNTER64:
897216295Ssyrinx			return (parse_uint64(&(object->val), str + len));
898216295Ssyrinx		case SNMP_SYNTAX_OCTETSTRING:
899216295Ssyrinx			return (snmp_tc2oct(object->info->tc, &(object->val),
900216295Ssyrinx			    str + len));
901216295Ssyrinx		case SNMP_SYNTAX_OID:
902216295Ssyrinx			return (parse_oid_string(snmptoolctx, &(object->val),
903216295Ssyrinx			    str + len));
904216295Ssyrinx		default:
905216295Ssyrinx			/* NOTREACHED */
906216295Ssyrinx			break;
907216295Ssyrinx	}
908216295Ssyrinx
909216295Ssyrinx	return (-1);
910216295Ssyrinx}
911216295Ssyrinx
912216295Ssyrinxstatic int32_t
913216295Ssyrinxparse_pair_stroid_val(struct snmp_toolinfo *snmptoolctx,
914216295Ssyrinx    struct snmp_object *obj, char *argv)
915216295Ssyrinx{
916216295Ssyrinx	char *ptr;
917216295Ssyrinx
918216295Ssyrinx	if ((ptr = snmptools_parse_stroid(snmptoolctx, obj, argv)) == NULL)
919216295Ssyrinx		return (-1);
920310903Sngie
921216295Ssyrinx	if (*ptr != '=') {
922216295Ssyrinx		warnx("Value to set expected after OID");
923216295Ssyrinx		return (-1);
924216295Ssyrinx	}
925216295Ssyrinx
926311466Sngie	if (parse_syntax_strval(snmptoolctx, obj, ptr + 1) < 0)
927216295Ssyrinx		return (-1);
928216295Ssyrinx
929216295Ssyrinx	return (1);
930216295Ssyrinx}
931216295Ssyrinx
932216295Ssyrinx
933216295Ssyrinxstatic int32_t
934216295Ssyrinxsnmpset_parse_oid(struct snmp_toolinfo *snmptoolctx,
935216295Ssyrinx    struct snmp_object *obj, char *argv)
936216295Ssyrinx{
937216295Ssyrinx	if (argv == NULL)
938216295Ssyrinx		return (-1);
939216295Ssyrinx
940216295Ssyrinx	if (ISSET_NUMERIC(snmptoolctx)) {
941216295Ssyrinx		if (parse_pair_numoid_val(argv, &(obj->val)) < 0)
942216295Ssyrinx			return (-1);
943216295Ssyrinx	} else {
944216295Ssyrinx		if (parse_pair_stroid_val(snmptoolctx, obj, argv) < 0)
945216295Ssyrinx			return (-1);
946216295Ssyrinx	}
947216295Ssyrinx
948216295Ssyrinx	return (1);
949216295Ssyrinx}
950216295Ssyrinx
951216295Ssyrinxstatic int32_t
952216295Ssyrinxadd_ip_syntax(struct snmp_value *dst, struct snmp_value *src)
953216295Ssyrinx{
954216295Ssyrinx	int8_t i;
955216295Ssyrinx
956216295Ssyrinx	dst->syntax = SNMP_SYNTAX_IPADDRESS;
957216295Ssyrinx	for (i = 0; i < 4; i++)
958216295Ssyrinx		dst->v.ipaddress[i] = src->v.ipaddress[i];
959216295Ssyrinx
960216295Ssyrinx	return (1);
961216295Ssyrinx}
962216295Ssyrinx
963216295Ssyrinxstatic int32_t
964216295Ssyrinxadd_octstring_syntax(struct snmp_value *dst, struct snmp_value *src)
965216295Ssyrinx{
966216295Ssyrinx	if (src->v.octetstring.len > ASN_MAXOCTETSTRING) {
967311595Sngie		warnx("OctetString len too big - %u", src->v.octetstring.len);
968216295Ssyrinx		return (-1);
969216295Ssyrinx	}
970216295Ssyrinx
971216295Ssyrinx	if ((dst->v.octetstring.octets = malloc(src->v.octetstring.len)) ==
972216295Ssyrinx	    NULL) {
973216295Ssyrinx		syslog(LOG_ERR, "malloc() failed - %s", strerror(errno));
974216295Ssyrinx		return (-1);
975216295Ssyrinx	}
976216295Ssyrinx
977216295Ssyrinx	memcpy(dst->v.octetstring.octets, src->v.octetstring.octets,
978216295Ssyrinx	    src->v.octetstring.len);
979216295Ssyrinx	dst->syntax = SNMP_SYNTAX_OCTETSTRING;
980216295Ssyrinx	dst->v.octetstring.len = src->v.octetstring.len;
981216295Ssyrinx
982216295Ssyrinx	return(0);
983216295Ssyrinx}
984216295Ssyrinx
985216295Ssyrinxstatic int32_t
986216295Ssyrinxadd_oid_syntax(struct snmp_value *dst, struct snmp_value *src)
987216295Ssyrinx{
988216295Ssyrinx	asn_append_oid(&(dst->v.oid), &(src->v.oid));
989216295Ssyrinx	dst->syntax = SNMP_SYNTAX_OID;
990216295Ssyrinx	return (0);
991216295Ssyrinx}
992216295Ssyrinx
993216295Ssyrinx/*
994216295Ssyrinx * Check syntax - if one of SNMP_SYNTAX_NULL, SNMP_SYNTAX_NOSUCHOBJECT,
995216295Ssyrinx * SNMP_SYNTAX_NOSUCHINSTANCE, SNMP_SYNTAX_ENDOFMIBVIEW or anything not known -
996216295Ssyrinx * return error.
997216295Ssyrinx */
998216295Ssyrinxstatic int32_t
999216295Ssyrinxsnmpset_add_value(struct snmp_value *dst, struct snmp_value *src)
1000216295Ssyrinx{
1001216295Ssyrinx	if (dst == NULL || src == NULL)
1002216295Ssyrinx		return (-1);
1003216295Ssyrinx
1004216295Ssyrinx	switch (src->syntax) {
1005216295Ssyrinx		case SNMP_SYNTAX_INTEGER:
1006216295Ssyrinx			dst->v.integer = src->v.integer;
1007216295Ssyrinx			dst->syntax = SNMP_SYNTAX_INTEGER;
1008216295Ssyrinx			break;
1009216295Ssyrinx		case SNMP_SYNTAX_TIMETICKS:
1010216295Ssyrinx			dst->v.uint32 = src->v.uint32;
1011216295Ssyrinx			dst->syntax = SNMP_SYNTAX_TIMETICKS;
1012216295Ssyrinx			break;
1013216295Ssyrinx		case SNMP_SYNTAX_GAUGE:
1014216295Ssyrinx			dst->v.uint32 = src->v.uint32;
1015216295Ssyrinx			dst->syntax = SNMP_SYNTAX_GAUGE;
1016216295Ssyrinx			break;
1017216295Ssyrinx		case SNMP_SYNTAX_COUNTER:
1018216295Ssyrinx			dst->v.uint32 = src->v.uint32;
1019216295Ssyrinx			dst->syntax = SNMP_SYNTAX_COUNTER;
1020216295Ssyrinx			break;
1021216295Ssyrinx		case SNMP_SYNTAX_COUNTER64:
1022216295Ssyrinx			dst->v.counter64 = src->v.counter64;
1023216295Ssyrinx			dst->syntax = SNMP_SYNTAX_COUNTER64;
1024216295Ssyrinx			break;
1025216295Ssyrinx		case SNMP_SYNTAX_IPADDRESS:
1026216295Ssyrinx			add_ip_syntax(dst, src);
1027216295Ssyrinx			break;
1028216295Ssyrinx		case SNMP_SYNTAX_OCTETSTRING:
1029216295Ssyrinx			add_octstring_syntax(dst, src);
1030216295Ssyrinx			break;
1031216295Ssyrinx		case SNMP_SYNTAX_OID:
1032216295Ssyrinx			add_oid_syntax(dst, src);
1033216295Ssyrinx			break;
1034216295Ssyrinx		default:
1035216295Ssyrinx			warnx("Unknown syntax %d", src->syntax);
1036216295Ssyrinx			return (-1);
1037216295Ssyrinx	}
1038216295Ssyrinx
1039216295Ssyrinx	return (0);
1040216295Ssyrinx}
1041216295Ssyrinx
1042216295Ssyrinxstatic int32_t
1043216295Ssyrinxsnmpset_verify_vbind(struct snmp_toolinfo *snmptoolctx, struct snmp_pdu *pdu,
1044216295Ssyrinx    struct snmp_object *obj)
1045216295Ssyrinx{
1046216295Ssyrinx	if (pdu->version == SNMP_V1 && obj->val.syntax ==
1047216295Ssyrinx	    SNMP_SYNTAX_COUNTER64) {
1048216295Ssyrinx		warnx("64-bit counters are not supported in SNMPv1 PDU");
1049216295Ssyrinx		return (-1);
1050216295Ssyrinx	}
1051216295Ssyrinx
1052216295Ssyrinx	if (ISSET_NUMERIC(snmptoolctx) || ISSET_ERRIGNORE(snmptoolctx))
1053216295Ssyrinx		return (1);
1054216295Ssyrinx
1055216295Ssyrinx	if (obj->info->access < SNMP_ACCESS_SET) {
1056216295Ssyrinx		warnx("Object %s not accessible for set - try 'bsnmpset -a'",
1057216295Ssyrinx		    obj->info->string);
1058216295Ssyrinx		return (-1);
1059216295Ssyrinx	}
1060216295Ssyrinx
1061216295Ssyrinx	return (1);
1062216295Ssyrinx}
1063216295Ssyrinx
1064216295Ssyrinxstatic int32_t
1065216295Ssyrinxsnmpset_add_vbind(struct snmp_pdu *pdu, struct snmp_object *obj)
1066216295Ssyrinx{
1067216295Ssyrinx	if (pdu->nbindings > SNMP_MAX_BINDINGS) {
1068216295Ssyrinx		warnx("Too many OIDs for one PDU");
1069216295Ssyrinx		return (-1);
1070216295Ssyrinx	}
1071216295Ssyrinx
1072216295Ssyrinx	if (obj->error > 0)
1073216295Ssyrinx		return (0);
1074216295Ssyrinx
1075216295Ssyrinx	if (snmpset_add_value(&(pdu->bindings[pdu->nbindings]), &(obj->val))
1076216295Ssyrinx	    < 0)
1077216295Ssyrinx		return (-1);
1078216295Ssyrinx
1079216295Ssyrinx	asn_append_oid(&(pdu->bindings[pdu->nbindings].var), &(obj->val.var));
1080216295Ssyrinx	pdu->nbindings++;
1081216295Ssyrinx
1082216295Ssyrinx	return (pdu->nbindings);
1083216295Ssyrinx}
1084216295Ssyrinx
1085216295Ssyrinxstatic int
1086216295Ssyrinxsnmptool_set(struct snmp_toolinfo *snmptoolctx)
1087216295Ssyrinx{
1088216295Ssyrinx	struct snmp_pdu req, resp;
1089216295Ssyrinx
1090216295Ssyrinx	snmp_pdu_create(&req, SNMP_PDU_SET);
1091216295Ssyrinx
1092216295Ssyrinx	while ((snmp_pdu_add_bindings(snmptoolctx, snmpset_verify_vbind,
1093216295Ssyrinx	    snmpset_add_vbind, &req, SNMP_MAX_BINDINGS)) > 0) {
1094216295Ssyrinx		if (snmp_dialog(&req, &resp)) {
1095311595Sngie			warn("Snmp dialog");
1096216295Ssyrinx			break;
1097216295Ssyrinx		}
1098216295Ssyrinx
1099216295Ssyrinx		if (snmp_pdu_check(&req, &resp) > 0) {
1100216295Ssyrinx			if (GET_OUTPUT(snmptoolctx) != OUTPUT_QUIET)
1101229933Ssyrinx				snmp_output_resp(snmptoolctx, &resp, NULL);
1102312050Sngie			snmp_pdu_free(&resp);
1103216295Ssyrinx			break;
1104216295Ssyrinx		}
1105216295Ssyrinx
1106216295Ssyrinx		snmp_output_err_resp(snmptoolctx, &resp);
1107312050Sngie		if (!ISSET_RETRY(snmptoolctx)) {
1108312050Sngie			snmp_pdu_free(&resp);
1109216295Ssyrinx			break;
1110312050Sngie		}
1111216295Ssyrinx
1112216295Ssyrinx		if (snmp_object_seterror(snmptoolctx,
1113216295Ssyrinx		    &(resp.bindings[resp.error_index - 1]),
1114312050Sngie		    resp.error_status) <= 0) {
1115312050Sngie			snmp_pdu_free(&resp);
1116216295Ssyrinx			break;
1117312050Sngie		}
1118216295Ssyrinx
1119216295Ssyrinx		fprintf(stderr, "Retrying...\n");
1120216295Ssyrinx		snmp_pdu_free(&req);
1121216295Ssyrinx		snmp_pdu_create(&req, SNMP_PDU_SET);
1122216295Ssyrinx	}
1123216295Ssyrinx
1124312050Sngie	snmp_pdu_free(&req);
1125216295Ssyrinx
1126216295Ssyrinx	return (0);
1127216295Ssyrinx}
1128216295Ssyrinx
1129216295Ssyrinx/* *****************************************************************************
1130216295Ssyrinx * main
1131216295Ssyrinx */
1132216295Ssyrinx/*
1133216295Ssyrinx * According to command line options prepare SNMP Get | GetNext | GetBulk PDU.
1134228990Suqs * Wait for a response and print it.
1135216295Ssyrinx */
1136216295Ssyrinx/*
1137216295Ssyrinx * Do a 'snmp walk' - according to command line options request for values
1138216295Ssyrinx * lexicographically subsequent and subrooted at a common node. Send a GetNext
1139228990Suqs * PDU requesting the value for each next variable and print the response. Stop
1140228990Suqs * when a Response PDU is received that contains the value of a variable not
1141216295Ssyrinx * subrooted at the variable the walk started.
1142216295Ssyrinx */
1143216295Ssyrinxint
1144216295Ssyrinxmain(int argc, char ** argv)
1145216295Ssyrinx{
1146216295Ssyrinx	struct snmp_toolinfo snmptoolctx;
1147216295Ssyrinx	int32_t oid_cnt, last_oid, opt_num;
1148216295Ssyrinx	int rc = 0;
1149216295Ssyrinx
1150216295Ssyrinx	/* Make sure program_name is set and valid. */
1151216295Ssyrinx	if (*argv == NULL)
1152216295Ssyrinx		program_name = "snmptool";
1153216295Ssyrinx	else {
1154216295Ssyrinx		program_name = strrchr(*argv, '/');
1155216295Ssyrinx		if (program_name != NULL)
1156216295Ssyrinx			program_name++;
1157216295Ssyrinx		else
1158216295Ssyrinx			program_name = *argv;
1159216295Ssyrinx	}
1160216295Ssyrinx
1161216295Ssyrinx	if (program_name == NULL) {
1162216295Ssyrinx		fprintf(stderr, "Error: No program name?\n");
1163216295Ssyrinx		exit (1);
1164216295Ssyrinx	} else if (strcmp(program_name, "bsnmpget") == 0)
1165216295Ssyrinx		program = BSNMPGET;
1166216295Ssyrinx	else if (strcmp(program_name, "bsnmpwalk") == 0)
1167216295Ssyrinx		program = BSNMPWALK;
1168216295Ssyrinx	else if (strcmp(program_name, "bsnmpset") == 0)
1169216295Ssyrinx		program = BSNMPSET;
1170216295Ssyrinx	else {
1171216295Ssyrinx		fprintf(stderr, "Unknown snmp tool name '%s'.\n", program_name);
1172216295Ssyrinx		exit (1);
1173216295Ssyrinx	}
1174216295Ssyrinx
1175216295Ssyrinx	/* Initialize. */
1176216295Ssyrinx	if (snmptool_init(&snmptoolctx) < 0)
1177216295Ssyrinx		exit (1);
1178216295Ssyrinx
1179216295Ssyrinx	if ((opt_num = snmptool_parse_options(&snmptoolctx, argc, argv)) < 0) {
1180216295Ssyrinx		snmp_tool_freeall(&snmptoolctx);
1181216295Ssyrinx		/* On -h (help) exit without error. */
1182216295Ssyrinx		if (opt_num == -2)
1183216295Ssyrinx			exit(0);
1184216295Ssyrinx		else
1185216295Ssyrinx			exit(1);
1186216295Ssyrinx	}
1187216295Ssyrinx
1188216295Ssyrinx	oid_cnt = argc - opt_num - 1;
1189216295Ssyrinx	if (oid_cnt == 0) {
1190216295Ssyrinx		switch (program) {
1191216295Ssyrinx		case BSNMPGET:
1192216295Ssyrinx			if (!ISSET_EDISCOVER(&snmptoolctx) &&
1193216295Ssyrinx			    !ISSET_LOCALKEY(&snmptoolctx)) {
1194216295Ssyrinx				fprintf(stderr, "No OID given.\n");
1195216295Ssyrinx				usage();
1196216295Ssyrinx				snmp_tool_freeall(&snmptoolctx);
1197216295Ssyrinx				exit(1);
1198216295Ssyrinx			}
1199216295Ssyrinx			break;
1200216295Ssyrinx
1201216295Ssyrinx		case BSNMPWALK:
1202216295Ssyrinx			if (snmp_object_add(&snmptoolctx, snmpwalk_add_default,
1203216295Ssyrinx			    NULL) < 0) {
1204216295Ssyrinx				fprintf(stderr,
1205216295Ssyrinx				    "Error setting default subtree.\n");
1206216295Ssyrinx				snmp_tool_freeall(&snmptoolctx);
1207216295Ssyrinx				exit(1);
1208216295Ssyrinx			}
1209216295Ssyrinx			break;
1210216295Ssyrinx
1211216295Ssyrinx		case BSNMPSET:
1212216295Ssyrinx			fprintf(stderr, "No OID given.\n");
1213216295Ssyrinx			usage();
1214216295Ssyrinx			snmp_tool_freeall(&snmptoolctx);
1215216295Ssyrinx			exit(1);
1216216295Ssyrinx		}
1217216295Ssyrinx	}
1218216295Ssyrinx
1219216295Ssyrinx	if (snmp_import_all(&snmptoolctx) < 0) {
1220216295Ssyrinx		snmp_tool_freeall(&snmptoolctx);
1221216295Ssyrinx		exit(1);
1222216295Ssyrinx	}
1223216295Ssyrinx
1224216295Ssyrinx	/* A simple sanity check - can not send GETBULK when using SNMPv1. */
1225216295Ssyrinx	if (program == BSNMPGET && snmp_client.version == SNMP_V1 &&
1226216295Ssyrinx	    GET_PDUTYPE(&snmptoolctx) == SNMP_PDU_GETBULK) {
1227216295Ssyrinx		fprintf(stderr, "Cannot send GETBULK PDU with SNMPv1.\n");
1228216295Ssyrinx		snmp_tool_freeall(&snmptoolctx);
1229216295Ssyrinx		exit(1);
1230216295Ssyrinx	}
1231216295Ssyrinx
1232216295Ssyrinx	for (last_oid = argc - 1; oid_cnt > 0; last_oid--, oid_cnt--) {
1233216295Ssyrinx		if ((snmp_object_add(&snmptoolctx, (program == BSNMPSET) ?
1234216295Ssyrinx		    snmpset_parse_oid : snmptools_parse_oid,
1235216295Ssyrinx		    argv[last_oid])) < 0) {
1236216295Ssyrinx			fprintf(stderr, "Error parsing OID string '%s'.\n",
1237216295Ssyrinx			    argv[last_oid]);
1238216295Ssyrinx			snmp_tool_freeall(&snmptoolctx);
1239216295Ssyrinx			exit(1);
1240216295Ssyrinx		}
1241216295Ssyrinx	}
1242216295Ssyrinx
1243216295Ssyrinx	if (snmp_open(NULL, NULL, NULL, NULL)) {
1244311595Sngie		warn("Failed to open snmp session");
1245216295Ssyrinx		snmp_tool_freeall(&snmptoolctx);
1246216295Ssyrinx		exit(1);
1247216295Ssyrinx	}
1248216295Ssyrinx
1249216295Ssyrinx	if (snmp_client.version == SNMP_V3 && snmp_client.engine.engine_len == 0)
1250216295Ssyrinx		SET_EDISCOVER(&snmptoolctx);
1251216295Ssyrinx
1252216295Ssyrinx	if (ISSET_EDISCOVER(&snmptoolctx) &&
1253216295Ssyrinx	    snmp_discover_engine(snmptoolctx.passwd) < 0) {
1254311595Sngie		warn("Unknown SNMP Engine ID");
1255216295Ssyrinx		rc = 1;
1256216295Ssyrinx		goto cleanup;
1257216295Ssyrinx	}
1258216295Ssyrinx
1259216295Ssyrinx	if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE ||
1260216295Ssyrinx	    ISSET_EDISCOVER(&snmptoolctx))
1261216295Ssyrinx		snmp_output_engine();
1262216295Ssyrinx
1263216295Ssyrinx	if (snmp_client.version == SNMP_V3 && ISSET_LOCALKEY(&snmptoolctx) &&
1264216295Ssyrinx	    !ISSET_EDISCOVER(&snmptoolctx)) {
1265216295Ssyrinx		if (snmp_passwd_to_keys(&snmp_client.user,
1266216295Ssyrinx		    snmptoolctx.passwd) != SNMP_CODE_OK ||
1267216295Ssyrinx		    snmp_get_local_keys(&snmp_client.user,
1268216295Ssyrinx		    snmp_client.engine.engine_id,
1269216295Ssyrinx		    snmp_client.engine.engine_len) != SNMP_CODE_OK) {
1270311595Sngie		    	warn("Failed to get keys");
1271216295Ssyrinx			rc = 1;
1272216295Ssyrinx			goto cleanup;
1273216295Ssyrinx		}
1274216295Ssyrinx	}
1275216295Ssyrinx
1276216295Ssyrinx	if (GET_OUTPUT(&snmptoolctx) == OUTPUT_VERBOSE ||
1277216295Ssyrinx	    ISSET_EDISCOVER(&snmptoolctx))
1278216295Ssyrinx		snmp_output_keys();
1279216295Ssyrinx
1280216295Ssyrinx	if (ISSET_EDISCOVER(&snmptoolctx) && snmptoolctx.objects == 0)
1281216295Ssyrinx		goto cleanup;
1282216295Ssyrinx
1283216295Ssyrinx	switch (program) {
1284216295Ssyrinx	case BSNMPGET:
1285216295Ssyrinx		rc = snmptool_get(&snmptoolctx);
1286216295Ssyrinx		break;
1287216295Ssyrinx	case BSNMPWALK:
1288216295Ssyrinx		rc = snmptool_walk(&snmptoolctx);
1289216295Ssyrinx		break;
1290216295Ssyrinx	case BSNMPSET:
1291216295Ssyrinx		rc = snmptool_set(&snmptoolctx);
1292216295Ssyrinx		break;
1293216295Ssyrinx	}
1294216295Ssyrinx
1295216295Ssyrinx
1296216295Ssyrinxcleanup:
1297216295Ssyrinx	snmp_tool_freeall(&snmptoolctx);
1298216295Ssyrinx	snmp_close();
1299216295Ssyrinx
1300216295Ssyrinx	exit(rc);
1301216295Ssyrinx}
1302