1 /* 2 * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 7 #pragma ident "%Z%%M% %I% %E% SMI" 8 9 10 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- 11 * 12 * The contents of this file are subject to the Netscape Public License 13 * Version 1.0 (the "NPL"); you may not use this file except in 14 * compliance with the NPL. You may obtain a copy of the NPL at 15 * http://www.mozilla.org/NPL/ 16 * 17 * Software distributed under the NPL is distributed on an "AS IS" basis, 18 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL 19 * for the specific language governing rights and limitations under the 20 * NPL. 21 * 22 * The Initial Developer of this code under the NPL is Netscape 23 * Communications Corporation. Portions created by Netscape are 24 * Copyright (C) 1998 Netscape Communications Corporation. All Rights 25 * Reserved. 26 */ 27 28 /* 29 * Copyright (c) 1990 Regents of the University of Michigan. 30 * All rights reserved. 31 * 32 * Redistribution and use in source and binary forms are permitted 33 * provided that this notice is preserved and that due credit is given 34 * to the University of Michigan at Ann Arbor. The name of the University 35 * may not be used to endorse or promote products derived from this 36 * software without specific prior written permission. This software 37 * is provided ``as is'' without express or implied warranty. 38 */ 39 40 /* decode.c - ber input decoding routines */ 41 42 #include "lber-int.h" 43 LDAP_API(void) LDAP_CALL ber_svecfree( char **vals ); 44 45 /* 46 * Note: ber_get_tag() only uses the ber_end and ber_ptr elements of ber. 47 * If that changes, the ber_peek_tag() and/or ber_skip_tag() implementations 48 * will need to be changed. 49 */ 50 /* return the tag - LBER_DEFAULT returned means trouble */ 51 ber_tag_t 52 LDAP_CALL 53 ber_get_tag( BerElement *ber ) 54 { 55 unsigned char xbyte; 56 ber_tag_t tag; 57 char *tagp; 58 int i; 59 60 if ( ber_read( ber, (char *) &xbyte, 1 ) != 1 ) 61 return( LBER_DEFAULT ); 62 63 if ( (xbyte & LBER_BIG_TAG_MASK) != LBER_BIG_TAG_MASK ) 64 return( (ber_uint_t) xbyte ); 65 66 tagp = (char *) &tag; 67 tagp[0] = xbyte; 68 for ( i = 1; i < sizeof(ber_int_t); i++ ) { 69 if ( ber_read( ber, (char *) &xbyte, 1 ) != 1 ) 70 return( LBER_DEFAULT ); 71 72 tagp[i] = xbyte; 73 74 if ( ! (xbyte & LBER_MORE_TAG_MASK) ) 75 break; 76 } 77 78 /* tag too big! */ 79 if ( i == sizeof(ber_int_t) ) 80 return( LBER_DEFAULT ); 81 82 /* want leading, not trailing 0's */ 83 return( tag >> (sizeof(ber_int_t) - i - 1) ); 84 } 85 86 /* 87 * Note: ber_skip_tag() only uses the ber_end and ber_ptr elements of ber. 88 * If that changes, the implementation of ber_peek_tag() will need to 89 * be changed. 90 */ 91 ber_tag_t 92 LDAP_CALL 93 ber_skip_tag( BerElement *ber, ber_len_t *len ) 94 { 95 ber_tag_t tag; 96 unsigned char lc; 97 int noctets, diff; 98 ber_len_t netlen; 99 100 /* 101 * Any ber element looks like this: tag length contents. 102 * Assuming everything's ok, we return the tag byte (we 103 * can assume a single byte), and return the length in len. 104 * 105 * Assumptions: 106 * 1) definite lengths 107 * 2) primitive encodings used whenever possible 108 */ 109 110 /* 111 * First, we read the tag. 112 */ 113 114 if ( (tag = ber_get_tag( ber )) == LBER_DEFAULT ) 115 return( LBER_DEFAULT ); 116 117 /* 118 * Next, read the length. The first byte contains the length of 119 * the length. If bit 8 is set, the length is the long form, 120 * otherwise it's the short form. We don't allow a length that's 121 * greater than what we can hold in an unsigned long. 122 */ 123 124 *len = netlen = 0; 125 if ( ber_read( ber, (char *) &lc, 1 ) != 1 ) 126 return( LBER_DEFAULT ); 127 if ( lc & 0x80 ) { 128 noctets = (lc & 0x7f); 129 if ( noctets > sizeof(ber_uint_t) ) 130 return( LBER_DEFAULT ); 131 diff = sizeof(ber_int_t) - noctets; 132 if ( ber_read( ber, (char *) &netlen + diff, noctets ) 133 != noctets ) 134 return( LBER_DEFAULT ); 135 *len = LBER_NTOHL( netlen ); 136 } else { 137 *len = lc; 138 } 139 140 return( tag ); 141 } 142 143 144 /* 145 * Note: Previously, we passed the "ber" parameter directly to ber_skip_tag(), 146 * saving and restoring the ber_ptr element only. We now take advantage 147 * of the fact that the only ber structure elements touched by ber_skip_tag() 148 * are ber_end and ber_ptr. If that changes, this code must change too. 149 */ 150 ber_tag_t 151 LDAP_CALL 152 ber_peek_tag( BerElement *ber, ber_len_t *len ) 153 { 154 BerElement bercopy; 155 156 bercopy.ber_end = ber->ber_end; 157 bercopy.ber_ptr = ber->ber_ptr; 158 return( ber_skip_tag( &bercopy, len )); 159 } 160 161 static int 162 ber_getnint( BerElement *ber, ber_int_t *num, ber_slen_t len ) 163 { 164 int i; 165 ber_int_t value; 166 unsigned char buffer[sizeof(ber_int_t)]; 167 /* 168 * The tag and length have already been stripped off. We should 169 * be sitting right before len bytes of 2's complement integer, 170 * ready to be read straight into an int. We may have to sign 171 * extend after we read it in. 172 */ 173 174 if ( len > sizeof(ber_slen_t) ) 175 return( -1 ); 176 177 /* read into the low-order bytes of netnum */ 178 if ( ber_read( ber, (char *) buffer, len ) != len ) 179 return( -1 ); 180 181 /* This sets the required sign extension */ 182 if ( len != 0) { 183 value = 0x80 & buffer[0] ? (LBER_FUNC_VALUE) : 0; 184 } else { 185 value = 0; 186 } 187 188 for ( i = 0; i < len; i++ ) 189 value = (value << 8) | buffer[i]; 190 191 *num = value; 192 193 return( len ); 194 } 195 196 ber_tag_t 197 LDAP_CALL 198 ber_get_int( BerElement *ber, ber_int_t *num ) 199 { 200 ber_tag_t tag; 201 ber_len_t len; 202 203 if ( (tag = ber_skip_tag( ber, &len )) == LBER_DEFAULT ) 204 return( LBER_DEFAULT ); 205 206 /* 207 * len is being demoted to a long here -- possible conversion error 208 */ 209 210 if ( ber_getnint( ber, num, (int)len ) != (ber_slen_t)len ) 211 return( LBER_DEFAULT ); 212 else 213 return( tag ); 214 } 215 216 ber_tag_t 217 LDAP_CALL 218 ber_get_stringb( BerElement *ber, char *buf, ber_len_t *len ) 219 { 220 ber_len_t datalen; 221 ber_tag_t tag; 222 #ifdef STR_TRANSLATION 223 char *transbuf; 224 #endif /* STR_TRANSLATION */ 225 226 if ( (tag = ber_skip_tag( ber, &datalen )) == LBER_DEFAULT ) 227 return( LBER_DEFAULT ); 228 if ( datalen > (*len - 1) ) 229 return( LBER_DEFAULT ); 230 231 /* 232 * datalen is being demoted to a long here -- possible conversion error 233 */ 234 235 if ( ber_read( ber, buf, datalen ) != (ber_slen_t) datalen ) 236 return( LBER_DEFAULT ); 237 238 buf[datalen] = '\0'; 239 240 #ifdef STR_TRANSLATION 241 if ( datalen > 0 && ( ber->ber_options & LBER_OPT_TRANSLATE_STRINGS ) 242 != 0 && ber->ber_decode_translate_proc != NULL ) { 243 transbuf = buf; 244 ++datalen; 245 if ( (*(ber->ber_decode_translate_proc))( &transbuf, &datalen, 246 0 ) != 0 ) { 247 return( LBER_DEFAULT ); 248 } 249 if ( datalen > *len ) { 250 NSLBERI_FREE( transbuf ); 251 return( LBER_DEFAULT ); 252 } 253 SAFEMEMCPY( buf, transbuf, datalen ); 254 NSLBERI_FREE( transbuf ); 255 --datalen; 256 } 257 #endif /* STR_TRANSLATION */ 258 259 *len = datalen; 260 return( tag ); 261 } 262 263 ber_tag_t 264 LDAP_CALL 265 ber_get_stringa( BerElement *ber, char **buf ) 266 { 267 ber_len_t datalen; 268 ber_tag_t tag; 269 270 if ( (tag = ber_skip_tag( ber, &datalen )) == LBER_DEFAULT ) 271 return( LBER_DEFAULT ); 272 273 if ( (*buf = (char *)NSLBERI_MALLOC( (size_t)datalen + 1 )) == NULL ) 274 return( LBER_DEFAULT ); 275 276 /* 277 * datalen is being demoted to a long here -- possible conversion error 278 */ 279 if ( ber_read( ber, *buf, datalen ) != (ber_slen_t) datalen ) 280 return( LBER_DEFAULT ); 281 (*buf)[datalen] = '\0'; 282 283 #ifdef STR_TRANSLATION 284 if ( datalen > 0 && ( ber->ber_options & LBER_OPT_TRANSLATE_STRINGS ) 285 != 0 && ber->ber_decode_translate_proc != NULL ) { 286 ++datalen; 287 if ( (*(ber->ber_decode_translate_proc))( buf, &datalen, 1 ) 288 != 0 ) { 289 NSLBERI_FREE( *buf ); 290 return( LBER_DEFAULT ); 291 } 292 } 293 #endif /* STR_TRANSLATION */ 294 295 return( tag ); 296 } 297 298 ber_tag_t 299 LDAP_CALL 300 ber_get_stringal( BerElement *ber, struct berval **bv ) 301 { 302 ber_len_t len; 303 ber_tag_t tag; 304 305 if ( (*bv = (struct berval *)NSLBERI_MALLOC( sizeof(struct berval) )) 306 == NULL ) { 307 return( LBER_DEFAULT ); 308 } 309 310 if ( (tag = ber_skip_tag( ber, &len )) == LBER_DEFAULT ) { 311 return( LBER_DEFAULT ); 312 } 313 314 if ( ((*bv)->bv_val = (char *)NSLBERI_MALLOC( (size_t)len + 1 )) 315 == NULL ) { 316 return( LBER_DEFAULT ); 317 } 318 319 /* 320 * len is being demoted to a long here -- possible conversion error 321 */ 322 if ( ber_read( ber, (*bv)->bv_val, len ) != (ber_slen_t) len ) 323 return( LBER_DEFAULT ); 324 ((*bv)->bv_val)[len] = '\0'; 325 (*bv)->bv_len = len; 326 327 #ifdef STR_TRANSLATION 328 if ( len > 0 && ( ber->ber_options & LBER_OPT_TRANSLATE_STRINGS ) != 0 329 && ber->ber_decode_translate_proc != NULL ) { 330 ++len; 331 if ( (*(ber->ber_decode_translate_proc))( &((*bv)->bv_val), 332 &len, 1 ) != 0 ) { 333 NSLBERI_FREE( (*bv)->bv_val ); 334 return( LBER_DEFAULT ); 335 } 336 (*bv)->bv_len = len - 1; 337 } 338 #endif /* STR_TRANSLATION */ 339 340 return( tag ); 341 } 342 343 ber_tag_t 344 LDAP_CALL 345 ber_get_bitstringa( BerElement *ber, char **buf, ber_len_t *blen ) 346 { 347 ber_len_t datalen; 348 ber_tag_t tag; 349 unsigned char unusedbits; 350 351 if ( (tag = ber_skip_tag( ber, &datalen )) == LBER_DEFAULT ) 352 return( LBER_DEFAULT ); 353 --datalen; 354 355 if ( (*buf = (char *)NSLBERI_MALLOC( (size_t)datalen )) == NULL ) 356 return( LBER_DEFAULT ); 357 358 if ( ber_read( ber, (char *)&unusedbits, 1 ) != 1 ) 359 return( LBER_DEFAULT ); 360 361 /* 362 * datalen is being demoted to a long here -- possible conversion error 363 */ 364 if ( ber_read( ber, *buf, datalen ) != (ber_slen_t) datalen ) 365 return( LBER_DEFAULT ); 366 367 *blen = datalen * 8 - unusedbits; 368 return( tag ); 369 } 370 371 ber_tag_t 372 LDAP_CALL 373 ber_get_null( BerElement *ber ) 374 { 375 ber_len_t len; 376 ber_tag_t tag; 377 378 if ( (tag = ber_skip_tag( ber, &len )) == LBER_DEFAULT ) 379 return( LBER_DEFAULT ); 380 381 if ( len != 0 ) 382 return( LBER_DEFAULT ); 383 384 return( tag ); 385 } 386 387 ber_tag_t 388 LDAP_CALL 389 ber_get_boolean( BerElement *ber, int *boolval ) 390 { 391 ber_int_t longbool; 392 int rc; 393 394 rc = ber_get_int( ber, &longbool ); 395 *boolval = longbool; 396 397 return( rc ); 398 } 399 400 ber_tag_t 401 LDAP_CALL 402 ber_first_element( BerElement *ber, ber_len_t *len, char **last ) 403 { 404 /* skip the sequence header, use the len to mark where to stop */ 405 if ( ber_skip_tag( ber, len ) == LBER_DEFAULT ) { 406 return( LBER_ERROR ); 407 } 408 409 *last = ber->ber_ptr + *len; 410 411 if ( *last == ber->ber_ptr ) { 412 return( LBER_END_OF_SEQORSET ); 413 } 414 415 return( ber_peek_tag( ber, len ) ); 416 } 417 418 ber_tag_t 419 LDAP_CALL 420 ber_next_element( BerElement *ber, ber_len_t *len, char *last ) 421 { 422 if ( ber->ber_ptr == last ) { 423 return( LBER_END_OF_SEQORSET ); 424 } 425 426 return( ber_peek_tag( ber, len ) ); 427 } 428 429 /* VARARGS */ 430 ber_tag_t 431 LDAP_C 432 ber_scanf( BerElement *ber, const char *fmt, ... ) 433 { 434 va_list ap; 435 char *last, *p; 436 char *s, **ss, ***sss; 437 struct berval ***bv, **bvp, *bval; 438 int *i, j; 439 ber_int_t *l, rc, tag; 440 ber_tag_t *t; 441 ber_len_t len; 442 size_t array_size; 443 444 va_start( ap, fmt ); 445 446 #ifdef LDAP_DEBUG 447 if ( lber_debug & 64 ) { 448 char msg[80]; 449 sprintf( msg, "ber_scanf fmt (%s) ber:\n", fmt ); 450 ber_err_print( msg ); 451 ber_dump( ber, 1 ); 452 } 453 #endif 454 455 for ( rc = 0, p = (char *)fmt; *p && rc != LBER_DEFAULT; p++ ) { 456 switch ( *p ) { 457 case 'a': /* octet string - allocate storage as needed */ 458 ss = va_arg( ap, char ** ); 459 rc = ber_get_stringa( ber, ss ); 460 break; 461 462 case 'b': /* boolean */ 463 i = va_arg( ap, int * ); 464 rc = ber_get_boolean( ber, i ); 465 break; 466 467 case 'e': /* enumerated */ 468 case 'i': /* int */ 469 l = va_arg( ap, ber_slen_t * ); 470 rc = ber_get_int( ber, l ); 471 break; 472 473 case 'l': /* length of next item */ 474 l = va_arg( ap, ber_slen_t * ); 475 rc = ber_peek_tag( ber, (ber_len_t *)l ); 476 break; 477 478 case 'n': /* null */ 479 rc = ber_get_null( ber ); 480 break; 481 482 case 's': /* octet string - in a buffer */ 483 s = va_arg( ap, char * ); 484 l = va_arg( ap, ber_slen_t * ); 485 rc = ber_get_stringb( ber, s, (ber_len_t *)l ); 486 break; 487 488 case 'o': /* octet string in a supplied berval */ 489 bval = va_arg( ap, struct berval * ); 490 ber_peek_tag( ber, &bval->bv_len ); 491 rc = ber_get_stringa( ber, &bval->bv_val ); 492 break; 493 494 case 'O': /* octet string - allocate & include length */ 495 bvp = va_arg( ap, struct berval ** ); 496 rc = ber_get_stringal( ber, bvp ); 497 break; 498 499 case 'B': /* bit string - allocate storage as needed */ 500 ss = va_arg( ap, char ** ); 501 l = va_arg( ap, ber_slen_t * ); /* for length, in bits */ 502 rc = ber_get_bitstringa( ber, ss, (ber_len_t *)l ); 503 break; 504 505 case 't': /* tag of next item */ 506 t = va_arg( ap, ber_tag_t * ); 507 *t = rc = ber_peek_tag( ber, &len ); 508 break; 509 510 case 'T': /* skip tag of next item */ 511 t = va_arg( ap, ber_tag_t * ); 512 *t = rc = ber_skip_tag( ber, &len ); 513 break; 514 515 case 'v': /* sequence of strings */ 516 sss = va_arg( ap, char *** ); 517 *sss = NULL; 518 j = 0; 519 array_size = 0; 520 for ( tag = ber_first_element( ber, &len, &last ); 521 tag != LBER_DEFAULT && tag != LBER_END_OF_SEQORSET 522 && rc != LBER_DEFAULT; 523 tag = ber_next_element( ber, &len, last ) ) { 524 if ( *sss == NULL ) { 525 /* Make room for at least 15 strings */ 526 *sss = (char **)NSLBERI_MALLOC(16 * sizeof(char *) ); 527 array_size = 16; 528 } else { 529 if ( (size_t)(j+2) > array_size) { 530 /* We'v overflowed our buffer */ 531 *sss = (char **)NSLBERI_REALLOC( *sss, (array_size * 2) * sizeof(char *) ); 532 array_size = array_size * 2; 533 } 534 } 535 rc = ber_get_stringa( ber, &((*sss)[j]) ); 536 j++; 537 } 538 if ( rc != LBER_DEFAULT && 539 tag != LBER_END_OF_SEQORSET ) { 540 rc = LBER_DEFAULT; 541 } 542 if ( j > 0 ) 543 (*sss)[j] = NULL; 544 break; 545 546 case 'V': /* sequence of strings + lengths */ 547 bv = va_arg( ap, struct berval *** ); 548 *bv = NULL; 549 j = 0; 550 for ( tag = ber_first_element( ber, &len, &last ); 551 tag != LBER_DEFAULT && tag != LBER_END_OF_SEQORSET 552 && rc != LBER_DEFAULT; 553 tag = ber_next_element( ber, &len, last ) ) { 554 if ( *bv == NULL ) { 555 *bv = (struct berval **)NSLBERI_MALLOC( 556 2 * sizeof(struct berval *) ); 557 } else { 558 *bv = (struct berval **)NSLBERI_REALLOC( 559 *bv, 560 (j + 2) * sizeof(struct berval *) ); 561 } 562 rc = ber_get_stringal( ber, &((*bv)[j]) ); 563 j++; 564 } 565 if ( rc != LBER_DEFAULT && 566 tag != LBER_END_OF_SEQORSET ) { 567 rc = LBER_DEFAULT; 568 } 569 if ( j > 0 ) 570 (*bv)[j] = NULL; 571 break; 572 573 case 'x': /* skip the next element - whatever it is */ 574 if ( (rc = ber_skip_tag( ber, &len )) == LBER_DEFAULT ) 575 break; 576 ber->ber_ptr += len; 577 break; 578 579 case '{': /* begin sequence */ 580 case '[': /* begin set */ 581 if ( *(p + 1) != 'v' && *(p + 1) != 'V' ) 582 rc = ber_skip_tag( ber, &len ); 583 break; 584 585 case '}': /* end sequence */ 586 case ']': /* end set */ 587 break; 588 589 default: 590 { 591 char msg[80]; 592 sprintf( msg, "unknown fmt %c\n", *p ); 593 ber_err_print( msg ); 594 } 595 rc = LBER_DEFAULT; 596 break; 597 } 598 } 599 600 601 va_end( ap ); 602 if (rc == LBER_DEFAULT) { 603 va_start( ap, fmt ); 604 for ( p--; fmt < p && *fmt; fmt++ ) { 605 switch ( *fmt ) { 606 case 'a': /* octet string - allocate storage as needed */ 607 ss = va_arg( ap, char ** ); 608 NSLBERI_FREE(*ss); 609 *ss = NULL; 610 break; 611 612 case 'b': /* boolean */ 613 i = va_arg( ap, int * ); 614 break; 615 616 case 'e': /* enumerated */ 617 case 'i': /* int */ 618 l = va_arg( ap, ber_slen_t * ); 619 break; 620 621 case 'l': /* length of next item */ 622 l = va_arg( ap, ber_slen_t * ); 623 break; 624 625 case 'n': /* null */ 626 break; 627 628 case 's': /* octet string - in a buffer */ 629 s = va_arg( ap, char * ); 630 l = va_arg( ap, ber_slen_t * ); 631 break; 632 633 case 'o': /* octet string in a supplied berval */ 634 bval = va_arg( ap, struct berval * ); 635 if (bval->bv_val) NSLBERI_FREE(bval->bv_val); 636 memset(bval, 0, sizeof(struct berval)); 637 break; 638 639 case 'O': /* octet string - allocate & include length */ 640 bvp = va_arg( ap, struct berval ** ); 641 ber_bvfree(*bvp); 642 bvp = NULL; 643 break; 644 645 case 'B': /* bit string - allocate storage as needed */ 646 ss = va_arg( ap, char ** ); 647 l = va_arg( ap, ber_slen_t * ); /* for length, in bits */ 648 if (*ss) NSLBERI_FREE(*ss); 649 *ss = NULL; 650 break; 651 652 case 't': /* tag of next item */ 653 t = va_arg( ap, ber_tag_t * ); 654 break; 655 case 'T': /* skip tag of next item */ 656 t = va_arg( ap, ber_tag_t * ); 657 break; 658 659 case 'v': /* sequence of strings */ 660 sss = va_arg( ap, char *** ); 661 ber_svecfree(*sss); 662 *sss = NULL; 663 break; 664 665 case 'V': /* sequence of strings + lengths */ 666 bv = va_arg( ap, struct berval *** ); 667 ber_bvecfree(*bv); 668 *bv = NULL; 669 break; 670 671 case 'x': /* skip the next element - whatever it is */ 672 break; 673 674 case '{': /* begin sequence */ 675 case '[': /* begin set */ 676 break; 677 678 case '}': /* end sequence */ 679 case ']': /* end set */ 680 break; 681 682 default: 683 break; 684 } 685 } /* for */ 686 va_end( ap ); 687 } /* if */ 688 689 690 return( rc ); 691 } 692 693 void 694 LDAP_CALL 695 ber_bvfree( struct berval *bv ) 696 { 697 if ( bv != NULL ) { 698 if ( bv->bv_val != NULL ) { 699 NSLBERI_FREE( bv->bv_val ); 700 } 701 NSLBERI_FREE( (char *) bv ); 702 } 703 } 704 705 void 706 LDAP_CALL 707 ber_bvecfree( struct berval **bv ) 708 { 709 int i; 710 711 if ( bv != NULL ) { 712 for ( i = 0; bv[i] != NULL; i++ ) { 713 ber_bvfree( bv[i] ); 714 } 715 NSLBERI_FREE( (char *) bv ); 716 } 717 } 718 719 struct berval * 720 LDAP_CALL 721 ber_bvdup( const struct berval *bv ) 722 { 723 struct berval *new; 724 725 if ( (new = (struct berval *)NSLBERI_MALLOC( sizeof(struct berval) )) 726 == NULL ) { 727 return( NULL ); 728 } 729 if ( bv->bv_val == NULL ) { 730 new->bv_val = NULL; 731 new->bv_len = 0; 732 } else { 733 if ( (new->bv_val = (char *)NSLBERI_MALLOC( bv->bv_len + 1 )) 734 == NULL ) { 735 return( NULL ); 736 } 737 SAFEMEMCPY( new->bv_val, bv->bv_val, (size_t) bv->bv_len ); 738 new->bv_val[bv->bv_len] = '\0'; 739 new->bv_len = bv->bv_len; 740 } 741 742 return( new ); 743 } 744 745 void 746 LDAP_CALL 747 ber_svecfree( char **vals ) 748 { 749 int i; 750 751 if ( vals == NULL ) 752 return; 753 for ( i = 0; vals[i] != NULL; i++ ) 754 NSLBERI_FREE( vals[i] ); 755 NSLBERI_FREE( (char *) vals ); 756 } 757 758 #ifdef STR_TRANSLATION 759 void 760 LDAP_CALL 761 ber_set_string_translators( 762 BerElement *ber, 763 BERTranslateProc encode_proc, 764 BERTranslateProc decode_proc 765 ) 766 { 767 ber->ber_encode_translate_proc = encode_proc; 768 ber->ber_decode_translate_proc = decode_proc; 769 } 770 #endif /* STR_TRANSLATION */ 771