Annotation of src/usr.bin/snmp/snmpc.c, Revision 1.41
1.41 ! jsg 1: /* $OpenBSD: snmpc.c,v 1.40 2022/12/26 19:16:03 jmc 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>
1.28 martijn 32: #include <locale.h>
1.1 martijn 33: #include <netdb.h>
34: #include <poll.h>
35: #include <stdio.h>
36: #include <stdlib.h>
37: #include <stdint.h>
38: #include <string.h>
39: #include <time.h>
40: #include <unistd.h>
1.19 martijn 41: #include <util.h>
1.29 martijn 42: #include <wchar.h>
1.1 martijn 43:
44: #include "smi.h"
45: #include "snmp.h"
1.9 martijn 46: #include "usm.h"
1.1 martijn 47:
1.11 martijn 48: #define GETOPT_COMMON "A:a:c:E:e:K:k:l:n:O:r:t:u:v:X:x:Z:"
1.1 martijn 49:
50: int snmpc_get(int, char *[]);
51: int snmpc_walk(int, char *[]);
1.13 martijn 52: int snmpc_set(int, char *[]);
1.1 martijn 53: int snmpc_trap(int, char *[]);
1.19 martijn 54: int snmpc_df(int, char *[]);
1.1 martijn 55: int snmpc_mibtree(int, char *[]);
1.8 martijn 56: struct snmp_agent *snmpc_connect(char *, char *);
1.1 martijn 57: int snmpc_parseagent(char *, char *);
58: int snmpc_print(struct ber_element *);
1.15 martijn 59: __dead void snmpc_printerror(enum snmp_error, struct ber_element *, int,
60: const char *);
1.9 martijn 61: char *snmpc_hex2bin(char *, size_t *);
1.29 martijn 62: ssize_t snmpc_mbswidth(char *);
1.13 martijn 63: struct ber_element *snmpc_varbindparse(int, char *[]);
1.1 martijn 64: void usage(void);
65:
66: struct snmp_app {
67: const char *name;
68: const int usecommonopt;
69: const char *optstring;
70: const char *usage;
1.2 deraadt 71: int (*exec)(int, char *[]);
1.1 martijn 72: };
73:
74: struct snmp_app snmp_apps[] = {
1.2 deraadt 75: { "get", 1, NULL, "agent oid ...", snmpc_get },
76: { "getnext", 1, NULL, "agent oid ...", snmpc_get },
1.20 martijn 77: { "walk", 1, "C:", "[-C cIipt] [-C E endoid] [-C s skipoid] agent [oid]", snmpc_walk },
1.2 deraadt 78: { "bulkget", 1, "C:", "[-C n<nonrep>r<maxrep>] agent oid ...", snmpc_get },
1.20 martijn 79: { "bulkwalk", 1, "C:", "[-C cipn<nonrep>r<maxrep>] [-C s skipoid] agent [oid]", snmpc_walk },
1.13 martijn 80: { "set", 1, NULL, "agent oid type value [oid type value] ...", snmpc_set },
1.2 deraadt 81: { "trap", 1, NULL, "agent uptime oid [oid type value] ...", snmpc_trap },
1.20 martijn 82: { "df", 1, "C:", "[-Ch] [-Cr<maxrep>] agent", snmpc_df },
1.30 martijn 83: { "mibtree", 0, "O:", "[-O fnS] [oid ...]", snmpc_mibtree }
1.1 martijn 84: };
85: struct snmp_app *snmp_app = NULL;
86:
1.37 martijn 87: char *community = NULL;
1.9 martijn 88: struct snmp_v3 *v3;
1.1 martijn 89: char *mib = "mib_2";
90: int retries = 5;
91: int timeout = 1;
1.37 martijn 92: enum snmp_version version = SNMP_V3;
1.1 martijn 93: int print_equals = 1;
94: int print_varbind_only = 0;
95: int print_summary = 0;
96: int print_time = 0;
1.19 martijn 97: int print_human = 0;
1.1 martijn 98: int walk_check_increase = 1;
99: int walk_fallback_oid = 1;
100: int walk_include_oid = 0;
101: int smi_print_hint = 1;
102: int non_repeaters = 0;
103: int max_repetitions = 10;
104: struct ber_oid walk_end = {{0}, 0};
1.18 martijn 105: struct ber_oid *walk_skip = NULL;
106: size_t walk_skip_len = 0;
1.1 martijn 107: enum smi_oid_lookup oid_lookup = smi_oidl_short;
108: enum smi_output_string output_string = smi_os_default;
1.28 martijn 109: int utf8 = 0;
1.1 martijn 110:
111: int
112: main(int argc, char *argv[])
113: {
1.10 martijn 114: const EVP_MD *md = NULL;
1.11 martijn 115: const EVP_CIPHER *cipher = NULL;
1.9 martijn 116: struct snmp_sec *sec;
117: char *user = NULL;
1.24 martijn 118: enum usm_key_level authkeylevel = USM_KEY_UNSET;
1.10 martijn 119: char *authkey = NULL;
120: size_t authkeylen = 0;
1.24 martijn 121: enum usm_key_level privkeylevel = USM_KEY_UNSET;
1.11 martijn 122: char *privkey = NULL;
123: size_t privkeylen = 0;
1.9 martijn 124: int seclevel = SNMP_MSGFLAG_REPORT;
125: char *ctxname = NULL;
126: char *ctxengineid = NULL, *secengineid = NULL;
127: size_t ctxengineidlen, secengineidlen;
128: int zflag = 0;
1.24 martijn 129: long long boots = 0, time = 0;
1.1 martijn 130: char optstr[BUFSIZ];
131: const char *errstr;
132: char *strtolp;
133: int ch;
134: size_t i;
135:
1.28 martijn 136: /*
137: * Determine if output can handle UTF-8 based on locale.
138: */
139: setlocale(LC_CTYPE, "");
140: utf8 = MB_CUR_MAX > 1;
141: /*
142: * SMIv2 allows for UTF-8 text at some locations.
143: * Set it explicitly so we can handle it on the input side.
144: */
145: if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL)
146: errx(1, "setlocale(LC_CTYPE, \"en_US.UTF-8\") failed");
147:
1.22 martijn 148: if (pledge("stdio inet dns unix", NULL) == -1)
1.1 martijn 149: err(1, "pledge");
1.2 deraadt 150:
1.1 martijn 151: if (argc <= 1)
152: usage();
153:
1.14 bluhm 154: optstr[0] = '\0';
1.1 martijn 155: for (i = 0; i < sizeof(snmp_apps)/sizeof(*snmp_apps); i++) {
156: if (strcmp(snmp_apps[i].name, argv[1]) == 0) {
157: snmp_app = &snmp_apps[i];
158: if (snmp_app->optstring != NULL) {
159: if (strlcpy(optstr, snmp_app->optstring,
160: sizeof(optstr)) > sizeof(optstr))
161: errx(1, "strlcat");
162: }
163: break;
164: }
165: }
166: if (snmp_app == NULL)
167: usage();
168:
169: if (snmp_app->usecommonopt) {
170: if (strlcat(optstr, GETOPT_COMMON, sizeof(optstr)) >
171: sizeof(optstr))
172: errx(1, "strlcpy");
173: }
174:
175: argc--;
176: argv++;
177:
178: smi_init();
179:
180: while ((ch = getopt(argc, argv, optstr)) != -1) {
181: switch (ch) {
1.10 martijn 182: case 'A':
183: authkey = optarg;
184: authkeylen = strlen(authkey);
185: authkeylevel = USM_KEY_PASSWORD;
186: break;
187: case 'a':
188: if (strcasecmp(optarg, "MD5") == 0)
189: md = EVP_md5();
190: else if (strcasecmp(optarg, "SHA") == 0)
191: md = EVP_sha1();
192: else if (strcasecmp(optarg, "SHA-224") == 0)
193: md = EVP_sha224();
194: else if (strcasecmp(optarg, "SHA-256") == 0)
195: md = EVP_sha256();
196: else if (strcasecmp(optarg, "SHA-384") == 0)
197: md = EVP_sha384();
198: else if (strcasecmp(optarg, "SHA-512") == 0)
199: md = EVP_sha512();
200: else
201: errx(1, "Invalid authentication protocol "
202: "specified after -a flag: %s", optarg);
203: break;
1.1 martijn 204: case 'c':
205: community = optarg;
206: break;
1.9 martijn 207: case 'E':
208: ctxengineid = snmpc_hex2bin(optarg,
209: &ctxengineidlen);
210: if (ctxengineid == NULL) {
211: if (errno == EINVAL)
212: errx(1, "Bad engine ID value "
213: "after -3E flag.");
214: err(1, "-3E");
215: }
216: break;
217: case 'e':
218: secengineid = snmpc_hex2bin(optarg,
219: &secengineidlen);
220: if (secengineid == NULL) {
221: if (errno == EINVAL)
222: errx(1, "Bad engine ID value "
223: "after -3e flag.");
224: err(1, "-3e");
225: }
226: break;
1.11 martijn 227: case 'K':
228: privkey = snmpc_hex2bin(optarg, &privkeylen);
229: if (privkey == NULL) {
230: if (errno == EINVAL)
231: errx(1, "Bad key value after "
232: "-3K flag.");
233: errx(1, "-3K");
234: }
235: privkeylevel = USM_KEY_LOCALIZED;
1.41 ! jsg 236: break;
1.10 martijn 237: case 'k':
238: authkey = snmpc_hex2bin(optarg, &authkeylen);
239: if (authkey == NULL) {
240: if (errno == EINVAL)
241: errx(1, "Bad key value after -k flag.");
242: err(1, "-k");
243: }
244: authkeylevel = USM_KEY_LOCALIZED;
245: break;
246: case 'l':
247: if (strcasecmp(optarg, "noAuthNoPriv") == 0)
248: seclevel = SNMP_MSGFLAG_REPORT;
249: else if (strcasecmp(optarg, "authNoPriv") == 0)
250: seclevel = SNMP_MSGFLAG_AUTH |
251: SNMP_MSGFLAG_REPORT;
1.11 martijn 252: else if (strcasecmp(optarg, "authPriv") == 0)
253: seclevel = SNMP_MSGFLAG_AUTH |
254: SNMP_MSGFLAG_PRIV | SNMP_MSGFLAG_REPORT;
1.10 martijn 255: else
256: errx(1, "Invalid security level specified "
257: "after -l flag: %s", optarg);
258: break;
1.9 martijn 259: case 'n':
260: ctxname = optarg;
261: break;
1.1 martijn 262: case 'r':
263: if ((retries = strtonum(optarg, 0, INT_MAX,
264: &errstr)) == 0) {
265: if (errstr != NULL)
266: errx(1, "-r: %s argument", errstr);
267: }
268: break;
269: case 't':
270: if ((timeout = strtonum(optarg, 1, INT_MAX,
271: &errstr)) == 0) {
272: if (errstr != NULL)
273: errx(1, "-t: %s argument", errstr);
274: }
275: break;
1.9 martijn 276: case 'u':
277: user = optarg;
278: break;
1.1 martijn 279: case 'v':
280: if (strcmp(optarg, "1") == 0)
281: version = SNMP_V1;
282: else if (strcmp(optarg, "2c") == 0)
283: version = SNMP_V2C;
1.9 martijn 284: else if (strcmp(optarg, "3") == 0)
285: version = SNMP_V3;
1.1 martijn 286: else
287: errc(1, EINVAL, "-v");
288: break;
289: case 'C':
290: for (i = 0; i < strlen(optarg); i++) {
291: switch (optarg[i]) {
292: case 'c':
293: if (strcmp(snmp_app->name, "walk") &&
294: strcmp(snmp_app->name, "bulkwalk"))
295: usage();
296: walk_check_increase = 0;
297: break;
1.19 martijn 298: case 'h':
299: if (strcmp(snmp_app->name, "df"))
300: usage();
301: print_human = 1;
302: break;
1.1 martijn 303: case 'i':
304: if (strcmp(snmp_app->name, "walk") &&
305: strcmp(snmp_app->name, "bulkwalk"))
306: usage();
307: walk_include_oid = 1;
308: break;
309: case 'n':
310: if (strcmp(snmp_app->name, "bulkget") &&
311: strcmp(snmp_app->name, "bulkwalk"))
312: usage();
313: errno = 0;
314: non_repeaters = strtol(&optarg[i + 1],
315: &strtolp, 10);
316: if (non_repeaters < 0 ||
317: errno == ERANGE) {
318: if (non_repeaters < 0)
319: errx(1, "%s%s",
320: "-Cn: too small ",
321: "argument");
322: else
323: errx(1, "%s%s",
324: "-Cn: too large",
325: "argument");
326: } else if (&optarg[i + 1] == strtolp)
327: errx(1, "-Cn invalid argument");
328: i = strtolp - optarg - 1;
329: break;
330: case 'p':
331: if (strcmp(snmp_app->name, "walk") &&
332: strcmp(snmp_app->name, "bulkwalk"))
333: usage();
334: print_summary = 1;
335: break;
336: case 'r':
337: if (strcmp(snmp_app->name, "bulkget") &&
1.19 martijn 338: strcmp(snmp_app->name, "bulkwalk") &&
339: strcmp(snmp_app->name, "df"))
1.1 martijn 340: usage();
341: errno = 0;
342: max_repetitions = strtol(&optarg[i + 1],
343: &strtolp, 10);
344: if (max_repetitions < 0 ||
345: errno == ERANGE) {
346: if (max_repetitions < 0)
347: errx(1, "%s%s",
348: "-Cr: too small ",
349: "argument");
350: else
351: errx(1, "%s%s",
352: "-Cr: too large",
353: "argument");
354: } else if (&optarg[i + 1] == strtolp)
355: errx(1, "-Cr invalid argument");
356: i = strtolp - optarg - 1;
357: break;
1.18 martijn 358: case 's':
359: if (strcmp(snmp_app->name, "walk") &&
360: strcmp(snmp_app->name, "bulkwalk"))
361: usage();
362: if ((walk_skip = recallocarray(
363: walk_skip, walk_skip_len,
364: walk_skip_len + 1,
365: sizeof(*walk_skip))) == NULL)
366: errx(1, "malloc");
367: if (smi_string2oid(argv[optind],
368: &(walk_skip[walk_skip_len])) != 0)
369: errx(1, "%s: %s",
370: "Unknown Object Identifier",
371: argv[optind]);
372: walk_skip_len++;
373: optind++;
374: break;
1.1 martijn 375: case 't':
376: if (strcmp(snmp_app->name, "walk"))
377: usage();
378: print_time = 1;
379: break;
380: case 'E':
381: if (strcmp(snmp_app->name, "walk"))
382: usage();
383: if (smi_string2oid(argv[optind],
384: &walk_end) != 0)
385: errx(1, "%s: %s",
386: "Unknown Object Identifier",
387: argv[optind]);
388: optind++;
389: continue;
390: case 'I':
391: if (strcmp(snmp_app->name, "walk"))
392: usage();
393: walk_fallback_oid = 0;
394: break;
395: default:
396: usage();
397: }
398: if (optarg[i] == 'E')
399: break;
400: }
401: break;
402: case 'O':
403: for (i = 0; i < strlen(optarg); i++) {
404: if (strcmp(snmp_app->name, "mibtree") == 0 &&
405: optarg[i] != 'f' && optarg[i] != 'n' &&
406: optarg[i] != 'S')
407: usage();
408: switch (optarg[i]) {
409: case 'a':
410: output_string = smi_os_ascii;
411: break;
412: case 'f':
413: oid_lookup = smi_oidl_full;
414: break;
415: case 'n':
416: oid_lookup = smi_oidl_numeric;
417: break;
418: case 'q':
419: print_equals = 0;
420: smi_print_hint = 0;
421: break;
422: case 'v':
1.2 deraadt 423: print_varbind_only = 1;
1.1 martijn 424: break;
425: case 'x':
426: output_string = smi_os_hex;
427: break;
428: case 'S':
429: oid_lookup = smi_oidl_short;
430: break;
431: case 'Q':
432: smi_print_hint = 0;
433: break;
434: default:
435: usage();
436: }
437: }
438: break;
1.11 martijn 439: case 'X':
440: privkey = optarg;
441: privkeylen = strlen(privkey);
442: privkeylevel = USM_KEY_PASSWORD;
443: break;
444: case 'x':
445: if (strcasecmp(optarg, "DES") == 0)
446: cipher = EVP_des_cbc();
447: else if (strcasecmp(optarg, "AES") == 0)
448: cipher = EVP_aes_128_cfb128();
449: else
450: errx(1, "Invalid privacy protocol "
451: "specified after -3x flag: %s",
452: optarg);
453: break;
1.9 martijn 454: case 'Z':
455: boots = strtoll(optarg, &strtolp, 10);
456: if (boots < 0 || strtolp == optarg || strtolp[0] != ',')
457: usage();
458: strtolp++;
459: time = strtoll(strtolp, &strtolp, 10);
460: if (boots < 0 || strtolp == optarg)
461: usage();
462: zflag = 1;
463: break;
1.1 martijn 464: default:
465: usage();
466: }
467: }
468: argc -= optind;
469: argv += optind;
470:
1.39 martijn 471: if (!snmp_app->usecommonopt) {
472: /* No SNMP protocol settings */
473: } else if (version == SNMP_V1 || version == SNMP_V2C) {
1.37 martijn 474: if (community == NULL || community[0] == '\0')
475: errx(1, "No community name specified.");
476: } else if (version == SNMP_V3) {
1.9 martijn 477: /* Setup USM */
478: if (user == NULL || user[0] == '\0')
479: errx(1, "No securityName specified");
480: if ((sec = usm_init(user, strlen(user))) == NULL)
481: err(1, "usm_init");
1.10 martijn 482: if (seclevel & SNMP_MSGFLAG_AUTH) {
483: if (md == NULL)
1.35 sthen 484: md = EVP_sha1();
1.10 martijn 485: if (authkey == NULL)
486: errx(1, "No authKey or authPassword specified");
487: if (usm_setauth(sec, md, authkey, authkeylen,
488: authkeylevel) == -1)
489: err(1, "Can't set authkey");
490: }
1.11 martijn 491: if (seclevel & SNMP_MSGFLAG_PRIV) {
492: if (cipher == NULL)
1.34 martijn 493: cipher = EVP_aes_128_cfb128();
1.11 martijn 494: if (privkey == NULL)
495: errx(1, "No privKey or privPassword specified");
496: if (usm_setpriv(sec, cipher, privkey, privkeylen,
497: privkeylevel) == -1)
498: err(1, "Can't set authkey");
499: }
1.9 martijn 500: if (secengineid != NULL) {
501: if (usm_setengineid(sec, secengineid,
502: secengineidlen) == -1)
503: err(1, "Can't set secengineid");
504: }
505: if (zflag)
506: if (usm_setbootstime(sec, boots, time) == -1)
507: err(1, "Can't set boots/time");
508: v3 = snmp_v3_init(seclevel, ctxname, ctxname == NULL ? 0 :
509: strlen(ctxname), sec);
510: if (v3 == NULL)
511: err(1, "snmp_v3_init");
512: if (ctxengineid != NULL) {
513: if (snmp_v3_setengineid(v3, ctxengineid,
514: ctxengineidlen) == -1)
515: err(1, "Can't set ctxengineid");
516: }
517: }
518:
519:
1.1 martijn 520: return snmp_app->exec(argc, argv);
521: }
522:
523: int
524: snmpc_get(int argc, char *argv[])
525: {
526: struct ber_oid *oid;
527: struct ber_element *pdu, *varbind;
528: struct snmp_agent *agent;
529: int errorstatus, errorindex;
530: int i;
1.9 martijn 531: int class;
532: unsigned type;
1.15 martijn 533: char *hint = NULL;
1.1 martijn 534:
535: if (argc < 2)
536: usage();
537:
1.8 martijn 538: if ((agent = snmpc_connect(argv[0], "161")) == NULL)
1.1 martijn 539: err(1, "%s", snmp_app->name);
540: agent->timeout = timeout;
541: agent->retries = retries;
542:
543: if (pledge("stdio", NULL) == -1)
544: err(1, "pledge");
545: argc--;
546: argv++;
547:
548: oid = reallocarray(NULL, argc, sizeof(*oid));
1.3 deraadt 549: if (oid == NULL)
550: err(1, "malloc");
1.1 martijn 551: for (i = 0; i < argc; i++) {
552: if (smi_string2oid(argv[i], &oid[i]) == -1)
1.12 semarie 553: errx(1, "%s: Unknown object identifier", argv[i]);
1.1 martijn 554: }
555: if (strcmp(snmp_app->name, "getnext") == 0) {
556: if ((pdu = snmp_getnext(agent, oid, argc)) == NULL)
557: err(1, "getnext");
558: } else if (strcmp(snmp_app->name, "bulkget") == 0) {
559: if (version < SNMP_V2C)
560: errx(1, "Cannot send V2 PDU on V1 session");
561: if (non_repeaters > argc)
562: errx(1, "need more objects than -Cn<num>");
563: if ((pdu = snmp_getbulk(agent, oid, argc, non_repeaters,
564: max_repetitions)) == NULL)
565: err(1, "bulkget");
566: } else {
567: if ((pdu = snmp_get(agent, oid, argc)) == NULL)
568: err(1, "get");
569: }
570:
1.16 tb 571: (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type, &errorstatus,
1.9 martijn 572: &errorindex, &varbind);
1.15 martijn 573: if (errorstatus != 0) {
574: if (errorindex >= 1 && errorindex <= argc)
575: hint = argv[errorindex - 1];
576: snmpc_printerror((enum snmp_error) errorstatus, varbind,
577: errorindex, hint);
578: }
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: for (; varbind != NULL; varbind = varbind->be_next) {
583: if (!snmpc_print(varbind))
584: err(1, "Can't print response");
585: }
1.16 tb 586: ober_free_elements(pdu);
1.1 martijn 587: snmp_free_agent(agent);
588: return 0;
589: }
590:
591: int
592: snmpc_walk(int argc, char *argv[])
593: {
594: struct ber_oid oid, loid, noid;
595: struct ber_element *pdu, *varbind, *value;
596: struct timespec start, finish;
597: struct snmp_agent *agent;
598: const char *oids;
1.18 martijn 599: int n = 0, prev_cmp, skip_cmp;
1.1 martijn 600: int errorstatus, errorindex;
1.9 martijn 601: int class;
1.18 martijn 602: size_t i;
1.9 martijn 603: unsigned type;
1.1 martijn 604:
605: if (strcmp(snmp_app->name, "bulkwalk") == 0 && version < SNMP_V2C)
606: errx(1, "Cannot send V2 PDU on V1 session");
607: if (argc < 1 || argc > 2)
608: usage();
609: oids = argc == 1 ? mib : argv[1];
610:
1.8 martijn 611: if ((agent = snmpc_connect(argv[0], "161"))== NULL)
1.1 martijn 612: err(1, "%s", snmp_app->name);
613: agent->timeout = timeout;
614: agent->retries = retries;
615: if (pledge("stdio", NULL) == -1)
616: err(1, "pledge");
617:
618: if (smi_string2oid(oids, &oid) == -1)
619: errx(1, "%s: Unknown object identifier", oids);
620: bcopy(&oid, &noid, sizeof(noid));
621: if (print_time)
622: clock_gettime(CLOCK_MONOTONIC, &start);
623:
624: if (walk_include_oid) {
625: if ((pdu = snmp_get(agent, &oid, 1)) == NULL)
626: err(1, "%s", snmp_app->name);
627:
1.16 tb 628: (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type,
1.9 martijn 629: &errorstatus, &errorindex, &varbind);
1.1 martijn 630: if (errorstatus != 0)
1.15 martijn 631: snmpc_printerror((enum snmp_error) errorstatus, varbind,
632: errorindex, oids);
1.1 martijn 633:
1.9 martijn 634: if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
635: printf("Received report:\n");
1.1 martijn 636: if (!snmpc_print(varbind))
637: err(1, "Can't print response");
1.16 tb 638: ober_free_element(pdu);
1.9 martijn 639: if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
640: return 1;
1.1 martijn 641: n++;
642: }
643: while (1) {
1.18 martijn 644: for (i = 0; i < walk_skip_len; i++) {
1.38 martijn 645: skip_cmp = ober_oid_cmp(&noid, &(walk_skip[i]));
1.18 martijn 646: if (skip_cmp == 0 || skip_cmp == 2) {
647: bcopy(&(walk_skip[i]), &noid, sizeof(noid));
648: noid.bo_id[noid.bo_n -1]++;
649: break;
650: }
651: }
1.1 martijn 652: bcopy(&noid, &loid, sizeof(loid));
653: if (strcmp(snmp_app->name, "bulkwalk") == 0) {
654: if ((pdu = snmp_getbulk(agent, &noid, 1,
655: non_repeaters, max_repetitions)) == NULL)
656: err(1, "bulkwalk");
657: } else {
658: if ((pdu = snmp_getnext(agent, &noid, 1)) == NULL)
659: err(1, "walk");
660: }
661:
1.16 tb 662: (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type,
1.9 martijn 663: &errorstatus, &errorindex, &varbind);
1.1 martijn 664: if (errorstatus != 0) {
1.15 martijn 665: snmpc_printerror((enum snmp_error) errorstatus, varbind,
666: errorindex, NULL);
1.1 martijn 667: }
668:
1.9 martijn 669: if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
670: printf("Received report:\n");
1.2 deraadt 671: for (; varbind != NULL; varbind = varbind->be_next) {
1.16 tb 672: (void) ober_scanf_elements(varbind, "{oe}", &noid,
1.1 martijn 673: &value);
674: if (value->be_class == BER_CLASS_CONTEXT &&
675: value->be_type == BER_TYPE_EOC)
676: break;
1.18 martijn 677: for (i = 0; i < walk_skip_len; i++) {
1.38 martijn 678: skip_cmp = ober_oid_cmp(&noid, &(walk_skip[i]));
1.18 martijn 679: if (skip_cmp == 0 || skip_cmp == 2)
680: break;
681: }
682: if (i < walk_skip_len)
683: continue;
1.38 martijn 684: prev_cmp = ober_oid_cmp(&noid, &loid);
1.1 martijn 685: if (walk_check_increase && prev_cmp == -1)
686: errx(1, "OID not increasing");
1.38 martijn 687: if (prev_cmp == 0 || ober_oid_cmp(&noid, &oid) != 2)
1.1 martijn 688: break;
689: if (walk_end.bo_n != 0 &&
1.38 martijn 690: ober_oid_cmp(&noid, &walk_end) != -1)
1.1 martijn 691: break;
692:
693: if (!snmpc_print(varbind))
694: err(1, "Can't print response");
695: n++;
696: }
1.16 tb 697: ober_free_elements(pdu);
1.9 martijn 698: if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
699: return 1;
1.1 martijn 700: if (varbind != NULL)
701: break;
702: }
703: if (walk_fallback_oid && n == 0) {
704: if ((pdu = snmp_get(agent, &oid, 1)) == NULL)
705: err(1, "%s", snmp_app->name);
706:
1.16 tb 707: (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type,
1.9 martijn 708: &errorstatus, &errorindex, &varbind);
1.1 martijn 709: if (errorstatus != 0)
1.15 martijn 710: snmpc_printerror((enum snmp_error) errorstatus, varbind,
711: errorindex, oids);
1.1 martijn 712:
1.9 martijn 713: if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
714: printf("Received report:\n");
1.1 martijn 715: if (!snmpc_print(varbind))
716: err(1, "Can't print response");
1.16 tb 717: ober_free_element(pdu);
1.9 martijn 718: if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
719: return 1;
1.1 martijn 720: n++;
721: }
722: if (print_time)
723: clock_gettime(CLOCK_MONOTONIC, &finish);
724: if (print_summary)
725: printf("Variables found: %d\n", n);
726: if (print_time) {
727: if ((finish.tv_nsec -= start.tv_nsec) < 0) {
728: finish.tv_sec -= 1;
729: finish.tv_nsec += 1000000000;
730: }
731: finish.tv_sec -= start.tv_sec;
732: fprintf(stderr, "Total traversal time: %lld.%09ld seconds\n",
733: finish.tv_sec, finish.tv_nsec);
734: }
735: snmp_free_agent(agent);
736: return 0;
737: }
738:
739: int
1.13 martijn 740: snmpc_set(int argc, char *argv[])
741: {
742: struct snmp_agent *agent;
743: struct ber_element *pdu, *varbind;
744: int errorstatus, errorindex;
745: int class;
746: unsigned type;
1.15 martijn 747: char *hint = NULL;
1.13 martijn 748:
749: if (argc < 4)
750: usage();
751: if ((agent = snmpc_connect(argv[0], "161")) == NULL)
752: err(1, "%s", snmp_app->name);
753: argc--;
754: argv++;
755:
756: if (pledge("stdio", NULL) == -1)
757: err(1, "pledge");
758:
1.15 martijn 759: if ((pdu = snmp_set(agent, snmpc_varbindparse(argc, argv))) == NULL)
760: err(1, "set");
1.13 martijn 761:
1.16 tb 762: (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type, &errorstatus,
1.13 martijn 763: &errorindex, &varbind);
1.15 martijn 764: if (errorstatus != 0) {
765: if (errorindex >= 1 && errorindex <= argc / 3)
766: hint = argv[(errorindex - 1) * 3];
767: snmpc_printerror((enum snmp_error) errorstatus, varbind,
768: errorindex, hint);
769: }
1.13 martijn 770:
771: if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT)
772: printf("Received report:\n");
773: for (; varbind != NULL; varbind = varbind->be_next) {
774: if (!snmpc_print(varbind))
775: err(1, "Can't print response");
776: }
1.16 tb 777: ober_free_elements(pdu);
1.13 martijn 778: snmp_free_agent(agent);
779: return 0;
780: }
781:
782: int
1.1 martijn 783: snmpc_trap(int argc, char *argv[])
784: {
785: struct snmp_agent *agent;
786: struct timespec ts;
1.13 martijn 787: struct ber_oid trapoid;
1.1 martijn 788: const char *errstr = NULL;
789: long long lval;
790:
791: if (version == SNMP_V1)
792: errx(1, "trap is not supported for snmp v1");
1.31 martijn 793:
794: if (argc < 3)
795: usage();
1.1 martijn 796:
1.8 martijn 797: if ((agent = snmpc_connect(argv[0], "162")) == NULL)
1.1 martijn 798: err(1, "%s", snmp_app->name);
799:
800: if (pledge("stdio", NULL) == -1)
801: err(1, "pledge");
802:
803: if (argv[1][0] == '\0') {
804: if (clock_gettime(CLOCK_UPTIME, &ts) == -1)
805: err(1, "clock_gettime");
806: } else {
1.33 martijn 807: lval = strtonum(argv[1], 0, UINT32_MAX, &errstr);
1.1 martijn 808: if (errstr != NULL)
809: errx(1, "Bad value notation (%s)", argv[1]);
810: ts.tv_sec = lval / 100;
811: ts.tv_nsec = (lval % 100) * 10000000;
812: }
813: if (smi_string2oid(argv[2], &trapoid) == -1)
814: errx(1, "Invalid oid: %s\n", argv[2]);
815:
816: argc -= 3;
817: argv += 3;
818:
1.13 martijn 819: snmp_trap(agent, &ts, &trapoid, snmpc_varbindparse(argc, argv));
1.19 martijn 820:
821: return 0;
822: }
823:
824: #define INCR_NEXTTAB(x) ((x + 8) & ~7)
825: #define NEXTTAB(x) (8 - (x & 7))
826: int
827: snmpc_df(int argc, char *argv[])
828: {
829: struct snmpc_df {
830: uint32_t index;
1.29 martijn 831: char *descr;
832: int descrwidth;
1.19 martijn 833: /* Theoretical maximum for 2 32 bit values multiplied */
834: char size[21];
835: char used[21];
836: char avail[21];
837: char proc[5];
838: } *df = NULL;
839: struct ber_oid descroid = {{ 1, 3, 6, 1, 2, 1, 25, 2, 3, 1, 3 }, 11};
840: struct ber_oid unitsoid = {{ 1, 3, 6, 1, 2, 1, 25, 2, 3, 1, 4 }, 11};
841: struct ber_oid sizeoid = {{ 1, 3, 6, 1, 2, 1, 25, 2, 3, 1, 5 }, 11};
842: struct ber_oid usedoid = {{ 1, 3, 6, 1, 2, 1, 25, 2, 3, 1, 6 }, 11};
843: struct ber_oid oid, *reqoid;
1.29 martijn 844: char oids[SNMP_MAX_OID_STRLEN];
845: struct ber_element *pdu, *varbind, *elm;
1.19 martijn 846: struct snmp_agent *agent;
847: int errorstatus, errorindex;
848: int class;
849: size_t i, j, rows = 0;
850: unsigned type;
851: char *string;
852: int descrlen = 0, sizelen = 0, usedlen = 0, availlen = 0, proclen = 0;
853: int len;
854: long long units, size, used;
855: int fmtret;
856:
857: if (argc != 1)
858: usage();
859:
860: if ((agent = snmpc_connect(argv[0], "161")) == NULL)
861: err(1, "%s", snmp_app->name);
862: agent->timeout = timeout;
863: agent->retries = retries;
864:
865: if (pledge("stdio", NULL) == -1)
866: err(1, "pledge");
867:
868: descrlen = sizeof("Description") - 1;
869: sizelen = sizeof("Size") - 1;
870: usedlen = sizeof("Used") - 1;
871: availlen = sizeof("Available") - 1;
872: proclen = sizeof("Used%") - 1;
873:
874: bcopy(&descroid, &oid, sizeof(descroid));
875:
876: i = 0;
877: while(1) {
878: if (version < SNMP_V2C) {
879: if ((pdu = snmp_getnext(agent, &oid, 1)) == NULL)
880: err(1, "df");
881: } else {
882: if ((pdu = snmp_getbulk(agent, &oid, 1, 0,
883: max_repetitions)) == NULL)
884: err(1, "df");
885: }
886:
887: (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type,
888: &errorstatus, &errorindex, &varbind);
889: if (errorstatus != 0)
890: snmpc_printerror((enum snmp_error) errorstatus, varbind,
891: errorindex, NULL);
892:
893: if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) {
894: printf("Received report:\n");
895: for (; varbind != NULL; varbind = varbind->be_next) {
896: if (!snmpc_print(varbind))
897: err(1, "Can't print response");
898: }
899: return 1;
900: }
901: for (; varbind != NULL; varbind = varbind->be_next) {
1.29 martijn 902: if (ober_scanf_elements(varbind, "{os", &oid,
903: &string) == -1 ||
1.38 martijn 904: ober_oid_cmp(&oid, &descroid) != 2)
1.19 martijn 905: break;
906: rows++;
907: }
908: if ((df = reallocarray(df, rows, sizeof(*df))) == NULL)
909: err(1, "malloc");
910: (void) ober_scanf_elements(pdu, "{SSS{e", &varbind);
911: for (; i < rows; varbind = varbind->be_next, i++) {
1.29 martijn 912: if (ober_scanf_elements(varbind, "{oe", &oid,
913: &elm) == -1) {
1.19 martijn 914: i--;
915: rows--;
916: continue;
917: }
1.38 martijn 918: if (ober_oid_cmp(&oid, &descroid) != 2)
1.29 martijn 919: break;
1.19 martijn 920: df[i].index = oid.bo_id[oid.bo_n - 1];
1.29 martijn 921: if ((df[i].descr = smi_print_element(&oid, elm, 0,
922: smi_os_ascii, 0, utf8)) == NULL) {
923: smi_oid2string(&oid, oids, sizeof(oids),
924: oid_lookup);
925: warn("df: can't print oid %s", oids);
926: i--;
927: rows--;
928: continue;
929: }
930: if ((df[i].descrwidth =
931: (int) snmpc_mbswidth(df[i].descr)) == -1)
932: err(1, "df: invalid hrStorageDescr");
933: if (df[i].descrwidth > descrlen)
934: descrlen = df[i].descrwidth;
1.19 martijn 935: }
936: ober_free_elements(pdu);
937: if (varbind != NULL)
938: break;
939: }
940:
941: if (max_repetitions < 3)
942: max_repetitions = 3;
943: if ((reqoid = reallocarray(NULL, max_repetitions, sizeof(*reqoid))) == NULL)
944: err(1, "malloc");
945: for (i = 0; i < rows;) {
946: for (j = 0; i + j < rows && j < (size_t)max_repetitions / 3;
947: j++) {
948: bcopy(&unitsoid, &(reqoid[(j * 3) + 0]),
949: sizeof(unitsoid));
950: reqoid[(j * 3) + 0].bo_id[
951: reqoid[(j * 3) + 0].bo_n++] = df[i + j].index;
952: bcopy(&sizeoid, &(reqoid[(j * 3) + 1]),
953: sizeof(sizeoid));
954: reqoid[(j * 3) + 1].bo_id[
955: reqoid[(j * 3) + 1].bo_n++] = df[i + j].index;
956: bcopy(&usedoid, &(reqoid[(j * 3) + 2]),
957: sizeof(usedoid));
958: reqoid[(j * 3) + 2].bo_id[
959: reqoid[(j * 3) + 2].bo_n++] = df[i + j].index;
960: }
961: if ((pdu = snmp_get(agent, reqoid, j * 3)) == NULL)
962: err(1, "df");
963: (void) ober_scanf_elements(pdu, "t{Sdd{e", &class, &type,
964: &errorstatus, &errorindex, &varbind);
965: if (errorstatus != 0)
966: snmpc_printerror((enum snmp_error) errorstatus, varbind,
967: errorindex, NULL);
968: if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) {
969: printf("Received report:\n");
970: for (; varbind != NULL; varbind = varbind->be_next) {
971: if (!snmpc_print(varbind))
972: err(1, "Can't print response");
973: }
974: }
975: for (j = 0; varbind != NULL; i++) {
976: if (ober_scanf_elements(varbind, "{oi}{oi}{oi}",
977: &(reqoid[0]), &units, &(reqoid[1]), &size,
978: &(reqoid[2]), &used, &varbind) == -1) {
979: break;
980: }
981: varbind = varbind->be_next->be_next->be_next;
982:
983: unitsoid.bo_id[unitsoid.bo_n++] = df[i].index;
984: if (ober_oid_cmp(&unitsoid, &(reqoid[0])) != 0) {
985: warnx("df: received invalid object");
986: break;
987: }
988: unitsoid.bo_n--;
989: sizeoid.bo_id[sizeoid.bo_n++] = df[i].index;
990: if (ober_oid_cmp(&sizeoid, &(reqoid[1])) != 0) {
991: warnx("df: received invalid object");
992: break;
993: }
994: sizeoid.bo_n--;
995: usedoid.bo_id[usedoid.bo_n++] = df[i].index;
996: if (ober_oid_cmp(&usedoid, &(reqoid[2])) != 0) {
997: warnx("df: received invalid object");
998: break;
999: }
1000: usedoid.bo_n--;
1001: if (print_human)
1002: fmtret = fmt_scaled((units * size), df[i].size);
1003: if (!print_human || fmtret == -1)
1004: snprintf(df[i].size, sizeof(df[i].size), "%lld",
1005: (units * size) / 1024);
1006: len = (int) strlen(df[i].size);
1007: if (len > sizelen)
1008: sizelen = len;
1009: if (print_human)
1010: fmtret = fmt_scaled(units * used, df[i].used);
1011: if (!print_human || fmtret == -1)
1012: snprintf(df[i].used, sizeof(df[i].used), "%lld",
1013: (units * used) / 1024);
1014: len = (int) strlen(df[i].used);
1015: if (len > usedlen)
1016: usedlen = len;
1017: if (print_human)
1018: fmtret = fmt_scaled(units * (size - used),
1019: df[i].avail);
1020: if (!print_human || fmtret == -1)
1021: snprintf(df[i].avail, sizeof(df[i].avail),
1022: "%lld", (units * (size - used)) / 1024);
1023: len = (int) strlen(df[i].avail);
1.23 martijn 1024: if (len > availlen)
1.19 martijn 1025: availlen = len;
1026: if (size == 0)
1027: strlcpy(df[i].proc, "0%", sizeof(df[i].proc));
1028: else {
1029: snprintf(df[i].proc, sizeof(df[i].proc),
1030: "%lld%%", (used * 100) / size);
1031: }
1032: len = (int) strlen(df[i].proc);
1033: if (len > proclen)
1034: proclen = len;
1035: j++;
1036: }
1037: if (j == 0) {
1038: warnx("Failed to retrieve information for %s",
1039: df[i].descr);
1040: memmove(df + i, df + i + 1,
1041: (rows - i - 1) * sizeof(*df));
1042: rows--;
1043: i--;
1044: }
1045: }
1046:
1047: printf("%-*s%*s%*s%*s%*s\n",
1048: descrlen, "Description",
1049: NEXTTAB(descrlen) + sizelen, "Size",
1050: NEXTTAB(sizelen) + usedlen, "Used",
1051: NEXTTAB(usedlen) + availlen, "Available",
1052: NEXTTAB(availlen) + proclen, "Used%");
1053: for (i = 0; i < rows; i++) {
1.29 martijn 1054: printf("%s%*s%*s%*s%*s%*s\n",
1055: df[i].descr, descrlen - df[i].descrwidth, "",
1.19 martijn 1056: NEXTTAB(descrlen) + sizelen, df[i].size,
1057: NEXTTAB(sizelen) + usedlen, df[i].used,
1058: NEXTTAB(usedlen) + availlen, df[i].avail,
1059: NEXTTAB(availlen) + proclen, df[i].proc);
1060: }
1.1 martijn 1061:
1062: return 0;
1063: }
1064:
1065: int
1066: snmpc_mibtree(int argc, char *argv[])
1067: {
1068: struct oid *oid;
1.30 martijn 1069: struct ber_oid soid;
1.1 martijn 1070: char buf[BUFSIZ];
1.30 martijn 1071: int i;
1.1 martijn 1072:
1.30 martijn 1073: if (argc == 0) {
1074: for (oid = NULL; (oid = smi_foreach(oid)) != NULL;) {
1075: smi_oid2string(&oid->o_id, buf, sizeof(buf),
1076: oid_lookup);
1077: printf("%s\n", buf);
1078: }
1079: } else {
1080: for (i = 0; i < argc; i++) {
1081: if (smi_string2oid(argv[i], &soid) == -1) {
1082: warnx("%s: Unknown object identifier", argv[i]);
1083: continue;
1084: }
1085: smi_oid2string(&soid, buf, sizeof(buf), oid_lookup);
1086: printf("%s\n", buf);
1087: }
1.1 martijn 1088: }
1089: return 0;
1.8 martijn 1090: }
1091:
1092: struct snmp_agent *
1093: snmpc_connect(char *host, char *port)
1094: {
1095: switch (version) {
1096: case SNMP_V1:
1097: case SNMP_V2C:
1098: return snmp_connect_v12(snmpc_parseagent(host, port), version,
1099: community);
1.9 martijn 1100: case SNMP_V3:
1101: return snmp_connect_v3(snmpc_parseagent(host, port), v3);
1.8 martijn 1102: }
1103: return NULL;
1.1 martijn 1104: }
1105:
1106: int
1107: snmpc_print(struct ber_element *elm)
1108: {
1109: struct ber_oid oid;
1110: char oids[SNMP_MAX_OID_STRLEN];
1111: char *value;
1112:
1113: elm = elm->be_sub;
1.16 tb 1114: if (ober_get_oid(elm, &oid) != 0) {
1.1 martijn 1115: errno = EINVAL;
1116: return 0;
1117: }
1118:
1119: elm = elm->be_next;
1.28 martijn 1120: value = smi_print_element(&oid, elm, smi_print_hint, output_string,
1121: oid_lookup, utf8);
1.1 martijn 1122: if (value == NULL)
1123: return 0;
1124:
1125: if (print_varbind_only)
1126: printf("%s\n", value);
1127: else if (print_equals) {
1128: smi_oid2string(&oid, oids, sizeof(oids), oid_lookup);
1129: printf("%s = %s\n", oids, value);
1130: } else {
1131: smi_oid2string(&oid, oids, sizeof(oids), oid_lookup);
1132: printf("%s %s\n", oids, value);
1133: }
1134: free(value);
1.2 deraadt 1135:
1.1 martijn 1136: return 1;
1137: }
1138:
1139: __dead void
1.15 martijn 1140: snmpc_printerror(enum snmp_error error, struct ber_element *varbind,
1141: int index, const char *hint)
1.1 martijn 1142: {
1.15 martijn 1143: struct ber_oid hoid, vboid;
1144: char oids[SNMP_MAX_OID_STRLEN];
1145: const char *oid = NULL;
1146: int i;
1147:
1148: if (index >= 1) {
1149: /* Only print if the index is in the reply */
1.26 martijn 1150: for (i = 1; varbind != NULL && i < index;
1.15 martijn 1151: varbind = varbind->be_next)
1152: i++;
1153: if (varbind != NULL &&
1.16 tb 1154: ober_get_oid(varbind->be_sub, &vboid) == 0) {
1.15 martijn 1155: /* If user and reply conform print user input */
1156: if (hint != NULL &&
1157: smi_string2oid(hint, &hoid) == 0 &&
1.16 tb 1158: ober_oid_cmp(&hoid, &vboid) == 0)
1.15 martijn 1159: oid = hint;
1160: else
1161: oid = smi_oid2string(&vboid, oids,
1162: sizeof(oids), oid_lookup);
1163: }
1164: }
1165: if (oid == NULL)
1166: oid = "?";
1167:
1.2 deraadt 1168: switch (error) {
1.1 martijn 1169: case SNMP_ERROR_NONE:
1170: errx(1, "No error, how did I get here?");
1171: case SNMP_ERROR_TOOBIG:
1172: errx(1, "Can't parse oid %s: Response too big", oid);
1173: case SNMP_ERROR_NOSUCHNAME:
1174: errx(1, "Can't parse oid %s: No such object", oid);
1175: case SNMP_ERROR_BADVALUE:
1176: errx(1, "Can't parse oid %s: Bad value", oid);
1177: case SNMP_ERROR_READONLY:
1178: errx(1, "Can't parse oid %s: Read only", oid);
1179: case SNMP_ERROR_GENERR:
1180: errx(1, "Can't parse oid %s: Generic error", oid);
1181: case SNMP_ERROR_NOACCESS:
1182: errx(1, "Can't parse oid %s: Access denied", oid);
1183: case SNMP_ERROR_WRONGTYPE:
1184: errx(1, "Can't parse oid %s: Wrong type", oid);
1185: case SNMP_ERROR_WRONGLENGTH:
1186: errx(1, "Can't parse oid %s: Wrong length", oid);
1187: case SNMP_ERROR_WRONGENC:
1188: errx(1, "Can't parse oid %s: Wrong encoding", oid);
1189: case SNMP_ERROR_WRONGVALUE:
1190: errx(1, "Can't parse oid %s: Wrong value", oid);
1191: case SNMP_ERROR_NOCREATION:
1192: errx(1, "Can't parse oid %s: Can't be created", oid);
1193: case SNMP_ERROR_INCONVALUE:
1194: errx(1, "Can't parse oid %s: Inconsistent value", oid);
1195: case SNMP_ERROR_RESUNAVAIL:
1196: errx(1, "Can't parse oid %s: Resource unavailable", oid);
1197: case SNMP_ERROR_COMMITFAILED:
1198: errx(1, "Can't parse oid %s: Commit failed", oid);
1199: case SNMP_ERROR_UNDOFAILED:
1.40 jmc 1200: errx(1, "Can't parse oid %s: Undo failed", oid);
1.1 martijn 1201: case SNMP_ERROR_AUTHERROR:
1202: errx(1, "Can't parse oid %s: Authorization error", oid);
1203: case SNMP_ERROR_NOTWRITABLE:
1204: errx(1, "Can't parse oid %s: Not writable", oid);
1205: case SNMP_ERROR_INCONNAME:
1206: errx(1, "Can't parse oid %s: Inconsistent name", oid);
1207: }
1208: errx(1, "Can't parse oid %s: Unknown error (%d)", oid, error);
1209: }
1210:
1211: int
1212: snmpc_parseagent(char *agent, char *defaultport)
1213: {
1214: struct addrinfo hints, *ai, *ai0 = NULL;
1215: struct sockaddr_un saddr;
1216: char *agentdup, *specifier, *hostname, *port = NULL;
1217: int error;
1218: int s;
1219:
1220: if ((agentdup = specifier = strdup(agent)) == NULL)
1221: err(1, NULL);
1222:
1223: bzero(&hints, sizeof(hints));
1224: if ((hostname = strchr(specifier, ':')) != NULL) {
1225: *hostname++ = '\0';
1226: if (strcasecmp(specifier, "udp") == 0) {
1227: hints.ai_family = AF_INET;
1228: hints.ai_socktype = SOCK_DGRAM;
1229: } else if (strcasecmp(specifier, "tcp") == 0) {
1230: hints.ai_family = AF_INET;
1231: hints.ai_socktype = SOCK_STREAM;
1232: } else if (strcasecmp(specifier, "udp6") == 0 ||
1233: strcasecmp(specifier, "udpv6") == 0 ||
1234: strcasecmp(specifier, "udpipv6") == 0) {
1235: hints.ai_family = AF_INET6;
1236: hints.ai_socktype = SOCK_DGRAM;
1237: } else if (strcasecmp(specifier, "tcp6") == 0 ||
1238: strcasecmp(specifier, "tcpv6") == 0 ||
1239: strcasecmp(specifier, "tcpipv6") == 0) {
1240: hints.ai_family = AF_INET6;
1241: hints.ai_socktype = SOCK_STREAM;
1242: } else if (strcasecmp(specifier, "unix") == 0) {
1243: hints.ai_family = AF_UNIX;
1244: hints.ai_addr = (struct sockaddr *)&saddr;
1245: hints.ai_addrlen = sizeof(saddr);
1246: saddr.sun_len = sizeof(saddr);
1247: saddr.sun_family = AF_UNIX;
1248: if (strlcpy(saddr.sun_path, hostname,
1249: sizeof(saddr.sun_path)) > sizeof(saddr.sun_path))
1250: errx(1, "Hostname path too long");
1251: ai = &hints;
1252: } else {
1.22 martijn 1253: *--hostname = ':';
1.1 martijn 1254: hostname = specifier;
1255: }
1256: } else {
1257: hostname = specifier;
1.22 martijn 1258: }
1259:
1260: if (hints.ai_family == AF_INET) {
1261: if ((port = strchr(hostname, ':')) != NULL)
1262: *port++ = '\0';
1263: } else if (hints.ai_family == AF_INET6 || hints.ai_family == 0) {
1264: if (hostname[0] == '[') {
1265: hints.ai_family = AF_INET6;
1266: hostname++;
1267: if ((port = strchr(hostname, ']')) == NULL)
1268: errx(1, "invalid agent");
1269: *port++ = '\0';
1270: if (port[0] == ':')
1271: *port++ = '\0';
1272: else if (port[0] == '\0')
1273: port = NULL;
1274: else
1275: errx(1, "invalid agent");
1276: } else {
1277: if ((port = strrchr(hostname, ':')) != NULL)
1278: *port++ = '\0';
1279: }
1.1 martijn 1280: }
1281:
1282: if (hints.ai_family != AF_UNIX) {
1.22 martijn 1283: if (hints.ai_socktype == 0)
1284: hints.ai_socktype = SOCK_DGRAM;
1.1 martijn 1285: if (port == NULL)
1286: port = defaultport;
1287: error = getaddrinfo(hostname, port, &hints, &ai0);
1.22 martijn 1288: if (error) {
1.27 martijn 1289: if (error != EAI_NODATA || port == defaultport)
1.22 martijn 1290: errx(1, "%s", gai_strerror(error));
1291: *--port = ':';
1292: error = getaddrinfo(hostname, defaultport, &hints,
1293: &ai0);
1294: if (error)
1295: errx(1, "%s", gai_strerror(error));
1296: }
1.1 martijn 1297: s = -1;
1298: for (ai = ai0; ai != NULL; ai = ai->ai_next) {
1299: if ((s = socket(ai->ai_family, ai->ai_socktype,
1.22 martijn 1300: ai->ai_protocol)) != -1 &&
1301: connect(s, (struct sockaddr *)ai->ai_addr,
1302: ai->ai_addrlen) != -1)
1303: break;
1.27 martijn 1304: close(s);
1305: s = -1;
1.1 martijn 1306: }
1.22 martijn 1307: } else {
1308: s = socket(AF_UNIX, SOCK_STREAM, 0);
1309: if (connect(s, (struct sockaddr *)ai->ai_addr,
1310: ai->ai_addrlen) == -1)
1311: err(1, "Can't connect to %s", agent);
1312: }
1.1 martijn 1313: if (s == -1)
1.22 martijn 1314: err(1, "Can't connect to agent %s", agent);
1.1 martijn 1315:
1316:
1317: if (ai0 != NULL)
1318: freeaddrinfo(ai0);
1319: free(agentdup);
1320: return s;
1321: }
1322:
1.9 martijn 1323: char *
1324: snmpc_hex2bin(char *hexstr, size_t *binlen)
1325: {
1326: char *decstr;
1327:
1328: if (hexstr[0] == '0' && hexstr[1] == 'x')
1329: hexstr += 2;
1330: while (hexstr[0] == ' ' || hexstr[0] == '\t')
1331: hexstr++;
1332:
1333: if ((decstr = malloc((strlen(hexstr) / 2) + 1)) == NULL)
1334: return NULL;
1335:
1336: for (*binlen = 0; hexstr[0] != '\0'; (*binlen)++) {
1337: hexstr[0] = toupper(hexstr[0]);
1338: hexstr[1] = toupper(hexstr[1]);
1339: if (hexstr[0] >= '0' && hexstr[0] <= '9')
1340: decstr[*binlen] = (hexstr[0] - '0') << 4;
1341: else if (hexstr[0] >= 'A' && hexstr[0] <= 'F')
1342: decstr[*binlen] = ((hexstr[0] - 'A') + 10) << 4;
1343: else
1344: goto fail;
1345: if (hexstr[1] >= '0' && hexstr[1] <= '9')
1346: decstr[*binlen] |= (hexstr[1] - '0');
1347: else if (hexstr[1] >= 'A' && hexstr[1] <= 'F')
1348: decstr[*binlen] |= (hexstr[1] - 'A') + 10;
1349: else
1350: goto fail;
1351:
1352: hexstr += 2;
1353: while (hexstr[0] == ' ' || hexstr[0] == '\t')
1354: hexstr++;
1355: }
1356:
1357: return decstr;
1358: fail:
1359: errno = EINVAL;
1360: free(decstr);
1361: return NULL;
1.29 martijn 1362: }
1363:
1364: ssize_t
1365: snmpc_mbswidth(char *str)
1366: {
1367: wchar_t wc;
1368: size_t width = 0;
1369: size_t i;
1370: int len;
1371:
1372: for (i = 0; (len = mbtowc(&wc, &(str[i]), MB_CUR_MAX)) != 0; i += len) {
1373: if (len == -1) {
1374: mbtowc(NULL, NULL, MB_CUR_MAX);
1375: return -1;
1376: }
1377: width += wcwidth(wc);
1378: }
1379: return width;
1.13 martijn 1380: }
1381:
1382: struct ber_element *
1383: snmpc_varbindparse(int argc, char *argv[])
1384: {
1385: struct ber_oid oid, oidval;
1386: struct in_addr addr4;
1387: char *addr = (char *)&addr4;
1388: char *str = NULL, *tmpstr, *endstr;
1389: const char *errstr = NULL;
1390: struct ber_element *varbind = NULL, *vblist = NULL;
1391: int i, ret;
1392: size_t strl, byte;
1393: long long lval;
1394:
1395: if (argc % 3 != 0)
1396: usage();
1397: for (i = 0; i < argc; i += 3) {
1398: if (smi_string2oid(argv[i], &oid) == -1)
1399: errx(1, "Invalid oid: %s\n", argv[i]);
1400: switch (argv[i + 1][0]) {
1401: case 'a':
1402: ret = inet_pton(AF_INET, argv[i + 2], &addr4);
1403: if (ret == -1)
1404: err(1, "inet_pton");
1405: if (ret == 0)
1406: errx(1, "%s: Bad value notation (%s)", argv[i],
1407: argv[i + 2]);
1.16 tb 1408: if ((varbind = ober_printf_elements(varbind, "{Oxt}",
1.13 martijn 1409: &oid, addr, sizeof(addr4), BER_CLASS_APPLICATION,
1410: SNMP_T_IPADDR)) == NULL)
1.16 tb 1411: err(1, "ober_printf_elements");
1.13 martijn 1412: break;
1413: case 'b':
1414: tmpstr = argv[i + 2];
1415: strl = 0;
1416: do {
1417: lval = strtoll(tmpstr, &endstr, 10);
1418: if (endstr[0] != ' ' && endstr[0] != '\t' &&
1419: endstr[0] != ',' && endstr[0] != '\0')
1420: errx(1, "%s: Bad value notation (%s)",
1421: argv[i], argv[i + 2]);
1422: if (tmpstr == endstr) {
1423: tmpstr++;
1424: continue;
1425: }
1426: if (lval < 0)
1427: errx(1, "%s: Bad value notation (%s)",
1428: argv[i], argv[i + 2]);
1429: byte = lval / 8;
1430: if (byte >= strl) {
1431: if ((str = recallocarray(str, strl,
1432: byte + 1, 1)) == NULL)
1433: err(1, "malloc");
1434: strl = byte + 1;
1435: }
1436: str[byte] |= 0x80 >> (lval % 8);
1437: tmpstr = endstr + 1;
1438: } while (endstr[0] != '\0');
1439: /*
1440: * RFC3416 Section 2.5
1441: * A BITS value is encoded as an OCTET STRING
1442: */
1443: goto pastestring;
1444: case 'c':
1.33 martijn 1445: lval = strtonum(argv[i + 2], 0, UINT32_MAX,
1.13 martijn 1446: &errstr);
1447: if (errstr != NULL)
1448: errx(1, "%s: Bad value notation (%s)", argv[i],
1449: argv[i + 2]);
1.16 tb 1450: if ((varbind = ober_printf_elements(varbind, "{Oit}",
1.13 martijn 1451: &oid, lval, BER_CLASS_APPLICATION,
1452: SNMP_T_COUNTER32)) == NULL)
1.16 tb 1453: err(1, "ober_printf_elements");
1.13 martijn 1454: break;
1455: case 'd':
1456: /* String always shrinks */
1457: if ((str = malloc(strlen(argv[i + 2]))) == NULL)
1458: err(1, "malloc");
1459: tmpstr = argv[i + 2];
1460: strl = 0;
1461: do {
1462: lval = strtoll(tmpstr, &endstr, 10);
1463: if (endstr[0] != ' ' && endstr[0] != '\t' &&
1464: endstr[0] != '\0')
1465: errx(1, "%s: Bad value notation (%s)",
1466: argv[i], argv[i + 2]);
1467: if (tmpstr == endstr) {
1468: tmpstr++;
1469: continue;
1470: }
1471: if (lval < 0 || lval > 0xff)
1472: errx(1, "%s: Bad value notation (%s)",
1473: argv[i], argv[i + 2]);
1474: str[strl++] = (unsigned char) lval;
1475: tmpstr = endstr + 1;
1476: } while (endstr[0] != '\0');
1477: goto pastestring;
1478: case 'i':
1.33 martijn 1479: lval = strtonum(argv[i + 2], INT32_MIN, INT32_MAX,
1.13 martijn 1480: &errstr);
1481: if (errstr != NULL)
1482: errx(1, "%s: Bad value notation (%s)", argv[i],
1483: argv[i + 2]);
1.16 tb 1484: if ((varbind = ober_printf_elements(varbind, "{Oi}",
1.13 martijn 1485: &oid, lval)) == NULL)
1.16 tb 1486: err(1, "ober_printf_elements");
1.13 martijn 1487: break;
1488: case 'n':
1.16 tb 1489: if ((varbind = ober_printf_elements(varbind, "{O0}",
1.13 martijn 1490: &oid)) == NULL)
1.16 tb 1491: err(1, "ober_printf_elements");
1.13 martijn 1492: break;
1493: case 'o':
1494: if (smi_string2oid(argv[i + 2], &oidval) == -1)
1495: errx(1, "%s: Unknown Object Identifier (Sub-id "
1496: "not found: (top) -> %s)", argv[i],
1497: argv[i + 2]);
1.16 tb 1498: if ((varbind = ober_printf_elements(varbind, "{OO}",
1.13 martijn 1499: &oid, &oidval)) == NULL)
1.16 tb 1500: err(1, "ober_printf_elements");
1.13 martijn 1501: break;
1502: case 's':
1503: if ((str = strdup(argv[i + 2])) == NULL)
1504: err(1, NULL);
1505: strl = strlen(argv[i + 2]);
1506: pastestring:
1.16 tb 1507: if ((varbind = ober_printf_elements(varbind, "{Ox}",
1.13 martijn 1508: &oid, str, strl)) == NULL)
1.16 tb 1509: err(1, "ober_printf_elements");
1.13 martijn 1510: free(str);
1511: break;
1512: case 't':
1.33 martijn 1513: lval = strtonum(argv[i + 2], 0, UINT32_MAX,
1.13 martijn 1514: &errstr);
1515: if (errstr != NULL)
1516: errx(1, "%s: Bad value notation (%s)", argv[i],
1517: argv[i + 2]);
1.16 tb 1518: if ((varbind = ober_printf_elements(varbind, "{Oit}",
1.13 martijn 1519: &oid, lval, BER_CLASS_APPLICATION,
1520: SNMP_T_TIMETICKS)) == NULL)
1.33 martijn 1521: err(1, "ober_printf_elements");
1522: break;
1523: case 'u':
1524: lval = strtonum(argv[i + 2], 0, UINT32_MAX,
1525: &errstr);
1526: if (errstr != NULL)
1527: errx(1, "%s: Bad value notation (%s)", argv[i],
1528: argv[i + 2]);
1529: if ((varbind = ober_printf_elements(varbind, "{Oit}",
1530: &oid, lval, BER_CLASS_APPLICATION,
1531: SNMP_T_GAUGE32)) == NULL)
1.16 tb 1532: err(1, "ober_printf_elements");
1.13 martijn 1533: break;
1534: case 'x':
1535: /* String always shrinks */
1536: if ((str = malloc(strlen(argv[i + 2]))) == NULL)
1537: err(1, "malloc");
1538: tmpstr = argv[i + 2];
1539: strl = 0;
1540: do {
1541: lval = strtoll(tmpstr, &endstr, 16);
1542: if (endstr[0] != ' ' && endstr[0] != '\t' &&
1543: endstr[0] != '\0')
1544: errx(1, "%s: Bad value notation (%s)",
1545: argv[i], argv[i + 2]);
1546: if (tmpstr == endstr) {
1547: tmpstr++;
1548: continue;
1549: }
1550: if (lval < 0 || lval > 0xff)
1551: errx(1, "%s: Bad value notation (%s)",
1552: argv[i], argv[i + 2]);
1553: str[strl++] = (unsigned char) lval;
1554: tmpstr = endstr + 1;
1555: } while (endstr[0] != '\0');
1556: goto pastestring;
1557: default:
1558: usage();
1559: }
1560: if (vblist == NULL)
1561: vblist = varbind;
1562: }
1563:
1564: return vblist;
1.9 martijn 1565: }
1566:
1.1 martijn 1567: __dead void
1568: usage(void)
1569: {
1570: size_t i;
1571:
1572: if (snmp_app != NULL) {
1.9 martijn 1573: fprintf(stderr, "usage: snmp %s%s%s\n",
1.4 martijn 1574: snmp_app->name,
1.1 martijn 1575: snmp_app->usecommonopt ?
1.10 martijn 1576: " [-A authpass] [-a digest] [-c community] [-e secengineid]\n"
1.11 martijn 1577: " [-E ctxengineid] [-K localpriv] [-k localauth] [-l seclevel]\n"
1578: " [-n ctxname] [-O afnqvxSQ] [-r retries] [-t timeout] [-u user]\n"
1579: " [-v version] [-X privpass] [-x cipher] [-Z boots,time]\n"
1.9 martijn 1580: " " : "",
1.36 martijn 1581: snmp_app->usage == NULL ? " " : snmp_app->usage);
1.1 martijn 1582: exit(1);
1583: }
1584: for (i = 0; i < (sizeof(snmp_apps)/sizeof(*snmp_apps)); i++) {
1.4 martijn 1585: if (i == 0)
1586: fprintf(stderr, "usage: ");
1587: else
1588: fprintf(stderr, " ");
1589: fprintf(stderr, "snmp %s%s %s\n",
1590: snmp_apps[i].name,
1.1 martijn 1591: snmp_apps[i].usecommonopt ?
1.17 martijn 1592: " [options]" : "",
1.4 martijn 1593: snmp_apps[i].usage ? snmp_apps[i].usage : "");
1.1 martijn 1594: }
1595: exit(1);
1596: }