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