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