xref: /openbsd/usr.bin/snmp/snmpc.c (revision 3cab2bb3)
1 /*	$OpenBSD: snmpc.c,v 1.28 2020/08/03 14:45:54 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 <locale.h>
33 #include <netdb.h>
34 #include <poll.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdint.h>
38 #include <string.h>
39 #include <time.h>
40 #include <unistd.h>
41 #include <util.h>
42 
43 #include "smi.h"
44 #include "snmp.h"
45 #include "usm.h"
46 
47 #define GETOPT_COMMON		"A:a:c:E:e:K:k:l:n:O:r:t:u:v:X:x:Z:"
48 
49 int snmpc_get(int, char *[]);
50 int snmpc_walk(int, char *[]);
51 int snmpc_set(int, char *[]);
52 int snmpc_trap(int, char *[]);
53 int snmpc_df(int, char *[]);
54 int snmpc_mibtree(int, char *[]);
55 struct snmp_agent *snmpc_connect(char *, char *);
56 int snmpc_parseagent(char *, char *);
57 int snmpc_print(struct ber_element *);
58 __dead void snmpc_printerror(enum snmp_error, struct ber_element *, int,
59     const char *);
60 char *snmpc_hex2bin(char *, size_t *);
61 struct ber_element *snmpc_varbindparse(int, char *[]);
62 void usage(void);
63 
64 struct snmp_app {
65 	const char *name;
66 	const int usecommonopt;
67 	const char *optstring;
68 	const char *usage;
69 	int (*exec)(int, char *[]);
70 };
71 
72 struct snmp_app snmp_apps[] = {
73 	{ "get", 1, NULL, "agent oid ...", snmpc_get },
74 	{ "getnext", 1, NULL, "agent oid ...", snmpc_get },
75 	{ "walk", 1, "C:", "[-C cIipt] [-C E endoid] [-C s skipoid] agent [oid]", snmpc_walk },
76 	{ "bulkget", 1, "C:", "[-C n<nonrep>r<maxrep>] agent oid ...", snmpc_get },
77 	{ "bulkwalk", 1, "C:", "[-C cipn<nonrep>r<maxrep>] [-C s skipoid] agent [oid]", snmpc_walk },
78 	{ "set", 1, NULL, "agent oid type value [oid type value] ...", snmpc_set },
79 	{ "trap", 1, NULL, "agent uptime oid [oid type value] ...", snmpc_trap },
80 	{ "df", 1, "C:", "[-Ch] [-Cr<maxrep>] agent", snmpc_df },
81 	{ "mibtree", 0, "O:", "[-O fnS]", snmpc_mibtree }
82 };
83 struct snmp_app *snmp_app = NULL;
84 
85 char *community = "public";
86 struct snmp_v3 *v3;
87 char *mib = "mib_2";
88 int retries = 5;
89 int timeout = 1;
90 enum snmp_version version = SNMP_V2C;
91 int print_equals = 1;
92 int print_varbind_only = 0;
93 int print_summary = 0;
94 int print_time = 0;
95 int print_human = 0;
96 int walk_check_increase = 1;
97 int walk_fallback_oid = 1;
98 int walk_include_oid = 0;
99 int smi_print_hint = 1;
100 int non_repeaters = 0;
101 int max_repetitions = 10;
102 struct ber_oid walk_end = {{0}, 0};
103 struct ber_oid *walk_skip = NULL;
104 size_t walk_skip_len = 0;
105 enum smi_oid_lookup oid_lookup = smi_oidl_short;
106 enum smi_output_string output_string = smi_os_default;
107 int utf8 = 0;
108 
109 int
110 main(int argc, char *argv[])
111 {
112 	const EVP_MD *md = NULL;
113 	const EVP_CIPHER *cipher = NULL;
114 	struct snmp_sec *sec;
115 	char *user = NULL;
116 	enum usm_key_level authkeylevel = USM_KEY_UNSET;
117 	char *authkey = NULL;
118 	size_t authkeylen = 0;
119 	enum usm_key_level privkeylevel = USM_KEY_UNSET;
120 	char *privkey = NULL;
121 	size_t privkeylen = 0;
122 	int seclevel = SNMP_MSGFLAG_REPORT;
123 	char *ctxname = NULL;
124 	char *ctxengineid = NULL, *secengineid = NULL;
125 	size_t ctxengineidlen, secengineidlen;
126 	int zflag = 0;
127 	long long boots = 0, time = 0;
128 	char optstr[BUFSIZ];
129 	const char *errstr;
130 	char *strtolp;
131 	int ch;
132 	size_t i;
133 
134 	/*
135 	 * Determine if output can handle UTF-8 based on locale.
136 	 */
137 	setlocale(LC_CTYPE, "");
138 	utf8 = MB_CUR_MAX > 1;
139 	/*
140 	 * SMIv2 allows for UTF-8 text at some locations.
141 	 * Set it explicitly so we can handle it on the input side.
142 	 */
143 	if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL)
144 		errx(1, "setlocale(LC_CTYPE, \"en_US.UTF-8\") failed");
145 
146 	if (pledge("stdio inet dns unix", NULL) == -1)
147 		err(1, "pledge");
148 
149 	if (argc <= 1)
150 		usage();
151 
152 	optstr[0] = '\0';
153 	for (i = 0; i < sizeof(snmp_apps)/sizeof(*snmp_apps); i++) {
154 		if (strcmp(snmp_apps[i].name, argv[1]) == 0) {
155 			snmp_app = &snmp_apps[i];
156 			if (snmp_app->optstring != NULL) {
157 				if (strlcpy(optstr, snmp_app->optstring,
158 				    sizeof(optstr)) > sizeof(optstr))
159 					errx(1, "strlcat");
160 			}
161 			break;
162 		}
163 	}
164 	if (snmp_app == NULL)
165 		usage();
166 
167 	if (snmp_app->usecommonopt) {
168 		if (strlcat(optstr, GETOPT_COMMON, sizeof(optstr)) >
169 		    sizeof(optstr))
170 			errx(1, "strlcpy");
171 	}
172 
173 	argc--;
174 	argv++;
175 
176 	smi_init();
177 
178 	while ((ch = getopt(argc, argv, optstr)) != -1) {
179 		switch (ch) {
180 		case 'A':
181 			authkey = optarg;
182 			authkeylen = strlen(authkey);
183 			authkeylevel = USM_KEY_PASSWORD;
184 			break;
185 		case 'a':
186 			if (strcasecmp(optarg, "MD5") == 0)
187 				md = EVP_md5();
188 			else if (strcasecmp(optarg, "SHA") == 0)
189 				md = EVP_sha1();
190 			else if (strcasecmp(optarg, "SHA-224") == 0)
191 				md = EVP_sha224();
192 			else if (strcasecmp(optarg, "SHA-256") == 0)
193 				md = EVP_sha256();
194 			else if (strcasecmp(optarg, "SHA-384") == 0)
195 				md = EVP_sha384();
196 			else if (strcasecmp(optarg, "SHA-512") == 0)
197 				md = EVP_sha512();
198 			else
199 				errx(1, "Invalid authentication protocol "
200 				    "specified after -a flag: %s", optarg);
201 			break;
202 		case 'c':
203 			community = optarg;
204 			break;
205 		case 'E':
206 			ctxengineid = snmpc_hex2bin(optarg,
207 			    &ctxengineidlen);
208 			if (ctxengineid == NULL) {
209 				if (errno == EINVAL)
210 					errx(1, "Bad engine ID value "
211 					    "after -3E flag.");
212 				err(1, "-3E");
213 			}
214 			break;
215 		case 'e':
216 			secengineid = snmpc_hex2bin(optarg,
217 			    &secengineidlen);
218 			if (secengineid == NULL) {
219 				if (errno == EINVAL)
220 					errx(1, "Bad engine ID value "
221 					    "after -3e flag.");
222 				err(1, "-3e");
223 			}
224 			break;
225 		case 'K':
226 			privkey = snmpc_hex2bin(optarg, &privkeylen);
227 			if (privkey == NULL) {
228 				if (errno == EINVAL)
229 					errx(1, "Bad key value after "
230 					    "-3K flag.");
231 				errx(1, "-3K");
232 			}
233 			privkeylevel = USM_KEY_LOCALIZED;
234 				break;
235 		case 'k':
236 			authkey = snmpc_hex2bin(optarg, &authkeylen);
237 			if (authkey == NULL) {
238 				if (errno == EINVAL)
239 					errx(1, "Bad key value after -k flag.");
240 				err(1, "-k");
241 			}
242 			authkeylevel = USM_KEY_LOCALIZED;
243 			break;
244 		case 'l':
245 			if (strcasecmp(optarg, "noAuthNoPriv") == 0)
246 				seclevel = SNMP_MSGFLAG_REPORT;
247 			else if (strcasecmp(optarg, "authNoPriv") == 0)
248 				seclevel = SNMP_MSGFLAG_AUTH |
249 				    SNMP_MSGFLAG_REPORT;
250 			else if (strcasecmp(optarg, "authPriv") == 0)
251 				seclevel = SNMP_MSGFLAG_AUTH |
252 				    SNMP_MSGFLAG_PRIV | SNMP_MSGFLAG_REPORT;
253 			else
254 				errx(1, "Invalid security level specified "
255 				    "after -l flag: %s", optarg);
256 			break;
257 		case 'n':
258 			ctxname = optarg;
259 			break;
260 		case 'r':
261 			if ((retries = strtonum(optarg, 0, INT_MAX,
262 			    &errstr)) == 0) {
263 				if (errstr != NULL)
264 					errx(1, "-r: %s argument", errstr);
265 			}
266 			break;
267 		case 't':
268 			if ((timeout = strtonum(optarg, 1, INT_MAX,
269 			    &errstr)) == 0) {
270 				if (errstr != NULL)
271 					errx(1, "-t: %s argument", errstr);
272 			}
273 			break;
274 		case 'u':
275 			user = optarg;
276 			break;
277 		case 'v':
278 			if (strcmp(optarg, "1") == 0)
279 				version = SNMP_V1;
280 			else if (strcmp(optarg, "2c") == 0)
281 				version = SNMP_V2C;
282 			else if (strcmp(optarg, "3") == 0)
283 				version = SNMP_V3;
284 			else
285 				errc(1, EINVAL, "-v");
286 			break;
287 		case 'C':
288 			for (i = 0; i < strlen(optarg); i++) {
289 				switch (optarg[i]) {
290 				case 'c':
291 					if (strcmp(snmp_app->name, "walk") &&
292 					    strcmp(snmp_app->name, "bulkwalk"))
293 						usage();
294 					walk_check_increase = 0;
295 					break;
296 				case 'h':
297 					if (strcmp(snmp_app->name, "df"))
298 						usage();
299 					print_human = 1;
300 					break;
301 				case 'i':
302 					if (strcmp(snmp_app->name, "walk") &&
303 					    strcmp(snmp_app->name, "bulkwalk"))
304 						usage();
305 					walk_include_oid = 1;
306 					break;
307 				case 'n':
308 					if (strcmp(snmp_app->name, "bulkget") &&
309 					    strcmp(snmp_app->name, "bulkwalk"))
310 						usage();
311 					errno = 0;
312 					non_repeaters = strtol(&optarg[i + 1],
313 					    &strtolp, 10);
314 					if (non_repeaters < 0 ||
315 					    errno == ERANGE) {
316 						if (non_repeaters < 0)
317 							errx(1, "%s%s",
318 							    "-Cn: too small ",
319 							    "argument");
320 						else
321 							errx(1, "%s%s",
322 							    "-Cn: too large",
323 							    "argument");
324 					} else if (&optarg[i + 1] == strtolp)
325 						errx(1, "-Cn invalid argument");
326 					i = strtolp - optarg - 1;
327 					break;
328 				case 'p':
329 					if (strcmp(snmp_app->name, "walk") &&
330 					    strcmp(snmp_app->name, "bulkwalk"))
331 						usage();
332 					print_summary = 1;
333 					break;
334 				case 'r':
335 					if (strcmp(snmp_app->name, "bulkget") &&
336 					    strcmp(snmp_app->name, "bulkwalk") &&
337 					    strcmp(snmp_app->name, "df"))
338 						usage();
339 					errno = 0;
340 					max_repetitions = strtol(&optarg[i + 1],
341 					    &strtolp, 10);
342 					if (max_repetitions < 0 ||
343 					    errno == ERANGE) {
344 						if (max_repetitions < 0)
345 							errx(1, "%s%s",
346 							    "-Cr: too small ",
347 							    "argument");
348 						else
349 							errx(1, "%s%s",
350 							    "-Cr: too large",
351 							    "argument");
352 					} else if (&optarg[i + 1] == strtolp)
353 						errx(1, "-Cr invalid argument");
354 					i = strtolp - optarg - 1;
355 					break;
356 				case 's':
357 					if (strcmp(snmp_app->name, "walk") &&
358 					    strcmp(snmp_app->name, "bulkwalk"))
359 						usage();
360 					if ((walk_skip = recallocarray(
361 					    walk_skip, walk_skip_len,
362 					    walk_skip_len + 1,
363 					    sizeof(*walk_skip))) == NULL)
364 						errx(1, "malloc");
365 					if (smi_string2oid(argv[optind],
366 					    &(walk_skip[walk_skip_len])) != 0)
367 						errx(1, "%s: %s",
368 						    "Unknown Object Identifier",
369 						    argv[optind]);
370 					walk_skip_len++;
371 					optind++;
372 					break;
373 				case 't':
374 					if (strcmp(snmp_app->name, "walk"))
375 						usage();
376 					print_time = 1;
377 					break;
378 				case 'E':
379 					if (strcmp(snmp_app->name, "walk"))
380 						usage();
381 					if (smi_string2oid(argv[optind],
382 					    &walk_end) != 0)
383 						errx(1, "%s: %s",
384 						    "Unknown Object Identifier",
385 						    argv[optind]);
386 					optind++;
387 					continue;
388 				case 'I':
389 					if (strcmp(snmp_app->name, "walk"))
390 						usage();
391 					walk_fallback_oid = 0;
392 					break;
393 				default:
394 					usage();
395 				}
396 				if (optarg[i] == 'E')
397 					break;
398 			}
399 			break;
400 		case 'O':
401 			for (i = 0; i < strlen(optarg); i++) {
402 				if (strcmp(snmp_app->name, "mibtree") == 0 &&
403 				    optarg[i] != 'f' && optarg[i] != 'n' &&
404 				    optarg[i] != 'S')
405 						usage();
406 				switch (optarg[i]) {
407 				case 'a':
408 					output_string = smi_os_ascii;
409 					break;
410 				case 'f':
411 					oid_lookup = smi_oidl_full;
412 					break;
413 				case 'n':
414 					oid_lookup = smi_oidl_numeric;
415 					break;
416 				case 'q':
417 					print_equals = 0;
418 					smi_print_hint = 0;
419 					break;
420 				case 'v':
421 					print_varbind_only = 1;
422 					break;
423 				case 'x':
424 					output_string = smi_os_hex;
425 					break;
426 				case 'S':
427 					oid_lookup = smi_oidl_short;
428 					break;
429 				case 'Q':
430 					smi_print_hint = 0;
431 					break;
432 				default:
433 					usage();
434 				}
435 			}
436 			break;
437 		case 'X':
438 			privkey = optarg;
439 			privkeylen = strlen(privkey);
440 			privkeylevel = USM_KEY_PASSWORD;
441 			break;
442 		case 'x':
443 			if (strcasecmp(optarg, "DES") == 0)
444 				cipher = EVP_des_cbc();
445 			else if (strcasecmp(optarg, "AES") == 0)
446 				cipher = EVP_aes_128_cfb128();
447 			else
448 				errx(1, "Invalid privacy protocol "
449 				    "specified after -3x flag: %s",
450 				    optarg);
451 			break;
452 		case 'Z':
453 			boots = strtoll(optarg, &strtolp, 10);
454 			if (boots < 0 || strtolp == optarg || strtolp[0] != ',')
455 				usage();
456 			strtolp++;
457 			while (strtolp[0] == ' ' && strtolp[0] == '\t')
458 				strtolp++;
459 			time = strtoll(strtolp, &strtolp, 10);
460 			if (boots < 0 || strtolp == optarg)
461 				usage();
462 			zflag = 1;
463 			break;
464 		default:
465 			usage();
466 		}
467 	}
468 	argc -= optind;
469 	argv += optind;
470 
471 	if (version == SNMP_V3) {
472 		/* Setup USM */
473 		if (user == NULL || user[0] == '\0')
474 			errx(1, "No securityName specified");
475 		if ((sec = usm_init(user, strlen(user))) == NULL)
476 			err(1, "usm_init");
477 		if (seclevel & SNMP_MSGFLAG_AUTH) {
478 			if (md == NULL)
479 				md = EVP_md5();
480 			if (authkey == NULL)
481 				errx(1, "No authKey or authPassword specified");
482 			if (usm_setauth(sec, md, authkey, authkeylen,
483 			    authkeylevel) == -1)
484 				err(1, "Can't set authkey");
485 		}
486 		if (seclevel & SNMP_MSGFLAG_PRIV) {
487 			if (cipher == NULL)
488 				cipher = EVP_des_cbc();
489 			if (privkey == NULL)
490 				errx(1, "No privKey or privPassword specified");
491 			if (usm_setpriv(sec, cipher, privkey, privkeylen,
492 			    privkeylevel) == -1)
493 				err(1, "Can't set authkey");
494 		}
495 		if (secengineid != NULL) {
496 			if (usm_setengineid(sec, secengineid,
497 			    secengineidlen) == -1)
498 				err(1, "Can't set secengineid");
499 		}
500 		if (zflag)
501 			if (usm_setbootstime(sec, boots, time) == -1)
502 				err(1, "Can't set boots/time");
503 		v3 = snmp_v3_init(seclevel, ctxname, ctxname == NULL ? 0 :
504 		    strlen(ctxname), sec);
505 		if (v3 == NULL)
506 			err(1, "snmp_v3_init");
507 		if (ctxengineid != NULL) {
508 			if (snmp_v3_setengineid(v3, ctxengineid,
509 			    ctxengineidlen) == -1)
510 				err(1, "Can't set ctxengineid");
511 		}
512 	}
513 
514 
515 	return snmp_app->exec(argc, argv);
516 }
517 
518 int
519 snmpc_get(int argc, char *argv[])
520 {
521 	struct ber_oid *oid;
522 	struct ber_element *pdu, *varbind;
523 	struct snmp_agent *agent;
524 	int errorstatus, errorindex;
525 	int i;
526 	int class;
527 	unsigned type;
528 	char *hint = NULL;
529 
530 	if (argc < 2)
531 		usage();
532 
533 	if ((agent = snmpc_connect(argv[0], "161")) == NULL)
534 		err(1, "%s", snmp_app->name);
535 	agent->timeout = timeout;
536 	agent->retries = retries;
537 
538 	if (pledge("stdio", NULL) == -1)
539 		err(1, "pledge");
540 	argc--;
541 	argv++;
542 
543 	oid = reallocarray(NULL, argc, sizeof(*oid));
544 	if (oid == NULL)
545 		err(1, "malloc");
546 	for (i = 0; i < argc; i++) {
547 		if (smi_string2oid(argv[i], &oid[i]) == -1)
548 			errx(1, "%s: Unknown object identifier", argv[i]);
549 	}
550 	if (strcmp(snmp_app->name, "getnext") == 0) {
551 		if ((pdu = snmp_getnext(agent, oid, argc)) == NULL)
552 			err(1, "getnext");
553 	} else if (strcmp(snmp_app->name, "bulkget") == 0) {
554 		if (version < SNMP_V2C)
555 			errx(1, "Cannot send V2 PDU on V1 session");
556 		if (non_repeaters > argc)
557 			errx(1, "need more objects than -Cn<num>");
558 		if ((pdu = snmp_getbulk(agent, oid, argc, non_repeaters,
559 		    max_repetitions)) == NULL)
560 			err(1, "bulkget");
561 	} else {
562 		if ((pdu = snmp_get(agent, oid, argc)) == NULL)
563 			err(1, "get");
564 	}
565 
566 	(void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type, &errorstatus,
567 	    &errorindex, &varbind);
568 	if (errorstatus != 0) {
569 		if (errorindex >= 1 && errorindex <= argc)
570 			hint = argv[errorindex - 1];
571 		snmpc_printerror((enum snmp_error) errorstatus, varbind,
572 		    errorindex, hint);
573 	}
574 
575 	if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
576 		printf("Received report:\n");
577 	for (; varbind != NULL; varbind = varbind->be_next) {
578 		if (!snmpc_print(varbind))
579 			err(1, "Can't print response");
580 	}
581 	ober_free_elements(pdu);
582 	snmp_free_agent(agent);
583 	return 0;
584 }
585 
586 int
587 snmpc_walk(int argc, char *argv[])
588 {
589 	struct ber_oid oid, loid, noid;
590 	struct ber_element *pdu, *varbind, *value;
591 	struct timespec start, finish;
592 	struct snmp_agent *agent;
593 	const char *oids;
594 	int n = 0, prev_cmp, skip_cmp;
595 	int errorstatus, errorindex;
596 	int class;
597 	size_t i;
598 	unsigned type;
599 
600 	if (strcmp(snmp_app->name, "bulkwalk") == 0 && version < SNMP_V2C)
601 		errx(1, "Cannot send V2 PDU on V1 session");
602 	if (argc < 1 || argc > 2)
603 		usage();
604 	oids = argc == 1 ? mib : argv[1];
605 
606 	if ((agent = snmpc_connect(argv[0], "161"))== NULL)
607 		err(1, "%s", snmp_app->name);
608 	agent->timeout = timeout;
609 	agent->retries = retries;
610 	if (pledge("stdio", NULL) == -1)
611 		err(1, "pledge");
612 
613 	if (smi_string2oid(oids, &oid) == -1)
614 		errx(1, "%s: Unknown object identifier", oids);
615 	bcopy(&oid, &noid, sizeof(noid));
616 	if (print_time)
617 		clock_gettime(CLOCK_MONOTONIC, &start);
618 
619 	if (walk_include_oid) {
620 		if ((pdu = snmp_get(agent, &oid, 1)) == NULL)
621 			err(1, "%s", snmp_app->name);
622 
623 		(void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type,
624 		    &errorstatus, &errorindex, &varbind);
625 		if (errorstatus != 0)
626 			snmpc_printerror((enum snmp_error) errorstatus, varbind,
627 			    errorindex, oids);
628 
629 		if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
630 			printf("Received report:\n");
631 		if (!snmpc_print(varbind))
632 			err(1, "Can't print response");
633 		ober_free_element(pdu);
634 		if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
635 			return 1;
636 		n++;
637 	}
638 	while (1) {
639 		for (i = 0; i < walk_skip_len; i++) {
640 			skip_cmp = ober_oid_cmp(&(walk_skip[i]), &noid);
641 			if (skip_cmp == 0 || skip_cmp == 2) {
642 				bcopy(&(walk_skip[i]), &noid, sizeof(noid));
643 				noid.bo_id[noid.bo_n -1]++;
644 				break;
645 			}
646 		}
647 		bcopy(&noid, &loid, sizeof(loid));
648 		if (strcmp(snmp_app->name, "bulkwalk") == 0) {
649 			if ((pdu = snmp_getbulk(agent, &noid, 1,
650 			    non_repeaters, max_repetitions)) == NULL)
651 				err(1, "bulkwalk");
652 		} else {
653 			if ((pdu = snmp_getnext(agent, &noid, 1)) == NULL)
654 				err(1, "walk");
655 		}
656 
657 		(void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type,
658 		    &errorstatus, &errorindex, &varbind);
659 		if (errorstatus != 0) {
660 			snmpc_printerror((enum snmp_error) errorstatus, varbind,
661 			    errorindex, NULL);
662 		}
663 
664 		if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
665 			printf("Received report:\n");
666 		for (; varbind != NULL; varbind = varbind->be_next) {
667 			(void) ober_scanf_elements(varbind, "{oe}", &noid,
668 			    &value);
669 			if (value->be_class == BER_CLASS_CONTEXT &&
670 			    value->be_type == BER_TYPE_EOC)
671 				break;
672 			for (i = 0; i < walk_skip_len; i++) {
673 				skip_cmp = ober_oid_cmp(&(walk_skip[i]), &noid);
674 				if (skip_cmp == 0 || skip_cmp == 2)
675 					break;
676 			}
677 			if (i < walk_skip_len)
678 				continue;
679 			prev_cmp = ober_oid_cmp(&loid, &noid);
680 			if (walk_check_increase && prev_cmp == -1)
681 				errx(1, "OID not increasing");
682 			if (prev_cmp == 0 || ober_oid_cmp(&oid, &noid) != 2)
683 				break;
684 			if (walk_end.bo_n != 0 &&
685 			    ober_oid_cmp(&walk_end, &noid) != -1)
686 				break;
687 
688 			if (!snmpc_print(varbind))
689 				err(1, "Can't print response");
690 			n++;
691 		}
692 		ober_free_elements(pdu);
693 		if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
694 			return 1;
695 		if (varbind != NULL)
696 			break;
697 	}
698 	if (walk_fallback_oid && n == 0) {
699 		if ((pdu = snmp_get(agent, &oid, 1)) == NULL)
700 			err(1, "%s", snmp_app->name);
701 
702 		(void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type,
703 		    &errorstatus, &errorindex, &varbind);
704 		if (errorstatus != 0)
705 			snmpc_printerror((enum snmp_error) errorstatus, varbind,
706 			    errorindex, oids);
707 
708 		if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
709 			printf("Received report:\n");
710 		if (!snmpc_print(varbind))
711 			err(1, "Can't print response");
712 		ober_free_element(pdu);
713 		if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
714 			return 1;
715 		n++;
716 	}
717 	if (print_time)
718 		clock_gettime(CLOCK_MONOTONIC, &finish);
719 	if (print_summary)
720 		printf("Variables found: %d\n", n);
721 	if (print_time) {
722 		if ((finish.tv_nsec -= start.tv_nsec) < 0) {
723 			finish.tv_sec -= 1;
724 			finish.tv_nsec += 1000000000;
725 		}
726 		finish.tv_sec -= start.tv_sec;
727 		fprintf(stderr, "Total traversal time: %lld.%09ld seconds\n",
728 		    finish.tv_sec, finish.tv_nsec);
729 	}
730 	snmp_free_agent(agent);
731 	return 0;
732 }
733 
734 int
735 snmpc_set(int argc, char *argv[])
736 {
737 	struct snmp_agent *agent;
738 	struct ber_element *pdu, *varbind;
739 	int errorstatus, errorindex;
740 	int class;
741 	unsigned type;
742 	char *hint = NULL;
743 
744 	if (argc < 4)
745 		usage();
746 	if ((agent = snmpc_connect(argv[0], "161")) == NULL)
747 		err(1, "%s", snmp_app->name);
748 	argc--;
749 	argv++;
750 
751 	if (pledge("stdio", NULL) == -1)
752 		err(1, "pledge");
753 
754 	if ((pdu = snmp_set(agent, snmpc_varbindparse(argc, argv))) == NULL)
755 		err(1, "set");
756 
757 	(void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type, &errorstatus,
758 	    &errorindex, &varbind);
759 	if (errorstatus != 0) {
760 		if (errorindex >= 1 && errorindex <= argc / 3)
761 			hint = argv[(errorindex - 1) * 3];
762 		snmpc_printerror((enum snmp_error) errorstatus, varbind,
763 		    errorindex, hint);
764 	}
765 
766 	if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
767 		printf("Received report:\n");
768 	for (; varbind != NULL; varbind = varbind->be_next) {
769 		if (!snmpc_print(varbind))
770 			err(1, "Can't print response");
771 	}
772 	ober_free_elements(pdu);
773 	snmp_free_agent(agent);
774 	return 0;
775 }
776 
777 int
778 snmpc_trap(int argc, char *argv[])
779 {
780 	struct snmp_agent *agent;
781 	struct timespec ts;
782 	struct ber_oid trapoid;
783 	const char *errstr = NULL;
784 	long long lval;
785 
786 	if (version == SNMP_V1)
787 		errx(1, "trap is not supported for snmp v1");
788 
789 	if ((agent = snmpc_connect(argv[0], "162")) == NULL)
790 		err(1, "%s", snmp_app->name);
791 
792 	if (pledge("stdio", NULL) == -1)
793 		err(1, "pledge");
794 
795 	if (argv[1][0] == '\0') {
796 		if (clock_gettime(CLOCK_UPTIME, &ts) == -1)
797 			err(1, "clock_gettime");
798 	} else {
799 		lval = strtonum(argv[1], 0, LLONG_MAX, &errstr);
800 		if (errstr != NULL)
801 			errx(1, "Bad value notation (%s)", argv[1]);
802 		ts.tv_sec = lval / 100;
803 		ts.tv_nsec = (lval % 100) * 10000000;
804 	}
805 	if (smi_string2oid(argv[2], &trapoid) == -1)
806 		errx(1, "Invalid oid: %s\n", argv[2]);
807 
808 	argc -= 3;
809 	argv += 3;
810 
811 	snmp_trap(agent, &ts, &trapoid, snmpc_varbindparse(argc, argv));
812 
813 	return 0;
814 }
815 
816 #define INCR_NEXTTAB(x) ((x + 8) & ~7)
817 #define NEXTTAB(x) (8 - (x & 7))
818 int
819 snmpc_df(int argc, char *argv[])
820 {
821 	struct snmpc_df {
822 		uint32_t index;
823 		/* DisplayString is 255a DISPLAY-HINT */
824 		char descr[256];
825 		/* Theoretical maximum for 2 32 bit values multiplied */
826 		char size[21];
827 		char used[21];
828 		char avail[21];
829 		char proc[5];
830 	} *df = NULL;
831 	struct ber_oid descroid = {{ 1, 3, 6, 1, 2, 1, 25, 2, 3, 1, 3 }, 11};
832 	struct ber_oid unitsoid = {{ 1, 3, 6, 1, 2, 1, 25, 2, 3, 1, 4 }, 11};
833 	struct ber_oid sizeoid = {{ 1, 3, 6, 1, 2, 1, 25, 2, 3, 1, 5 }, 11};
834 	struct ber_oid usedoid = {{ 1, 3, 6, 1, 2, 1, 25, 2, 3, 1, 6 }, 11};
835 	struct ber_oid oid, *reqoid;
836 	struct ber_element *pdu, *varbind;
837 	struct snmp_agent *agent;
838 	int errorstatus, errorindex;
839 	int class;
840 	size_t i, j, rows = 0;
841 	unsigned type;
842 	char *string;
843 	int descrlen = 0, sizelen = 0, usedlen = 0, availlen = 0, proclen = 0;
844 	int len;
845 	long long units, size, used;
846 	int fmtret;
847 
848 	if (argc != 1)
849 		usage();
850 
851 	if ((agent = snmpc_connect(argv[0], "161")) == NULL)
852 		err(1, "%s", snmp_app->name);
853 	agent->timeout = timeout;
854 	agent->retries = retries;
855 
856 	if (pledge("stdio", NULL) == -1)
857 		err(1, "pledge");
858 
859 	descrlen = sizeof("Description") - 1;
860 	sizelen = sizeof("Size") - 1;
861 	usedlen = sizeof("Used") - 1;
862 	availlen = sizeof("Available") - 1;
863 	proclen = sizeof("Used%") - 1;
864 
865 	bcopy(&descroid, &oid, sizeof(descroid));
866 
867 	i = 0;
868 	while(1) {
869 		if (version < SNMP_V2C) {
870 			if ((pdu = snmp_getnext(agent, &oid, 1)) == NULL)
871 				err(1, "df");
872 		} else {
873 			if ((pdu = snmp_getbulk(agent, &oid, 1, 0,
874 			    max_repetitions)) == NULL)
875 				err(1, "df");
876 		}
877 
878 		(void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type,
879 		    &errorstatus, &errorindex, &varbind);
880 		if (errorstatus != 0)
881 			snmpc_printerror((enum snmp_error) errorstatus, varbind,
882 			    errorindex, NULL);
883 
884 		if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) {
885 			printf("Received report:\n");
886 			for (; varbind != NULL; varbind = varbind->be_next) {
887 				if (!snmpc_print(varbind))
888 					err(1, "Can't print response");
889 			}
890 			return 1;
891 		}
892 		for (; varbind != NULL; varbind = varbind->be_next) {
893 			(void) ober_scanf_elements(varbind, "{oS", &oid);
894 			if (ober_oid_cmp(&descroid, &oid) != 2)
895 				break;
896 			rows++;
897 		}
898 		if ((df = reallocarray(df, rows, sizeof(*df))) == NULL)
899 			err(1, "malloc");
900 		(void) ober_scanf_elements(pdu, "{SSS{e", &varbind);
901 		for (; i < rows; varbind = varbind->be_next, i++) {
902 			if (ober_scanf_elements(varbind, "{os", &oid,
903 			    &string) == -1) {
904 				i--;
905 				rows--;
906 				continue;
907 			}
908 			df[i].index = oid.bo_id[oid.bo_n - 1];
909 			len = strlcpy(df[i].descr, string,
910 			    sizeof(df[i].descr));
911 			if (len > (int) sizeof(df[i].descr))
912 				len = (int) sizeof(df[i].descr) - 1;
913 			if (len > descrlen)
914 				descrlen = len;
915 		}
916 		ober_free_elements(pdu);
917 		if (varbind != NULL)
918 			break;
919 	}
920 
921 	if (max_repetitions < 3)
922 		max_repetitions = 3;
923 	if ((reqoid = reallocarray(NULL, max_repetitions, sizeof(*reqoid))) == NULL)
924 		err(1, "malloc");
925 	for (i = 0; i < rows;) {
926 		for (j = 0; i + j < rows && j < (size_t)max_repetitions / 3;
927 		    j++) {
928 			bcopy(&unitsoid, &(reqoid[(j * 3) + 0]),
929 			    sizeof(unitsoid));
930 			reqoid[(j * 3) + 0].bo_id[
931 			    reqoid[(j * 3) + 0].bo_n++] = df[i + j].index;
932 			bcopy(&sizeoid, &(reqoid[(j * 3) + 1]),
933 			    sizeof(sizeoid));
934 			reqoid[(j * 3) + 1].bo_id[
935 			    reqoid[(j * 3) + 1].bo_n++] = df[i + j].index;
936 			bcopy(&usedoid, &(reqoid[(j * 3) + 2]),
937 			    sizeof(usedoid));
938 			reqoid[(j * 3) + 2].bo_id[
939 			    reqoid[(j * 3) + 2].bo_n++] = df[i + j].index;
940 		}
941 		if ((pdu = snmp_get(agent, reqoid, j * 3)) == NULL)
942 			err(1, "df");
943 		(void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type,
944 		    &errorstatus, &errorindex, &varbind);
945 		if (errorstatus != 0)
946 			snmpc_printerror((enum snmp_error) errorstatus, varbind,
947 			    errorindex, NULL);
948 		if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) {
949 			printf("Received report:\n");
950 			for (; varbind != NULL; varbind = varbind->be_next) {
951 				if (!snmpc_print(varbind))
952 					err(1, "Can't print response");
953 			}
954 		}
955 		for (j = 0; varbind != NULL; i++) {
956 			if (ober_scanf_elements(varbind, "{oi}{oi}{oi}",
957 			    &(reqoid[0]), &units, &(reqoid[1]), &size,
958 			    &(reqoid[2]), &used, &varbind) == -1) {
959 				break;
960 			}
961 			varbind = varbind->be_next->be_next->be_next;
962 
963 			unitsoid.bo_id[unitsoid.bo_n++] = df[i].index;
964 			if (ober_oid_cmp(&unitsoid, &(reqoid[0])) != 0) {
965 				warnx("df: received invalid object");
966 				break;
967 			}
968 			unitsoid.bo_n--;
969 			sizeoid.bo_id[sizeoid.bo_n++] = df[i].index;
970 			if (ober_oid_cmp(&sizeoid, &(reqoid[1])) != 0) {
971 				warnx("df: received invalid object");
972 				break;
973 			}
974 			sizeoid.bo_n--;
975 			usedoid.bo_id[usedoid.bo_n++] = df[i].index;
976 			if (ober_oid_cmp(&usedoid, &(reqoid[2])) != 0) {
977 				warnx("df: received invalid object");
978 				break;
979 			}
980 			usedoid.bo_n--;
981 			if (print_human)
982 				fmtret = fmt_scaled((units * size), df[i].size);
983 			if (!print_human || fmtret == -1)
984 				snprintf(df[i].size, sizeof(df[i].size), "%lld",
985 				    (units * size) / 1024);
986 			len = (int) strlen(df[i].size);
987 			if (len > sizelen)
988 				sizelen = len;
989 			if (print_human)
990 				fmtret = fmt_scaled(units * used, df[i].used);
991 			if (!print_human || fmtret == -1)
992 				snprintf(df[i].used, sizeof(df[i].used), "%lld",
993 				    (units * used) / 1024);
994 			len = (int) strlen(df[i].used);
995 			if (len > usedlen)
996 				usedlen = len;
997 			if (print_human)
998 				fmtret = fmt_scaled(units * (size - used),
999 				    df[i].avail);
1000 			if (!print_human || fmtret == -1)
1001 				snprintf(df[i].avail, sizeof(df[i].avail),
1002 				    "%lld", (units * (size - used)) / 1024);
1003 			len = (int) strlen(df[i].avail);
1004 			if (len > availlen)
1005 				availlen = len;
1006 			if (size == 0)
1007 				strlcpy(df[i].proc, "0%", sizeof(df[i].proc));
1008 			else {
1009 				snprintf(df[i].proc, sizeof(df[i].proc),
1010 				    "%lld%%", (used * 100) / size);
1011 			}
1012 			len = (int) strlen(df[i].proc);
1013 			if (len > proclen)
1014 				proclen = len;
1015 			j++;
1016 		}
1017 		if (j == 0) {
1018 			warnx("Failed to retrieve information for %s",
1019 			    df[i].descr);
1020 			memmove(df + i, df + i + 1,
1021 			    (rows - i - 1) * sizeof(*df));
1022 			rows--;
1023 			i--;
1024 		}
1025 	}
1026 
1027 	printf("%-*s%*s%*s%*s%*s\n",
1028 	    descrlen, "Description",
1029 	    NEXTTAB(descrlen) + sizelen, "Size",
1030 	    NEXTTAB(sizelen) + usedlen, "Used",
1031 	    NEXTTAB(usedlen) + availlen, "Available",
1032 	    NEXTTAB(availlen) + proclen, "Used%");
1033 	for (i = 0; i < rows; i++) {
1034 		printf("%-*s%*s%*s%*s%*s\n",
1035 		    descrlen, df[i].descr,
1036 		    NEXTTAB(descrlen) + sizelen, df[i].size,
1037 		    NEXTTAB(sizelen) + usedlen, df[i].used,
1038 		    NEXTTAB(usedlen) + availlen, df[i].avail,
1039 		    NEXTTAB(availlen) + proclen, df[i].proc);
1040 	}
1041 
1042 	return 0;
1043 }
1044 
1045 int
1046 snmpc_mibtree(int argc, char *argv[])
1047 {
1048 	struct oid *oid;
1049 	char buf[BUFSIZ];
1050 
1051 	for (oid = NULL; (oid = smi_foreach(oid)) != NULL;) {
1052 		smi_oid2string(&oid->o_id, buf, sizeof(buf), oid_lookup);
1053 		printf("%s\n", buf);
1054 	}
1055 	return 0;
1056 }
1057 
1058 struct snmp_agent *
1059 snmpc_connect(char *host, char *port)
1060 {
1061 	switch (version) {
1062 	case SNMP_V1:
1063 	case SNMP_V2C:
1064 		return snmp_connect_v12(snmpc_parseagent(host, port), version,
1065 		    community);
1066 	case SNMP_V3:
1067 		return snmp_connect_v3(snmpc_parseagent(host, port), v3);
1068 	}
1069 	return NULL;
1070 }
1071 
1072 int
1073 snmpc_print(struct ber_element *elm)
1074 {
1075 	struct ber_oid oid;
1076 	char oids[SNMP_MAX_OID_STRLEN];
1077 	char *value;
1078 
1079 	elm = elm->be_sub;
1080 	if (ober_get_oid(elm, &oid) != 0) {
1081 		errno = EINVAL;
1082 		return 0;
1083 	}
1084 
1085 	elm = elm->be_next;
1086 	value = smi_print_element(&oid, elm, smi_print_hint, output_string,
1087 	    oid_lookup, utf8);
1088 	if (value == NULL)
1089 		return 0;
1090 
1091 	if (print_varbind_only)
1092 		printf("%s\n", value);
1093 	else if (print_equals) {
1094 		smi_oid2string(&oid, oids, sizeof(oids), oid_lookup);
1095 		printf("%s = %s\n", oids, value);
1096 	} else {
1097 		smi_oid2string(&oid, oids, sizeof(oids), oid_lookup);
1098 		printf("%s %s\n", oids, value);
1099 	}
1100 	free(value);
1101 
1102 	return 1;
1103 }
1104 
1105 __dead void
1106 snmpc_printerror(enum snmp_error error, struct ber_element *varbind,
1107     int index, const char *hint)
1108 {
1109 	struct ber_oid hoid, vboid;
1110 	char oids[SNMP_MAX_OID_STRLEN];
1111 	const char *oid = NULL;
1112 	int i;
1113 
1114 	if (index >= 1) {
1115 		/* Only print if the index is in the reply */
1116 		for (i = 1; varbind != NULL && i < index;
1117 		    varbind = varbind->be_next)
1118 			i++;
1119 		if (varbind != NULL &&
1120 		    ober_get_oid(varbind->be_sub, &vboid) == 0) {
1121 			/* If user and reply conform print user input */
1122 			if (hint != NULL &&
1123 			    smi_string2oid(hint, &hoid) == 0 &&
1124 			    ober_oid_cmp(&hoid, &vboid) == 0)
1125 				oid = hint;
1126 			else
1127 				oid = smi_oid2string(&vboid, oids,
1128 				    sizeof(oids), oid_lookup);
1129 		}
1130 	}
1131 	if (oid == NULL)
1132 		oid = "?";
1133 
1134 	switch (error) {
1135 	case SNMP_ERROR_NONE:
1136 		errx(1, "No error, how did I get here?");
1137 	case SNMP_ERROR_TOOBIG:
1138 		errx(1, "Can't parse oid %s: Response too big", oid);
1139 	case SNMP_ERROR_NOSUCHNAME:
1140 		errx(1, "Can't parse oid %s: No such object", oid);
1141 	case SNMP_ERROR_BADVALUE:
1142 		errx(1, "Can't parse oid %s: Bad value", oid);
1143 	case SNMP_ERROR_READONLY:
1144 		errx(1, "Can't parse oid %s: Read only", oid);
1145 	case SNMP_ERROR_GENERR:
1146 		errx(1, "Can't parse oid %s: Generic error", oid);
1147 	case SNMP_ERROR_NOACCESS:
1148 		errx(1, "Can't parse oid %s: Access denied", oid);
1149 	case SNMP_ERROR_WRONGTYPE:
1150 		errx(1, "Can't parse oid %s: Wrong type", oid);
1151 	case SNMP_ERROR_WRONGLENGTH:
1152 		errx(1, "Can't parse oid %s: Wrong length", oid);
1153 	case SNMP_ERROR_WRONGENC:
1154 		errx(1, "Can't parse oid %s: Wrong encoding", oid);
1155 	case SNMP_ERROR_WRONGVALUE:
1156 		errx(1, "Can't parse oid %s: Wrong value", oid);
1157 	case SNMP_ERROR_NOCREATION:
1158 		errx(1, "Can't parse oid %s: Can't be created", oid);
1159 	case SNMP_ERROR_INCONVALUE:
1160 		errx(1, "Can't parse oid %s: Inconsistent value", oid);
1161 	case SNMP_ERROR_RESUNAVAIL:
1162 		errx(1, "Can't parse oid %s: Resource unavailable", oid);
1163 	case SNMP_ERROR_COMMITFAILED:
1164 		errx(1, "Can't parse oid %s: Commit failed", oid);
1165 	case SNMP_ERROR_UNDOFAILED:
1166 		errx(1, "Can't parse oid %s: Undo faild", oid);
1167 	case SNMP_ERROR_AUTHERROR:
1168 		errx(1, "Can't parse oid %s: Authorization error", oid);
1169 	case SNMP_ERROR_NOTWRITABLE:
1170 		errx(1, "Can't parse oid %s: Not writable", oid);
1171 	case SNMP_ERROR_INCONNAME:
1172 		errx(1, "Can't parse oid %s: Inconsistent name", oid);
1173 	}
1174 	errx(1, "Can't parse oid %s: Unknown error (%d)", oid, error);
1175 }
1176 
1177 int
1178 snmpc_parseagent(char *agent, char *defaultport)
1179 {
1180 	struct addrinfo hints, *ai, *ai0 = NULL;
1181 	struct sockaddr_un saddr;
1182 	char *agentdup, *specifier, *hostname, *port = NULL;
1183 	int error;
1184 	int s;
1185 
1186 	if ((agentdup = specifier = strdup(agent)) == NULL)
1187 		err(1, NULL);
1188 
1189 	bzero(&hints, sizeof(hints));
1190 	if ((hostname = strchr(specifier, ':')) != NULL) {
1191 		*hostname++ = '\0';
1192 		if (strcasecmp(specifier, "udp") == 0) {
1193 			hints.ai_family = AF_INET;
1194 			hints.ai_socktype = SOCK_DGRAM;
1195 		} else if (strcasecmp(specifier, "tcp") == 0) {
1196 			hints.ai_family = AF_INET;
1197 			hints.ai_socktype = SOCK_STREAM;
1198 		} else if (strcasecmp(specifier, "udp6") == 0 ||
1199 		    strcasecmp(specifier, "udpv6") == 0 ||
1200 		    strcasecmp(specifier, "udpipv6") == 0) {
1201 			hints.ai_family = AF_INET6;
1202 			hints.ai_socktype = SOCK_DGRAM;
1203 		} else if (strcasecmp(specifier, "tcp6") == 0 ||
1204 		    strcasecmp(specifier, "tcpv6") == 0 ||
1205 		    strcasecmp(specifier, "tcpipv6") == 0) {
1206 			hints.ai_family = AF_INET6;
1207 			hints.ai_socktype = SOCK_STREAM;
1208 		} else if (strcasecmp(specifier, "unix") == 0) {
1209 			hints.ai_family = AF_UNIX;
1210 			hints.ai_addr = (struct sockaddr *)&saddr;
1211 			hints.ai_addrlen = sizeof(saddr);
1212 			saddr.sun_len = sizeof(saddr);
1213 			saddr.sun_family = AF_UNIX;
1214 			if (strlcpy(saddr.sun_path, hostname,
1215 			    sizeof(saddr.sun_path)) > sizeof(saddr.sun_path))
1216 				errx(1, "Hostname path too long");
1217 			ai = &hints;
1218 		} else {
1219 			*--hostname = ':';
1220 			hostname = specifier;
1221 		}
1222 	} else {
1223 		hostname = specifier;
1224 	}
1225 
1226 	if (hints.ai_family == AF_INET) {
1227 		if ((port = strchr(hostname, ':')) != NULL)
1228 			*port++ = '\0';
1229 	} else if (hints.ai_family == AF_INET6 || hints.ai_family == 0) {
1230 		if (hostname[0] == '[') {
1231 			hints.ai_family = AF_INET6;
1232 			hostname++;
1233 			if ((port = strchr(hostname, ']')) == NULL)
1234 				errx(1, "invalid agent");
1235 			*port++ = '\0';
1236 			if (port[0] == ':')
1237 				*port++ = '\0';
1238 			else if (port[0] == '\0')
1239 				port = NULL;
1240 			else
1241 				errx(1, "invalid agent");
1242 		} else {
1243 			if ((port = strrchr(hostname, ':')) != NULL)
1244 				*port++ = '\0';
1245 		}
1246 	}
1247 
1248 	if (hints.ai_family != AF_UNIX) {
1249 		if (hints.ai_socktype == 0)
1250 			hints.ai_socktype = SOCK_DGRAM;
1251 		if (port == NULL)
1252 			port = defaultport;
1253 		error = getaddrinfo(hostname, port, &hints, &ai0);
1254 		if (error) {
1255 			if (error != EAI_NODATA || port == defaultport)
1256 				errx(1, "%s", gai_strerror(error));
1257 			*--port = ':';
1258 			error = getaddrinfo(hostname, defaultport, &hints,
1259 			    &ai0);
1260 			if (error)
1261 				errx(1, "%s", gai_strerror(error));
1262 		}
1263 		s = -1;
1264 		for (ai = ai0; ai != NULL; ai = ai->ai_next) {
1265 			if ((s = socket(ai->ai_family, ai->ai_socktype,
1266 			    ai->ai_protocol)) != -1 &&
1267 			    connect(s, (struct sockaddr *)ai->ai_addr,
1268 			    ai->ai_addrlen) != -1)
1269 				break;
1270 			close(s);
1271 			s = -1;
1272 		}
1273 	} else {
1274 		s = socket(AF_UNIX, SOCK_STREAM, 0);
1275 		if (connect(s, (struct sockaddr *)ai->ai_addr,
1276 		    ai->ai_addrlen) == -1)
1277 			err(1, "Can't connect to %s", agent);
1278 	}
1279 	if (s == -1)
1280 		err(1, "Can't connect to agent %s", agent);
1281 
1282 
1283 	if (ai0 != NULL)
1284 		freeaddrinfo(ai0);
1285 	free(agentdup);
1286 	return s;
1287 }
1288 
1289 char *
1290 snmpc_hex2bin(char *hexstr, size_t *binlen)
1291 {
1292 	char *decstr;
1293 
1294 	if (hexstr[0] == '0' && hexstr[1] == 'x')
1295 		hexstr += 2;
1296 	while (hexstr[0] == ' ' || hexstr[0] == '\t')
1297 		hexstr++;
1298 
1299 	if ((decstr = malloc((strlen(hexstr) / 2) + 1)) == NULL)
1300 		return NULL;
1301 
1302 	for (*binlen = 0; hexstr[0] != '\0'; (*binlen)++) {
1303 		hexstr[0] = toupper(hexstr[0]);
1304 		hexstr[1] = toupper(hexstr[1]);
1305 		if (hexstr[0] >= '0' && hexstr[0] <= '9')
1306 			decstr[*binlen] = (hexstr[0] - '0') << 4;
1307 		else if (hexstr[0] >= 'A' && hexstr[0] <= 'F')
1308 			decstr[*binlen] = ((hexstr[0] - 'A') + 10) << 4;
1309 		else
1310 			goto fail;
1311 		if (hexstr[1] >= '0' && hexstr[1] <= '9')
1312 			decstr[*binlen] |= (hexstr[1] - '0');
1313 		else if (hexstr[1] >= 'A' && hexstr[1] <= 'F')
1314 			decstr[*binlen] |= (hexstr[1] - 'A') + 10;
1315 		else
1316 			goto fail;
1317 
1318 		hexstr += 2;
1319 		while (hexstr[0] == ' ' || hexstr[0] == '\t')
1320 			hexstr++;
1321 	}
1322 
1323 	return decstr;
1324 fail:
1325 	errno = EINVAL;
1326 	free(decstr);
1327 	return NULL;
1328 }
1329 
1330 struct ber_element *
1331 snmpc_varbindparse(int argc, char *argv[])
1332 {
1333 	struct ber_oid oid, oidval;
1334 	struct in_addr addr4;
1335 	char *addr = (char *)&addr4;
1336 	char *str = NULL, *tmpstr, *endstr;
1337 	const char *errstr = NULL;
1338 	struct ber_element *varbind = NULL, *vblist = NULL;
1339 	int i, ret;
1340 	size_t strl, byte;
1341 	long long lval;
1342 
1343 	if (argc % 3 != 0)
1344 		usage();
1345 	for (i = 0; i < argc; i += 3) {
1346 		if (smi_string2oid(argv[i], &oid) == -1)
1347 			errx(1, "Invalid oid: %s\n", argv[i]);
1348 		switch (argv[i + 1][0]) {
1349 		case 'a':
1350 			ret = inet_pton(AF_INET, argv[i + 2], &addr4);
1351 			if (ret == -1)
1352 				err(1, "inet_pton");
1353 			if (ret == 0)
1354 				errx(1, "%s: Bad value notation (%s)", argv[i],
1355 				    argv[i + 2]);
1356 			if ((varbind = ober_printf_elements(varbind, "{Oxt}",
1357 			    &oid, addr, sizeof(addr4), BER_CLASS_APPLICATION,
1358 			    SNMP_T_IPADDR)) == NULL)
1359 				err(1, "ober_printf_elements");
1360 			break;
1361 		case 'b':
1362 			tmpstr = argv[i + 2];
1363 			strl = 0;
1364 			do {
1365 				lval = strtoll(tmpstr, &endstr, 10);
1366 				if (endstr[0] != ' ' && endstr[0] != '\t' &&
1367 				    endstr[0] != ',' && endstr[0] != '\0')
1368 					errx(1, "%s: Bad value notation (%s)",
1369 					    argv[i], argv[i + 2]);
1370 				if (tmpstr == endstr) {
1371 					tmpstr++;
1372 					continue;
1373 				}
1374 				if (lval < 0)
1375 					errx(1, "%s: Bad value notation (%s)",
1376 					    argv[i], argv[i + 2]);
1377 				byte = lval / 8;
1378 				if (byte >= strl) {
1379 					if ((str = recallocarray(str, strl,
1380 					    byte + 1, 1)) == NULL)
1381 						err(1, "malloc");
1382 					strl = byte + 1;
1383 				}
1384 				str[byte] |= 0x80 >> (lval % 8);
1385 				tmpstr = endstr + 1;
1386 			} while (endstr[0] != '\0');
1387 			/*
1388 			 * RFC3416 Section 2.5
1389 			 * A BITS value is encoded as an OCTET STRING
1390 			 */
1391 			goto pastestring;
1392 		case 'c':
1393 			lval = strtonum(argv[i + 2], INT32_MIN, INT32_MAX,
1394 			    &errstr);
1395 			if (errstr != NULL)
1396 				errx(1, "%s: Bad value notation (%s)", argv[i],
1397 				    argv[i + 2]);
1398 			if ((varbind = ober_printf_elements(varbind, "{Oit}",
1399 			    &oid, lval, BER_CLASS_APPLICATION,
1400 			    SNMP_T_COUNTER32)) == NULL)
1401 				err(1, "ober_printf_elements");
1402 			break;
1403 		case 'd':
1404 			/* String always shrinks */
1405 			if ((str = malloc(strlen(argv[i + 2]))) == NULL)
1406 				err(1, "malloc");
1407 			tmpstr = argv[i + 2];
1408 			strl = 0;
1409 			do {
1410 				lval = strtoll(tmpstr, &endstr, 10);
1411 				if (endstr[0] != ' ' && endstr[0] != '\t' &&
1412 				    endstr[0] != '\0')
1413 					errx(1, "%s: Bad value notation (%s)",
1414 					    argv[i], argv[i + 2]);
1415 				if (tmpstr == endstr) {
1416 					tmpstr++;
1417 					continue;
1418 				}
1419 				if (lval < 0 || lval > 0xff)
1420 					errx(1, "%s: Bad value notation (%s)",
1421 					    argv[i], argv[i + 2]);
1422 				str[strl++] = (unsigned char) lval;
1423 				tmpstr = endstr + 1;
1424 			} while (endstr[0] != '\0');
1425 			goto pastestring;
1426 		case 'u':
1427 		case 'i':
1428 			lval = strtonum(argv[i + 2], LLONG_MIN, LLONG_MAX,
1429 			    &errstr);
1430 			if (errstr != NULL)
1431 				errx(1, "%s: Bad value notation (%s)", argv[i],
1432 				    argv[i + 2]);
1433 			if ((varbind = ober_printf_elements(varbind, "{Oi}",
1434 			    &oid, lval)) == NULL)
1435 				err(1, "ober_printf_elements");
1436 			break;
1437 		case 'n':
1438 			if ((varbind = ober_printf_elements(varbind, "{O0}",
1439 			    &oid)) == NULL)
1440 				err(1, "ober_printf_elements");
1441 			break;
1442 		case 'o':
1443 			if (smi_string2oid(argv[i + 2], &oidval) == -1)
1444 				errx(1, "%s: Unknown Object Identifier (Sub-id "
1445 				    "not found: (top) -> %s)", argv[i],
1446 				    argv[i + 2]);
1447 			if ((varbind = ober_printf_elements(varbind, "{OO}",
1448 			    &oid, &oidval)) == NULL)
1449 				err(1, "ober_printf_elements");
1450 			break;
1451 		case 's':
1452 			if ((str = strdup(argv[i + 2])) == NULL)
1453 				err(1, NULL);
1454 			strl = strlen(argv[i + 2]);
1455 pastestring:
1456 			if ((varbind = ober_printf_elements(varbind, "{Ox}",
1457 			    &oid, str, strl)) == NULL)
1458 				err(1, "ober_printf_elements");
1459 			free(str);
1460 			break;
1461 		case 't':
1462 			lval = strtonum(argv[i + 2], LLONG_MIN, LLONG_MAX,
1463 			    &errstr);
1464 			if (errstr != NULL)
1465 				errx(1, "%s: Bad value notation (%s)", argv[i],
1466 				    argv[i + 2]);
1467 			if ((varbind = ober_printf_elements(varbind, "{Oit}",
1468 			    &oid, lval, BER_CLASS_APPLICATION,
1469 			    SNMP_T_TIMETICKS)) == NULL)
1470 				err(1, "ober_printf_elements");
1471 			break;
1472 		case 'x':
1473 			/* String always shrinks */
1474 			if ((str = malloc(strlen(argv[i + 2]))) == NULL)
1475 				err(1, "malloc");
1476 			tmpstr = argv[i + 2];
1477 			strl = 0;
1478 			do {
1479 				lval = strtoll(tmpstr, &endstr, 16);
1480 				if (endstr[0] != ' ' && endstr[0] != '\t' &&
1481 				    endstr[0] != '\0')
1482 					errx(1, "%s: Bad value notation (%s)",
1483 					    argv[i], argv[i + 2]);
1484 				if (tmpstr == endstr) {
1485 					tmpstr++;
1486 					continue;
1487 				}
1488 				if (lval < 0 || lval > 0xff)
1489 					errx(1, "%s: Bad value notation (%s)",
1490 					    argv[i], argv[i + 2]);
1491 				str[strl++] = (unsigned char) lval;
1492 				tmpstr = endstr + 1;
1493 			} while (endstr[0] != '\0');
1494 			goto pastestring;
1495 		default:
1496 			usage();
1497 		}
1498 		if (vblist == NULL)
1499 			vblist = varbind;
1500 	}
1501 
1502 	return vblist;
1503 }
1504 
1505 __dead void
1506 usage(void)
1507 {
1508 	size_t i;
1509 
1510 	if (snmp_app != NULL) {
1511 		fprintf(stderr, "usage: snmp %s%s%s\n",
1512 		    snmp_app->name,
1513 		    snmp_app->usecommonopt ?
1514 		    " [-A authpass] [-a digest] [-c community] [-e secengineid]\n"
1515 		    "            [-E ctxengineid] [-K localpriv] [-k localauth] [-l seclevel]\n"
1516 		    "            [-n ctxname] [-O afnqvxSQ] [-r retries] [-t timeout] [-u user]\n"
1517 		    "            [-v version] [-X privpass] [-x cipher] [-Z boots,time]\n"
1518 		    "            " : "",
1519 		    snmp_app->usage == NULL ? "" : snmp_app->usage);
1520 		exit(1);
1521 	}
1522 	for (i = 0; i < (sizeof(snmp_apps)/sizeof(*snmp_apps)); i++) {
1523 		if (i == 0)
1524 			fprintf(stderr, "usage: ");
1525 		else
1526 			fprintf(stderr, "       ");
1527 		fprintf(stderr, "snmp %s%s %s\n",
1528 		    snmp_apps[i].name,
1529 		    snmp_apps[i].usecommonopt ?
1530 		    " [options]" : "",
1531 		    snmp_apps[i].usage ? snmp_apps[i].usage : "");
1532 	}
1533 	exit(1);
1534 }
1535