xref: /openbsd-src/usr.bin/snmp/smi.c (revision 893ac8ce76059d9b03d6af2b2bc0e32767cc4f0b)
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