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