1 /* $OpenBSD: snmpc.c,v 1.41 2024/04/23 13:34:50 jsg 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 = NULL;
88 struct snmp_v3 *v3;
89 char *mib = "mib_2";
90 int retries = 5;
91 int timeout = 1;
92 enum snmp_version version = SNMP_V3;
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
main(int argc,char * argv[])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 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 (!snmp_app->usecommonopt) {
472 /* No SNMP protocol settings */
473 } else if (version == SNMP_V1 || version == SNMP_V2C) {
474 if (community == NULL || community[0] == '\0')
475 errx(1, "No community name specified.");
476 } else if (version == SNMP_V3) {
477 /* Setup USM */
478 if (user == NULL || user[0] == '\0')
479 errx(1, "No securityName specified");
480 if ((sec = usm_init(user, strlen(user))) == NULL)
481 err(1, "usm_init");
482 if (seclevel & SNMP_MSGFLAG_AUTH) {
483 if (md == NULL)
484 md = EVP_sha1();
485 if (authkey == NULL)
486 errx(1, "No authKey or authPassword specified");
487 if (usm_setauth(sec, md, authkey, authkeylen,
488 authkeylevel) == -1)
489 err(1, "Can't set authkey");
490 }
491 if (seclevel & SNMP_MSGFLAG_PRIV) {
492 if (cipher == NULL)
493 cipher = EVP_aes_128_cfb128();
494 if (privkey == NULL)
495 errx(1, "No privKey or privPassword specified");
496 if (usm_setpriv(sec, cipher, privkey, privkeylen,
497 privkeylevel) == -1)
498 err(1, "Can't set authkey");
499 }
500 if (secengineid != NULL) {
501 if (usm_setengineid(sec, secengineid,
502 secengineidlen) == -1)
503 err(1, "Can't set secengineid");
504 }
505 if (zflag)
506 if (usm_setbootstime(sec, boots, time) == -1)
507 err(1, "Can't set boots/time");
508 v3 = snmp_v3_init(seclevel, ctxname, ctxname == NULL ? 0 :
509 strlen(ctxname), sec);
510 if (v3 == NULL)
511 err(1, "snmp_v3_init");
512 if (ctxengineid != NULL) {
513 if (snmp_v3_setengineid(v3, ctxengineid,
514 ctxengineidlen) == -1)
515 err(1, "Can't set ctxengineid");
516 }
517 }
518
519
520 return snmp_app->exec(argc, argv);
521 }
522
523 int
snmpc_get(int argc,char * argv[])524 snmpc_get(int argc, char *argv[])
525 {
526 struct ber_oid *oid;
527 struct ber_element *pdu, *varbind;
528 struct snmp_agent *agent;
529 int errorstatus, errorindex;
530 int i;
531 int class;
532 unsigned type;
533 char *hint = NULL;
534
535 if (argc < 2)
536 usage();
537
538 if ((agent = snmpc_connect(argv[0], "161")) == NULL)
539 err(1, "%s", snmp_app->name);
540 agent->timeout = timeout;
541 agent->retries = retries;
542
543 if (pledge("stdio", NULL) == -1)
544 err(1, "pledge");
545 argc--;
546 argv++;
547
548 oid = reallocarray(NULL, argc, sizeof(*oid));
549 if (oid == NULL)
550 err(1, "malloc");
551 for (i = 0; i < argc; i++) {
552 if (smi_string2oid(argv[i], &oid[i]) == -1)
553 errx(1, "%s: Unknown object identifier", argv[i]);
554 }
555 if (strcmp(snmp_app->name, "getnext") == 0) {
556 if ((pdu = snmp_getnext(agent, oid, argc)) == NULL)
557 err(1, "getnext");
558 } else if (strcmp(snmp_app->name, "bulkget") == 0) {
559 if (version < SNMP_V2C)
560 errx(1, "Cannot send V2 PDU on V1 session");
561 if (non_repeaters > argc)
562 errx(1, "need more objects than -Cn<num>");
563 if ((pdu = snmp_getbulk(agent, oid, argc, non_repeaters,
564 max_repetitions)) == NULL)
565 err(1, "bulkget");
566 } else {
567 if ((pdu = snmp_get(agent, oid, argc)) == NULL)
568 err(1, "get");
569 }
570
571 (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type, &errorstatus,
572 &errorindex, &varbind);
573 if (errorstatus != 0) {
574 if (errorindex >= 1 && errorindex <= argc)
575 hint = argv[errorindex - 1];
576 snmpc_printerror((enum snmp_error) errorstatus, varbind,
577 errorindex, hint);
578 }
579
580 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
581 printf("Received report:\n");
582 for (; varbind != NULL; varbind = varbind->be_next) {
583 if (!snmpc_print(varbind))
584 err(1, "Can't print response");
585 }
586 ober_free_elements(pdu);
587 snmp_free_agent(agent);
588 return 0;
589 }
590
591 int
snmpc_walk(int argc,char * argv[])592 snmpc_walk(int argc, char *argv[])
593 {
594 struct ber_oid oid, loid, noid;
595 struct ber_element *pdu, *varbind, *value;
596 struct timespec start, finish;
597 struct snmp_agent *agent;
598 const char *oids;
599 int n = 0, prev_cmp, skip_cmp;
600 int errorstatus, errorindex;
601 int class;
602 size_t i;
603 unsigned type;
604
605 if (strcmp(snmp_app->name, "bulkwalk") == 0 && version < SNMP_V2C)
606 errx(1, "Cannot send V2 PDU on V1 session");
607 if (argc < 1 || argc > 2)
608 usage();
609 oids = argc == 1 ? mib : argv[1];
610
611 if ((agent = snmpc_connect(argv[0], "161"))== NULL)
612 err(1, "%s", snmp_app->name);
613 agent->timeout = timeout;
614 agent->retries = retries;
615 if (pledge("stdio", NULL) == -1)
616 err(1, "pledge");
617
618 if (smi_string2oid(oids, &oid) == -1)
619 errx(1, "%s: Unknown object identifier", oids);
620 bcopy(&oid, &noid, sizeof(noid));
621 if (print_time)
622 clock_gettime(CLOCK_MONOTONIC, &start);
623
624 if (walk_include_oid) {
625 if ((pdu = snmp_get(agent, &oid, 1)) == NULL)
626 err(1, "%s", snmp_app->name);
627
628 (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type,
629 &errorstatus, &errorindex, &varbind);
630 if (errorstatus != 0)
631 snmpc_printerror((enum snmp_error) errorstatus, varbind,
632 errorindex, oids);
633
634 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
635 printf("Received report:\n");
636 if (!snmpc_print(varbind))
637 err(1, "Can't print response");
638 ober_free_element(pdu);
639 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
640 return 1;
641 n++;
642 }
643 while (1) {
644 for (i = 0; i < walk_skip_len; i++) {
645 skip_cmp = ober_oid_cmp(&noid, &(walk_skip[i]));
646 if (skip_cmp == 0 || skip_cmp == 2) {
647 bcopy(&(walk_skip[i]), &noid, sizeof(noid));
648 noid.bo_id[noid.bo_n -1]++;
649 break;
650 }
651 }
652 bcopy(&noid, &loid, sizeof(loid));
653 if (strcmp(snmp_app->name, "bulkwalk") == 0) {
654 if ((pdu = snmp_getbulk(agent, &noid, 1,
655 non_repeaters, max_repetitions)) == NULL)
656 err(1, "bulkwalk");
657 } else {
658 if ((pdu = snmp_getnext(agent, &noid, 1)) == NULL)
659 err(1, "walk");
660 }
661
662 (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type,
663 &errorstatus, &errorindex, &varbind);
664 if (errorstatus != 0) {
665 snmpc_printerror((enum snmp_error) errorstatus, varbind,
666 errorindex, NULL);
667 }
668
669 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
670 printf("Received report:\n");
671 for (; varbind != NULL; varbind = varbind->be_next) {
672 (void) ober_scanf_elements(varbind, "{oe}", &noid,
673 &value);
674 if (value->be_class == BER_CLASS_CONTEXT &&
675 value->be_type == BER_TYPE_EOC)
676 break;
677 for (i = 0; i < walk_skip_len; i++) {
678 skip_cmp = ober_oid_cmp(&noid, &(walk_skip[i]));
679 if (skip_cmp == 0 || skip_cmp == 2)
680 break;
681 }
682 if (i < walk_skip_len)
683 continue;
684 prev_cmp = ober_oid_cmp(&noid, &loid);
685 if (walk_check_increase && prev_cmp == -1)
686 errx(1, "OID not increasing");
687 if (prev_cmp == 0 || ober_oid_cmp(&noid, &oid) != 2)
688 break;
689 if (walk_end.bo_n != 0 &&
690 ober_oid_cmp(&noid, &walk_end) != -1)
691 break;
692
693 if (!snmpc_print(varbind))
694 err(1, "Can't print response");
695 n++;
696 }
697 ober_free_elements(pdu);
698 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
699 return 1;
700 if (varbind != NULL)
701 break;
702 }
703 if (walk_fallback_oid && n == 0) {
704 if ((pdu = snmp_get(agent, &oid, 1)) == NULL)
705 err(1, "%s", snmp_app->name);
706
707 (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type,
708 &errorstatus, &errorindex, &varbind);
709 if (errorstatus != 0)
710 snmpc_printerror((enum snmp_error) errorstatus, varbind,
711 errorindex, oids);
712
713 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
714 printf("Received report:\n");
715 if (!snmpc_print(varbind))
716 err(1, "Can't print response");
717 ober_free_element(pdu);
718 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
719 return 1;
720 n++;
721 }
722 if (print_time)
723 clock_gettime(CLOCK_MONOTONIC, &finish);
724 if (print_summary)
725 printf("Variables found: %d\n", n);
726 if (print_time) {
727 if ((finish.tv_nsec -= start.tv_nsec) < 0) {
728 finish.tv_sec -= 1;
729 finish.tv_nsec += 1000000000;
730 }
731 finish.tv_sec -= start.tv_sec;
732 fprintf(stderr, "Total traversal time: %lld.%09ld seconds\n",
733 finish.tv_sec, finish.tv_nsec);
734 }
735 snmp_free_agent(agent);
736 return 0;
737 }
738
739 int
snmpc_set(int argc,char * argv[])740 snmpc_set(int argc, char *argv[])
741 {
742 struct snmp_agent *agent;
743 struct ber_element *pdu, *varbind;
744 int errorstatus, errorindex;
745 int class;
746 unsigned type;
747 char *hint = NULL;
748
749 if (argc < 4)
750 usage();
751 if ((agent = snmpc_connect(argv[0], "161")) == NULL)
752 err(1, "%s", snmp_app->name);
753 argc--;
754 argv++;
755
756 if (pledge("stdio", NULL) == -1)
757 err(1, "pledge");
758
759 if ((pdu = snmp_set(agent, snmpc_varbindparse(argc, argv))) == NULL)
760 err(1, "set");
761
762 (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type, &errorstatus,
763 &errorindex, &varbind);
764 if (errorstatus != 0) {
765 if (errorindex >= 1 && errorindex <= argc / 3)
766 hint = argv[(errorindex - 1) * 3];
767 snmpc_printerror((enum snmp_error) errorstatus, varbind,
768 errorindex, hint);
769 }
770
771 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
772 printf("Received report:\n");
773 for (; varbind != NULL; varbind = varbind->be_next) {
774 if (!snmpc_print(varbind))
775 err(1, "Can't print response");
776 }
777 ober_free_elements(pdu);
778 snmp_free_agent(agent);
779 return 0;
780 }
781
782 int
snmpc_trap(int argc,char * argv[])783 snmpc_trap(int argc, char *argv[])
784 {
785 struct snmp_agent *agent;
786 struct timespec ts;
787 struct ber_oid trapoid;
788 const char *errstr = NULL;
789 long long lval;
790
791 if (version == SNMP_V1)
792 errx(1, "trap is not supported for snmp v1");
793
794 if (argc < 3)
795 usage();
796
797 if ((agent = snmpc_connect(argv[0], "162")) == NULL)
798 err(1, "%s", snmp_app->name);
799
800 if (pledge("stdio", NULL) == -1)
801 err(1, "pledge");
802
803 if (argv[1][0] == '\0') {
804 if (clock_gettime(CLOCK_UPTIME, &ts) == -1)
805 err(1, "clock_gettime");
806 } else {
807 lval = strtonum(argv[1], 0, UINT32_MAX, &errstr);
808 if (errstr != NULL)
809 errx(1, "Bad value notation (%s)", argv[1]);
810 ts.tv_sec = lval / 100;
811 ts.tv_nsec = (lval % 100) * 10000000;
812 }
813 if (smi_string2oid(argv[2], &trapoid) == -1)
814 errx(1, "Invalid oid: %s\n", argv[2]);
815
816 argc -= 3;
817 argv += 3;
818
819 snmp_trap(agent, &ts, &trapoid, snmpc_varbindparse(argc, argv));
820
821 return 0;
822 }
823
824 #define INCR_NEXTTAB(x) ((x + 8) & ~7)
825 #define NEXTTAB(x) (8 - (x & 7))
826 int
snmpc_df(int argc,char * argv[])827 snmpc_df(int argc, char *argv[])
828 {
829 struct snmpc_df {
830 uint32_t index;
831 char *descr;
832 int descrwidth;
833 /* Theoretical maximum for 2 32 bit values multiplied */
834 char size[21];
835 char used[21];
836 char avail[21];
837 char proc[5];
838 } *df = NULL;
839 struct ber_oid descroid = {{ 1, 3, 6, 1, 2, 1, 25, 2, 3, 1, 3 }, 11};
840 struct ber_oid unitsoid = {{ 1, 3, 6, 1, 2, 1, 25, 2, 3, 1, 4 }, 11};
841 struct ber_oid sizeoid = {{ 1, 3, 6, 1, 2, 1, 25, 2, 3, 1, 5 }, 11};
842 struct ber_oid usedoid = {{ 1, 3, 6, 1, 2, 1, 25, 2, 3, 1, 6 }, 11};
843 struct ber_oid oid, *reqoid;
844 char oids[SNMP_MAX_OID_STRLEN];
845 struct ber_element *pdu, *varbind, *elm;
846 struct snmp_agent *agent;
847 int errorstatus, errorindex;
848 int class;
849 size_t i, j, rows = 0;
850 unsigned type;
851 char *string;
852 int descrlen = 0, sizelen = 0, usedlen = 0, availlen = 0, proclen = 0;
853 int len;
854 long long units, size, used;
855 int fmtret;
856
857 if (argc != 1)
858 usage();
859
860 if ((agent = snmpc_connect(argv[0], "161")) == NULL)
861 err(1, "%s", snmp_app->name);
862 agent->timeout = timeout;
863 agent->retries = retries;
864
865 if (pledge("stdio", NULL) == -1)
866 err(1, "pledge");
867
868 descrlen = sizeof("Description") - 1;
869 sizelen = sizeof("Size") - 1;
870 usedlen = sizeof("Used") - 1;
871 availlen = sizeof("Available") - 1;
872 proclen = sizeof("Used%") - 1;
873
874 bcopy(&descroid, &oid, sizeof(descroid));
875
876 i = 0;
877 while(1) {
878 if (version < SNMP_V2C) {
879 if ((pdu = snmp_getnext(agent, &oid, 1)) == NULL)
880 err(1, "df");
881 } else {
882 if ((pdu = snmp_getbulk(agent, &oid, 1, 0,
883 max_repetitions)) == NULL)
884 err(1, "df");
885 }
886
887 (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type,
888 &errorstatus, &errorindex, &varbind);
889 if (errorstatus != 0)
890 snmpc_printerror((enum snmp_error) errorstatus, varbind,
891 errorindex, NULL);
892
893 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) {
894 printf("Received report:\n");
895 for (; varbind != NULL; varbind = varbind->be_next) {
896 if (!snmpc_print(varbind))
897 err(1, "Can't print response");
898 }
899 return 1;
900 }
901 for (; varbind != NULL; varbind = varbind->be_next) {
902 if (ober_scanf_elements(varbind, "{os", &oid,
903 &string) == -1 ||
904 ober_oid_cmp(&oid, &descroid) != 2)
905 break;
906 rows++;
907 }
908 if ((df = reallocarray(df, rows, sizeof(*df))) == NULL)
909 err(1, "malloc");
910 (void) ober_scanf_elements(pdu, "{SSS{e", &varbind);
911 for (; i < rows; varbind = varbind->be_next, i++) {
912 if (ober_scanf_elements(varbind, "{oe", &oid,
913 &elm) == -1) {
914 i--;
915 rows--;
916 continue;
917 }
918 if (ober_oid_cmp(&oid, &descroid) != 2)
919 break;
920 df[i].index = oid.bo_id[oid.bo_n - 1];
921 if ((df[i].descr = smi_print_element(&oid, elm, 0,
922 smi_os_ascii, 0, utf8)) == NULL) {
923 smi_oid2string(&oid, oids, sizeof(oids),
924 oid_lookup);
925 warn("df: can't print oid %s", oids);
926 i--;
927 rows--;
928 continue;
929 }
930 if ((df[i].descrwidth =
931 (int) snmpc_mbswidth(df[i].descr)) == -1)
932 err(1, "df: invalid hrStorageDescr");
933 if (df[i].descrwidth > descrlen)
934 descrlen = df[i].descrwidth;
935 }
936 ober_free_elements(pdu);
937 if (varbind != NULL)
938 break;
939 }
940
941 if (max_repetitions < 3)
942 max_repetitions = 3;
943 if ((reqoid = reallocarray(NULL, max_repetitions, sizeof(*reqoid))) == NULL)
944 err(1, "malloc");
945 for (i = 0; i < rows;) {
946 for (j = 0; i + j < rows && j < (size_t)max_repetitions / 3;
947 j++) {
948 bcopy(&unitsoid, &(reqoid[(j * 3) + 0]),
949 sizeof(unitsoid));
950 reqoid[(j * 3) + 0].bo_id[
951 reqoid[(j * 3) + 0].bo_n++] = df[i + j].index;
952 bcopy(&sizeoid, &(reqoid[(j * 3) + 1]),
953 sizeof(sizeoid));
954 reqoid[(j * 3) + 1].bo_id[
955 reqoid[(j * 3) + 1].bo_n++] = df[i + j].index;
956 bcopy(&usedoid, &(reqoid[(j * 3) + 2]),
957 sizeof(usedoid));
958 reqoid[(j * 3) + 2].bo_id[
959 reqoid[(j * 3) + 2].bo_n++] = df[i + j].index;
960 }
961 if ((pdu = snmp_get(agent, reqoid, j * 3)) == NULL)
962 err(1, "df");
963 (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type,
964 &errorstatus, &errorindex, &varbind);
965 if (errorstatus != 0)
966 snmpc_printerror((enum snmp_error) errorstatus, varbind,
967 errorindex, NULL);
968 if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) {
969 printf("Received report:\n");
970 for (; varbind != NULL; varbind = varbind->be_next) {
971 if (!snmpc_print(varbind))
972 err(1, "Can't print response");
973 }
974 }
975 for (j = 0; varbind != NULL; i++) {
976 if (ober_scanf_elements(varbind, "{oi}{oi}{oi}",
977 &(reqoid[0]), &units, &(reqoid[1]), &size,
978 &(reqoid[2]), &used, &varbind) == -1) {
979 break;
980 }
981 varbind = varbind->be_next->be_next->be_next;
982
983 unitsoid.bo_id[unitsoid.bo_n++] = df[i].index;
984 if (ober_oid_cmp(&unitsoid, &(reqoid[0])) != 0) {
985 warnx("df: received invalid object");
986 break;
987 }
988 unitsoid.bo_n--;
989 sizeoid.bo_id[sizeoid.bo_n++] = df[i].index;
990 if (ober_oid_cmp(&sizeoid, &(reqoid[1])) != 0) {
991 warnx("df: received invalid object");
992 break;
993 }
994 sizeoid.bo_n--;
995 usedoid.bo_id[usedoid.bo_n++] = df[i].index;
996 if (ober_oid_cmp(&usedoid, &(reqoid[2])) != 0) {
997 warnx("df: received invalid object");
998 break;
999 }
1000 usedoid.bo_n--;
1001 if (print_human)
1002 fmtret = fmt_scaled((units * size), df[i].size);
1003 if (!print_human || fmtret == -1)
1004 snprintf(df[i].size, sizeof(df[i].size), "%lld",
1005 (units * size) / 1024);
1006 len = (int) strlen(df[i].size);
1007 if (len > sizelen)
1008 sizelen = len;
1009 if (print_human)
1010 fmtret = fmt_scaled(units * used, df[i].used);
1011 if (!print_human || fmtret == -1)
1012 snprintf(df[i].used, sizeof(df[i].used), "%lld",
1013 (units * used) / 1024);
1014 len = (int) strlen(df[i].used);
1015 if (len > usedlen)
1016 usedlen = len;
1017 if (print_human)
1018 fmtret = fmt_scaled(units * (size - used),
1019 df[i].avail);
1020 if (!print_human || fmtret == -1)
1021 snprintf(df[i].avail, sizeof(df[i].avail),
1022 "%lld", (units * (size - used)) / 1024);
1023 len = (int) strlen(df[i].avail);
1024 if (len > availlen)
1025 availlen = len;
1026 if (size == 0)
1027 strlcpy(df[i].proc, "0%", sizeof(df[i].proc));
1028 else {
1029 snprintf(df[i].proc, sizeof(df[i].proc),
1030 "%lld%%", (used * 100) / size);
1031 }
1032 len = (int) strlen(df[i].proc);
1033 if (len > proclen)
1034 proclen = len;
1035 j++;
1036 }
1037 if (j == 0) {
1038 warnx("Failed to retrieve information for %s",
1039 df[i].descr);
1040 memmove(df + i, df + i + 1,
1041 (rows - i - 1) * sizeof(*df));
1042 rows--;
1043 i--;
1044 }
1045 }
1046
1047 printf("%-*s%*s%*s%*s%*s\n",
1048 descrlen, "Description",
1049 NEXTTAB(descrlen) + sizelen, "Size",
1050 NEXTTAB(sizelen) + usedlen, "Used",
1051 NEXTTAB(usedlen) + availlen, "Available",
1052 NEXTTAB(availlen) + proclen, "Used%");
1053 for (i = 0; i < rows; i++) {
1054 printf("%s%*s%*s%*s%*s%*s\n",
1055 df[i].descr, descrlen - df[i].descrwidth, "",
1056 NEXTTAB(descrlen) + sizelen, df[i].size,
1057 NEXTTAB(sizelen) + usedlen, df[i].used,
1058 NEXTTAB(usedlen) + availlen, df[i].avail,
1059 NEXTTAB(availlen) + proclen, df[i].proc);
1060 }
1061
1062 return 0;
1063 }
1064
1065 int
snmpc_mibtree(int argc,char * argv[])1066 snmpc_mibtree(int argc, char *argv[])
1067 {
1068 struct oid *oid;
1069 struct ber_oid soid;
1070 char buf[BUFSIZ];
1071 int i;
1072
1073 if (argc == 0) {
1074 for (oid = NULL; (oid = smi_foreach(oid)) != NULL;) {
1075 smi_oid2string(&oid->o_id, buf, sizeof(buf),
1076 oid_lookup);
1077 printf("%s\n", buf);
1078 }
1079 } else {
1080 for (i = 0; i < argc; i++) {
1081 if (smi_string2oid(argv[i], &soid) == -1) {
1082 warnx("%s: Unknown object identifier", argv[i]);
1083 continue;
1084 }
1085 smi_oid2string(&soid, buf, sizeof(buf), oid_lookup);
1086 printf("%s\n", buf);
1087 }
1088 }
1089 return 0;
1090 }
1091
1092 struct snmp_agent *
snmpc_connect(char * host,char * port)1093 snmpc_connect(char *host, char *port)
1094 {
1095 switch (version) {
1096 case SNMP_V1:
1097 case SNMP_V2C:
1098 return snmp_connect_v12(snmpc_parseagent(host, port), version,
1099 community);
1100 case SNMP_V3:
1101 return snmp_connect_v3(snmpc_parseagent(host, port), v3);
1102 }
1103 return NULL;
1104 }
1105
1106 int
snmpc_print(struct ber_element * elm)1107 snmpc_print(struct ber_element *elm)
1108 {
1109 struct ber_oid oid;
1110 char oids[SNMP_MAX_OID_STRLEN];
1111 char *value;
1112
1113 elm = elm->be_sub;
1114 if (ober_get_oid(elm, &oid) != 0) {
1115 errno = EINVAL;
1116 return 0;
1117 }
1118
1119 elm = elm->be_next;
1120 value = smi_print_element(&oid, elm, smi_print_hint, output_string,
1121 oid_lookup, utf8);
1122 if (value == NULL)
1123 return 0;
1124
1125 if (print_varbind_only)
1126 printf("%s\n", value);
1127 else if (print_equals) {
1128 smi_oid2string(&oid, oids, sizeof(oids), oid_lookup);
1129 printf("%s = %s\n", oids, value);
1130 } else {
1131 smi_oid2string(&oid, oids, sizeof(oids), oid_lookup);
1132 printf("%s %s\n", oids, value);
1133 }
1134 free(value);
1135
1136 return 1;
1137 }
1138
1139 __dead void
snmpc_printerror(enum snmp_error error,struct ber_element * varbind,int index,const char * hint)1140 snmpc_printerror(enum snmp_error error, struct ber_element *varbind,
1141 int index, const char *hint)
1142 {
1143 struct ber_oid hoid, vboid;
1144 char oids[SNMP_MAX_OID_STRLEN];
1145 const char *oid = NULL;
1146 int i;
1147
1148 if (index >= 1) {
1149 /* Only print if the index is in the reply */
1150 for (i = 1; varbind != NULL && i < index;
1151 varbind = varbind->be_next)
1152 i++;
1153 if (varbind != NULL &&
1154 ober_get_oid(varbind->be_sub, &vboid) == 0) {
1155 /* If user and reply conform print user input */
1156 if (hint != NULL &&
1157 smi_string2oid(hint, &hoid) == 0 &&
1158 ober_oid_cmp(&hoid, &vboid) == 0)
1159 oid = hint;
1160 else
1161 oid = smi_oid2string(&vboid, oids,
1162 sizeof(oids), oid_lookup);
1163 }
1164 }
1165 if (oid == NULL)
1166 oid = "?";
1167
1168 switch (error) {
1169 case SNMP_ERROR_NONE:
1170 errx(1, "No error, how did I get here?");
1171 case SNMP_ERROR_TOOBIG:
1172 errx(1, "Can't parse oid %s: Response too big", oid);
1173 case SNMP_ERROR_NOSUCHNAME:
1174 errx(1, "Can't parse oid %s: No such object", oid);
1175 case SNMP_ERROR_BADVALUE:
1176 errx(1, "Can't parse oid %s: Bad value", oid);
1177 case SNMP_ERROR_READONLY:
1178 errx(1, "Can't parse oid %s: Read only", oid);
1179 case SNMP_ERROR_GENERR:
1180 errx(1, "Can't parse oid %s: Generic error", oid);
1181 case SNMP_ERROR_NOACCESS:
1182 errx(1, "Can't parse oid %s: Access denied", oid);
1183 case SNMP_ERROR_WRONGTYPE:
1184 errx(1, "Can't parse oid %s: Wrong type", oid);
1185 case SNMP_ERROR_WRONGLENGTH:
1186 errx(1, "Can't parse oid %s: Wrong length", oid);
1187 case SNMP_ERROR_WRONGENC:
1188 errx(1, "Can't parse oid %s: Wrong encoding", oid);
1189 case SNMP_ERROR_WRONGVALUE:
1190 errx(1, "Can't parse oid %s: Wrong value", oid);
1191 case SNMP_ERROR_NOCREATION:
1192 errx(1, "Can't parse oid %s: Can't be created", oid);
1193 case SNMP_ERROR_INCONVALUE:
1194 errx(1, "Can't parse oid %s: Inconsistent value", oid);
1195 case SNMP_ERROR_RESUNAVAIL:
1196 errx(1, "Can't parse oid %s: Resource unavailable", oid);
1197 case SNMP_ERROR_COMMITFAILED:
1198 errx(1, "Can't parse oid %s: Commit failed", oid);
1199 case SNMP_ERROR_UNDOFAILED:
1200 errx(1, "Can't parse oid %s: Undo failed", oid);
1201 case SNMP_ERROR_AUTHERROR:
1202 errx(1, "Can't parse oid %s: Authorization error", oid);
1203 case SNMP_ERROR_NOTWRITABLE:
1204 errx(1, "Can't parse oid %s: Not writable", oid);
1205 case SNMP_ERROR_INCONNAME:
1206 errx(1, "Can't parse oid %s: Inconsistent name", oid);
1207 }
1208 errx(1, "Can't parse oid %s: Unknown error (%d)", oid, error);
1209 }
1210
1211 int
snmpc_parseagent(char * agent,char * defaultport)1212 snmpc_parseagent(char *agent, char *defaultport)
1213 {
1214 struct addrinfo hints, *ai, *ai0 = NULL;
1215 struct sockaddr_un saddr;
1216 char *agentdup, *specifier, *hostname, *port = NULL;
1217 int error;
1218 int s;
1219
1220 if ((agentdup = specifier = strdup(agent)) == NULL)
1221 err(1, NULL);
1222
1223 bzero(&hints, sizeof(hints));
1224 if ((hostname = strchr(specifier, ':')) != NULL) {
1225 *hostname++ = '\0';
1226 if (strcasecmp(specifier, "udp") == 0) {
1227 hints.ai_family = AF_INET;
1228 hints.ai_socktype = SOCK_DGRAM;
1229 } else if (strcasecmp(specifier, "tcp") == 0) {
1230 hints.ai_family = AF_INET;
1231 hints.ai_socktype = SOCK_STREAM;
1232 } else if (strcasecmp(specifier, "udp6") == 0 ||
1233 strcasecmp(specifier, "udpv6") == 0 ||
1234 strcasecmp(specifier, "udpipv6") == 0) {
1235 hints.ai_family = AF_INET6;
1236 hints.ai_socktype = SOCK_DGRAM;
1237 } else if (strcasecmp(specifier, "tcp6") == 0 ||
1238 strcasecmp(specifier, "tcpv6") == 0 ||
1239 strcasecmp(specifier, "tcpipv6") == 0) {
1240 hints.ai_family = AF_INET6;
1241 hints.ai_socktype = SOCK_STREAM;
1242 } else if (strcasecmp(specifier, "unix") == 0) {
1243 hints.ai_family = AF_UNIX;
1244 hints.ai_addr = (struct sockaddr *)&saddr;
1245 hints.ai_addrlen = sizeof(saddr);
1246 saddr.sun_len = sizeof(saddr);
1247 saddr.sun_family = AF_UNIX;
1248 if (strlcpy(saddr.sun_path, hostname,
1249 sizeof(saddr.sun_path)) > sizeof(saddr.sun_path))
1250 errx(1, "Hostname path too long");
1251 ai = &hints;
1252 } else {
1253 *--hostname = ':';
1254 hostname = specifier;
1255 }
1256 } else {
1257 hostname = specifier;
1258 }
1259
1260 if (hints.ai_family == AF_INET) {
1261 if ((port = strchr(hostname, ':')) != NULL)
1262 *port++ = '\0';
1263 } else if (hints.ai_family == AF_INET6 || hints.ai_family == 0) {
1264 if (hostname[0] == '[') {
1265 hints.ai_family = AF_INET6;
1266 hostname++;
1267 if ((port = strchr(hostname, ']')) == NULL)
1268 errx(1, "invalid agent");
1269 *port++ = '\0';
1270 if (port[0] == ':')
1271 *port++ = '\0';
1272 else if (port[0] == '\0')
1273 port = NULL;
1274 else
1275 errx(1, "invalid agent");
1276 } else {
1277 if ((port = strrchr(hostname, ':')) != NULL)
1278 *port++ = '\0';
1279 }
1280 }
1281
1282 if (hints.ai_family != AF_UNIX) {
1283 if (hints.ai_socktype == 0)
1284 hints.ai_socktype = SOCK_DGRAM;
1285 if (port == NULL)
1286 port = defaultport;
1287 error = getaddrinfo(hostname, port, &hints, &ai0);
1288 if (error) {
1289 if (error != EAI_NODATA || port == defaultport)
1290 errx(1, "%s", gai_strerror(error));
1291 *--port = ':';
1292 error = getaddrinfo(hostname, defaultport, &hints,
1293 &ai0);
1294 if (error)
1295 errx(1, "%s", gai_strerror(error));
1296 }
1297 s = -1;
1298 for (ai = ai0; ai != NULL; ai = ai->ai_next) {
1299 if ((s = socket(ai->ai_family, ai->ai_socktype,
1300 ai->ai_protocol)) != -1 &&
1301 connect(s, (struct sockaddr *)ai->ai_addr,
1302 ai->ai_addrlen) != -1)
1303 break;
1304 close(s);
1305 s = -1;
1306 }
1307 } else {
1308 s = socket(AF_UNIX, SOCK_STREAM, 0);
1309 if (connect(s, (struct sockaddr *)ai->ai_addr,
1310 ai->ai_addrlen) == -1)
1311 err(1, "Can't connect to %s", agent);
1312 }
1313 if (s == -1)
1314 err(1, "Can't connect to agent %s", agent);
1315
1316
1317 if (ai0 != NULL)
1318 freeaddrinfo(ai0);
1319 free(agentdup);
1320 return s;
1321 }
1322
1323 char *
snmpc_hex2bin(char * hexstr,size_t * binlen)1324 snmpc_hex2bin(char *hexstr, size_t *binlen)
1325 {
1326 char *decstr;
1327
1328 if (hexstr[0] == '0' && hexstr[1] == 'x')
1329 hexstr += 2;
1330 while (hexstr[0] == ' ' || hexstr[0] == '\t')
1331 hexstr++;
1332
1333 if ((decstr = malloc((strlen(hexstr) / 2) + 1)) == NULL)
1334 return NULL;
1335
1336 for (*binlen = 0; hexstr[0] != '\0'; (*binlen)++) {
1337 hexstr[0] = toupper(hexstr[0]);
1338 hexstr[1] = toupper(hexstr[1]);
1339 if (hexstr[0] >= '0' && hexstr[0] <= '9')
1340 decstr[*binlen] = (hexstr[0] - '0') << 4;
1341 else if (hexstr[0] >= 'A' && hexstr[0] <= 'F')
1342 decstr[*binlen] = ((hexstr[0] - 'A') + 10) << 4;
1343 else
1344 goto fail;
1345 if (hexstr[1] >= '0' && hexstr[1] <= '9')
1346 decstr[*binlen] |= (hexstr[1] - '0');
1347 else if (hexstr[1] >= 'A' && hexstr[1] <= 'F')
1348 decstr[*binlen] |= (hexstr[1] - 'A') + 10;
1349 else
1350 goto fail;
1351
1352 hexstr += 2;
1353 while (hexstr[0] == ' ' || hexstr[0] == '\t')
1354 hexstr++;
1355 }
1356
1357 return decstr;
1358 fail:
1359 errno = EINVAL;
1360 free(decstr);
1361 return NULL;
1362 }
1363
1364 ssize_t
snmpc_mbswidth(char * str)1365 snmpc_mbswidth(char *str)
1366 {
1367 wchar_t wc;
1368 size_t width = 0;
1369 size_t i;
1370 int len;
1371
1372 for (i = 0; (len = mbtowc(&wc, &(str[i]), MB_CUR_MAX)) != 0; i += len) {
1373 if (len == -1) {
1374 mbtowc(NULL, NULL, MB_CUR_MAX);
1375 return -1;
1376 }
1377 width += wcwidth(wc);
1378 }
1379 return width;
1380 }
1381
1382 struct ber_element *
snmpc_varbindparse(int argc,char * argv[])1383 snmpc_varbindparse(int argc, char *argv[])
1384 {
1385 struct ber_oid oid, oidval;
1386 struct in_addr addr4;
1387 char *addr = (char *)&addr4;
1388 char *str = NULL, *tmpstr, *endstr;
1389 const char *errstr = NULL;
1390 struct ber_element *varbind = NULL, *vblist = NULL;
1391 int i, ret;
1392 size_t strl, byte;
1393 long long lval;
1394
1395 if (argc % 3 != 0)
1396 usage();
1397 for (i = 0; i < argc; i += 3) {
1398 if (smi_string2oid(argv[i], &oid) == -1)
1399 errx(1, "Invalid oid: %s\n", argv[i]);
1400 switch (argv[i + 1][0]) {
1401 case 'a':
1402 ret = inet_pton(AF_INET, argv[i + 2], &addr4);
1403 if (ret == -1)
1404 err(1, "inet_pton");
1405 if (ret == 0)
1406 errx(1, "%s: Bad value notation (%s)", argv[i],
1407 argv[i + 2]);
1408 if ((varbind = ober_printf_elements(varbind, "{Oxt}",
1409 &oid, addr, sizeof(addr4), BER_CLASS_APPLICATION,
1410 SNMP_T_IPADDR)) == NULL)
1411 err(1, "ober_printf_elements");
1412 break;
1413 case 'b':
1414 tmpstr = argv[i + 2];
1415 strl = 0;
1416 do {
1417 lval = strtoll(tmpstr, &endstr, 10);
1418 if (endstr[0] != ' ' && endstr[0] != '\t' &&
1419 endstr[0] != ',' && endstr[0] != '\0')
1420 errx(1, "%s: Bad value notation (%s)",
1421 argv[i], argv[i + 2]);
1422 if (tmpstr == endstr) {
1423 tmpstr++;
1424 continue;
1425 }
1426 if (lval < 0)
1427 errx(1, "%s: Bad value notation (%s)",
1428 argv[i], argv[i + 2]);
1429 byte = lval / 8;
1430 if (byte >= strl) {
1431 if ((str = recallocarray(str, strl,
1432 byte + 1, 1)) == NULL)
1433 err(1, "malloc");
1434 strl = byte + 1;
1435 }
1436 str[byte] |= 0x80 >> (lval % 8);
1437 tmpstr = endstr + 1;
1438 } while (endstr[0] != '\0');
1439 /*
1440 * RFC3416 Section 2.5
1441 * A BITS value is encoded as an OCTET STRING
1442 */
1443 goto pastestring;
1444 case 'c':
1445 lval = strtonum(argv[i + 2], 0, UINT32_MAX,
1446 &errstr);
1447 if (errstr != NULL)
1448 errx(1, "%s: Bad value notation (%s)", argv[i],
1449 argv[i + 2]);
1450 if ((varbind = ober_printf_elements(varbind, "{Oit}",
1451 &oid, lval, BER_CLASS_APPLICATION,
1452 SNMP_T_COUNTER32)) == NULL)
1453 err(1, "ober_printf_elements");
1454 break;
1455 case 'd':
1456 /* String always shrinks */
1457 if ((str = malloc(strlen(argv[i + 2]))) == NULL)
1458 err(1, "malloc");
1459 tmpstr = argv[i + 2];
1460 strl = 0;
1461 do {
1462 lval = strtoll(tmpstr, &endstr, 10);
1463 if (endstr[0] != ' ' && endstr[0] != '\t' &&
1464 endstr[0] != '\0')
1465 errx(1, "%s: Bad value notation (%s)",
1466 argv[i], argv[i + 2]);
1467 if (tmpstr == endstr) {
1468 tmpstr++;
1469 continue;
1470 }
1471 if (lval < 0 || lval > 0xff)
1472 errx(1, "%s: Bad value notation (%s)",
1473 argv[i], argv[i + 2]);
1474 str[strl++] = (unsigned char) lval;
1475 tmpstr = endstr + 1;
1476 } while (endstr[0] != '\0');
1477 goto pastestring;
1478 case 'i':
1479 lval = strtonum(argv[i + 2], INT32_MIN, INT32_MAX,
1480 &errstr);
1481 if (errstr != NULL)
1482 errx(1, "%s: Bad value notation (%s)", argv[i],
1483 argv[i + 2]);
1484 if ((varbind = ober_printf_elements(varbind, "{Oi}",
1485 &oid, lval)) == NULL)
1486 err(1, "ober_printf_elements");
1487 break;
1488 case 'n':
1489 if ((varbind = ober_printf_elements(varbind, "{O0}",
1490 &oid)) == NULL)
1491 err(1, "ober_printf_elements");
1492 break;
1493 case 'o':
1494 if (smi_string2oid(argv[i + 2], &oidval) == -1)
1495 errx(1, "%s: Unknown Object Identifier (Sub-id "
1496 "not found: (top) -> %s)", argv[i],
1497 argv[i + 2]);
1498 if ((varbind = ober_printf_elements(varbind, "{OO}",
1499 &oid, &oidval)) == NULL)
1500 err(1, "ober_printf_elements");
1501 break;
1502 case 's':
1503 if ((str = strdup(argv[i + 2])) == NULL)
1504 err(1, NULL);
1505 strl = strlen(argv[i + 2]);
1506 pastestring:
1507 if ((varbind = ober_printf_elements(varbind, "{Ox}",
1508 &oid, str, strl)) == NULL)
1509 err(1, "ober_printf_elements");
1510 free(str);
1511 break;
1512 case 't':
1513 lval = strtonum(argv[i + 2], 0, UINT32_MAX,
1514 &errstr);
1515 if (errstr != NULL)
1516 errx(1, "%s: Bad value notation (%s)", argv[i],
1517 argv[i + 2]);
1518 if ((varbind = ober_printf_elements(varbind, "{Oit}",
1519 &oid, lval, BER_CLASS_APPLICATION,
1520 SNMP_T_TIMETICKS)) == NULL)
1521 err(1, "ober_printf_elements");
1522 break;
1523 case 'u':
1524 lval = strtonum(argv[i + 2], 0, UINT32_MAX,
1525 &errstr);
1526 if (errstr != NULL)
1527 errx(1, "%s: Bad value notation (%s)", argv[i],
1528 argv[i + 2]);
1529 if ((varbind = ober_printf_elements(varbind, "{Oit}",
1530 &oid, lval, BER_CLASS_APPLICATION,
1531 SNMP_T_GAUGE32)) == NULL)
1532 err(1, "ober_printf_elements");
1533 break;
1534 case 'x':
1535 /* String always shrinks */
1536 if ((str = malloc(strlen(argv[i + 2]))) == NULL)
1537 err(1, "malloc");
1538 tmpstr = argv[i + 2];
1539 strl = 0;
1540 do {
1541 lval = strtoll(tmpstr, &endstr, 16);
1542 if (endstr[0] != ' ' && endstr[0] != '\t' &&
1543 endstr[0] != '\0')
1544 errx(1, "%s: Bad value notation (%s)",
1545 argv[i], argv[i + 2]);
1546 if (tmpstr == endstr) {
1547 tmpstr++;
1548 continue;
1549 }
1550 if (lval < 0 || lval > 0xff)
1551 errx(1, "%s: Bad value notation (%s)",
1552 argv[i], argv[i + 2]);
1553 str[strl++] = (unsigned char) lval;
1554 tmpstr = endstr + 1;
1555 } while (endstr[0] != '\0');
1556 goto pastestring;
1557 default:
1558 usage();
1559 }
1560 if (vblist == NULL)
1561 vblist = varbind;
1562 }
1563
1564 return vblist;
1565 }
1566
1567 __dead void
usage(void)1568 usage(void)
1569 {
1570 size_t i;
1571
1572 if (snmp_app != NULL) {
1573 fprintf(stderr, "usage: snmp %s%s%s\n",
1574 snmp_app->name,
1575 snmp_app->usecommonopt ?
1576 " [-A authpass] [-a digest] [-c community] [-e secengineid]\n"
1577 " [-E ctxengineid] [-K localpriv] [-k localauth] [-l seclevel]\n"
1578 " [-n ctxname] [-O afnqvxSQ] [-r retries] [-t timeout] [-u user]\n"
1579 " [-v version] [-X privpass] [-x cipher] [-Z boots,time]\n"
1580 " " : "",
1581 snmp_app->usage == NULL ? " " : snmp_app->usage);
1582 exit(1);
1583 }
1584 for (i = 0; i < (sizeof(snmp_apps)/sizeof(*snmp_apps)); i++) {
1585 if (i == 0)
1586 fprintf(stderr, "usage: ");
1587 else
1588 fprintf(stderr, " ");
1589 fprintf(stderr, "snmp %s%s %s\n",
1590 snmp_apps[i].name,
1591 snmp_apps[i].usecommonopt ?
1592 " [options]" : "",
1593 snmp_apps[i].usage ? snmp_apps[i].usage : "");
1594 }
1595 exit(1);
1596 }
1597