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