smi.c revision 1.13
1/*	$OpenBSD: smi.c,v 1.13 2020/12/14 07:44:26 martijn Exp $	*/
2
3/*
4 * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
5 * Copyright (c) 2007, 2008 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/tree.h>
22#include <sys/queue.h>
23
24#include <arpa/inet.h>
25
26#include <ctype.h>
27#include <errno.h>
28#include <stdlib.h>
29#include <stdio.h>
30#include <string.h>
31#include <strings.h>
32#include <wctype.h>
33
34#include "ber.h"
35#include "mib.h"
36#include "snmp.h"
37#include "smi.h"
38
39#define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
40
41char *smi_displayhint_os(struct textconv *, int, const char *, size_t, int);
42char *smi_displayhint_int(struct textconv*, int, long long);
43
44int smi_oid_cmp(struct oid *, struct oid *);
45int smi_key_cmp(struct oid *, struct oid *);
46int smi_textconv_cmp(struct textconv *, struct textconv *);
47struct oid * smi_findkey(char *);
48
49RB_HEAD(oidtree, oid);
50RB_PROTOTYPE(oidtree, oid, o_element, smi_oid_cmp)
51struct oidtree smi_oidtree;
52
53RB_HEAD(keytree, oid);
54RB_PROTOTYPE(keytree, oid, o_keyword, smi_key_cmp)
55struct keytree smi_keytree;
56
57RB_HEAD(textconvtree, textconv);
58RB_PROTOTYPE(textconvtree, textconv, tc_entry, smi_textconv_cmp);
59struct textconvtree smi_tctree;
60
61int
62smi_init(void)
63{
64	/* Initialize the Structure of Managed Information (SMI) */
65	RB_INIT(&smi_oidtree);
66	mib_init();
67	return (0);
68}
69
70void
71smi_debug_elements(struct ber_element *root, int utf8)
72{
73	static int	 indent = 0;
74	char		*value;
75	int		 constructed;
76
77	/* calculate lengths */
78	ober_calc_len(root);
79
80	switch (root->be_encoding) {
81	case BER_TYPE_SEQUENCE:
82	case BER_TYPE_SET:
83		constructed = root->be_encoding;
84		break;
85	default:
86		constructed = 0;
87		break;
88	}
89
90	fprintf(stderr, "%*slen %lu ", indent, "", root->be_len);
91	switch (root->be_class) {
92	case BER_CLASS_UNIVERSAL:
93		fprintf(stderr, "class: universal(%u) type: ", root->be_class);
94		switch (root->be_type) {
95		case BER_TYPE_EOC:
96			fprintf(stderr, "end-of-content");
97			break;
98		case BER_TYPE_BOOLEAN:
99			fprintf(stderr, "boolean");
100			break;
101		case BER_TYPE_INTEGER:
102			fprintf(stderr, "integer");
103			break;
104		case BER_TYPE_BITSTRING:
105			fprintf(stderr, "bit-string");
106			break;
107		case BER_TYPE_OCTETSTRING:
108			fprintf(stderr, "octet-string");
109			break;
110		case BER_TYPE_NULL:
111			fprintf(stderr, "null");
112			break;
113		case BER_TYPE_OBJECT:
114			fprintf(stderr, "object");
115			break;
116		case BER_TYPE_ENUMERATED:
117			fprintf(stderr, "enumerated");
118			break;
119		case BER_TYPE_SEQUENCE:
120			fprintf(stderr, "sequence");
121			break;
122		case BER_TYPE_SET:
123			fprintf(stderr, "set");
124			break;
125		}
126		break;
127	case BER_CLASS_APPLICATION:
128		fprintf(stderr, "class: application(%u) type: ",
129		    root->be_class);
130		switch (root->be_type) {
131		case SNMP_T_IPADDR:
132			fprintf(stderr, "ipaddr");
133			break;
134		case SNMP_T_COUNTER32:
135			fprintf(stderr, "counter32");
136			break;
137		case SNMP_T_GAUGE32:
138			fprintf(stderr, "gauge32");
139			break;
140		case SNMP_T_TIMETICKS:
141			fprintf(stderr, "timeticks");
142			break;
143		case SNMP_T_OPAQUE:
144			fprintf(stderr, "opaque");
145			break;
146		case SNMP_T_COUNTER64:
147			fprintf(stderr, "counter64");
148			break;
149		}
150		break;
151	case BER_CLASS_CONTEXT:
152		fprintf(stderr, "class: context(%u) type: ",
153		    root->be_class);
154		switch (root->be_type) {
155		case SNMP_C_GETREQ:
156			fprintf(stderr, "getreq");
157			break;
158		case SNMP_C_GETNEXTREQ:
159			fprintf(stderr, "nextreq");
160			break;
161		case SNMP_C_GETRESP:
162			fprintf(stderr, "getresp");
163			break;
164		case SNMP_C_SETREQ:
165			fprintf(stderr, "setreq");
166			break;
167		case SNMP_C_TRAP:
168			fprintf(stderr, "trap");
169			break;
170		case SNMP_C_GETBULKREQ:
171			fprintf(stderr, "getbulkreq");
172			break;
173		case SNMP_C_INFORMREQ:
174			fprintf(stderr, "informreq");
175			break;
176		case SNMP_C_TRAPV2:
177			fprintf(stderr, "trapv2");
178			break;
179		case SNMP_C_REPORT:
180			fprintf(stderr, "report");
181			break;
182		}
183		break;
184	case BER_CLASS_PRIVATE:
185		fprintf(stderr, "class: private(%u) type: ", root->be_class);
186		break;
187	default:
188		fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class);
189		break;
190	}
191	fprintf(stderr, "(%u) encoding %u ",
192	    root->be_type, root->be_encoding);
193
194	if ((value = smi_print_element(NULL, root, 1, smi_os_default,
195	    smi_oidl_numeric, utf8)) == NULL)
196		goto invalid;
197
198	switch (root->be_encoding) {
199	case BER_TYPE_BOOLEAN:
200		fprintf(stderr, "%s", value);
201		break;
202	case BER_TYPE_INTEGER:
203	case BER_TYPE_ENUMERATED:
204		fprintf(stderr, "value %s", value);
205		break;
206	case BER_TYPE_BITSTRING:
207		fprintf(stderr, "hexdump %s", value);
208		break;
209	case BER_TYPE_OBJECT:
210		fprintf(stderr, "oid %s", value);
211		break;
212	case BER_TYPE_OCTETSTRING:
213		if (root->be_class == BER_CLASS_APPLICATION &&
214		    root->be_type == SNMP_T_IPADDR) {
215			fprintf(stderr, "addr %s", value);
216		} else {
217			fprintf(stderr, "string %s", value);
218		}
219		break;
220	case BER_TYPE_NULL:	/* no payload */
221	case BER_TYPE_EOC:
222	case BER_TYPE_SEQUENCE:
223	case BER_TYPE_SET:
224	default:
225		fprintf(stderr, "%s", value);
226		break;
227	}
228
229 invalid:
230	if (value == NULL)
231		fprintf(stderr, "<INVALID>");
232	else
233		free(value);
234	fprintf(stderr, "\n");
235
236	if (constructed)
237		root->be_encoding = constructed;
238
239	if (constructed && root->be_sub) {
240		indent += 2;
241		smi_debug_elements(root->be_sub, utf8);
242		indent -= 2;
243	}
244	if (root->be_next)
245		smi_debug_elements(root->be_next, utf8);
246}
247
248char *
249smi_print_element(struct ber_oid *oid, struct ber_element *root, int print_hint,
250    enum smi_output_string output_string, enum smi_oid_lookup lookup, int utf8)
251{
252	char		*str = NULL, *buf, *p;
253	struct oid	 okey;
254	struct oid	*object = NULL;
255	struct textconv	 tckey;
256	size_t		 len, i, slen;
257	long long	 v, ticks;
258	int		 d;
259	int		 is_hex = 0, ret;
260	struct ber_oid	 o;
261	char		 strbuf[BUFSIZ];
262	char		*hint;
263	int		 days, hours, min, sec, csec;
264
265	if (oid != NULL) {
266		bcopy(oid, &(okey.o_id), sizeof(okey));
267		do {
268			object = RB_FIND(oidtree, &smi_oidtree, &okey);
269			okey.o_id.bo_n--;
270		} while (object == NULL && okey.o_id.bo_n > 0);
271		if (object != NULL && object->o_textconv == NULL &&
272		    object->o_tcname != NULL) {
273			tckey.tc_name = object->o_tcname;
274			object->o_textconv = RB_FIND(textconvtree, &smi_tctree,
275			    &tckey);
276		}
277	}
278
279	switch (root->be_encoding) {
280	case BER_TYPE_BOOLEAN:
281		if (ober_get_boolean(root, &d) == -1)
282			goto fail;
283		if (print_hint) {
284			if (asprintf(&str, "INTEGER: %s(%d)",
285			    d ? "true" : "false", d) == -1)
286				goto fail;
287		} else
288			if (asprintf(&str, "%s", d ? "true" : "false") == -1)
289				goto fail;
290		break;
291	case BER_TYPE_INTEGER:
292	case BER_TYPE_ENUMERATED:
293		if (ober_get_integer(root, &v) == -1)
294			goto fail;
295		if (root->be_class == BER_CLASS_APPLICATION &&
296		    root->be_type == SNMP_T_TIMETICKS) {
297			ticks = v;
298			days = ticks / (60 * 60 * 24 * 100);
299			ticks %= (60 * 60 * 24 * 100);
300			hours = ticks / (60 * 60 * 100);
301			ticks %= (60 * 60 * 100);
302			min = ticks / (60 * 100);
303			ticks %= (60 * 100);
304			sec = ticks / 100;
305			ticks %= 100;
306			csec = ticks;
307
308			if (print_hint) {
309				if (days == 0) {
310					if (asprintf(&str,
311					    "Timeticks: (%lld) "
312					    "%d:%02d:%02d.%02d",
313					    v, hours, min, sec, csec) == -1)
314						goto fail;
315				} else if (days == 1) {
316					if (asprintf(&str,
317					    "Timeticks: (%lld) "
318					    "1 day %d:%02d:%02d.%02d",
319					    v, hours, min, sec, csec) == -1)
320						goto fail;
321				} else {
322					if (asprintf(&str,
323					    "Timeticks: (%lld) "
324					    "%d day %d:%02d:%02d.%02d",
325					    v, days, hours, min, sec, csec) ==
326					    -1)
327						goto fail;
328				}
329			} else {
330				if (days == 0) {
331					if (asprintf(&str, "%d:%02d:%02d.%02d",
332					    hours, min, sec, csec) == -1)
333						goto fail;
334				} else if (days == 1) {
335					if (asprintf(&str,
336					    "1 day %d:%02d:%02d.%02d",
337					    hours, min, sec, csec) == -1)
338						goto fail;
339				} else {
340					if (asprintf(&str,
341					    "%d day %d:%02d:%02d.%02d",
342					    days, hours, min, sec, csec) == -1)
343						goto fail;
344				}
345			}
346			break;
347		}
348		hint = "INTEGER: ";
349		if (object != NULL && object->o_textconv != NULL &&
350		    object->o_textconv->tc_syntax == root->be_encoding)
351			return smi_displayhint_int(object->o_textconv,
352			    print_hint, v);
353		if (root->be_class == BER_CLASS_APPLICATION) {
354			if (root->be_type == SNMP_T_COUNTER32)
355				hint = "Counter32: ";
356			else if (root->be_type == SNMP_T_GAUGE32)
357				hint = "Gauge32: ";
358			else if (root->be_type == SNMP_T_OPAQUE)
359				hint = "Opaque: ";
360			else if (root->be_type == SNMP_T_COUNTER64)
361				hint = "Counter64: ";
362		}
363		if (asprintf(&str, "%s%lld", print_hint ? hint : "", v) == -1)
364			goto fail;
365		break;
366	case BER_TYPE_BITSTRING:
367		if (ober_get_bitstring(root, (void *)&buf, &len) == -1)
368			goto fail;
369		slen = len * 2 + 1 + sizeof("BITS: ");
370		if ((str = calloc(1, slen)) == NULL)
371			goto fail;
372		p = str;
373		if (print_hint) {
374			strlcpy(str, "BITS: ", slen);
375			p += sizeof("BITS: ");
376		}
377		for (i = 0; i < len; i++) {
378			snprintf(p, 3, "%02x", buf[i]);
379			p += 2;
380		}
381		break;
382	case BER_TYPE_OBJECT:
383		if (ober_get_oid(root, &o) == -1)
384			goto fail;
385		if (asprintf(&str, "%s%s",
386		    print_hint ? "OID: " : "",
387		    smi_oid2string(&o, strbuf, sizeof(strbuf), lookup)) == -1)
388			goto fail;
389		break;
390	case BER_TYPE_OCTETSTRING:
391		if (ober_get_string(root, &buf) == -1)
392			goto fail;
393		if (root->be_class == BER_CLASS_APPLICATION &&
394		    root->be_type == SNMP_T_IPADDR) {
395			if (asprintf(&str, "%s%s",
396			    print_hint ? "IpAddress: " : "",
397			    inet_ntoa(*(struct in_addr *)buf)) == -1)
398				goto fail;
399		} else if (root->be_class == BER_CLASS_CONTEXT) {
400			if (root->be_type == SNMP_E_NOSUCHOBJECT)
401				str = strdup("No Such Object available on this "
402				    "agent at this OID");
403			else if (root->be_type == SNMP_E_NOSUCHINSTANCE)
404				str = strdup("No Such Instance currently "
405				    "exists at this OID");
406			else if (root->be_type == SNMP_E_ENDOFMIB)
407				str = strdup("No more variables left in this "
408				    "MIB View (It is past the end of the MIB "
409				    "tree)");
410			else
411				str = strdup("Unknown status at this OID");
412		} else {
413			if (object != NULL && object->o_textconv != NULL &&
414			    object->o_textconv->tc_syntax == root->be_encoding)
415				return smi_displayhint_os(object->o_textconv,
416				    print_hint, buf, root->be_len, utf8);
417			for (i = 0; i < root->be_len; i++) {
418				if (!isprint(buf[i])) {
419					if (output_string == smi_os_default)
420						output_string = smi_os_hex;
421					else if (output_string == smi_os_ascii)
422						is_hex = 1;
423					break;
424				}
425			}
426			/*
427			 * hex is 3 * n (2 digits + n - 1 spaces + NUL-byte)
428			 * ascii can be max (2 * n) + 2 quotes + NUL-byte
429			 */
430			len = output_string == smi_os_hex ? 3 : 2;
431			p = str = reallocarray(NULL, root->be_len + 2, len);
432			if (p == NULL)
433				goto fail;
434			len *= root->be_len + 2;
435			if (is_hex) {
436				*str++ = '"';
437				len--;
438			}
439			for (i = 0; i < root->be_len; i++) {
440				switch (output_string) {
441				case smi_os_default:
442					/* FALLTHROUGH */
443				case smi_os_ascii:
444					/*
445					 * There's probably more edgecases here,
446					 * not fully investigated
447					 */
448					if (len < 2)
449						goto fail;
450					if (is_hex && buf[i] == '\\') {
451						*str++ = '\\';
452						len--;
453					}
454					*str++ = isprint(buf[i]) ? buf[i] : '.';
455					len--;
456					break;
457				case smi_os_hex:
458					ret = snprintf(str, len, "%s%02hhX",
459					    i == 0 ? "" :
460					    i % 16 == 0 ? "\n" : " ", buf[i]);
461					if (ret == -1 || ret > (int) len)
462						goto fail;
463					len -= ret;
464					str += ret;
465					break;
466				}
467			}
468			if (is_hex) {
469				if (len < 2)
470					goto fail;
471				*str++ = '"';
472				len--;
473			}
474			if (len == 0)
475				goto fail;
476			*str = '\0';
477			str = NULL;
478			if (asprintf(&str, "%s%s",
479			    print_hint ?
480			    output_string == smi_os_hex ? "Hex-STRING: " :
481			    "STRING: " :
482			    "", p) == -1) {
483				free(p);
484				goto fail;
485			}
486			free(p);
487		}
488		break;
489	case BER_TYPE_NULL:	/* no payload */
490	case BER_TYPE_EOC:
491	case BER_TYPE_SEQUENCE:
492	case BER_TYPE_SET:
493	default:
494		str = strdup("");
495		break;
496	}
497
498	return (str);
499
500 fail:
501	free(str);
502	return (NULL);
503}
504
505int
506smi_string2oid(const char *oidstr, struct ber_oid *o)
507{
508	char			*sp, *p, str[BUFSIZ];
509	const char		*errstr;
510	struct oid		*oid;
511	struct ber_oid		 ko;
512
513	if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str))
514		return (-1);
515	bzero(o, sizeof(*o));
516
517	/*
518	 * Parse OID strings in the common form n.n.n or n-n-n.
519	 * Based on ober_string2oid with additional support for symbolic names.
520	 */
521	p = sp = str[0] == '.' ? str + 1 : str;
522	for (; p != NULL; sp = p) {
523		if ((p = strpbrk(p, ".-")) != NULL)
524			*p++ = '\0';
525		if ((oid = smi_findkey(sp)) != NULL) {
526			bcopy(&oid->o_id, &ko, sizeof(ko));
527			if (o->bo_n && ober_oid_cmp(o, &ko) != 2)
528				return (-1);
529			bcopy(&ko, o, sizeof(*o));
530			errstr = NULL;
531		} else {
532			o->bo_id[o->bo_n++] =
533			    strtonum(sp, 0, UINT_MAX, &errstr);
534		}
535		if (errstr || o->bo_n > BER_MAX_OID_LEN)
536			return (-1);
537	}
538
539	return (0);
540}
541
542unsigned int
543smi_application(struct ber_element *elm)
544{
545	if (elm->be_class != BER_CLASS_APPLICATION)
546		return (BER_TYPE_OCTETSTRING);
547
548	switch (elm->be_type) {
549	case SNMP_T_IPADDR:
550		return (BER_TYPE_OCTETSTRING);
551	case SNMP_T_COUNTER32:
552	case SNMP_T_GAUGE32:
553	case SNMP_T_TIMETICKS:
554	case SNMP_T_OPAQUE:
555	case SNMP_T_COUNTER64:
556		return (BER_TYPE_INTEGER);
557	default:
558		break;
559	}
560	return (BER_TYPE_OCTETSTRING);
561
562}
563
564char *
565smi_oid2string(struct ber_oid *o, char *buf, size_t len,
566    enum smi_oid_lookup lookup)
567{
568	char		 str[256];
569	struct oid	*value, key;
570	size_t		 i;
571
572	bzero(buf, len);
573	bzero(&key, sizeof(key));
574	bcopy(o, &key.o_id, sizeof(struct ber_oid));
575
576	for (i = 0; i < o->bo_n; i++) {
577		key.o_oidlen = i + 1;
578		if (lookup != smi_oidl_numeric &&
579		    (value = RB_FIND(oidtree, &smi_oidtree, &key)) != NULL) {
580			snprintf(str, sizeof(str), "%s", value->o_name);
581			if (lookup == smi_oidl_short && i + 1 < o->bo_n) {
582				key.o_oidlen = i + 2;
583				if (RB_FIND(oidtree, &smi_oidtree, &key) != NULL)
584					continue;
585			}
586		} else
587			snprintf(str, sizeof(str), "%u", key.o_oid[i]);
588		if (*buf != '\0' || i == 0)
589			strlcat(buf, ".", len);
590		strlcat(buf, str, len);
591	}
592
593	return (buf);
594}
595
596void
597smi_mibtree(struct oid *oids)
598{
599	size_t		 i;
600
601	for (i = 0; oids[i].o_name != NULL; i++) {
602		RB_INSERT(oidtree, &smi_oidtree, &(oids[i]));
603		RB_INSERT(keytree, &smi_keytree, &(oids[i]));
604	}
605}
606
607void
608smi_textconvtree(struct textconv *textconvs)
609{
610	size_t		 i = 0;
611
612	for (i = 0; textconvs[i].tc_name != NULL; i++)
613		RB_INSERT(textconvtree, &smi_tctree, &(textconvs[i]));
614}
615
616struct oid *
617smi_findkey(char *name)
618{
619	struct oid	oid;
620	if (name == NULL)
621		return (NULL);
622	oid.o_name = name;
623	return (RB_FIND(keytree, &smi_keytree, &oid));
624}
625
626struct oid *
627smi_foreach(struct oid *oid)
628{
629	/*
630	 * Traverse the tree of MIBs with the option to check
631	 * for specific OID flags.
632	 */
633	if (oid == NULL)
634		return RB_MIN(oidtree, &smi_oidtree);
635	return RB_NEXT(oidtree, &smi_oidtree, oid);
636}
637
638char *
639smi_displayhint_int(struct textconv *tc, int print_hint, long long v)
640{
641	size_t i;
642	char *rbuf;
643
644	for (i = 0; tc->tc_enum[i].tce_name != NULL; i++) {
645		if (tc->tc_enum[i].tce_number == v) {
646			if (print_hint) {
647				if (asprintf(&rbuf, "INTEGER: %s(%lld)",
648				    tc->tc_enum[i].tce_name, v) == -1)
649					return NULL;
650			} else {
651				if (asprintf(&rbuf, "%s",
652				    tc->tc_enum[i].tce_name) == -1)
653					return NULL;
654			}
655			return rbuf;
656		}
657	}
658	if (asprintf(&rbuf, "%s%lld", print_hint ? "INTEGER: " : "", v) == -1)
659		return NULL;
660	return rbuf;
661}
662
663#define REPLACEMENT "\357\277\275"
664char *
665smi_displayhint_os(struct textconv *tc, int print_hint, const char *src,
666    size_t srclen, int utf8)
667{
668	size_t octetlength, i = 0, j = 0;
669	size_t prefixlen;
670	unsigned long ulval;
671	int clen;
672	char *displayformat;
673	const char *prefix;
674	char *rbuf, *dst;
675	wchar_t wc;
676
677	prefix = print_hint ? "STRING: " : "";
678	prefixlen = strlen(prefix);
679
680	errno = 0;
681	ulval = strtoul(tc->tc_display_hint, &displayformat, 10);
682	octetlength = ulval;
683	if (!isdigit(tc->tc_display_hint[0]) ||
684	    (errno != 0 && (ulval == 0 || ulval == ULONG_MAX)) ||
685	    (unsigned long) octetlength != ulval) {
686		errno = EINVAL;
687		return NULL;
688	}
689
690	if (displayformat[0] == 't' || displayformat[0] == 'a') {
691		if ((rbuf = malloc(prefixlen + octetlength + 1)) == NULL)
692			return NULL;
693		(void)strlcpy(rbuf, prefix, prefixlen + octetlength + 1);
694		dst = rbuf + prefixlen;
695		while (j < octetlength && i < srclen) {
696			clen = mbtowc(&wc, &(src[i]), srclen - i);
697			if (displayformat[0] == 'a' && clen > 1)
698				clen = -1;
699			switch (clen) {
700			case 0:
701				dst[j++] = '.';
702				i++;
703				break;
704			case -1:
705				mbtowc(NULL, NULL, MB_CUR_MAX);
706				if (utf8) {
707					if (octetlength - j <
708					    sizeof(REPLACEMENT) - 1) {
709						dst[j] = '\0';
710						return rbuf;
711					}
712					memcpy(&(dst[j]), REPLACEMENT,
713					    sizeof(REPLACEMENT) - 1);
714					j += sizeof(REPLACEMENT) - 1;
715				} else
716					dst[j++] = '?';
717				i++;
718				break;
719			default:
720				if (!iswprint(wc) || (!utf8 && clen > 1))
721					dst[j++] = '.';
722				else if (octetlength - j < (size_t)clen) {
723					dst[j] = '\0';
724					return rbuf;
725				} else {
726					memcpy(&(dst[j]), &(src[i]), clen);
727					j += clen;
728				}
729				i += clen;
730				break;
731			}
732		}
733		dst[j] = '\0';
734		return rbuf;
735	}
736	errno = EINVAL;
737	return NULL;
738}
739
740int
741smi_oid_cmp(struct oid *a, struct oid *b)
742{
743	size_t	 i;
744
745	for (i = 0; i < MINIMUM(a->o_oidlen, b->o_oidlen); i++) {
746		if (a->o_oid[i] != b->o_oid[i])
747			return (a->o_oid[i] - b->o_oid[i]);
748	}
749
750	return (a->o_oidlen - b->o_oidlen);
751}
752
753int
754smi_key_cmp(struct oid *a, struct oid *b)
755{
756	if (a->o_name == NULL || b->o_name == NULL)
757		return (-1);
758	return (strcasecmp(a->o_name, b->o_name));
759}
760
761int
762smi_textconv_cmp(struct textconv *a, struct textconv *b)
763{
764	return strcmp(a->tc_name, b->tc_name);
765}
766
767RB_GENERATE(oidtree, oid, o_element, smi_oid_cmp)
768RB_GENERATE(keytree, oid, o_keyword, smi_key_cmp)
769RB_GENERATE(textconvtree, textconv, tc_entry, smi_textconv_cmp);
770