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