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 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 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 * 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 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 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 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 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 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 * 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 * 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 * 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 * 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 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 743 int 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