Annotation of src/usr.bin/snmp/smi.c, Revision 1.14
1.14 ! martijn 1: /* $OpenBSD: smi.c,v 1.13 2020/12/14 07:44:26 martijn Exp $ */
1.1 martijn 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>
1.10 martijn 27: #include <errno.h>
1.1 martijn 28: #include <stdlib.h>
29: #include <stdio.h>
30: #include <string.h>
31: #include <strings.h>
1.10 martijn 32: #include <wctype.h>
1.1 martijn 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:
1.10 martijn 41: char *smi_displayhint_os(struct textconv *, int, const char *, size_t, int);
1.13 martijn 42: char *smi_displayhint_int(struct textconv*, int, long long);
1.10 martijn 43:
1.1 martijn 44: int smi_oid_cmp(struct oid *, struct oid *);
45: int smi_key_cmp(struct oid *, struct oid *);
1.10 martijn 46: int smi_textconv_cmp(struct textconv *, struct textconv *);
1.1 martijn 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:
1.10 martijn 57: RB_HEAD(textconvtree, textconv);
58: RB_PROTOTYPE(textconvtree, textconv, tc_entry, smi_textconv_cmp);
59: struct textconvtree smi_tctree;
60:
1.1 martijn 61: int
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
1.10 martijn 71: smi_debug_elements(struct ber_element *root, int utf8)
1.1 martijn 72: {
73: static int indent = 0;
74: char *value;
75: int constructed;
76:
77: /* calculate lengths */
1.6 tb 78: ober_calc_len(root);
1.1 martijn 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:
1.10 martijn 191: if ((value = smi_print_element(NULL, root, 1, smi_os_default,
192: smi_oidl_numeric, utf8)) == NULL)
1.1 martijn 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;
1.10 martijn 235: smi_debug_elements(root->be_sub, utf8);
1.1 martijn 236: indent -= 2;
237: }
238: if (root->be_next)
1.10 martijn 239: smi_debug_elements(root->be_next, utf8);
1.1 martijn 240: }
241:
242: char *
1.10 martijn 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)
1.1 martijn 245: {
246: char *str = NULL, *buf, *p;
1.10 martijn 247: struct oid okey;
248: struct oid *object = NULL;
249: struct textconv tckey;
1.5 jsg 250: size_t len, i, slen;
1.1 martijn 251: long long v, ticks;
1.4 martijn 252: int is_hex = 0, ret;
1.1 martijn 253: struct ber_oid o;
254: char strbuf[BUFSIZ];
255: char *hint;
256: int days, hours, min, sec, csec;
257:
1.10 martijn 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:
1.1 martijn 272: switch (root->be_encoding) {
273: case BER_TYPE_INTEGER:
274: case BER_TYPE_ENUMERATED:
1.6 tb 275: if (ober_get_integer(root, &v) == -1)
1.1 martijn 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: ";
1.13 martijn 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);
1.1 martijn 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: }
1.3 deraadt 345: if (asprintf(&str, "%s%lld", print_hint ? hint : "", v) == -1)
1.1 martijn 346: goto fail;
347: break;
348: case BER_TYPE_BITSTRING:
1.6 tb 349: if (ober_get_bitstring(root, (void *)&buf, &len) == -1)
1.1 martijn 350: goto fail;
1.5 jsg 351: slen = len * 2 + 1 + sizeof("BITS: ");
352: if ((str = calloc(1, slen)) == NULL)
1.1 martijn 353: goto fail;
354: p = str;
355: if (print_hint) {
1.5 jsg 356: strlcpy(str, "BITS: ", slen);
1.1 martijn 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:
1.6 tb 365: if (ober_get_oid(root, &o) == -1)
1.1 martijn 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:
1.6 tb 373: if (ober_get_string(root, &buf) == -1)
1.1 martijn 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;
1.7 martijn 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");
1.2 deraadt 394: } else {
1.10 martijn 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);
1.1 martijn 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: */
1.4 martijn 412: len = output_string == smi_os_hex ? 3 : 2;
413: p = str = reallocarray(NULL, root->be_len + 2, len);
414: if (p == NULL)
1.1 martijn 415: goto fail;
1.4 martijn 416: len *= root->be_len + 2;
417: if (is_hex) {
1.1 martijn 418: *str++ = '"';
1.4 martijn 419: len--;
420: }
1.1 martijn 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: */
1.4 martijn 430: if (len < 2)
431: goto fail;
432: if (is_hex && buf[i] == '\\') {
1.1 martijn 433: *str++ = '\\';
1.4 martijn 434: len--;
435: }
1.1 martijn 436: *str++ = isprint(buf[i]) ? buf[i] : '.';
1.4 martijn 437: len--;
1.1 martijn 438: break;
439: case smi_os_hex:
1.4 martijn 440: ret = snprintf(str, len, "%s%02hhX",
1.1 martijn 441: i == 0 ? "" :
442: i % 16 == 0 ? "\n" : " ", buf[i]);
1.4 martijn 443: if (ret == -1 || ret > (int) len)
444: goto fail;
445: len -= ret;
446: str += ret;
1.1 martijn 447: break;
448: }
449: }
1.4 martijn 450: if (is_hex) {
451: if (len < 2)
452: goto fail;
1.1 martijn 453: *str++ = '"';
1.4 martijn 454: len--;
455: }
456: if (len == 0)
457: goto fail;
1.1 martijn 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
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.
1.6 tb 501: * Based on ober_string2oid with additional support for symbolic names.
1.1 martijn 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));
1.6 tb 509: if (o->bo_n && ober_oid_cmp(o, &ko) != 2)
1.1 martijn 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
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 *
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
1.9 martijn 569: snprintf(str, sizeof(str), "%u", key.o_oid[i]);
1.1 martijn 570: if (*buf != '\0' || i == 0)
571: strlcat(buf, ".", len);
572: strlcat(buf, str, len);
573: }
574:
575: return (buf);
576: }
577:
578: void
579: smi_mibtree(struct oid *oids)
580: {
581: size_t i;
582:
1.12 martijn 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]));
1.1 martijn 586: }
587: }
588:
1.10 martijn 589: void
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:
1.1 martijn 598: struct oid *
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 *
1.8 martijn 609: smi_foreach(struct oid *oid)
1.1 martijn 610: {
611: /*
612: * Traverse the tree of MIBs with the option to check
613: * for specific OID flags.
614: */
1.8 martijn 615: if (oid == NULL)
616: return RB_MIN(oidtree, &smi_oidtree);
617: return RB_NEXT(oidtree, &smi_oidtree, oid);
1.13 martijn 618: }
619:
620: char *
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;
1.1 martijn 643: }
644:
1.10 martijn 645: #define REPLACEMENT "\357\277\275"
646: char *
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;
1.11 martijn 651: size_t prefixlen;
1.10 martijn 652: unsigned long ulval;
653: int clen;
654: char *displayformat;
1.11 martijn 655: const char *prefix;
1.10 martijn 656: char *rbuf, *dst;
657: wchar_t wc;
658:
1.11 martijn 659: prefix = print_hint ? "STRING: " : "";
660: prefixlen = strlen(prefix);
661:
1.10 martijn 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:
1.11 martijn 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;
1.10 martijn 677: while (j < octetlength && i < srclen) {
678: clen = mbtowc(&wc, &(src[i]), srclen - i);
1.11 martijn 679: if (displayformat[0] == 'a' && clen > 1)
680: clen = -1;
1.10 martijn 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:
1.1 martijn 722: int
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
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:
1.10 martijn 743: int
744: smi_textconv_cmp(struct textconv *a, struct textconv *b)
745: {
746: return strcmp(a->tc_name, b->tc_name);
747: }
748:
1.1 martijn 749: RB_GENERATE(oidtree, oid, o_element, smi_oid_cmp)
750: RB_GENERATE(keytree, oid, o_keyword, smi_key_cmp)
1.10 martijn 751: RB_GENERATE(textconvtree, textconv, tc_entry, smi_textconv_cmp);