1 /* 2 * Copyright (c) 2001 by Sun Microsystems, Inc. 3 * All rights reserved. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- 9 * 10 * The contents of this file are subject to the Netscape Public License 11 * Version 1.0 (the "NPL"); you may not use this file except in 12 * compliance with the NPL. You may obtain a copy of the NPL at 13 * http://www.mozilla.org/NPL/ 14 * 15 * Software distributed under the NPL is distributed on an "AS IS" basis, 16 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL 17 * for the specific language governing rights and limitations under the 18 * NPL. 19 * 20 * The Initial Developer of this code under the NPL is Netscape 21 * Communications Corporation. Portions created by Netscape are 22 * Copyright (C) 1998 Netscape Communications Corporation. All Rights 23 * Reserved. 24 */ 25 26 /* 27 * Copyright (c) 1990 Regents of the University of Michigan. 28 * All rights reserved. 29 * 30 * Redistribution and use in source and binary forms are permitted 31 * provided that this notice is preserved and that due credit is given 32 * to the University of Michigan at Ann Arbor. The name of the University 33 * may not be used to endorse or promote products derived from this 34 * software without specific prior written permission. This software 35 * is provided ``as is'' without express or implied warranty. 36 */ 37 38 /* encode.c - ber output encoding routines */ 39 40 #include "lber-int.h" 41 42 /* the following constants are used in ber_calc_lenlen */ 43 44 #define LENMASK1 0xFF 45 #define LENMASK2 0xFFFF 46 #define LENMASK3 0xFFFFFF 47 #define LENMASK4 0xFFFFFFFF 48 #define _MASK 0x80 49 50 static int 51 ber_calc_taglen( ber_tag_t tag ) 52 { 53 int i; 54 ber_int_t mask; 55 56 /* find the first non-all-zero byte in the tag */ 57 for ( i = sizeof(ber_int_t) - 1; i > 0; i-- ) { 58 mask = (LENMASK3 << (i * 8)); 59 /* not all zero */ 60 if ( tag & mask ) 61 break; 62 } 63 64 return( i + 1 ); 65 } 66 67 static int 68 ber_put_tag( BerElement *ber, ber_tag_t tag, int nosos ) 69 { 70 int taglen; 71 ber_tag_t ntag; 72 73 taglen = ber_calc_taglen( tag ); 74 75 ntag = LBER_HTONL( tag ); 76 77 return( ber_write( ber, ((char *) &ntag) + sizeof(ber_int_t) - taglen, 78 taglen, nosos ) ); 79 } 80 81 static int 82 ber_calc_lenlen( ber_len_t len ) 83 { 84 /* 85 * short len if it's less than 128 - one byte giving the len, 86 * with bit 8 0. 87 */ 88 89 if ( len <= 0x7F ) 90 return( 1 ); 91 92 /* 93 * long len otherwise - one byte with bit 8 set, giving the 94 * length of the length, followed by the length itself. 95 */ 96 97 if ( len <= LENMASK1 ) 98 return( 2 ); 99 if ( len <= LENMASK2 ) 100 return( 3 ); 101 if ( len <= LENMASK3 ) 102 return( 4 ); 103 104 return( 5 ); 105 } 106 107 static int 108 ber_put_len( BerElement *ber, ber_len_t len, int nosos ) 109 { 110 int i; 111 char lenlen; 112 ber_int_t mask; 113 ber_len_t netlen; 114 115 /* 116 * short len if it's less than 128 - one byte giving the len, 117 * with bit 8 0. 118 */ 119 120 if ( len <= 127 ) { 121 netlen = LBER_HTONL( len ); 122 return( ber_write( ber, (char *) &netlen + sizeof(ber_int_t) - 1, 123 1, nosos ) ); 124 } 125 126 /* 127 * long len otherwise - one byte with bit 8 set, giving the 128 * length of the length, followed by the length itself. 129 */ 130 131 /* find the first non-all-zero byte */ 132 for ( i = sizeof(ber_int_t) - 1; i > 0; i-- ) { 133 mask = (LENMASK1 << (i * 8)); 134 /* not all zero */ 135 if ( len & mask ) 136 break; 137 } 138 lenlen = ++i; 139 if ( lenlen > 4 ) 140 return( -1 ); 141 lenlen |= 0x80; 142 143 /* write the length of the length */ 144 if ( ber_write( ber, &lenlen, 1, nosos ) != 1 ) 145 return( -1 ); 146 147 /* write the length itself */ 148 netlen = LBER_HTONL( len ); 149 if ( ber_write( ber, (char *) &netlen + (sizeof(ber_int_t) - i), i, nosos ) 150 != i ) 151 return( -1 ); 152 153 return( i + 1 ); 154 } 155 156 static int 157 ber_put_int_or_enum( BerElement *ber, ber_int_t num, ber_tag_t tag ) 158 { 159 int i, sign, taglen; 160 int len, lenlen; 161 ber_int_t netnum, mask; 162 163 sign = (num < 0); 164 165 /* 166 * high bit is set - look for first non-all-one byte 167 * high bit is clear - look for first non-all-zero byte 168 */ 169 for ( i = sizeof(ber_int_t) - 1; i > 0; i-- ) { 170 mask = (LENMASK1 << (i * 8)); 171 172 if ( sign ) { 173 /* not all ones */ 174 if ( (num & mask) != mask ) 175 break; 176 } else { 177 /* not all zero */ 178 if ( num & mask ) 179 break; 180 } 181 } 182 183 /* 184 * we now have the "leading byte". if the high bit on this 185 * byte matches the sign bit, we need to "back up" a byte. 186 */ 187 mask = (num & (_MASK << (i * 8))); 188 if ( (mask && !sign) || (sign && !mask) ) 189 i++; 190 191 len = i + 1; 192 193 if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) 194 return( -1 ); 195 196 if ( (lenlen = ber_put_len( ber, len, 0 )) == -1 ) 197 return( -1 ); 198 i++; 199 netnum = LBER_HTONL( num ); 200 if ( ber_write( ber, (char *) &netnum + (sizeof(ber_int_t) - i), i, 0 ) 201 == i) 202 /* length of tag + length + contents */ 203 return( taglen + lenlen + i ); 204 205 return( -1 ); 206 } 207 208 int 209 LDAP_CALL 210 ber_put_enum( BerElement *ber, ber_int_t num, ber_tag_t tag ) 211 { 212 if ( tag == LBER_DEFAULT ) 213 tag = LBER_ENUMERATED; 214 215 return( ber_put_int_or_enum( ber, num, tag ) ); 216 } 217 218 int 219 LDAP_CALL 220 ber_put_int( BerElement *ber, ber_int_t num, ber_tag_t tag ) 221 { 222 if ( tag == LBER_DEFAULT ) 223 tag = LBER_INTEGER; 224 225 return( ber_put_int_or_enum( ber, num, tag ) ); 226 } 227 228 int 229 LDAP_CALL 230 ber_put_ostring( BerElement *ber, char *str, ber_len_t len, 231 ber_tag_t tag ) 232 { 233 int taglen, lenlen, rc; 234 #ifdef STR_TRANSLATION 235 int free_str; 236 #endif /* STR_TRANSLATION */ 237 238 if ( tag == LBER_DEFAULT ) 239 tag = LBER_OCTETSTRING; 240 241 if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) 242 return( -1 ); 243 244 #ifdef STR_TRANSLATION 245 if ( len > 0 && ( ber->ber_options & LBER_OPT_TRANSLATE_STRINGS ) != 0 246 && ber->ber_encode_translate_proc != NULL ) { 247 if ( (*(ber->ber_encode_translate_proc))( &str, &len, 0 ) 248 != 0 ) { 249 return( -1 ); 250 } 251 free_str = 1; 252 } else { 253 free_str = 0; 254 } 255 #endif /* STR_TRANSLATION */ 256 257 /* 258 * Note: below is a spot where we limit ber_write 259 * to signed long (instead of unsigned long) 260 */ 261 262 if ( (lenlen = ber_put_len( ber, len, 0 )) == -1 || 263 ber_write( ber, str, len, 0 ) != (ber_int_t) len ) { 264 rc = -1; 265 } else { 266 /* return length of tag + length + contents */ 267 rc = taglen + lenlen + len; 268 } 269 270 #ifdef STR_TRANSLATION 271 if ( free_str ) { 272 NSLBERI_FREE( str ); 273 } 274 #endif /* STR_TRANSLATION */ 275 276 return( rc ); 277 } 278 279 int 280 LDAP_CALL 281 ber_put_string( BerElement *ber, char *str, ber_tag_t tag ) 282 { 283 return( ber_put_ostring( ber, str, (ber_len_t) strlen( str ), tag )); 284 } 285 286 int 287 LDAP_CALL 288 ber_put_bitstring( BerElement *ber, char *str, 289 ber_len_t blen /* in bits */, ber_tag_t tag ) 290 { 291 int taglen, lenlen, len; 292 unsigned char unusedbits; 293 294 if ( tag == LBER_DEFAULT ) 295 tag = LBER_BITSTRING; 296 297 if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) 298 return( -1 ); 299 300 len = ( blen + 7 ) / 8; 301 unusedbits = (unsigned char) (len * 8 - blen); 302 if ( (lenlen = ber_put_len( ber, len + 1, 0 )) == -1 ) 303 return( -1 ); 304 305 if ( ber_write( ber, (char *)&unusedbits, 1, 0 ) != 1 ) 306 return( -1 ); 307 308 if ( ber_write( ber, str, len, 0 ) != len ) 309 return( -1 ); 310 311 /* return length of tag + length + unused bit count + contents */ 312 return( taglen + 1 + lenlen + len ); 313 } 314 315 int 316 LDAP_CALL 317 ber_put_null( BerElement *ber, ber_tag_t tag ) 318 { 319 int taglen; 320 321 if ( tag == LBER_DEFAULT ) 322 tag = LBER_NULL; 323 324 if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) 325 return( -1 ); 326 327 if ( ber_put_len( ber, 0, 0 ) != 1 ) 328 return( -1 ); 329 330 return( taglen + 1 ); 331 } 332 333 int 334 LDAP_CALL 335 ber_put_boolean( BerElement *ber, int boolval, ber_tag_t tag ) 336 { 337 int taglen; 338 unsigned char trueval = 0xff; 339 unsigned char falseval = 0x00; 340 341 if ( tag == LBER_DEFAULT ) 342 tag = LBER_BOOLEAN; 343 344 if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) 345 return( -1 ); 346 347 if ( ber_put_len( ber, 1, 0 ) != 1 ) 348 return( -1 ); 349 350 if ( ber_write( ber, (char *)(boolval ? &trueval : &falseval), 1, 0 ) 351 != 1 ) 352 return( -1 ); 353 354 return( taglen + 2 ); 355 } 356 357 #define FOUR_BYTE_LEN 5 358 359 360 /* the idea here is roughly this: we maintain a stack of these Seqorset 361 * structures. This is pushed when we see the beginning of a new set or 362 * sequence. It is popped when we see the end of a set or sequence. 363 * Since we don't want to malloc and free these structures all the time, 364 * we pre-allocate a small set of them within the ber element structure. 365 * thus we need to spot when we've overflowed this stack and fall back to 366 * malloc'ing instead. 367 */ 368 static int 369 ber_start_seqorset( BerElement *ber, ber_tag_t tag ) 370 { 371 Seqorset *new_sos; 372 373 /* can we fit into the local stack ? */ 374 if (ber->ber_sos_stack_posn < SOS_STACK_SIZE) { 375 /* yes */ 376 new_sos = &ber->ber_sos_stack[ber->ber_sos_stack_posn]; 377 } else { 378 /* no */ 379 if ( (new_sos = (Seqorset *)NSLBERI_MALLOC( sizeof(Seqorset))) 380 == NULLSEQORSET ) { 381 return( -1 ); 382 } 383 } 384 ber->ber_sos_stack_posn++; 385 386 if ( ber->ber_sos == NULLSEQORSET ) 387 new_sos->sos_first = ber->ber_ptr; 388 else 389 new_sos->sos_first = ber->ber_sos->sos_ptr; 390 391 /* Set aside room for a 4 byte length field */ 392 new_sos->sos_ptr = new_sos->sos_first + ber_calc_taglen( tag ) + FOUR_BYTE_LEN; 393 new_sos->sos_tag = tag; 394 395 new_sos->sos_next = ber->ber_sos; 396 new_sos->sos_clen = 0; 397 398 ber->ber_sos = new_sos; 399 if (ber->ber_sos->sos_ptr > ber->ber_end) { 400 nslberi_ber_realloc(ber, ber->ber_sos->sos_ptr - ber->ber_end); 401 } 402 return( 0 ); 403 } 404 405 int 406 LDAP_CALL 407 ber_start_seq( BerElement *ber, ber_tag_t tag ) 408 { 409 if ( tag == LBER_DEFAULT ) 410 tag = LBER_SEQUENCE; 411 412 return( ber_start_seqorset( ber, tag ) ); 413 } 414 415 int 416 LDAP_CALL 417 ber_start_set( BerElement *ber, ber_tag_t tag ) 418 { 419 if ( tag == LBER_DEFAULT ) 420 tag = LBER_SET; 421 422 return( ber_start_seqorset( ber, tag ) ); 423 } 424 425 static int 426 ber_put_seqorset( BerElement *ber ) 427 { 428 ber_len_t len, netlen; 429 int taglen, lenlen; 430 unsigned char ltag = 0x80 + FOUR_BYTE_LEN - 1; 431 Seqorset *next; 432 Seqorset **sos = &ber->ber_sos; 433 434 /* 435 * If this is the toplevel sequence or set, we need to actually 436 * write the stuff out. Otherwise, it's already been put in 437 * the appropriate buffer and will be written when the toplevel 438 * one is written. In this case all we need to do is update the 439 * length and tag. 440 */ 441 442 len = (*sos)->sos_clen; 443 netlen = LBER_HTONL( len ); 444 if ( sizeof(ber_int_t) > 4 && len > LENMASK4 ) 445 return( -1 ); 446 447 if ( ber->ber_options & LBER_OPT_USE_DER ) { 448 lenlen = ber_calc_lenlen( len ); 449 } else { 450 lenlen = FOUR_BYTE_LEN; 451 } 452 453 if ( (next = (*sos)->sos_next) == NULLSEQORSET ) { 454 /* write the tag */ 455 if ( (taglen = ber_put_tag( ber, (*sos)->sos_tag, 1 )) == -1 ) 456 return( -1 ); 457 458 if ( ber->ber_options & LBER_OPT_USE_DER ) { 459 /* Write the length in the minimum # of octets */ 460 if ( ber_put_len( ber, len, 1 ) == -1 ) 461 return( -1 ); 462 463 if (lenlen != FOUR_BYTE_LEN) { 464 /* 465 * We set aside FOUR_BYTE_LEN bytes for 466 * the length field. Move the data if 467 * we don't actually need that much 468 */ 469 SAFEMEMCPY( (*sos)->sos_first + taglen + 470 lenlen, (*sos)->sos_first + taglen + 471 FOUR_BYTE_LEN, len ); 472 } 473 } else { 474 /* Fill FOUR_BYTE_LEN bytes for length field */ 475 /* one byte of length length */ 476 if ( ber_write( ber, (char *)<ag, 1, 1 ) != 1 ) 477 return( -1 ); 478 479 /* the length itself */ 480 if ( ber_write( ber, (char *) &netlen + sizeof(ber_int_t) 481 - (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1, 1 ) 482 != FOUR_BYTE_LEN - 1 ) 483 return( -1 ); 484 } 485 /* The ber_ptr is at the set/seq start - move it to the end */ 486 ber->ber_ptr += len; 487 } else { 488 ber_tag_t ntag; 489 490 /* the tag */ 491 taglen = ber_calc_taglen( (*sos)->sos_tag ); 492 ntag = LBER_HTONL( (*sos)->sos_tag ); 493 SAFEMEMCPY( (*sos)->sos_first, (char *) &ntag + 494 sizeof(ber_int_t) - taglen, taglen ); 495 496 if ( ber->ber_options & LBER_OPT_USE_DER ) { 497 ltag = (lenlen == 1) ? (unsigned char)len : 498 (unsigned char) (0x80 + (lenlen - 1)); 499 } 500 501 /* one byte of length length */ 502 SAFEMEMCPY( (*sos)->sos_first + 1, <ag, 1 ); 503 504 if ( ber->ber_options & LBER_OPT_USE_DER ) { 505 if (lenlen > 1) { 506 /* Write the length itself */ 507 SAFEMEMCPY( (*sos)->sos_first + 2, 508 (char *)&netlen + sizeof(ber_uint_t) - 509 (lenlen - 1), 510 lenlen - 1 ); 511 } 512 if (lenlen != FOUR_BYTE_LEN) { 513 /* 514 * We set aside FOUR_BYTE_LEN bytes for 515 * the length field. Move the data if 516 * we don't actually need that much 517 */ 518 SAFEMEMCPY( (*sos)->sos_first + taglen + 519 lenlen, (*sos)->sos_first + taglen + 520 FOUR_BYTE_LEN, len ); 521 } 522 } else { 523 /* the length itself */ 524 SAFEMEMCPY( (*sos)->sos_first + taglen + 1, 525 (char *) &netlen + sizeof(ber_int_t) - 526 (FOUR_BYTE_LEN - 1), FOUR_BYTE_LEN - 1 ); 527 } 528 529 next->sos_clen += (taglen + lenlen + len); 530 next->sos_ptr += (taglen + lenlen + len); 531 } 532 533 /* we're done with this seqorset, so free it up */ 534 /* was this one from the local stack ? */ 535 if (ber->ber_sos_stack_posn <= SOS_STACK_SIZE) { 536 /* yes */ 537 } else { 538 /* no */ 539 NSLBERI_FREE( (char *) (*sos) ); 540 } 541 ber->ber_sos_stack_posn--; 542 *sos = next; 543 544 return( taglen + lenlen + len ); 545 } 546 547 int 548 LDAP_CALL 549 ber_put_seq( BerElement *ber ) 550 { 551 return( ber_put_seqorset( ber ) ); 552 } 553 554 int 555 LDAP_CALL 556 ber_put_set( BerElement *ber ) 557 { 558 return( ber_put_seqorset( ber ) ); 559 } 560 561 /* VARARGS */ 562 int 563 LDAP_C 564 ber_printf( BerElement *ber, const char *fmt, ... ) 565 { 566 va_list ap; 567 char *s, **ss; 568 struct berval **bv; 569 int rc, i; 570 ber_len_t len; 571 572 va_start( ap, fmt ); 573 574 #ifdef LDAP_DEBUG 575 if ( lber_debug & 64 ) { 576 char msg[80]; 577 sprintf( msg, "ber_printf fmt (%s)\n", fmt ); 578 ber_err_print( msg ); 579 } 580 #endif 581 582 for ( rc = 0; *fmt && rc != -1; fmt++ ) { 583 switch ( *fmt ) { 584 case 'b': /* boolean */ 585 i = va_arg( ap, int ); 586 rc = ber_put_boolean( ber, i, ber->ber_tag ); 587 break; 588 589 case 'i': /* int */ 590 i = va_arg( ap, int ); 591 rc = ber_put_int( ber, (ber_int_t)i, ber->ber_tag ); 592 break; 593 594 case 'e': /* enumeration */ 595 i = va_arg( ap, int ); 596 rc = ber_put_enum( ber, (ber_int_t)i, ber->ber_tag ); 597 break; 598 599 case 'n': /* null */ 600 rc = ber_put_null( ber, ber->ber_tag ); 601 break; 602 603 case 'o': /* octet string (non-null terminated) */ 604 s = va_arg( ap, char * ); 605 len = va_arg( ap, int ); 606 rc = ber_put_ostring( ber, s, len, ber->ber_tag ); 607 break; 608 609 case 's': /* string */ 610 s = va_arg( ap, char * ); 611 rc = ber_put_string( ber, s, ber->ber_tag ); 612 break; 613 614 case 'B': /* bit string */ 615 s = va_arg( ap, char * ); 616 len = va_arg( ap, int ); /* in bits */ 617 rc = ber_put_bitstring( ber, s, len, ber->ber_tag ); 618 break; 619 620 case 't': /* tag for the next element */ 621 ber->ber_tag = va_arg( ap, ber_tag_t ); 622 ber->ber_usertag = 1; 623 break; 624 625 case 'v': /* vector of strings */ 626 if ( (ss = va_arg( ap, char ** )) == NULL ) 627 break; 628 for ( i = 0; ss[i] != NULL; i++ ) { 629 if ( (rc = ber_put_string( ber, ss[i], 630 ber->ber_tag )) == -1 ) 631 break; 632 } 633 break; 634 635 case 'V': /* sequences of strings + lengths */ 636 if ( (bv = va_arg( ap, struct berval ** )) == NULL ) 637 break; 638 for ( i = 0; bv[i] != NULL; i++ ) { 639 if ( (rc = ber_put_ostring( ber, bv[i]->bv_val, 640 bv[i]->bv_len, ber->ber_tag )) == -1 ) 641 break; 642 } 643 break; 644 645 case '{': /* begin sequence */ 646 rc = ber_start_seq( ber, ber->ber_tag ); 647 break; 648 649 case '}': /* end sequence */ 650 rc = ber_put_seqorset( ber ); 651 break; 652 653 case '[': /* begin set */ 654 rc = ber_start_set( ber, ber->ber_tag ); 655 break; 656 657 case ']': /* end set */ 658 rc = ber_put_seqorset( ber ); 659 break; 660 661 default: { 662 char msg[80]; 663 sprintf( msg, "unknown fmt %c\n", *fmt ); 664 ber_err_print( msg ); 665 rc = -1; 666 break; 667 } 668 } 669 670 if ( ber->ber_usertag == 0 ) 671 ber->ber_tag = LBER_DEFAULT; 672 else 673 ber->ber_usertag = 0; 674 } 675 676 va_end( ap ); 677 678 return( rc ); 679 } 680