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