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