xref: /openbsd/usr.bin/snmp/snmpc.c (revision 274d7c50)
1 /*	$OpenBSD: snmpc.c,v 1.17 2019/10/26 19:34:15 martijn Exp $	*/
2 
3 /*
4  * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
5  * Copyright (c) 2013 Reyk Floeter <reyk@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/limits.h>
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <sys/un.h>
24 
25 #include <arpa/inet.h>
26 #include <openssl/evp.h>
27 
28 #include <ber.h>
29 #include <ctype.h>
30 #include <err.h>
31 #include <errno.h>
32 #include <netdb.h>
33 #include <poll.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <stdint.h>
37 #include <string.h>
38 #include <time.h>
39 #include <unistd.h>
40 
41 #include "smi.h"
42 #include "snmp.h"
43 #include "usm.h"
44 
45 #define GETOPT_COMMON		"A:a:c:E:e:K:k:l:n:O:r:t:u:v:X:x:Z:"
46 
47 int snmpc_get(int, char *[]);
48 int snmpc_walk(int, char *[]);
49 int snmpc_set(int, char *[]);
50 int snmpc_trap(int, char *[]);
51 int snmpc_mibtree(int, char *[]);
52 struct snmp_agent *snmpc_connect(char *, char *);
53 int snmpc_parseagent(char *, char *);
54 int snmpc_print(struct ber_element *);
55 __dead void snmpc_printerror(enum snmp_error, struct ber_element *, int,
56     const char *);
57 char *snmpc_hex2bin(char *, size_t *);
58 struct ber_element *snmpc_varbindparse(int, char *[]);
59 void usage(void);
60 
61 struct snmp_app {
62 	const char *name;
63 	const int usecommonopt;
64 	const char *optstring;
65 	const char *usage;
66 	int (*exec)(int, char *[]);
67 };
68 
69 struct snmp_app snmp_apps[] = {
70 	{ "get", 1, NULL, "agent oid ...", snmpc_get },
71 	{ "getnext", 1, NULL, "agent oid ...", snmpc_get },
72 	{ "walk", 1, "C:", "[-C cIipt] [-C E endoid] agent [oid]", snmpc_walk },
73 	{ "bulkget", 1, "C:", "[-C n<nonrep>r<maxrep>] agent oid ...", snmpc_get },
74 	{ "bulkwalk", 1, "C:", "[-C cipn<nonrep>r<maxrep>] agent [oid]", snmpc_walk },
75 	{ "set", 1, NULL, "agent oid type value [oid type value] ...", snmpc_set },
76 	{ "trap", 1, NULL, "agent uptime oid [oid type value] ...", snmpc_trap },
77 	{ "mibtree", 0, "O:", "[-O fnS]", snmpc_mibtree }
78 };
79 struct snmp_app *snmp_app = NULL;
80 
81 char *community = "public";
82 struct snmp_v3 *v3;
83 char *mib = "mib_2";
84 int retries = 5;
85 int timeout = 1;
86 enum snmp_version version = SNMP_V2C;
87 int print_equals = 1;
88 int print_varbind_only = 0;
89 int print_summary = 0;
90 int print_time = 0;
91 int walk_check_increase = 1;
92 int walk_fallback_oid = 1;
93 int walk_include_oid = 0;
94 int smi_print_hint = 1;
95 int non_repeaters = 0;
96 int max_repetitions = 10;
97 struct ber_oid walk_end = {{0}, 0};
98 enum smi_oid_lookup oid_lookup = smi_oidl_short;
99 enum smi_output_string output_string = smi_os_default;
100 
101 int
102 main(int argc, char *argv[])
103 {
104 	const EVP_MD *md = NULL;
105 	const EVP_CIPHER *cipher = NULL;
106 	struct snmp_sec *sec;
107 	char *user = NULL;
108 	enum usm_key_level authkeylevel;
109 	char *authkey = NULL;
110 	size_t authkeylen = 0;
111 	enum usm_key_level privkeylevel;
112 	char *privkey = NULL;
113 	size_t privkeylen = 0;
114 	int seclevel = SNMP_MSGFLAG_REPORT;
115 	char *ctxname = NULL;
116 	char *ctxengineid = NULL, *secengineid = NULL;
117 	size_t ctxengineidlen, secengineidlen;
118 	int zflag = 0;
119 	long long boots, time;
120 	char optstr[BUFSIZ];
121 	const char *errstr;
122 	char *strtolp;
123 	int ch;
124 	size_t i;
125 
126 	if (pledge("stdio inet dns", NULL) == -1)
127 		err(1, "pledge");
128 
129 	if (argc <= 1)
130 		usage();
131 
132 	optstr[0] = '\0';
133 	for (i = 0; i < sizeof(snmp_apps)/sizeof(*snmp_apps); i++) {
134 		if (strcmp(snmp_apps[i].name, argv[1]) == 0) {
135 			snmp_app = &snmp_apps[i];
136 			if (snmp_app->optstring != NULL) {
137 				if (strlcpy(optstr, snmp_app->optstring,
138 				    sizeof(optstr)) > sizeof(optstr))
139 					errx(1, "strlcat");
140 			}
141 			break;
142 		}
143 	}
144 	if (snmp_app == NULL)
145 		usage();
146 
147 	if (snmp_app->usecommonopt) {
148 		if (strlcat(optstr, GETOPT_COMMON, sizeof(optstr)) >
149 		    sizeof(optstr))
150 			errx(1, "strlcpy");
151 	}
152 
153 	argc--;
154 	argv++;
155 
156 	smi_init();
157 
158 	while ((ch = getopt(argc, argv, optstr)) != -1) {
159 		switch (ch) {
160 		case 'A':
161 			authkey = optarg;
162 			authkeylen = strlen(authkey);
163 			authkeylevel = USM_KEY_PASSWORD;
164 			break;
165 		case 'a':
166 			if (strcasecmp(optarg, "MD5") == 0)
167 				md = EVP_md5();
168 			else if (strcasecmp(optarg, "SHA") == 0)
169 				md = EVP_sha1();
170 			else if (strcasecmp(optarg, "SHA-224") == 0)
171 				md = EVP_sha224();
172 			else if (strcasecmp(optarg, "SHA-256") == 0)
173 				md = EVP_sha256();
174 			else if (strcasecmp(optarg, "SHA-384") == 0)
175 				md = EVP_sha384();
176 			else if (strcasecmp(optarg, "SHA-512") == 0)
177 				md = EVP_sha512();
178 			else
179 				errx(1, "Invalid authentication protocol "
180 				    "specified after -a flag: %s", optarg);
181 			break;
182 		case 'c':
183 			community = optarg;
184 			break;
185 		case 'E':
186 			ctxengineid = snmpc_hex2bin(optarg,
187 			    &ctxengineidlen);
188 			if (ctxengineid == NULL) {
189 				if (errno == EINVAL)
190 					errx(1, "Bad engine ID value "
191 					    "after -3E flag.");
192 				err(1, "-3E");
193 			}
194 			break;
195 		case 'e':
196 			secengineid = snmpc_hex2bin(optarg,
197 			    &secengineidlen);
198 			if (secengineid == NULL) {
199 				if (errno == EINVAL)
200 					errx(1, "Bad engine ID value "
201 					    "after -3e flag.");
202 				err(1, "-3e");
203 			}
204 			break;
205 		case 'K':
206 			privkey = snmpc_hex2bin(optarg, &privkeylen);
207 			if (privkey == NULL) {
208 				if (errno == EINVAL)
209 					errx(1, "Bad key value after "
210 					    "-3K flag.");
211 				errx(1, "-3K");
212 			}
213 			privkeylevel = USM_KEY_LOCALIZED;
214 				break;
215 		case 'k':
216 			authkey = snmpc_hex2bin(optarg, &authkeylen);
217 			if (authkey == NULL) {
218 				if (errno == EINVAL)
219 					errx(1, "Bad key value after -k flag.");
220 				err(1, "-k");
221 			}
222 			authkeylevel = USM_KEY_LOCALIZED;
223 			break;
224 		case 'l':
225 			if (strcasecmp(optarg, "noAuthNoPriv") == 0)
226 				seclevel = SNMP_MSGFLAG_REPORT;
227 			else if (strcasecmp(optarg, "authNoPriv") == 0)
228 				seclevel = SNMP_MSGFLAG_AUTH |
229 				    SNMP_MSGFLAG_REPORT;
230 			else if (strcasecmp(optarg, "authPriv") == 0)
231 				seclevel = SNMP_MSGFLAG_AUTH |
232 				    SNMP_MSGFLAG_PRIV | SNMP_MSGFLAG_REPORT;
233 			else
234 				errx(1, "Invalid security level specified "
235 				    "after -l flag: %s", optarg);
236 			break;
237 		case 'n':
238 			ctxname = optarg;
239 			break;
240 		case 'r':
241 			if ((retries = strtonum(optarg, 0, INT_MAX,
242 			    &errstr)) == 0) {
243 				if (errstr != NULL)
244 					errx(1, "-r: %s argument", errstr);
245 			}
246 			break;
247 		case 't':
248 			if ((timeout = strtonum(optarg, 1, INT_MAX,
249 			    &errstr)) == 0) {
250 				if (errstr != NULL)
251 					errx(1, "-t: %s argument", errstr);
252 			}
253 			break;
254 		case 'u':
255 			user = optarg;
256 			break;
257 		case 'v':
258 			if (strcmp(optarg, "1") == 0)
259 				version = SNMP_V1;
260 			else if (strcmp(optarg, "2c") == 0)
261 				version = SNMP_V2C;
262 			else if (strcmp(optarg, "3") == 0)
263 				version = SNMP_V3;
264 			else
265 				errc(1, EINVAL, "-v");
266 			break;
267 		case 'C':
268 			for (i = 0; i < strlen(optarg); i++) {
269 				switch (optarg[i]) {
270 				case 'c':
271 					if (strcmp(snmp_app->name, "walk") &&
272 					    strcmp(snmp_app->name, "bulkwalk"))
273 						usage();
274 					walk_check_increase = 0;
275 					break;
276 				case 'i':
277 					if (strcmp(snmp_app->name, "walk") &&
278 					    strcmp(snmp_app->name, "bulkwalk"))
279 						usage();
280 					walk_include_oid = 1;
281 					break;
282 				case 'n':
283 					if (strcmp(snmp_app->name, "bulkget") &&
284 					    strcmp(snmp_app->name, "bulkwalk"))
285 						usage();
286 					errno = 0;
287 					non_repeaters = strtol(&optarg[i + 1],
288 					    &strtolp, 10);
289 					if (non_repeaters < 0 ||
290 					    errno == ERANGE) {
291 						if (non_repeaters < 0)
292 							errx(1, "%s%s",
293 							    "-Cn: too small ",
294 							    "argument");
295 						else
296 							errx(1, "%s%s",
297 							    "-Cn: too large",
298 							    "argument");
299 					} else if (&optarg[i + 1] == strtolp)
300 						errx(1, "-Cn invalid argument");
301 					i = strtolp - optarg - 1;
302 					break;
303 				case 'p':
304 					if (strcmp(snmp_app->name, "walk") &&
305 					    strcmp(snmp_app->name, "bulkwalk"))
306 						usage();
307 					print_summary = 1;
308 					break;
309 				case 'r':
310 					if (strcmp(snmp_app->name, "bulkget") &&
311 					    strcmp(snmp_app->name, "bulkwalk"))
312 						usage();
313 					errno = 0;
314 					max_repetitions = strtol(&optarg[i + 1],
315 					    &strtolp, 10);
316 					if (max_repetitions < 0 ||
317 					    errno == ERANGE) {
318 						if (max_repetitions < 0)
319 							errx(1, "%s%s",
320 							    "-Cr: too small ",
321 							    "argument");
322 						else
323 							errx(1, "%s%s",
324 							    "-Cr: too large",
325 							    "argument");
326 					} else if (&optarg[i + 1] == strtolp)
327 						errx(1, "-Cr invalid argument");
328 					i = strtolp - optarg - 1;
329 					break;
330 				case 't':
331 					if (strcmp(snmp_app->name, "walk"))
332 						usage();
333 					print_time = 1;
334 					break;
335 				case 'E':
336 					if (strcmp(snmp_app->name, "walk"))
337 						usage();
338 					if (smi_string2oid(argv[optind],
339 					    &walk_end) != 0)
340 						errx(1, "%s: %s",
341 						    "Unknown Object Identifier",
342 						    argv[optind]);
343 					optind++;
344 					continue;
345 				case 'I':
346 					if (strcmp(snmp_app->name, "walk"))
347 						usage();
348 					walk_fallback_oid = 0;
349 					break;
350 				default:
351 					usage();
352 				}
353 				if (optarg[i] == 'E')
354 					break;
355 			}
356 			break;
357 		case 'O':
358 			for (i = 0; i < strlen(optarg); i++) {
359 				if (strcmp(snmp_app->name, "mibtree") == 0 &&
360 				    optarg[i] != 'f' && optarg[i] != 'n' &&
361 				    optarg[i] != 'S')
362 						usage();
363 				switch (optarg[i]) {
364 				case 'a':
365 					output_string = smi_os_ascii;
366 					break;
367 				case 'f':
368 					oid_lookup = smi_oidl_full;
369 					break;
370 				case 'n':
371 					oid_lookup = smi_oidl_numeric;
372 					break;
373 				case 'q':
374 					print_equals = 0;
375 					smi_print_hint = 0;
376 					break;
377 				case 'v':
378 					print_varbind_only = 1;
379 					break;
380 				case 'x':
381 					output_string = smi_os_hex;
382 					break;
383 				case 'S':
384 					oid_lookup = smi_oidl_short;
385 					break;
386 				case 'Q':
387 					smi_print_hint = 0;
388 					break;
389 				default:
390 					usage();
391 				}
392 			}
393 			break;
394 		case 'X':
395 			privkey = optarg;
396 			privkeylen = strlen(privkey);
397 			privkeylevel = USM_KEY_PASSWORD;
398 			break;
399 		case 'x':
400 			if (strcasecmp(optarg, "DES") == 0)
401 				cipher = EVP_des_cbc();
402 			else if (strcasecmp(optarg, "AES") == 0)
403 				cipher = EVP_aes_128_cfb128();
404 			else
405 				errx(1, "Invalid privacy protocol "
406 				    "specified after -3x flag: %s",
407 				    optarg);
408 			break;
409 		case 'Z':
410 			boots = strtoll(optarg, &strtolp, 10);
411 			if (boots < 0 || strtolp == optarg || strtolp[0] != ',')
412 				usage();
413 			strtolp++;
414 			while (strtolp[0] == ' ' && strtolp[0] == '\t')
415 				strtolp++;
416 			time = strtoll(strtolp, &strtolp, 10);
417 			if (boots < 0 || strtolp == optarg)
418 				usage();
419 			zflag = 1;
420 			break;
421 		default:
422 			usage();
423 		}
424 	}
425 	argc -= optind;
426 	argv += optind;
427 
428 	if (version == SNMP_V3) {
429 		/* Setup USM */
430 		if (user == NULL || user[0] == '\0')
431 			errx(1, "No securityName specified");
432 		if ((sec = usm_init(user, strlen(user))) == NULL)
433 			err(1, "usm_init");
434 		if (seclevel & SNMP_MSGFLAG_AUTH) {
435 			if (md == NULL)
436 				md = EVP_md5();
437 			if (authkey == NULL)
438 				errx(1, "No authKey or authPassword specified");
439 			if (usm_setauth(sec, md, authkey, authkeylen,
440 			    authkeylevel) == -1)
441 				err(1, "Can't set authkey");
442 		}
443 		if (seclevel & SNMP_MSGFLAG_PRIV) {
444 			if (cipher == NULL)
445 				cipher = EVP_des_cbc();
446 			if (privkey == NULL)
447 				errx(1, "No privKey or privPassword specified");
448 			if (usm_setpriv(sec, cipher, privkey, privkeylen,
449 			    privkeylevel) == -1)
450 				err(1, "Can't set authkey");
451 		}
452 		if (secengineid != NULL) {
453 			if (usm_setengineid(sec, secengineid,
454 			    secengineidlen) == -1)
455 				err(1, "Can't set secengineid");
456 		}
457 		if (zflag)
458 			if (usm_setbootstime(sec, boots, time) == -1)
459 				err(1, "Can't set boots/time");
460 		v3 = snmp_v3_init(seclevel, ctxname, ctxname == NULL ? 0 :
461 		    strlen(ctxname), sec);
462 		if (v3 == NULL)
463 			err(1, "snmp_v3_init");
464 		if (ctxengineid != NULL) {
465 			if (snmp_v3_setengineid(v3, ctxengineid,
466 			    ctxengineidlen) == -1)
467 				err(1, "Can't set ctxengineid");
468 		}
469 	}
470 
471 
472 	return snmp_app->exec(argc, argv);
473 }
474 
475 int
476 snmpc_get(int argc, char *argv[])
477 {
478 	struct ber_oid *oid;
479 	struct ber_element *pdu, *varbind;
480 	struct snmp_agent *agent;
481 	int errorstatus, errorindex;
482 	int i;
483 	int class;
484 	unsigned type;
485 	char *hint = NULL;
486 
487 	if (argc < 2)
488 		usage();
489 
490 	if ((agent = snmpc_connect(argv[0], "161")) == NULL)
491 		err(1, "%s", snmp_app->name);
492 	agent->timeout = timeout;
493 	agent->retries = retries;
494 
495 	if (pledge("stdio", NULL) == -1)
496 		err(1, "pledge");
497 	argc--;
498 	argv++;
499 
500 	oid = reallocarray(NULL, argc, sizeof(*oid));
501 	if (oid == NULL)
502 		err(1, "malloc");
503 	for (i = 0; i < argc; i++) {
504 		if (smi_string2oid(argv[i], &oid[i]) == -1)
505 			errx(1, "%s: Unknown object identifier", argv[i]);
506 	}
507 	if (strcmp(snmp_app->name, "getnext") == 0) {
508 		if ((pdu = snmp_getnext(agent, oid, argc)) == NULL)
509 			err(1, "getnext");
510 	} else if (strcmp(snmp_app->name, "bulkget") == 0) {
511 		if (version < SNMP_V2C)
512 			errx(1, "Cannot send V2 PDU on V1 session");
513 		if (non_repeaters > argc)
514 			errx(1, "need more objects than -Cn<num>");
515 		if ((pdu = snmp_getbulk(agent, oid, argc, non_repeaters,
516 		    max_repetitions)) == NULL)
517 			err(1, "bulkget");
518 	} else {
519 		if ((pdu = snmp_get(agent, oid, argc)) == NULL)
520 			err(1, "get");
521 	}
522 
523 	(void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type, &errorstatus,
524 	    &errorindex, &varbind);
525 	if (errorstatus != 0) {
526 		if (errorindex >= 1 && errorindex <= argc)
527 			hint = argv[errorindex - 1];
528 		snmpc_printerror((enum snmp_error) errorstatus, varbind,
529 		    errorindex, hint);
530 	}
531 
532 	if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
533 		printf("Received report:\n");
534 	for (; varbind != NULL; varbind = varbind->be_next) {
535 		if (!snmpc_print(varbind))
536 			err(1, "Can't print response");
537 	}
538 	ober_free_elements(pdu);
539 	snmp_free_agent(agent);
540 	return 0;
541 }
542 
543 int
544 snmpc_walk(int argc, char *argv[])
545 {
546 	struct ber_oid oid, loid, noid;
547 	struct ber_element *pdu, *varbind, *value;
548 	struct timespec start, finish;
549 	struct snmp_agent *agent;
550 	const char *oids;
551 	int n = 0, prev_cmp;
552 	int errorstatus, errorindex;
553 	int class;
554 	unsigned type;
555 
556 	if (strcmp(snmp_app->name, "bulkwalk") == 0 && version < SNMP_V2C)
557 		errx(1, "Cannot send V2 PDU on V1 session");
558 	if (argc < 1 || argc > 2)
559 		usage();
560 	oids = argc == 1 ? mib : argv[1];
561 
562 	if ((agent = snmpc_connect(argv[0], "161"))== NULL)
563 		err(1, "%s", snmp_app->name);
564 	agent->timeout = timeout;
565 	agent->retries = retries;
566 	if (pledge("stdio", NULL) == -1)
567 		err(1, "pledge");
568 
569 	if (smi_string2oid(oids, &oid) == -1)
570 		errx(1, "%s: Unknown object identifier", oids);
571 	bcopy(&oid, &noid, sizeof(noid));
572 	if (print_time)
573 		clock_gettime(CLOCK_MONOTONIC, &start);
574 
575 	if (walk_include_oid) {
576 		if ((pdu = snmp_get(agent, &oid, 1)) == NULL)
577 			err(1, "%s", snmp_app->name);
578 
579 		(void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type,
580 		    &errorstatus, &errorindex, &varbind);
581 		if (errorstatus != 0)
582 			snmpc_printerror((enum snmp_error) errorstatus, varbind,
583 			    errorindex, oids);
584 
585 		if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
586 			printf("Received report:\n");
587 		if (!snmpc_print(varbind))
588 			err(1, "Can't print response");
589 		ober_free_element(pdu);
590 		if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
591 			return 1;
592 		n++;
593 	}
594 	while (1) {
595 		bcopy(&noid, &loid, sizeof(loid));
596 		if (strcmp(snmp_app->name, "bulkwalk") == 0) {
597 			if ((pdu = snmp_getbulk(agent, &noid, 1,
598 			    non_repeaters, max_repetitions)) == NULL)
599 				err(1, "bulkwalk");
600 		} else {
601 			if ((pdu = snmp_getnext(agent, &noid, 1)) == NULL)
602 				err(1, "walk");
603 		}
604 
605 		(void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type,
606 		    &errorstatus, &errorindex, &varbind);
607 		if (errorstatus != 0) {
608 			snmpc_printerror((enum snmp_error) errorstatus, varbind,
609 			    errorindex, NULL);
610 		}
611 
612 		if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
613 			printf("Received report:\n");
614 		for (; varbind != NULL; varbind = varbind->be_next) {
615 			(void) ober_scanf_elements(varbind, "{oe}", &noid,
616 			    &value);
617 			if (value->be_class == BER_CLASS_CONTEXT &&
618 			    value->be_type == BER_TYPE_EOC)
619 				break;
620 			prev_cmp = ober_oid_cmp(&loid, &noid);
621 			if (walk_check_increase && prev_cmp == -1)
622 				errx(1, "OID not increasing");
623 			if (prev_cmp == 0 || ober_oid_cmp(&oid, &noid) != 2)
624 				break;
625 			if (walk_end.bo_n != 0 &&
626 			    ober_oid_cmp(&walk_end, &noid) != -1)
627 				break;
628 
629 			if (!snmpc_print(varbind))
630 				err(1, "Can't print response");
631 			n++;
632 		}
633 		ober_free_elements(pdu);
634 		if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
635 			return 1;
636 		if (varbind != NULL)
637 			break;
638 	}
639 	if (walk_fallback_oid && n == 0) {
640 		if ((pdu = snmp_get(agent, &oid, 1)) == NULL)
641 			err(1, "%s", snmp_app->name);
642 
643 		(void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type,
644 		    &errorstatus, &errorindex, &varbind);
645 		if (errorstatus != 0)
646 			snmpc_printerror((enum snmp_error) errorstatus, varbind,
647 			    errorindex, oids);
648 
649 		if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
650 			printf("Received report:\n");
651 		if (!snmpc_print(varbind))
652 			err(1, "Can't print response");
653 		ober_free_element(pdu);
654 		if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
655 			return 1;
656 		n++;
657 	}
658 	if (print_time)
659 		clock_gettime(CLOCK_MONOTONIC, &finish);
660 	if (print_summary)
661 		printf("Variables found: %d\n", n);
662 	if (print_time) {
663 		if ((finish.tv_nsec -= start.tv_nsec) < 0) {
664 			finish.tv_sec -= 1;
665 			finish.tv_nsec += 1000000000;
666 		}
667 		finish.tv_sec -= start.tv_sec;
668 		fprintf(stderr, "Total traversal time: %lld.%09ld seconds\n",
669 		    finish.tv_sec, finish.tv_nsec);
670 	}
671 	snmp_free_agent(agent);
672 	return 0;
673 }
674 
675 int
676 snmpc_set(int argc, char *argv[])
677 {
678 	struct snmp_agent *agent;
679 	struct ber_element *pdu, *varbind;
680 	int errorstatus, errorindex;
681 	int class;
682 	unsigned type;
683 	char *hint = NULL;
684 
685 	if (argc < 4)
686 		usage();
687 	if ((agent = snmpc_connect(argv[0], "161")) == NULL)
688 		err(1, "%s", snmp_app->name);
689 	argc--;
690 	argv++;
691 
692 	if (pledge("stdio", NULL) == -1)
693 		err(1, "pledge");
694 
695 	if ((pdu = snmp_set(agent, snmpc_varbindparse(argc, argv))) == NULL)
696 		err(1, "set");
697 
698 	(void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type, &errorstatus,
699 	    &errorindex, &varbind);
700 	if (errorstatus != 0) {
701 		if (errorindex >= 1 && errorindex <= argc / 3)
702 			hint = argv[(errorindex - 1) * 3];
703 		snmpc_printerror((enum snmp_error) errorstatus, varbind,
704 		    errorindex, hint);
705 	}
706 
707 	if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
708 		printf("Received report:\n");
709 	for (; varbind != NULL; varbind = varbind->be_next) {
710 		if (!snmpc_print(varbind))
711 			err(1, "Can't print response");
712 	}
713 	ober_free_elements(pdu);
714 	snmp_free_agent(agent);
715 	return 0;
716 }
717 
718 int
719 snmpc_trap(int argc, char *argv[])
720 {
721 	struct snmp_agent *agent;
722 	struct timespec ts;
723 	struct ber_oid trapoid;
724 	const char *errstr = NULL;
725 	long long lval;
726 
727 	if (version == SNMP_V1)
728 		errx(1, "trap is not supported for snmp v1");
729 
730 	if ((agent = snmpc_connect(argv[0], "162")) == NULL)
731 		err(1, "%s", snmp_app->name);
732 
733 	if (pledge("stdio", NULL) == -1)
734 		err(1, "pledge");
735 
736 	if (argv[1][0] == '\0') {
737 		if (clock_gettime(CLOCK_UPTIME, &ts) == -1)
738 			err(1, "clock_gettime");
739 	} else {
740 		lval = strtonum(argv[1], 0, LLONG_MAX, &errstr);
741 		if (errstr != NULL)
742 			errx(1, "Bad value notation (%s)", argv[1]);
743 		ts.tv_sec = lval / 100;
744 		ts.tv_nsec = (lval % 100) * 10000000;
745 	}
746 	if (smi_string2oid(argv[2], &trapoid) == -1)
747 		errx(1, "Invalid oid: %s\n", argv[2]);
748 
749 	argc -= 3;
750 	argv += 3;
751 
752 	snmp_trap(agent, &ts, &trapoid, snmpc_varbindparse(argc, argv));
753 
754 	return 0;
755 }
756 
757 int
758 snmpc_mibtree(int argc, char *argv[])
759 {
760 	struct oid *oid;
761 	char buf[BUFSIZ];
762 
763 	for (oid = NULL; (oid = smi_foreach(oid, 0)) != NULL;) {
764 		smi_oid2string(&oid->o_id, buf, sizeof(buf), oid_lookup);
765 		printf("%s\n", buf);
766 	}
767 	return 0;
768 }
769 
770 struct snmp_agent *
771 snmpc_connect(char *host, char *port)
772 {
773 	switch (version) {
774 	case SNMP_V1:
775 	case SNMP_V2C:
776 		return snmp_connect_v12(snmpc_parseagent(host, port), version,
777 		    community);
778 	case SNMP_V3:
779 		return snmp_connect_v3(snmpc_parseagent(host, port), v3);
780 	}
781 	return NULL;
782 }
783 
784 int
785 snmpc_print(struct ber_element *elm)
786 {
787 	struct ber_oid oid;
788 	char oids[SNMP_MAX_OID_STRLEN];
789 	char *value;
790 
791 	elm = elm->be_sub;
792 	if (ober_get_oid(elm, &oid) != 0) {
793 		errno = EINVAL;
794 		return 0;
795 	}
796 
797 	elm = elm->be_next;
798 	value = smi_print_element(elm, smi_print_hint, output_string, oid_lookup);
799 	if (value == NULL)
800 		return 0;
801 
802 	if (print_varbind_only)
803 		printf("%s\n", value);
804 	else if (print_equals) {
805 		smi_oid2string(&oid, oids, sizeof(oids), oid_lookup);
806 		printf("%s = %s\n", oids, value);
807 	} else {
808 		smi_oid2string(&oid, oids, sizeof(oids), oid_lookup);
809 		printf("%s %s\n", oids, value);
810 	}
811 	free(value);
812 
813 	return 1;
814 }
815 
816 __dead void
817 snmpc_printerror(enum snmp_error error, struct ber_element *varbind,
818     int index, const char *hint)
819 {
820 	struct ber_oid hoid, vboid;
821 	char oids[SNMP_MAX_OID_STRLEN];
822 	const char *oid = NULL;
823 	int i;
824 
825 	if (index >= 1) {
826 		/* Only print if the index is in the reply */
827 		for (i = 1; varbind != NULL && i <= index;
828 		    varbind = varbind->be_next)
829 			i++;
830 		if (varbind != NULL &&
831 		    ober_get_oid(varbind->be_sub, &vboid) == 0) {
832 			/* If user and reply conform print user input */
833 			if (hint != NULL &&
834 			    smi_string2oid(hint, &hoid) == 0 &&
835 			    ober_oid_cmp(&hoid, &vboid) == 0)
836 				oid = hint;
837 			else
838 				oid = smi_oid2string(&vboid, oids,
839 				    sizeof(oids), oid_lookup);
840 		}
841 	}
842 	if (oid == NULL)
843 		oid = "?";
844 
845 	switch (error) {
846 	case SNMP_ERROR_NONE:
847 		errx(1, "No error, how did I get here?");
848 	case SNMP_ERROR_TOOBIG:
849 		errx(1, "Can't parse oid %s: Response too big", oid);
850 	case SNMP_ERROR_NOSUCHNAME:
851 		errx(1, "Can't parse oid %s: No such object", oid);
852 	case SNMP_ERROR_BADVALUE:
853 		errx(1, "Can't parse oid %s: Bad value", oid);
854 	case SNMP_ERROR_READONLY:
855 		errx(1, "Can't parse oid %s: Read only", oid);
856 	case SNMP_ERROR_GENERR:
857 		errx(1, "Can't parse oid %s: Generic error", oid);
858 	case SNMP_ERROR_NOACCESS:
859 		errx(1, "Can't parse oid %s: Access denied", oid);
860 	case SNMP_ERROR_WRONGTYPE:
861 		errx(1, "Can't parse oid %s: Wrong type", oid);
862 	case SNMP_ERROR_WRONGLENGTH:
863 		errx(1, "Can't parse oid %s: Wrong length", oid);
864 	case SNMP_ERROR_WRONGENC:
865 		errx(1, "Can't parse oid %s: Wrong encoding", oid);
866 	case SNMP_ERROR_WRONGVALUE:
867 		errx(1, "Can't parse oid %s: Wrong value", oid);
868 	case SNMP_ERROR_NOCREATION:
869 		errx(1, "Can't parse oid %s: Can't be created", oid);
870 	case SNMP_ERROR_INCONVALUE:
871 		errx(1, "Can't parse oid %s: Inconsistent value", oid);
872 	case SNMP_ERROR_RESUNAVAIL:
873 		errx(1, "Can't parse oid %s: Resource unavailable", oid);
874 	case SNMP_ERROR_COMMITFAILED:
875 		errx(1, "Can't parse oid %s: Commit failed", oid);
876 	case SNMP_ERROR_UNDOFAILED:
877 		errx(1, "Can't parse oid %s: Undo faild", oid);
878 	case SNMP_ERROR_AUTHERROR:
879 		errx(1, "Can't parse oid %s: Authorization error", oid);
880 	case SNMP_ERROR_NOTWRITABLE:
881 		errx(1, "Can't parse oid %s: Not writable", oid);
882 	case SNMP_ERROR_INCONNAME:
883 		errx(1, "Can't parse oid %s: Inconsistent name", oid);
884 	}
885 	errx(1, "Can't parse oid %s: Unknown error (%d)", oid, error);
886 }
887 
888 int
889 snmpc_parseagent(char *agent, char *defaultport)
890 {
891 	struct addrinfo hints, *ai, *ai0 = NULL;
892 	struct sockaddr_un saddr;
893 	char *agentdup, *specifier, *hostname, *port = NULL;
894 	int error;
895 	int s;
896 
897 	if ((agentdup = specifier = strdup(agent)) == NULL)
898 		err(1, NULL);
899 
900 	bzero(&hints, sizeof(hints));
901 	if ((hostname = strchr(specifier, ':')) != NULL) {
902 		*hostname++ = '\0';
903 		if (strcasecmp(specifier, "udp") == 0) {
904 			hints.ai_family = AF_INET;
905 			hints.ai_socktype = SOCK_DGRAM;
906 		} else if (strcasecmp(specifier, "tcp") == 0) {
907 			hints.ai_family = AF_INET;
908 			hints.ai_socktype = SOCK_STREAM;
909 		} else if (strcasecmp(specifier, "udp6") == 0 ||
910 		    strcasecmp(specifier, "udpv6") == 0 ||
911 		    strcasecmp(specifier, "udpipv6") == 0) {
912 			hints.ai_family = AF_INET6;
913 			hints.ai_socktype = SOCK_DGRAM;
914 		} else if (strcasecmp(specifier, "tcp6") == 0 ||
915 		    strcasecmp(specifier, "tcpv6") == 0 ||
916 		    strcasecmp(specifier, "tcpipv6") == 0) {
917 			hints.ai_family = AF_INET6;
918 			hints.ai_socktype = SOCK_STREAM;
919 		} else if (strcasecmp(specifier, "unix") == 0) {
920 			hints.ai_family = AF_UNIX;
921 			hints.ai_socktype = SOCK_STREAM;
922 			hints.ai_addr = (struct sockaddr *)&saddr;
923 			hints.ai_addrlen = sizeof(saddr);
924 			saddr.sun_len = sizeof(saddr);
925 			saddr.sun_family = AF_UNIX;
926 			if (strlcpy(saddr.sun_path, hostname,
927 			    sizeof(saddr.sun_path)) > sizeof(saddr.sun_path))
928 				errx(1, "Hostname path too long");
929 			ai = &hints;
930 		} else {
931 			port = hostname;
932 			hostname = specifier;
933 			specifier = NULL;
934 			hints.ai_family = AF_INET;
935 			hints.ai_socktype = SOCK_DGRAM;
936 		}
937 		if (port == NULL) {
938 			if (hints.ai_family == AF_INET) {
939 				if ((port = strchr(hostname, ':')) != NULL)
940 					*port++ = '\0';
941 			} else if (hints.ai_family == AF_INET6) {
942 				if (hostname[0] == '[') {
943 					hostname++;
944 					if ((port = strchr(hostname, ']')) == NULL)
945 						errx(1, "invalid agent");
946 					*port++ = '\0';
947 					if (port[0] == ':')
948 						*port++ = '\0';
949 					else
950 						port = NULL;
951 				} else {
952 					if ((port = strrchr(hostname, ':')) == NULL)
953 						errx(1, "invalid agent");
954 					*port++ = '\0';
955 				}
956 			}
957 		}
958 	} else {
959 		hostname = specifier;
960 		hints.ai_family = AF_INET;
961 		hints.ai_socktype = SOCK_DGRAM;
962 	}
963 
964 	if (hints.ai_family != AF_UNIX) {
965 		if (port == NULL)
966 			port = defaultport;
967 		error = getaddrinfo(hostname, port, &hints, &ai0);
968 		if (error)
969 			errx(1, "%s", gai_strerror(error));
970 		s = -1;
971 		for (ai = ai0; ai != NULL; ai = ai->ai_next) {
972 			if ((s = socket(ai->ai_family, ai->ai_socktype,
973 			    ai->ai_protocol)) == -1)
974 				continue;
975 			break;
976 		}
977 	} else
978 		s = socket(hints.ai_family, hints.ai_socktype,
979 		    hints.ai_protocol);
980 	if (s == -1)
981 		err(1, "socket");
982 
983 	if (connect(s, (struct sockaddr *)ai->ai_addr, ai->ai_addrlen) == -1)
984 		err(1, "Can't connect to %s", agent);
985 
986 	if (ai0 != NULL)
987 		freeaddrinfo(ai0);
988 	free(agentdup);
989 	return s;
990 }
991 
992 char *
993 snmpc_hex2bin(char *hexstr, size_t *binlen)
994 {
995 	char *decstr;
996 
997 	if (hexstr[0] == '0' && hexstr[1] == 'x')
998 		hexstr += 2;
999 	while (hexstr[0] == ' ' || hexstr[0] == '\t')
1000 		hexstr++;
1001 
1002 	if ((decstr = malloc((strlen(hexstr) / 2) + 1)) == NULL)
1003 		return NULL;
1004 
1005 	for (*binlen = 0; hexstr[0] != '\0'; (*binlen)++) {
1006 		hexstr[0] = toupper(hexstr[0]);
1007 		hexstr[1] = toupper(hexstr[1]);
1008 		if (hexstr[0] >= '0' && hexstr[0] <= '9')
1009 			decstr[*binlen] = (hexstr[0] - '0') << 4;
1010 		else if (hexstr[0] >= 'A' && hexstr[0] <= 'F')
1011 			decstr[*binlen] = ((hexstr[0] - 'A') + 10) << 4;
1012 		else
1013 			goto fail;
1014 		if (hexstr[1] >= '0' && hexstr[1] <= '9')
1015 			decstr[*binlen] |= (hexstr[1] - '0');
1016 		else if (hexstr[1] >= 'A' && hexstr[1] <= 'F')
1017 			decstr[*binlen] |= (hexstr[1] - 'A') + 10;
1018 		else
1019 			goto fail;
1020 
1021 		hexstr += 2;
1022 		while (hexstr[0] == ' ' || hexstr[0] == '\t')
1023 			hexstr++;
1024 	}
1025 
1026 	return decstr;
1027 fail:
1028 	errno = EINVAL;
1029 	free(decstr);
1030 	return NULL;
1031 }
1032 
1033 struct ber_element *
1034 snmpc_varbindparse(int argc, char *argv[])
1035 {
1036 	struct ber_oid oid, oidval;
1037 	struct in_addr addr4;
1038 	char *addr = (char *)&addr4;
1039 	char *str = NULL, *tmpstr, *endstr;
1040 	const char *errstr = NULL;
1041 	struct ber_element *varbind = NULL, *vblist = NULL;
1042 	int i, ret;
1043 	size_t strl, byte;
1044 	long long lval;
1045 
1046 	if (argc % 3 != 0)
1047 		usage();
1048 	for (i = 0; i < argc; i += 3) {
1049 		if (smi_string2oid(argv[i], &oid) == -1)
1050 			errx(1, "Invalid oid: %s\n", argv[i]);
1051 		switch (argv[i + 1][0]) {
1052 		case 'a':
1053 			ret = inet_pton(AF_INET, argv[i + 2], &addr4);
1054 			if (ret == -1)
1055 				err(1, "inet_pton");
1056 			if (ret == 0)
1057 				errx(1, "%s: Bad value notation (%s)", argv[i],
1058 				    argv[i + 2]);
1059 			if ((varbind = ober_printf_elements(varbind, "{Oxt}",
1060 			    &oid, addr, sizeof(addr4), BER_CLASS_APPLICATION,
1061 			    SNMP_T_IPADDR)) == NULL)
1062 				err(1, "ober_printf_elements");
1063 			break;
1064 		case 'b':
1065 			tmpstr = argv[i + 2];
1066 			strl = 0;
1067 			do {
1068 				lval = strtoll(tmpstr, &endstr, 10);
1069 				if (endstr[0] != ' ' && endstr[0] != '\t' &&
1070 				    endstr[0] != ',' && endstr[0] != '\0')
1071 					errx(1, "%s: Bad value notation (%s)",
1072 					    argv[i], argv[i + 2]);
1073 				if (tmpstr == endstr) {
1074 					tmpstr++;
1075 					continue;
1076 				}
1077 				if (lval < 0)
1078 					errx(1, "%s: Bad value notation (%s)",
1079 					    argv[i], argv[i + 2]);
1080 				byte = lval / 8;
1081 				if (byte >= strl) {
1082 					if ((str = recallocarray(str, strl,
1083 					    byte + 1, 1)) == NULL)
1084 						err(1, "malloc");
1085 					strl = byte + 1;
1086 				}
1087 				str[byte] |= 0x80 >> (lval % 8);
1088 				tmpstr = endstr + 1;
1089 			} while (endstr[0] != '\0');
1090 			/*
1091 			 * RFC3416 Section 2.5
1092 			 * A BITS value is encoded as an OCTET STRING
1093 			 */
1094 			goto pastestring;
1095 		case 'c':
1096 			lval = strtonum(argv[i + 2], INT32_MIN, INT32_MAX,
1097 			    &errstr);
1098 			if (errstr != NULL)
1099 				errx(1, "%s: Bad value notation (%s)", argv[i],
1100 				    argv[i + 2]);
1101 			if ((varbind = ober_printf_elements(varbind, "{Oit}",
1102 			    &oid, lval, BER_CLASS_APPLICATION,
1103 			    SNMP_T_COUNTER32)) == NULL)
1104 				err(1, "ober_printf_elements");
1105 			break;
1106 		case 'd':
1107 			/* String always shrinks */
1108 			if ((str = malloc(strlen(argv[i + 2]))) == NULL)
1109 				err(1, "malloc");
1110 			tmpstr = argv[i + 2];
1111 			strl = 0;
1112 			do {
1113 				lval = strtoll(tmpstr, &endstr, 10);
1114 				if (endstr[0] != ' ' && endstr[0] != '\t' &&
1115 				    endstr[0] != '\0')
1116 					errx(1, "%s: Bad value notation (%s)",
1117 					    argv[i], argv[i + 2]);
1118 				if (tmpstr == endstr) {
1119 					tmpstr++;
1120 					continue;
1121 				}
1122 				if (lval < 0 || lval > 0xff)
1123 					errx(1, "%s: Bad value notation (%s)",
1124 					    argv[i], argv[i + 2]);
1125 				str[strl++] = (unsigned char) lval;
1126 				tmpstr = endstr + 1;
1127 			} while (endstr[0] != '\0');
1128 			goto pastestring;
1129 		case 'u':
1130 		case 'i':
1131 			lval = strtonum(argv[i + 2], LLONG_MIN, LLONG_MAX,
1132 			    &errstr);
1133 			if (errstr != NULL)
1134 				errx(1, "%s: Bad value notation (%s)", argv[i],
1135 				    argv[i + 2]);
1136 			if ((varbind = ober_printf_elements(varbind, "{Oi}",
1137 			    &oid, lval)) == NULL)
1138 				err(1, "ober_printf_elements");
1139 			break;
1140 		case 'n':
1141 			if ((varbind = ober_printf_elements(varbind, "{O0}",
1142 			    &oid)) == NULL)
1143 				err(1, "ober_printf_elements");
1144 			break;
1145 		case 'o':
1146 			if (smi_string2oid(argv[i + 2], &oidval) == -1)
1147 				errx(1, "%s: Unknown Object Identifier (Sub-id "
1148 				    "not found: (top) -> %s)", argv[i],
1149 				    argv[i + 2]);
1150 			if ((varbind = ober_printf_elements(varbind, "{OO}",
1151 			    &oid, &oidval)) == NULL)
1152 				err(1, "ober_printf_elements");
1153 			break;
1154 		case 's':
1155 			if ((str = strdup(argv[i + 2])) == NULL)
1156 				err(1, NULL);
1157 			strl = strlen(argv[i + 2]);
1158 pastestring:
1159 			if ((varbind = ober_printf_elements(varbind, "{Ox}",
1160 			    &oid, str, strl)) == NULL)
1161 				err(1, "ober_printf_elements");
1162 			free(str);
1163 			break;
1164 		case 't':
1165 			lval = strtonum(argv[i + 2], LLONG_MIN, LLONG_MAX,
1166 			    &errstr);
1167 			if (errstr != NULL)
1168 				errx(1, "%s: Bad value notation (%s)", argv[i],
1169 				    argv[i + 2]);
1170 			if ((varbind = ober_printf_elements(varbind, "{Oit}",
1171 			    &oid, lval, BER_CLASS_APPLICATION,
1172 			    SNMP_T_TIMETICKS)) == NULL)
1173 				err(1, "ober_printf_elements");
1174 			break;
1175 		case 'x':
1176 			/* String always shrinks */
1177 			if ((str = malloc(strlen(argv[i + 2]))) == NULL)
1178 				err(1, "malloc");
1179 			tmpstr = argv[i + 2];
1180 			strl = 0;
1181 			do {
1182 				lval = strtoll(tmpstr, &endstr, 16);
1183 				if (endstr[0] != ' ' && endstr[0] != '\t' &&
1184 				    endstr[0] != '\0')
1185 					errx(1, "%s: Bad value notation (%s)",
1186 					    argv[i], argv[i + 2]);
1187 				if (tmpstr == endstr) {
1188 					tmpstr++;
1189 					continue;
1190 				}
1191 				if (lval < 0 || lval > 0xff)
1192 					errx(1, "%s: Bad value notation (%s)",
1193 					    argv[i], argv[i + 2]);
1194 				str[strl++] = (unsigned char) lval;
1195 				tmpstr = endstr + 1;
1196 			} while (endstr[0] != '\0');
1197 			goto pastestring;
1198 		default:
1199 			usage();
1200 		}
1201 		if (vblist == NULL)
1202 			vblist = varbind;
1203 	}
1204 
1205 	return vblist;
1206 }
1207 
1208 __dead void
1209 usage(void)
1210 {
1211 	size_t i;
1212 
1213 	if (snmp_app != NULL) {
1214 		fprintf(stderr, "usage: snmp %s%s%s\n",
1215 		    snmp_app->name,
1216 		    snmp_app->usecommonopt ?
1217 		    " [-A authpass] [-a digest] [-c community] [-e secengineid]\n"
1218 		    "            [-E ctxengineid] [-K localpriv] [-k localauth] [-l seclevel]\n"
1219 		    "            [-n ctxname] [-O afnqvxSQ] [-r retries] [-t timeout] [-u user]\n"
1220 		    "            [-v version] [-X privpass] [-x cipher] [-Z boots,time]\n"
1221 		    "            " : "",
1222 		    snmp_app->usage == NULL ? "" : snmp_app->usage);
1223 		exit(1);
1224 	}
1225 	for (i = 0; i < (sizeof(snmp_apps)/sizeof(*snmp_apps)); i++) {
1226 		if (i == 0)
1227 			fprintf(stderr, "usage: ");
1228 		else
1229 			fprintf(stderr, "       ");
1230 		fprintf(stderr, "snmp %s%s %s\n",
1231 		    snmp_apps[i].name,
1232 		    snmp_apps[i].usecommonopt ?
1233 		    " [options]" : "",
1234 		    snmp_apps[i].usage ? snmp_apps[i].usage : "");
1235 	}
1236 	exit(1);
1237 }
1238