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