bsnmpget.c revision 310903
1115013Smarcel/*-
2160157Smarcel * Copyright (c) 2005-2006 The FreeBSD Project
3121642Smarcel * All rights reserved.
4121642Smarcel *
5121642Smarcel * Author: Shteryana Shopova <syrinx@FreeBSD.org>
6121642Smarcel *
7121642Smarcel * Redistribution of this software and documentation and use in source and
8121642Smarcel * binary forms, with or without modification, are permitted provided that
9121642Smarcel * the following conditions are met:
10121642Smarcel *
11115013Smarcel * 1. Redistributions of source code or documentation must retain the above
12121642Smarcel *    copyright notice, this list of conditions and the following disclaimer.
13121642Smarcel * 2. Redistributions in binary form must reproduce the above copyright
14121642Smarcel *    notice, this list of conditions and the following disclaimer in the
15121642Smarcel *    documentation and/or other materials provided with the distribution.
16121642Smarcel *
17121642Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18121642Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19121642Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20121642Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21121642Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22121642Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23121642Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24121642Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25160163Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26115013Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27160163Smarcel * SUCH DAMAGE.
28115013Smarcel *
29115013Smarcel * Bsnmpget and bsnmpwalk are simple tools for querying SNMP agents,
30115013Smarcel * bsnmpset can be used to set MIB objects in an agent.
31115013Smarcel *
32115013Smarcel * $FreeBSD: stable/10/usr.sbin/bsnmpd/tools/bsnmptools/bsnmpget.c 310903 2016-12-31 10:34:09Z ngie $
33160163Smarcel */
34160163Smarcel
35160163Smarcel#include <sys/queue.h>
36115013Smarcel#include <sys/types.h>
37160163Smarcel
38160163Smarcel#include <assert.h>
39160163Smarcel#include <ctype.h>
40160163Smarcel#include <err.h>
41160163Smarcel#include <errno.h>
42160163Smarcel#include <stdarg.h>
43160163Smarcel#include <stdio.h>
44160163Smarcel#include <stdlib.h>
45160163Smarcel#include <string.h>
46160163Smarcel#include <syslog.h>
47160163Smarcel#include <unistd.h>
48160163Smarcel
49160163Smarcel#include <bsnmp/asn1.h>
50160163Smarcel#include <bsnmp/snmp.h>
51160163Smarcel#include <bsnmp/snmpclient.h>
52160163Smarcel#include "bsnmptc.h"
53160163Smarcel#include "bsnmptools.h"
54160163Smarcel
55160163Smarcelstatic const char *program_name = NULL;
56160163Smarcelstatic enum program_e {
57160163Smarcel	BSNMPGET,
58160163Smarcel	BSNMPWALK,
59160163Smarcel	BSNMPSET
60160163Smarcel} program;
61160157Smarcel
62129059Smarcel/* *****************************************************************************
63160157Smarcel * Common bsnmptools functions.
64160157Smarcel */
65160157Smarcelstatic void
66160157Smarcelusage(void)
67160157Smarcel{
68129059Smarcel	fprintf(stderr,
69129059Smarcel"Usage:\n"
70115013Smarcel"%s %s [-A options] [-b buffersize] [-C options] [-I options]\n"
71115013Smarcel"\t[-i filelist] [-l filename]%s [-o output] [-P options]\n"
72115013Smarcel"\t%s[-r retries] [-s [trans::][community@][server][:port]]\n"
73115013Smarcel"\t[-t timeout] [-U options] [-v version]%s\n",
74115013Smarcel	program_name,
75115013Smarcel	(program == BSNMPGET) ? "[-aDdehnK]" :
76115013Smarcel	    (program == BSNMPWALK) ? "[-dhnK]" :
77115013Smarcel	    (program == BSNMPSET) ? "[-adehnK]" :
78115013Smarcel	    "",
79115013Smarcel	(program == BSNMPGET || program == BSNMPWALK) ?
80115013Smarcel	" [-M max-repetitions] [-N non-repeaters]" : "",
81115013Smarcel	(program == BSNMPGET || program == BSNMPWALK) ? "[-p pdu] " : "",
82115013Smarcel	(program == BSNMPGET) ? " OID [OID ...]" :
83115013Smarcel	    (program == BSNMPWALK || program == BSNMPSET) ? " [OID ...]" :
84115013Smarcel	    ""
85115013Smarcel	);
86115013Smarcel}
87115013Smarcel
88115013Smarcelstatic int32_t
89115013Smarcelparse_max_repetitions(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
90115013Smarcel{
91160157Smarcel	uint32_t v;
92115013Smarcel
93115013Smarcel	assert(opt_arg != NULL);
94115013Smarcel
95115013Smarcel	v = strtoul(opt_arg, (void *) NULL, 10);
96115013Smarcel
97115013Smarcel	if (v > SNMP_MAX_BINDINGS) {
98115013Smarcel		warnx("Max repetitions value greater than %d maximum allowed.",
99115013Smarcel		    SNMP_MAX_BINDINGS);
100115013Smarcel		return (-1);
101115013Smarcel	}
102120925Smarcel
103120925Smarcel	SET_MAXREP(snmptoolctx, v);
104115013Smarcel	return (2);
105115013Smarcel}
106115013Smarcel
107115013Smarcelstatic int32_t
108160163Smarcelparse_non_repeaters(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
109115013Smarcel{
110115013Smarcel	uint32_t v;
111115013Smarcel
112115013Smarcel	assert(opt_arg != NULL);
113115013Smarcel
114115013Smarcel	v = strtoul(opt_arg, (void *) NULL, 10);
115115013Smarcel
116115013Smarcel	if (v > SNMP_MAX_BINDINGS) {
117115013Smarcel		warnx("Non repeaters value greater than %d maximum allowed.",
118115013Smarcel		    SNMP_MAX_BINDINGS);
119115013Smarcel		return (-1);
120115013Smarcel	}
121115013Smarcel
122115013Smarcel	SET_NONREP(snmptoolctx, v);
123115013Smarcel	return (2);
124115013Smarcel}
125115013Smarcel
126115013Smarcelstatic int32_t
127115013Smarcelparse_pdu_type(struct snmp_toolinfo *snmptoolctx, char *opt_arg)
128115013Smarcel{
129115013Smarcel	assert(opt_arg != NULL);
130115013Smarcel
131115013Smarcel	if (strcasecmp(opt_arg, "getbulk") == 0)
132115013Smarcel		SET_PDUTYPE(snmptoolctx, SNMP_PDU_GETBULK);
133115013Smarcel	else if (strcasecmp(opt_arg, "getnext") == 0)
134115013Smarcel		SET_PDUTYPE(snmptoolctx, SNMP_PDU_GETNEXT);
135115013Smarcel	else if (strcasecmp(opt_arg, "get") == 0)
136115013Smarcel		SET_PDUTYPE(snmptoolctx, SNMP_PDU_GET);
137115013Smarcel	else {
138115013Smarcel		warnx("PDU type '%s' not supported.", opt_arg);
139115013Smarcel		return (-1);
140115013Smarcel	}
141115013Smarcel
142115013Smarcel	return (2);
143115013Smarcel}
144115013Smarcel
145115013Smarcelstatic int32_t
146115013Smarcelsnmptool_parse_options(struct snmp_toolinfo *snmptoolctx, int argc, char **argv)
147115013Smarcel{
148115013Smarcel	int32_t count, optnum = 0;
149115013Smarcel	int ch;
150115013Smarcel	const char *opts;
151115013Smarcel
152115013Smarcel	switch (program) {
153115013Smarcel		case BSNMPWALK:
154115013Smarcel			opts = "dhnKA:b:C:I:i:l:M:N:o:P:p:r:s:t:U:v:";
155115013Smarcel			break;
156115013Smarcel		case BSNMPGET:
157115013Smarcel			opts = "aDdehnKA:b:C:I:i:l:M:N:o:P:p:r:s:t:U:v:";
158115013Smarcel			break;
159115013Smarcel		case BSNMPSET:
160115013Smarcel			opts = "adehnKA:b:C:I:i:l:o:P:r:s:t:U:v:";
161115013Smarcel			break;
162115013Smarcel		default:
163115013Smarcel			return (-1);
164115013Smarcel	}
165115013Smarcel
166115013Smarcel	while ((ch = getopt(argc, argv, opts)) != EOF) {
167115013Smarcel		switch (ch) {
168115013Smarcel		case 'A':
169115013Smarcel			count = parse_authentication(snmptoolctx, optarg);
170115013Smarcel			break;
171115013Smarcel		case 'a':
172115013Smarcel			count = parse_skip_access(snmptoolctx);
173115013Smarcel			break;
174115013Smarcel		case 'b':
175115013Smarcel			count = parse_buflen(optarg);
176115013Smarcel			break;
177115013Smarcel		case 'D':
178115013Smarcel			count = parse_discovery(snmptoolctx);
179115013Smarcel			break;
180115013Smarcel		case 'd':
181115013Smarcel			count = parse_debug();
182115013Smarcel			break;
183160163Smarcel		case 'e':
184115013Smarcel			count = parse_errors(snmptoolctx);
185115013Smarcel			break;
186115013Smarcel		case 'h':
187115013Smarcel			usage();
188115013Smarcel			return (-2);
189115013Smarcel		case 'C':
190115013Smarcel			count = parse_context(snmptoolctx, optarg);
191115013Smarcel			break;
192115013Smarcel		case 'I':
193115013Smarcel			count = parse_include(snmptoolctx, optarg);
194115013Smarcel			break;
195115013Smarcel		case 'i':
196115013Smarcel			count = parse_file(snmptoolctx, optarg);
197115013Smarcel			break;
198115013Smarcel		case 'K':
199115013Smarcel			count = parse_local_key(snmptoolctx);
200115013Smarcel			break;
201115013Smarcel		case 'l':
202115013Smarcel			count = parse_local_path(optarg);
203115013Smarcel			break;
204115013Smarcel		case 'M':
205115013Smarcel			count = parse_max_repetitions(snmptoolctx, optarg);
206115013Smarcel			break;
207115013Smarcel		case 'N':
208115013Smarcel			count = parse_non_repeaters(snmptoolctx, optarg);
209115013Smarcel			break;
210115013Smarcel		case 'n':
211115013Smarcel			count = parse_num_oids(snmptoolctx);
212115013Smarcel			break;
213115013Smarcel		case 'o':
214115013Smarcel			count = parse_output(snmptoolctx, optarg);
215115013Smarcel			break;
216115013Smarcel		case 'P':
217115013Smarcel			count = parse_privacy(snmptoolctx, optarg);
218115013Smarcel			break;
219115013Smarcel		case 'p':
220115013Smarcel			count = parse_pdu_type(snmptoolctx, optarg);
221115013Smarcel			break;
222115013Smarcel		case 'r':
223115013Smarcel			count = parse_retry(optarg);
224115013Smarcel			break;
225115013Smarcel		case 's':
226115013Smarcel			count = parse_server(optarg);
227115013Smarcel			break;
228115013Smarcel		case 't':
229115013Smarcel			count = parse_timeout(optarg);
230115013Smarcel			break;
231115013Smarcel		case 'U':
232115013Smarcel			count = parse_user_security(snmptoolctx, optarg);
233115013Smarcel			break;
234115013Smarcel		case 'v':
235115013Smarcel			count = parse_version(optarg);
236115013Smarcel			break;
237115013Smarcel		case '?':
238115013Smarcel		default:
239115013Smarcel			usage();
240115013Smarcel			return (-1);
241115013Smarcel		}
242115013Smarcel		if (count < 0)
243115013Smarcel			return (-1);
244115013Smarcel	    optnum += count;
245115013Smarcel	}
246115013Smarcel
247115013Smarcel	return (optnum);
248115013Smarcel}
249115013Smarcel
250115013Smarcel/*
251115013Smarcel * Read user input OID - one of following formats:
252115013Smarcel * 1) 1.2.1.1.2.1.0 - that is if option numeric was given;
253115013Smarcel * 2) string - in such case append .0 to the asn_oid subs;
254115013Smarcel * 3) string.1 - no additional processing required in such case.
255115013Smarcel */
256115013Smarcelstatic char *
257115013Smarcelsnmptools_parse_stroid(struct snmp_toolinfo *snmptoolctx,
258115013Smarcel    struct snmp_object *obj, char *argv)
259115013Smarcel{
260115013Smarcel	char string[MAXSTR], *str;
261115013Smarcel	int32_t i = 0;
262115013Smarcel	struct asn_oid in_oid;
263115013Smarcel
264115013Smarcel	str = argv;
265115013Smarcel
266115013Smarcel	if (*str == '.')
267160163Smarcel		str++;
268115013Smarcel
269115013Smarcel	while (isalpha(*str) || *str == '_' || (i != 0 && isdigit(*str))) {
270115013Smarcel		str++;
271115013Smarcel		i++;
272115013Smarcel	}
273115013Smarcel
274115013Smarcel	if (i <= 0 || i >= MAXSTR)
275115013Smarcel		return (NULL);
276115013Smarcel
277115013Smarcel	memset(&in_oid, 0, sizeof(struct asn_oid));
278115013Smarcel	if ((str = snmp_parse_suboid((argv + i), &in_oid)) == NULL) {
279115013Smarcel		warnx("Invalid OID - %s", argv);
280115013Smarcel		return (NULL);
281115013Smarcel	}
282115013Smarcel
283115013Smarcel	strlcpy(string, argv, i + 1);
284115013Smarcel	if (snmp_lookup_oidall(snmptoolctx, obj, string) < 0) {
285115013Smarcel		warnx("No entry for %s in mapping lists", string);
286115013Smarcel		return (NULL);
287115013Smarcel	}
288115013Smarcel
289115013Smarcel	/* If OID given on command line append it. */
290115013Smarcel	if (in_oid.len > 0)
291115013Smarcel		asn_append_oid(&(obj->val.var), &in_oid);
292115013Smarcel	else if (*str == '[') {
293115013Smarcel		if ((str = snmp_parse_index(snmptoolctx, str + 1, obj)) == NULL)
294115013Smarcel			return (NULL);
295115013Smarcel	} else if (obj->val.syntax > 0 && GET_PDUTYPE(snmptoolctx) ==
296115013Smarcel	    SNMP_PDU_GET) {
297115013Smarcel		if (snmp_suboid_append(&(obj->val.var), (asn_subid_t) 0) < 0)
298115013Smarcel			return (NULL);
299115013Smarcel	}
300115013Smarcel
301115013Smarcel	return (str);
302115013Smarcel}
303115013Smarcel
304160157Smarcelstatic int32_t
305160157Smarcelsnmptools_parse_oid(struct snmp_toolinfo *snmptoolctx,
306160157Smarcel    struct snmp_object *obj, char *argv)
307160157Smarcel{
308160157Smarcel	if (argv == NULL)
309160157Smarcel		return (-1);
310115013Smarcel
311115013Smarcel	if (ISSET_NUMERIC(snmptoolctx)) {
312115013Smarcel		if (snmp_parse_numoid(argv, &(obj->val.var)) < 0)
313115013Smarcel			return (-1);
314115013Smarcel	} else {
315115013Smarcel		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