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