1*893ac8ceSmartijn /* $OpenBSD: smi.c,v 1.15 2021/10/21 08:17:34 martijn Exp $ */
2442e4f4fSmartijn
3442e4f4fSmartijn /*
4442e4f4fSmartijn * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
5442e4f4fSmartijn * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org>
6442e4f4fSmartijn *
7442e4f4fSmartijn * Permission to use, copy, modify, and distribute this software for any
8442e4f4fSmartijn * purpose with or without fee is hereby granted, provided that the above
9442e4f4fSmartijn * copyright notice and this permission notice appear in all copies.
10442e4f4fSmartijn *
11442e4f4fSmartijn * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12442e4f4fSmartijn * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13442e4f4fSmartijn * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14442e4f4fSmartijn * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15442e4f4fSmartijn * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16442e4f4fSmartijn * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17442e4f4fSmartijn * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18442e4f4fSmartijn */
19442e4f4fSmartijn
20442e4f4fSmartijn #include <sys/limits.h>
21442e4f4fSmartijn #include <sys/tree.h>
22442e4f4fSmartijn #include <sys/queue.h>
23442e4f4fSmartijn
24442e4f4fSmartijn #include <arpa/inet.h>
25442e4f4fSmartijn
26442e4f4fSmartijn #include <ctype.h>
271248928aSmartijn #include <errno.h>
28442e4f4fSmartijn #include <stdlib.h>
29442e4f4fSmartijn #include <stdio.h>
30442e4f4fSmartijn #include <string.h>
31442e4f4fSmartijn #include <strings.h>
321248928aSmartijn #include <wctype.h>
33442e4f4fSmartijn
34442e4f4fSmartijn #include "ber.h"
35442e4f4fSmartijn #include "mib.h"
36442e4f4fSmartijn #include "snmp.h"
37442e4f4fSmartijn #include "smi.h"
38442e4f4fSmartijn
39442e4f4fSmartijn #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
40442e4f4fSmartijn
411248928aSmartijn char *smi_displayhint_os(struct textconv *, int, const char *, size_t, int);
42dd843e54Smartijn char *smi_displayhint_int(struct textconv*, int, long long);
431248928aSmartijn
44442e4f4fSmartijn int smi_oid_cmp(struct oid *, struct oid *);
45442e4f4fSmartijn int smi_key_cmp(struct oid *, struct oid *);
461248928aSmartijn int smi_textconv_cmp(struct textconv *, struct textconv *);
47442e4f4fSmartijn struct oid * smi_findkey(char *);
48442e4f4fSmartijn
49442e4f4fSmartijn RB_HEAD(oidtree, oid);
50442e4f4fSmartijn RB_PROTOTYPE(oidtree, oid, o_element, smi_oid_cmp)
51442e4f4fSmartijn struct oidtree smi_oidtree;
52442e4f4fSmartijn
53442e4f4fSmartijn RB_HEAD(keytree, oid);
54442e4f4fSmartijn RB_PROTOTYPE(keytree, oid, o_keyword, smi_key_cmp)
55442e4f4fSmartijn struct keytree smi_keytree;
56442e4f4fSmartijn
571248928aSmartijn RB_HEAD(textconvtree, textconv);
581248928aSmartijn RB_PROTOTYPE(textconvtree, textconv, tc_entry, smi_textconv_cmp);
591248928aSmartijn struct textconvtree smi_tctree;
601248928aSmartijn
61442e4f4fSmartijn int
smi_init(void)62442e4f4fSmartijn smi_init(void)
63442e4f4fSmartijn {
64442e4f4fSmartijn /* Initialize the Structure of Managed Information (SMI) */
65442e4f4fSmartijn RB_INIT(&smi_oidtree);
66442e4f4fSmartijn mib_init();
67442e4f4fSmartijn return (0);
68442e4f4fSmartijn }
69442e4f4fSmartijn
70442e4f4fSmartijn void
smi_debug_elements(struct ber_element * root,int utf8)711248928aSmartijn smi_debug_elements(struct ber_element *root, int utf8)
72442e4f4fSmartijn {
73442e4f4fSmartijn static int indent = 0;
74442e4f4fSmartijn char *value;
75442e4f4fSmartijn int constructed;
76442e4f4fSmartijn
77442e4f4fSmartijn /* calculate lengths */
78696b5899Stb ober_calc_len(root);
79442e4f4fSmartijn
80442e4f4fSmartijn switch (root->be_encoding) {
81442e4f4fSmartijn case BER_TYPE_SEQUENCE:
82442e4f4fSmartijn case BER_TYPE_SET:
83442e4f4fSmartijn constructed = root->be_encoding;
84442e4f4fSmartijn break;
85442e4f4fSmartijn default:
86442e4f4fSmartijn constructed = 0;
87442e4f4fSmartijn break;
88442e4f4fSmartijn }
89442e4f4fSmartijn
90442e4f4fSmartijn fprintf(stderr, "%*slen %lu ", indent, "", root->be_len);
91442e4f4fSmartijn switch (root->be_class) {
92442e4f4fSmartijn case BER_CLASS_UNIVERSAL:
93442e4f4fSmartijn fprintf(stderr, "class: universal(%u) type: ", root->be_class);
94442e4f4fSmartijn switch (root->be_type) {
95442e4f4fSmartijn case BER_TYPE_EOC:
96442e4f4fSmartijn fprintf(stderr, "end-of-content");
97442e4f4fSmartijn break;
98442e4f4fSmartijn case BER_TYPE_INTEGER:
99442e4f4fSmartijn fprintf(stderr, "integer");
100442e4f4fSmartijn break;
101442e4f4fSmartijn case BER_TYPE_BITSTRING:
102442e4f4fSmartijn fprintf(stderr, "bit-string");
103442e4f4fSmartijn break;
104442e4f4fSmartijn case BER_TYPE_OCTETSTRING:
105442e4f4fSmartijn fprintf(stderr, "octet-string");
106442e4f4fSmartijn break;
107442e4f4fSmartijn case BER_TYPE_NULL:
108442e4f4fSmartijn fprintf(stderr, "null");
109442e4f4fSmartijn break;
110442e4f4fSmartijn case BER_TYPE_OBJECT:
111442e4f4fSmartijn fprintf(stderr, "object");
112442e4f4fSmartijn break;
113442e4f4fSmartijn case BER_TYPE_ENUMERATED:
114442e4f4fSmartijn fprintf(stderr, "enumerated");
115442e4f4fSmartijn break;
116442e4f4fSmartijn case BER_TYPE_SEQUENCE:
117442e4f4fSmartijn fprintf(stderr, "sequence");
118442e4f4fSmartijn break;
119442e4f4fSmartijn case BER_TYPE_SET:
120442e4f4fSmartijn fprintf(stderr, "set");
121442e4f4fSmartijn break;
122442e4f4fSmartijn }
123442e4f4fSmartijn break;
124442e4f4fSmartijn case BER_CLASS_APPLICATION:
125442e4f4fSmartijn fprintf(stderr, "class: application(%u) type: ",
126442e4f4fSmartijn root->be_class);
127442e4f4fSmartijn switch (root->be_type) {
128442e4f4fSmartijn case SNMP_T_IPADDR:
129442e4f4fSmartijn fprintf(stderr, "ipaddr");
130442e4f4fSmartijn break;
131442e4f4fSmartijn case SNMP_T_COUNTER32:
132442e4f4fSmartijn fprintf(stderr, "counter32");
133442e4f4fSmartijn break;
134442e4f4fSmartijn case SNMP_T_GAUGE32:
135442e4f4fSmartijn fprintf(stderr, "gauge32");
136442e4f4fSmartijn break;
137442e4f4fSmartijn case SNMP_T_TIMETICKS:
138442e4f4fSmartijn fprintf(stderr, "timeticks");
139442e4f4fSmartijn break;
140442e4f4fSmartijn case SNMP_T_OPAQUE:
141442e4f4fSmartijn fprintf(stderr, "opaque");
142442e4f4fSmartijn break;
143442e4f4fSmartijn case SNMP_T_COUNTER64:
144442e4f4fSmartijn fprintf(stderr, "counter64");
145442e4f4fSmartijn break;
146442e4f4fSmartijn }
147442e4f4fSmartijn break;
148442e4f4fSmartijn case BER_CLASS_CONTEXT:
149442e4f4fSmartijn fprintf(stderr, "class: context(%u) type: ",
150442e4f4fSmartijn root->be_class);
151442e4f4fSmartijn switch (root->be_type) {
152442e4f4fSmartijn case SNMP_C_GETREQ:
153442e4f4fSmartijn fprintf(stderr, "getreq");
154442e4f4fSmartijn break;
155442e4f4fSmartijn case SNMP_C_GETNEXTREQ:
156442e4f4fSmartijn fprintf(stderr, "nextreq");
157442e4f4fSmartijn break;
158442e4f4fSmartijn case SNMP_C_GETRESP:
159442e4f4fSmartijn fprintf(stderr, "getresp");
160442e4f4fSmartijn break;
161442e4f4fSmartijn case SNMP_C_SETREQ:
162442e4f4fSmartijn fprintf(stderr, "setreq");
163442e4f4fSmartijn break;
164442e4f4fSmartijn case SNMP_C_TRAP:
165442e4f4fSmartijn fprintf(stderr, "trap");
166442e4f4fSmartijn break;
167442e4f4fSmartijn case SNMP_C_GETBULKREQ:
168442e4f4fSmartijn fprintf(stderr, "getbulkreq");
169442e4f4fSmartijn break;
170442e4f4fSmartijn case SNMP_C_INFORMREQ:
171442e4f4fSmartijn fprintf(stderr, "informreq");
172442e4f4fSmartijn break;
173442e4f4fSmartijn case SNMP_C_TRAPV2:
174442e4f4fSmartijn fprintf(stderr, "trapv2");
175442e4f4fSmartijn break;
176442e4f4fSmartijn case SNMP_C_REPORT:
177442e4f4fSmartijn fprintf(stderr, "report");
178442e4f4fSmartijn break;
179442e4f4fSmartijn }
180442e4f4fSmartijn break;
181442e4f4fSmartijn case BER_CLASS_PRIVATE:
182442e4f4fSmartijn fprintf(stderr, "class: private(%u) type: ", root->be_class);
183442e4f4fSmartijn break;
184442e4f4fSmartijn default:
185442e4f4fSmartijn fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class);
186442e4f4fSmartijn break;
187442e4f4fSmartijn }
188442e4f4fSmartijn fprintf(stderr, "(%u) encoding %u ",
189442e4f4fSmartijn root->be_type, root->be_encoding);
190442e4f4fSmartijn
1911248928aSmartijn if ((value = smi_print_element(NULL, root, 1, smi_os_default,
1921248928aSmartijn smi_oidl_numeric, utf8)) == NULL)
193442e4f4fSmartijn goto invalid;
194442e4f4fSmartijn
195442e4f4fSmartijn switch (root->be_encoding) {
196442e4f4fSmartijn case BER_TYPE_INTEGER:
197442e4f4fSmartijn case BER_TYPE_ENUMERATED:
198442e4f4fSmartijn fprintf(stderr, "value %s", value);
199442e4f4fSmartijn break;
200442e4f4fSmartijn case BER_TYPE_BITSTRING:
201442e4f4fSmartijn fprintf(stderr, "hexdump %s", value);
202442e4f4fSmartijn break;
203442e4f4fSmartijn case BER_TYPE_OBJECT:
204442e4f4fSmartijn fprintf(stderr, "oid %s", value);
205442e4f4fSmartijn break;
206442e4f4fSmartijn case BER_TYPE_OCTETSTRING:
207442e4f4fSmartijn if (root->be_class == BER_CLASS_APPLICATION &&
208442e4f4fSmartijn root->be_type == SNMP_T_IPADDR) {
209442e4f4fSmartijn fprintf(stderr, "addr %s", value);
210442e4f4fSmartijn } else {
211442e4f4fSmartijn fprintf(stderr, "string %s", value);
212442e4f4fSmartijn }
213442e4f4fSmartijn break;
214442e4f4fSmartijn case BER_TYPE_NULL: /* no payload */
215442e4f4fSmartijn case BER_TYPE_EOC:
216442e4f4fSmartijn case BER_TYPE_SEQUENCE:
217442e4f4fSmartijn case BER_TYPE_SET:
218442e4f4fSmartijn default:
219442e4f4fSmartijn fprintf(stderr, "%s", value);
220442e4f4fSmartijn break;
221442e4f4fSmartijn }
222442e4f4fSmartijn
223442e4f4fSmartijn invalid:
224442e4f4fSmartijn if (value == NULL)
225442e4f4fSmartijn fprintf(stderr, "<INVALID>");
226442e4f4fSmartijn else
227442e4f4fSmartijn free(value);
228442e4f4fSmartijn fprintf(stderr, "\n");
229442e4f4fSmartijn
230442e4f4fSmartijn if (constructed)
231442e4f4fSmartijn root->be_encoding = constructed;
232442e4f4fSmartijn
233442e4f4fSmartijn if (constructed && root->be_sub) {
234442e4f4fSmartijn indent += 2;
2351248928aSmartijn smi_debug_elements(root->be_sub, utf8);
236442e4f4fSmartijn indent -= 2;
237442e4f4fSmartijn }
238442e4f4fSmartijn if (root->be_next)
2391248928aSmartijn smi_debug_elements(root->be_next, utf8);
240442e4f4fSmartijn }
241442e4f4fSmartijn
242442e4f4fSmartijn char *
smi_print_element(struct ber_oid * oid,struct ber_element * root,int print_hint,enum smi_output_string output_string,enum smi_oid_lookup lookup,int utf8)2431248928aSmartijn smi_print_element(struct ber_oid *oid, struct ber_element *root, int print_hint,
2441248928aSmartijn enum smi_output_string output_string, enum smi_oid_lookup lookup, int utf8)
245442e4f4fSmartijn {
246442e4f4fSmartijn char *str = NULL, *buf, *p;
2471248928aSmartijn struct oid okey;
2481248928aSmartijn struct oid *object = NULL;
2491248928aSmartijn struct textconv tckey;
25023cf8cd6Sjsg size_t len, i, slen;
251442e4f4fSmartijn long long v, ticks;
252860b4da4Smartijn int is_hex = 0, ret;
253442e4f4fSmartijn struct ber_oid o;
254442e4f4fSmartijn char strbuf[BUFSIZ];
255442e4f4fSmartijn char *hint;
256442e4f4fSmartijn int days, hours, min, sec, csec;
257442e4f4fSmartijn
2581248928aSmartijn if (oid != NULL) {
2591248928aSmartijn bcopy(oid, &(okey.o_id), sizeof(okey));
2601248928aSmartijn do {
2611248928aSmartijn object = RB_FIND(oidtree, &smi_oidtree, &okey);
2621248928aSmartijn okey.o_id.bo_n--;
2631248928aSmartijn } while (object == NULL && okey.o_id.bo_n > 0);
2641248928aSmartijn if (object != NULL && object->o_textconv == NULL &&
2651248928aSmartijn object->o_tcname != NULL) {
2661248928aSmartijn tckey.tc_name = object->o_tcname;
2671248928aSmartijn object->o_textconv = RB_FIND(textconvtree, &smi_tctree,
2681248928aSmartijn &tckey);
2691248928aSmartijn }
2701248928aSmartijn }
2711248928aSmartijn
272442e4f4fSmartijn switch (root->be_encoding) {
273442e4f4fSmartijn case BER_TYPE_INTEGER:
274442e4f4fSmartijn case BER_TYPE_ENUMERATED:
275696b5899Stb if (ober_get_integer(root, &v) == -1)
276442e4f4fSmartijn goto fail;
277442e4f4fSmartijn if (root->be_class == BER_CLASS_APPLICATION &&
278442e4f4fSmartijn root->be_type == SNMP_T_TIMETICKS) {
279442e4f4fSmartijn ticks = v;
280442e4f4fSmartijn days = ticks / (60 * 60 * 24 * 100);
281442e4f4fSmartijn ticks %= (60 * 60 * 24 * 100);
282442e4f4fSmartijn hours = ticks / (60 * 60 * 100);
283442e4f4fSmartijn ticks %= (60 * 60 * 100);
284442e4f4fSmartijn min = ticks / (60 * 100);
285442e4f4fSmartijn ticks %= (60 * 100);
286442e4f4fSmartijn sec = ticks / 100;
287442e4f4fSmartijn ticks %= 100;
288442e4f4fSmartijn csec = ticks;
289442e4f4fSmartijn
290442e4f4fSmartijn if (print_hint) {
291442e4f4fSmartijn if (days == 0) {
292442e4f4fSmartijn if (asprintf(&str,
293442e4f4fSmartijn "Timeticks: (%lld) "
294442e4f4fSmartijn "%d:%02d:%02d.%02d",
295442e4f4fSmartijn v, hours, min, sec, csec) == -1)
296442e4f4fSmartijn goto fail;
297442e4f4fSmartijn } else if (days == 1) {
298442e4f4fSmartijn if (asprintf(&str,
299442e4f4fSmartijn "Timeticks: (%lld) "
300442e4f4fSmartijn "1 day %d:%02d:%02d.%02d",
301442e4f4fSmartijn v, hours, min, sec, csec) == -1)
302442e4f4fSmartijn goto fail;
303442e4f4fSmartijn } else {
304442e4f4fSmartijn if (asprintf(&str,
305442e4f4fSmartijn "Timeticks: (%lld) "
306442e4f4fSmartijn "%d day %d:%02d:%02d.%02d",
307442e4f4fSmartijn v, days, hours, min, sec, csec) ==
308442e4f4fSmartijn -1)
309442e4f4fSmartijn goto fail;
310442e4f4fSmartijn }
311442e4f4fSmartijn } else {
312442e4f4fSmartijn if (days == 0) {
313442e4f4fSmartijn if (asprintf(&str, "%d:%02d:%02d.%02d",
314442e4f4fSmartijn hours, min, sec, csec) == -1)
315442e4f4fSmartijn goto fail;
316442e4f4fSmartijn } else if (days == 1) {
317442e4f4fSmartijn if (asprintf(&str,
318442e4f4fSmartijn "1 day %d:%02d:%02d.%02d",
319442e4f4fSmartijn hours, min, sec, csec) == -1)
320442e4f4fSmartijn goto fail;
321442e4f4fSmartijn } else {
322442e4f4fSmartijn if (asprintf(&str,
323442e4f4fSmartijn "%d day %d:%02d:%02d.%02d",
324442e4f4fSmartijn days, hours, min, sec, csec) == -1)
325442e4f4fSmartijn goto fail;
326442e4f4fSmartijn }
327442e4f4fSmartijn }
328442e4f4fSmartijn break;
329442e4f4fSmartijn }
330442e4f4fSmartijn hint = "INTEGER: ";
331dd843e54Smartijn if (object != NULL && object->o_textconv != NULL &&
332dd843e54Smartijn object->o_textconv->tc_syntax == root->be_encoding)
333dd843e54Smartijn return smi_displayhint_int(object->o_textconv,
334dd843e54Smartijn print_hint, v);
335442e4f4fSmartijn if (root->be_class == BER_CLASS_APPLICATION) {
336442e4f4fSmartijn if (root->be_type == SNMP_T_COUNTER32)
337442e4f4fSmartijn hint = "Counter32: ";
338442e4f4fSmartijn else if (root->be_type == SNMP_T_GAUGE32)
339442e4f4fSmartijn hint = "Gauge32: ";
340442e4f4fSmartijn else if (root->be_type == SNMP_T_OPAQUE)
341442e4f4fSmartijn hint = "Opaque: ";
342442e4f4fSmartijn else if (root->be_type == SNMP_T_COUNTER64)
343442e4f4fSmartijn hint = "Counter64: ";
344442e4f4fSmartijn }
345903c564cSderaadt if (asprintf(&str, "%s%lld", print_hint ? hint : "", v) == -1)
346442e4f4fSmartijn goto fail;
347442e4f4fSmartijn break;
348442e4f4fSmartijn case BER_TYPE_BITSTRING:
349696b5899Stb if (ober_get_bitstring(root, (void *)&buf, &len) == -1)
350442e4f4fSmartijn goto fail;
35123cf8cd6Sjsg slen = len * 2 + 1 + sizeof("BITS: ");
35223cf8cd6Sjsg if ((str = calloc(1, slen)) == NULL)
353442e4f4fSmartijn goto fail;
354442e4f4fSmartijn p = str;
355442e4f4fSmartijn if (print_hint) {
35623cf8cd6Sjsg strlcpy(str, "BITS: ", slen);
357442e4f4fSmartijn p += sizeof("BITS: ");
358442e4f4fSmartijn }
359442e4f4fSmartijn for (i = 0; i < len; i++) {
360442e4f4fSmartijn snprintf(p, 3, "%02x", buf[i]);
361442e4f4fSmartijn p += 2;
362442e4f4fSmartijn }
363442e4f4fSmartijn break;
364442e4f4fSmartijn case BER_TYPE_OBJECT:
365696b5899Stb if (ober_get_oid(root, &o) == -1)
366442e4f4fSmartijn goto fail;
367442e4f4fSmartijn if (asprintf(&str, "%s%s",
368442e4f4fSmartijn print_hint ? "OID: " : "",
369442e4f4fSmartijn smi_oid2string(&o, strbuf, sizeof(strbuf), lookup)) == -1)
370442e4f4fSmartijn goto fail;
371442e4f4fSmartijn break;
372442e4f4fSmartijn case BER_TYPE_OCTETSTRING:
373696b5899Stb if (ober_get_string(root, &buf) == -1)
374442e4f4fSmartijn goto fail;
375442e4f4fSmartijn if (root->be_class == BER_CLASS_APPLICATION &&
376442e4f4fSmartijn root->be_type == SNMP_T_IPADDR) {
377442e4f4fSmartijn if (asprintf(&str, "%s%s",
378442e4f4fSmartijn print_hint ? "IpAddress: " : "",
379442e4f4fSmartijn inet_ntoa(*(struct in_addr *)buf)) == -1)
380442e4f4fSmartijn goto fail;
38145091ad0Smartijn } else if (root->be_class == BER_CLASS_CONTEXT) {
38245091ad0Smartijn if (root->be_type == SNMP_E_NOSUCHOBJECT)
38345091ad0Smartijn str = strdup("No Such Object available on this "
38445091ad0Smartijn "agent at this OID");
38545091ad0Smartijn else if (root->be_type == SNMP_E_NOSUCHINSTANCE)
38645091ad0Smartijn str = strdup("No Such Instance currently "
38745091ad0Smartijn "exists at this OID");
38845091ad0Smartijn else if (root->be_type == SNMP_E_ENDOFMIB)
38945091ad0Smartijn str = strdup("No more variables left in this "
39045091ad0Smartijn "MIB View (It is past the end of the MIB "
39145091ad0Smartijn "tree)");
39245091ad0Smartijn else
39345091ad0Smartijn str = strdup("Unknown status at this OID");
394442e4f4fSmartijn } else {
3951248928aSmartijn if (object != NULL && object->o_textconv != NULL &&
3961248928aSmartijn object->o_textconv->tc_syntax == root->be_encoding)
3971248928aSmartijn return smi_displayhint_os(object->o_textconv,
3981248928aSmartijn print_hint, buf, root->be_len, utf8);
399442e4f4fSmartijn for (i = 0; i < root->be_len; i++) {
400442e4f4fSmartijn if (!isprint(buf[i])) {
401442e4f4fSmartijn if (output_string == smi_os_default)
402442e4f4fSmartijn output_string = smi_os_hex;
403442e4f4fSmartijn else if (output_string == smi_os_ascii)
404442e4f4fSmartijn is_hex = 1;
405442e4f4fSmartijn break;
406442e4f4fSmartijn }
407442e4f4fSmartijn }
408442e4f4fSmartijn /*
409442e4f4fSmartijn * hex is 3 * n (2 digits + n - 1 spaces + NUL-byte)
410442e4f4fSmartijn * ascii can be max (2 * n) + 2 quotes + NUL-byte
411442e4f4fSmartijn */
412860b4da4Smartijn len = output_string == smi_os_hex ? 3 : 2;
413860b4da4Smartijn p = str = reallocarray(NULL, root->be_len + 2, len);
414860b4da4Smartijn if (p == NULL)
415442e4f4fSmartijn goto fail;
416860b4da4Smartijn len *= root->be_len + 2;
417860b4da4Smartijn if (is_hex) {
418442e4f4fSmartijn *str++ = '"';
419860b4da4Smartijn len--;
420860b4da4Smartijn }
421442e4f4fSmartijn for (i = 0; i < root->be_len; i++) {
422442e4f4fSmartijn switch (output_string) {
423442e4f4fSmartijn case smi_os_default:
424442e4f4fSmartijn /* FALLTHROUGH */
425442e4f4fSmartijn case smi_os_ascii:
426442e4f4fSmartijn /*
427442e4f4fSmartijn * There's probably more edgecases here,
428442e4f4fSmartijn * not fully investigated
429442e4f4fSmartijn */
430860b4da4Smartijn if (len < 2)
431860b4da4Smartijn goto fail;
432860b4da4Smartijn if (is_hex && buf[i] == '\\') {
433442e4f4fSmartijn *str++ = '\\';
434860b4da4Smartijn len--;
435860b4da4Smartijn }
436442e4f4fSmartijn *str++ = isprint(buf[i]) ? buf[i] : '.';
437860b4da4Smartijn len--;
438442e4f4fSmartijn break;
439442e4f4fSmartijn case smi_os_hex:
440860b4da4Smartijn ret = snprintf(str, len, "%s%02hhX",
441442e4f4fSmartijn i == 0 ? "" :
442442e4f4fSmartijn i % 16 == 0 ? "\n" : " ", buf[i]);
443860b4da4Smartijn if (ret == -1 || ret > (int) len)
444860b4da4Smartijn goto fail;
445860b4da4Smartijn len -= ret;
446860b4da4Smartijn str += ret;
447442e4f4fSmartijn break;
448442e4f4fSmartijn }
449442e4f4fSmartijn }
450860b4da4Smartijn if (is_hex) {
451860b4da4Smartijn if (len < 2)
452860b4da4Smartijn goto fail;
453442e4f4fSmartijn *str++ = '"';
454860b4da4Smartijn len--;
455860b4da4Smartijn }
456860b4da4Smartijn if (len == 0)
457860b4da4Smartijn goto fail;
458442e4f4fSmartijn *str = '\0';
459442e4f4fSmartijn str = NULL;
460442e4f4fSmartijn if (asprintf(&str, "%s%s",
461442e4f4fSmartijn print_hint ?
462442e4f4fSmartijn output_string == smi_os_hex ? "Hex-STRING: " :
463442e4f4fSmartijn "STRING: " :
464442e4f4fSmartijn "", p) == -1) {
465442e4f4fSmartijn free(p);
466442e4f4fSmartijn goto fail;
467442e4f4fSmartijn }
468442e4f4fSmartijn free(p);
469442e4f4fSmartijn }
470442e4f4fSmartijn break;
471442e4f4fSmartijn case BER_TYPE_NULL: /* no payload */
472442e4f4fSmartijn case BER_TYPE_EOC:
473442e4f4fSmartijn case BER_TYPE_SEQUENCE:
474442e4f4fSmartijn case BER_TYPE_SET:
475442e4f4fSmartijn default:
476442e4f4fSmartijn str = strdup("");
477442e4f4fSmartijn break;
478442e4f4fSmartijn }
479442e4f4fSmartijn
480442e4f4fSmartijn return (str);
481442e4f4fSmartijn
482442e4f4fSmartijn fail:
483442e4f4fSmartijn free(str);
484442e4f4fSmartijn return (NULL);
485442e4f4fSmartijn }
486442e4f4fSmartijn
487442e4f4fSmartijn int
smi_string2oid(const char * oidstr,struct ber_oid * o)488442e4f4fSmartijn smi_string2oid(const char *oidstr, struct ber_oid *o)
489442e4f4fSmartijn {
490442e4f4fSmartijn char *sp, *p, str[BUFSIZ];
491442e4f4fSmartijn const char *errstr;
492442e4f4fSmartijn struct oid *oid;
493442e4f4fSmartijn struct ber_oid ko;
494442e4f4fSmartijn
495442e4f4fSmartijn if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str))
496442e4f4fSmartijn return (-1);
497442e4f4fSmartijn bzero(o, sizeof(*o));
498442e4f4fSmartijn
499442e4f4fSmartijn /*
500442e4f4fSmartijn * Parse OID strings in the common form n.n.n or n-n-n.
501696b5899Stb * Based on ober_string2oid with additional support for symbolic names.
502442e4f4fSmartijn */
503442e4f4fSmartijn p = sp = str[0] == '.' ? str + 1 : str;
504442e4f4fSmartijn for (; p != NULL; sp = p) {
505442e4f4fSmartijn if ((p = strpbrk(p, ".-")) != NULL)
506442e4f4fSmartijn *p++ = '\0';
507442e4f4fSmartijn if ((oid = smi_findkey(sp)) != NULL) {
508442e4f4fSmartijn bcopy(&oid->o_id, &ko, sizeof(ko));
509*893ac8ceSmartijn if (o->bo_n && ober_oid_cmp(&ko, o) != 2)
510442e4f4fSmartijn return (-1);
511442e4f4fSmartijn bcopy(&ko, o, sizeof(*o));
512442e4f4fSmartijn errstr = NULL;
513442e4f4fSmartijn } else {
514442e4f4fSmartijn o->bo_id[o->bo_n++] =
515442e4f4fSmartijn strtonum(sp, 0, UINT_MAX, &errstr);
516442e4f4fSmartijn }
517442e4f4fSmartijn if (errstr || o->bo_n > BER_MAX_OID_LEN)
518442e4f4fSmartijn return (-1);
519442e4f4fSmartijn }
520442e4f4fSmartijn
521442e4f4fSmartijn return (0);
522442e4f4fSmartijn }
523442e4f4fSmartijn
524442e4f4fSmartijn unsigned int
smi_application(struct ber_element * elm)525442e4f4fSmartijn smi_application(struct ber_element *elm)
526442e4f4fSmartijn {
527442e4f4fSmartijn if (elm->be_class != BER_CLASS_APPLICATION)
528442e4f4fSmartijn return (BER_TYPE_OCTETSTRING);
529442e4f4fSmartijn
530442e4f4fSmartijn switch (elm->be_type) {
531442e4f4fSmartijn case SNMP_T_IPADDR:
532442e4f4fSmartijn return (BER_TYPE_OCTETSTRING);
533442e4f4fSmartijn case SNMP_T_COUNTER32:
534442e4f4fSmartijn case SNMP_T_GAUGE32:
535442e4f4fSmartijn case SNMP_T_TIMETICKS:
536442e4f4fSmartijn case SNMP_T_OPAQUE:
537442e4f4fSmartijn case SNMP_T_COUNTER64:
538442e4f4fSmartijn return (BER_TYPE_INTEGER);
539442e4f4fSmartijn default:
540442e4f4fSmartijn break;
541442e4f4fSmartijn }
542442e4f4fSmartijn return (BER_TYPE_OCTETSTRING);
543442e4f4fSmartijn
544442e4f4fSmartijn }
545442e4f4fSmartijn
546442e4f4fSmartijn char *
smi_oid2string(struct ber_oid * o,char * buf,size_t len,enum smi_oid_lookup lookup)547442e4f4fSmartijn smi_oid2string(struct ber_oid *o, char *buf, size_t len,
548442e4f4fSmartijn enum smi_oid_lookup lookup)
549442e4f4fSmartijn {
550442e4f4fSmartijn char str[256];
551442e4f4fSmartijn struct oid *value, key;
552442e4f4fSmartijn size_t i;
553442e4f4fSmartijn
554442e4f4fSmartijn bzero(buf, len);
555442e4f4fSmartijn bzero(&key, sizeof(key));
556442e4f4fSmartijn bcopy(o, &key.o_id, sizeof(struct ber_oid));
557442e4f4fSmartijn
558442e4f4fSmartijn for (i = 0; i < o->bo_n; i++) {
559442e4f4fSmartijn key.o_oidlen = i + 1;
560442e4f4fSmartijn if (lookup != smi_oidl_numeric &&
561442e4f4fSmartijn (value = RB_FIND(oidtree, &smi_oidtree, &key)) != NULL) {
562442e4f4fSmartijn snprintf(str, sizeof(str), "%s", value->o_name);
563442e4f4fSmartijn if (lookup == smi_oidl_short && i + 1 < o->bo_n) {
564442e4f4fSmartijn key.o_oidlen = i + 2;
565442e4f4fSmartijn if (RB_FIND(oidtree, &smi_oidtree, &key) != NULL)
566442e4f4fSmartijn continue;
567442e4f4fSmartijn }
568442e4f4fSmartijn } else
56933ca237aSmartijn snprintf(str, sizeof(str), "%u", key.o_oid[i]);
570442e4f4fSmartijn if (*buf != '\0' || i == 0)
571442e4f4fSmartijn strlcat(buf, ".", len);
572442e4f4fSmartijn strlcat(buf, str, len);
573442e4f4fSmartijn }
574442e4f4fSmartijn
575442e4f4fSmartijn return (buf);
576442e4f4fSmartijn }
577442e4f4fSmartijn
578442e4f4fSmartijn void
smi_mibtree(struct oid * oids)579442e4f4fSmartijn smi_mibtree(struct oid *oids)
580442e4f4fSmartijn {
581442e4f4fSmartijn size_t i;
582442e4f4fSmartijn
5836972fbe4Smartijn for (i = 0; oids[i].o_name != NULL; i++) {
5846972fbe4Smartijn RB_INSERT(oidtree, &smi_oidtree, &(oids[i]));
5856972fbe4Smartijn RB_INSERT(keytree, &smi_keytree, &(oids[i]));
586442e4f4fSmartijn }
587442e4f4fSmartijn }
588442e4f4fSmartijn
5891248928aSmartijn void
smi_textconvtree(struct textconv * textconvs)5901248928aSmartijn smi_textconvtree(struct textconv *textconvs)
5911248928aSmartijn {
5921248928aSmartijn size_t i = 0;
5931248928aSmartijn
5941248928aSmartijn for (i = 0; textconvs[i].tc_name != NULL; i++)
5951248928aSmartijn RB_INSERT(textconvtree, &smi_tctree, &(textconvs[i]));
5961248928aSmartijn }
5971248928aSmartijn
598442e4f4fSmartijn struct oid *
smi_findkey(char * name)599442e4f4fSmartijn smi_findkey(char *name)
600442e4f4fSmartijn {
601442e4f4fSmartijn struct oid oid;
602442e4f4fSmartijn if (name == NULL)
603442e4f4fSmartijn return (NULL);
604442e4f4fSmartijn oid.o_name = name;
605442e4f4fSmartijn return (RB_FIND(keytree, &smi_keytree, &oid));
606442e4f4fSmartijn }
607442e4f4fSmartijn
608442e4f4fSmartijn struct oid *
smi_foreach(struct oid * oid)6099265c3a7Smartijn smi_foreach(struct oid *oid)
610442e4f4fSmartijn {
611442e4f4fSmartijn /*
612442e4f4fSmartijn * Traverse the tree of MIBs with the option to check
613442e4f4fSmartijn * for specific OID flags.
614442e4f4fSmartijn */
615442e4f4fSmartijn if (oid == NULL)
6169265c3a7Smartijn return RB_MIN(oidtree, &smi_oidtree);
6179265c3a7Smartijn return RB_NEXT(oidtree, &smi_oidtree, oid);
618442e4f4fSmartijn }
619442e4f4fSmartijn
620dd843e54Smartijn char *
smi_displayhint_int(struct textconv * tc,int print_hint,long long v)621dd843e54Smartijn smi_displayhint_int(struct textconv *tc, int print_hint, long long v)
622dd843e54Smartijn {
623dd843e54Smartijn size_t i;
624dd843e54Smartijn char *rbuf;
625dd843e54Smartijn
626dd843e54Smartijn for (i = 0; tc->tc_enum[i].tce_name != NULL; i++) {
627dd843e54Smartijn if (tc->tc_enum[i].tce_number == v) {
628dd843e54Smartijn if (print_hint) {
629dd843e54Smartijn if (asprintf(&rbuf, "INTEGER: %s(%lld)",
630dd843e54Smartijn tc->tc_enum[i].tce_name, v) == -1)
631dd843e54Smartijn return NULL;
632dd843e54Smartijn } else {
633dd843e54Smartijn if (asprintf(&rbuf, "%s",
634dd843e54Smartijn tc->tc_enum[i].tce_name) == -1)
635dd843e54Smartijn return NULL;
636dd843e54Smartijn }
637dd843e54Smartijn return rbuf;
638dd843e54Smartijn }
639dd843e54Smartijn }
640dd843e54Smartijn if (asprintf(&rbuf, "%s%lld", print_hint ? "INTEGER: " : "", v) == -1)
641dd843e54Smartijn return NULL;
642dd843e54Smartijn return rbuf;
643dd843e54Smartijn }
644dd843e54Smartijn
6451248928aSmartijn #define REPLACEMENT "\357\277\275"
6461248928aSmartijn char *
smi_displayhint_os(struct textconv * tc,int print_hint,const char * src,size_t srclen,int utf8)6471248928aSmartijn smi_displayhint_os(struct textconv *tc, int print_hint, const char *src,
6481248928aSmartijn size_t srclen, int utf8)
6491248928aSmartijn {
6501248928aSmartijn size_t octetlength, i = 0, j = 0;
65127b4df32Smartijn size_t prefixlen;
6521248928aSmartijn unsigned long ulval;
6531248928aSmartijn int clen;
6541248928aSmartijn char *displayformat;
65527b4df32Smartijn const char *prefix;
6561248928aSmartijn char *rbuf, *dst;
6571248928aSmartijn wchar_t wc;
6581248928aSmartijn
65927b4df32Smartijn prefix = print_hint ? "STRING: " : "";
66027b4df32Smartijn prefixlen = strlen(prefix);
66127b4df32Smartijn
6621248928aSmartijn errno = 0;
6631248928aSmartijn ulval = strtoul(tc->tc_display_hint, &displayformat, 10);
6641248928aSmartijn octetlength = ulval;
6651248928aSmartijn if (!isdigit(tc->tc_display_hint[0]) ||
6661248928aSmartijn (errno != 0 && (ulval == 0 || ulval == ULONG_MAX)) ||
6671248928aSmartijn (unsigned long) octetlength != ulval) {
6681248928aSmartijn errno = EINVAL;
6691248928aSmartijn return NULL;
6701248928aSmartijn }
6711248928aSmartijn
67227b4df32Smartijn if (displayformat[0] == 't' || displayformat[0] == 'a') {
67327b4df32Smartijn if ((rbuf = malloc(prefixlen + octetlength + 1)) == NULL)
6741248928aSmartijn return NULL;
67527b4df32Smartijn (void)strlcpy(rbuf, prefix, prefixlen + octetlength + 1);
67627b4df32Smartijn dst = rbuf + prefixlen;
6771248928aSmartijn while (j < octetlength && i < srclen) {
6781248928aSmartijn clen = mbtowc(&wc, &(src[i]), srclen - i);
67927b4df32Smartijn if (displayformat[0] == 'a' && clen > 1)
68027b4df32Smartijn clen = -1;
6811248928aSmartijn switch (clen) {
6821248928aSmartijn case 0:
6831248928aSmartijn dst[j++] = '.';
6841248928aSmartijn i++;
6851248928aSmartijn break;
6861248928aSmartijn case -1:
6871248928aSmartijn mbtowc(NULL, NULL, MB_CUR_MAX);
6881248928aSmartijn if (utf8) {
6891248928aSmartijn if (octetlength - j <
6901248928aSmartijn sizeof(REPLACEMENT) - 1) {
6911248928aSmartijn dst[j] = '\0';
6921248928aSmartijn return rbuf;
6931248928aSmartijn }
6941248928aSmartijn memcpy(&(dst[j]), REPLACEMENT,
6951248928aSmartijn sizeof(REPLACEMENT) - 1);
6961248928aSmartijn j += sizeof(REPLACEMENT) - 1;
6971248928aSmartijn } else
6981248928aSmartijn dst[j++] = '?';
6991248928aSmartijn i++;
7001248928aSmartijn break;
7011248928aSmartijn default:
7021248928aSmartijn if (!iswprint(wc) || (!utf8 && clen > 1))
7031248928aSmartijn dst[j++] = '.';
7041248928aSmartijn else if (octetlength - j < (size_t)clen) {
7051248928aSmartijn dst[j] = '\0';
7061248928aSmartijn return rbuf;
7071248928aSmartijn } else {
7081248928aSmartijn memcpy(&(dst[j]), &(src[i]), clen);
7091248928aSmartijn j += clen;
7101248928aSmartijn }
7111248928aSmartijn i += clen;
7121248928aSmartijn break;
7131248928aSmartijn }
7141248928aSmartijn }
7151248928aSmartijn dst[j] = '\0';
7161248928aSmartijn return rbuf;
7171248928aSmartijn }
7181248928aSmartijn errno = EINVAL;
7191248928aSmartijn return NULL;
7201248928aSmartijn }
7211248928aSmartijn
722442e4f4fSmartijn int
smi_oid_cmp(struct oid * a,struct oid * b)723442e4f4fSmartijn smi_oid_cmp(struct oid *a, struct oid *b)
724442e4f4fSmartijn {
725442e4f4fSmartijn size_t i;
726442e4f4fSmartijn
727442e4f4fSmartijn for (i = 0; i < MINIMUM(a->o_oidlen, b->o_oidlen); i++) {
728442e4f4fSmartijn if (a->o_oid[i] != b->o_oid[i])
729442e4f4fSmartijn return (a->o_oid[i] - b->o_oid[i]);
730442e4f4fSmartijn }
731442e4f4fSmartijn
732442e4f4fSmartijn return (a->o_oidlen - b->o_oidlen);
733442e4f4fSmartijn }
734442e4f4fSmartijn
735442e4f4fSmartijn int
smi_key_cmp(struct oid * a,struct oid * b)736442e4f4fSmartijn smi_key_cmp(struct oid *a, struct oid *b)
737442e4f4fSmartijn {
738442e4f4fSmartijn if (a->o_name == NULL || b->o_name == NULL)
739442e4f4fSmartijn return (-1);
740442e4f4fSmartijn return (strcasecmp(a->o_name, b->o_name));
741442e4f4fSmartijn }
742442e4f4fSmartijn
7431248928aSmartijn int
smi_textconv_cmp(struct textconv * a,struct textconv * b)7441248928aSmartijn smi_textconv_cmp(struct textconv *a, struct textconv *b)
7451248928aSmartijn {
7461248928aSmartijn return strcmp(a->tc_name, b->tc_name);
7471248928aSmartijn }
7481248928aSmartijn
749442e4f4fSmartijn RB_GENERATE(oidtree, oid, o_element, smi_oid_cmp)
750442e4f4fSmartijn RB_GENERATE(keytree, oid, o_keyword, smi_key_cmp)
7511248928aSmartijn RB_GENERATE(textconvtree, textconv, tc_entry, smi_textconv_cmp);
752