]> git.sur5r.net Git - openldap/blob - libraries/liblber/encode.c
fix test
[openldap] / libraries / liblber / encode.c
1 /* encode.c - ber output encoding routines */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2008 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* Portions Copyright (c) 1990 Regents of the University of Michigan.
17  * All rights reserved.
18  *
19  * Redistribution and use in source and binary forms are permitted
20  * provided that this notice is preserved and that due credit is given
21  * to the University of Michigan at Ann Arbor. The name of the University
22  * may not be used to endorse or promote products derived from this
23  * software without specific prior written permission. This software
24  * is provided ``as is'' without express or implied warranty.
25  */
26 /* ACKNOWLEDGEMENTS:
27  * This work was originally developed by the University of Michigan
28  * (as part of U-MICH LDAP).
29  */
30
31 #include "portable.h"
32
33 #include <ctype.h>
34 #include <stdio.h>
35
36 #include <ac/stdlib.h>
37
38 #include <ac/stdarg.h>
39 #include <ac/socket.h>
40 #include <ac/string.h>
41
42 #include "lber-int.h"
43
44 static int ber_put_len LDAP_P((
45         BerElement *ber,
46         ber_len_t len,
47         int nosos ));
48
49 static int ber_start_seqorset LDAP_P((
50         BerElement *ber,
51         ber_tag_t tag ));
52
53 static int ber_put_seqorset LDAP_P(( BerElement *ber ));
54
55 static int ber_put_int_or_enum LDAP_P((
56         BerElement *ber,
57         ber_int_t num,
58         ber_tag_t tag ));
59
60 #define BER_TOP_BYTE(type)      (sizeof(type)-1)
61 #define BER_TOP_MASK(type)      ((type)0xffU << (BER_TOP_BYTE(type)*8))
62
63 static int
64 ber_calc_taglen( ber_tag_t tag )
65 {
66         int     i = BER_TOP_BYTE(ber_tag_t);
67         ber_tag_t       mask = BER_TOP_MASK(ber_tag_t);
68
69         /* find the first non-all-zero byte in the tag */
70         for ( ; i > 0; i-- ) {
71                 /* not all zero */
72                 if ( tag & mask ) break;
73                 mask >>= 8;
74         }
75
76         return i + 1;
77 }
78
79 static int
80 ber_put_tag(
81         BerElement      *ber,
82         ber_tag_t tag,
83         int nosos )
84 {
85         int rc;
86         int taglen;
87         int     i;
88         unsigned char nettag[sizeof(ber_tag_t)];
89
90         assert( ber != NULL );
91         assert( LBER_VALID( ber ) );
92
93         taglen = ber_calc_taglen( tag );
94
95         for( i=taglen-1; i>=0; i-- ) {
96                 nettag[i] = (unsigned char)(tag & 0xffU);
97                 tag >>= 8;
98         }
99
100         rc = ber_write( ber, (char *) nettag, taglen, nosos );
101
102         return rc;
103 }
104
105 static ber_len_t
106 ber_calc_lenlen( ber_len_t len )
107 {
108         /*
109          * short len if it's less than 128 - one byte giving the len,
110          * with bit 8 0.
111          */
112
113         if ( len <= (ber_len_t) 0x7FU ) return 1;
114
115         /*
116          * long len otherwise - one byte with bit 8 set, giving the
117          * length of the length, followed by the length itself.
118          */
119
120         if ( len <= (ber_len_t) 0xffU ) return 2;
121         if ( len <= (ber_len_t) 0xffffU ) return 3;
122         if ( len <= (ber_len_t) 0xffffffU ) return 4;
123
124         return 5;
125 }
126
127 static int
128 ber_put_len( BerElement *ber, ber_len_t len, int nosos )
129 {
130         int rc;
131         int             i,j;
132         char            lenlen;
133         ber_len_t       mask;
134         unsigned char netlen[sizeof(ber_len_t)];
135
136         assert( ber != NULL );
137         assert( LBER_VALID( ber ) );
138
139         /*
140          * short len if it's less than 128 - one byte giving the len,
141          * with bit 8 0.
142          */
143
144         if ( len <= 127 ) {
145                 char length_byte = (char) len;
146                 return ber_write( ber, &length_byte, 1, nosos );
147         }
148
149         /*
150          * long len otherwise - one byte with bit 8 set, giving the
151          * length of the length, followed by the length itself.
152          */
153
154         /* find the first non-all-zero byte */
155         i = BER_TOP_BYTE(ber_len_t);
156         mask = BER_TOP_MASK(ber_len_t);
157         for ( ; i > 0; i-- ) {
158                 /* not all zero */
159                 if ( len & mask ) break;
160                 mask >>= 8;
161         }
162         lenlen = (unsigned char) ++i;
163         if ( lenlen > 4 ) return -1;
164
165         lenlen |= 0x80UL;
166
167         /* write the length of the length */
168         if ( ber_write( ber, &lenlen, 1, nosos ) != 1 ) return -1;
169
170         for( j=i-1; j>=0; j-- ) {
171                 netlen[j] = (unsigned char)(len & 0xffU);
172                 len >>= 8;
173         }
174
175         /* write the length itself */
176         rc = ber_write( ber, (char *) netlen, i, nosos );
177
178         return rc == i ?  i+1 : -1;
179 }
180
181 /* out->bv_len should be the buffer size on input */
182 int
183 ber_encode_oid( BerValue *in, BerValue *out )
184 {
185         unsigned char *der;
186         unsigned long val1, val;
187         int i, j, len;
188         char *ptr, *end, *inend;
189
190         assert( in != NULL );
191         assert( out != NULL );
192
193         if ( !out->bv_val || out->bv_len < in->bv_len/2 )
194                 return -1;
195
196         der = (unsigned char *) out->bv_val;
197         ptr = in->bv_val;
198         inend = ptr + in->bv_len;
199
200         /* OIDs start with <0-1>.<0-39> or 2.<any>, DER-encoded 40*val1+val2 */
201         if ( !isdigit( (unsigned char) *ptr )) return -1;
202         val1 = strtoul( ptr, &end, 10 );
203         if ( end == ptr || val1 > 2 ) return -1;
204         if ( *end++ != '.' || !isdigit( (unsigned char) *end )) return -1;
205         val = strtoul( end, &ptr, 10 );
206         if ( ptr == end ) return -1;
207         if ( val > (val1 < 2 ? 39 : LBER_OID_COMPONENT_MAX - 80) ) return -1;
208         val += val1 * 40;
209
210         for (;;) {
211                 if ( ptr > inend ) return -1;
212
213                 len = 0;
214                 do {
215                         der[len++] = (val & 0xff) | 0x80;
216                 } while ( (val >>= 7) != 0 );
217                 der[0] &= 0x7f;
218                 for ( i = 0, j = len; i < --j; i++ ) {
219                         unsigned char tmp = der[i];
220                         der[i] = der[j];
221                         der[j] = tmp;
222                 }
223                 der += len;
224                 if ( ptr == inend )
225                         break;
226
227                 if ( *ptr++ != '.' ) return -1;
228                 if ( !isdigit( (unsigned char) *ptr )) return -1;
229                 val = strtoul( ptr, &end, 10 );
230                 if ( end == ptr || val > LBER_OID_COMPONENT_MAX ) return -1;
231                 ptr = end;
232         }
233
234         out->bv_len = (char *)der - out->bv_val;
235         return 0;
236 }
237
238 static int
239 ber_put_int_or_enum(
240         BerElement *ber,
241         ber_int_t num,
242         ber_tag_t tag )
243 {
244         int rc;
245         int     i, j, sign, taglen, lenlen;
246         ber_len_t       len;
247         ber_uint_t      unum, mask;
248         unsigned char netnum[sizeof(ber_uint_t)];
249
250         assert( ber != NULL );
251         assert( LBER_VALID( ber ) );
252
253         sign = (num < 0);
254         unum = num;     /* Bit fiddling should be done with unsigned values */
255
256         /*
257          * high bit is set - look for first non-all-one byte
258          * high bit is clear - look for first non-all-zero byte
259          */
260         i = BER_TOP_BYTE(ber_int_t);
261         mask = BER_TOP_MASK(ber_uint_t);
262         for ( ; i > 0; i-- ) {
263                 if ( sign ) {
264                         /* not all ones */
265                         if ( (unum & mask) != mask ) break;
266                 } else {
267                         /* not all zero */
268                         if ( unum & mask ) break;
269                 }
270                 mask >>= 8;
271         }
272
273         /*
274          * we now have the "leading byte".  if the high bit on this
275          * byte matches the sign bit, we need to "back up" a byte.
276          */
277         mask = (unum & ((ber_uint_t)0x80U << (i * 8)));
278         if ( (mask && !sign) || (sign && !mask) ) {
279                 i++;
280         }
281
282         len = i + 1;
283
284         if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) {
285                 return -1;
286         }
287
288         if ( (lenlen = ber_put_len( ber, len, 0 )) == -1 ) {
289                 return -1;
290         }
291         i++;
292
293         for( j=i-1; j>=0; j-- ) {
294                 netnum[j] = (unsigned char)(unum & 0xffU);
295                 unum >>= 8;
296         }
297
298         rc = ber_write( ber, (char *) netnum, i, 0 );
299
300         /* length of tag + length + contents */
301         return rc == i ? taglen + lenlen + i : -1;
302 }
303
304 int
305 ber_put_enum(
306         BerElement *ber,
307         ber_int_t num,
308         ber_tag_t tag )
309 {
310         assert( ber != NULL );
311         assert( LBER_VALID( ber ) );
312
313         if ( tag == LBER_DEFAULT ) {
314                 tag = LBER_ENUMERATED;
315         }
316
317         return ber_put_int_or_enum( ber, num, tag );
318 }
319
320 int
321 ber_put_int(
322         BerElement *ber,
323         ber_int_t num,
324         ber_tag_t tag )
325 {
326         assert( ber != NULL );
327         assert( LBER_VALID( ber ) );
328
329         if ( tag == LBER_DEFAULT ) {
330                 tag = LBER_INTEGER;
331         }
332
333         return ber_put_int_or_enum( ber, num, tag );
334 }
335
336 int
337 ber_put_ostring(
338         BerElement *ber,
339         LDAP_CONST char *str,
340         ber_len_t len,
341         ber_tag_t tag )
342 {
343         int taglen, lenlen, rc;
344
345         assert( ber != NULL );
346         assert( str != NULL );
347
348         assert( LBER_VALID( ber ) );
349
350         if ( tag == LBER_DEFAULT ) {
351                 tag = LBER_OCTETSTRING;
352         }
353
354         if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 )
355                 return -1;
356
357         if ( (lenlen = ber_put_len( ber, len, 0 )) == -1 ||
358                 (ber_len_t) ber_write( ber, str, len, 0 ) != len )
359         {
360                 rc = -1;
361         } else {
362                 /* return length of tag + length + contents */
363                 rc = taglen + lenlen + len;
364         }
365
366         return rc;
367 }
368
369 int
370 ber_put_berval(
371         BerElement *ber,
372         struct berval *bv,
373         ber_tag_t tag )
374 {
375         assert( ber != NULL );
376         assert( LBER_VALID( ber ) );
377
378         if( bv == NULL || bv->bv_len == 0 ) {
379                 return ber_put_ostring( ber, "", (ber_len_t) 0, tag );
380         }
381
382         return ber_put_ostring( ber, bv->bv_val, bv->bv_len, tag );
383 }
384
385 int
386 ber_put_string(
387         BerElement *ber,
388         LDAP_CONST char *str,
389         ber_tag_t tag )
390 {
391         assert( ber != NULL );
392         assert( str != NULL );
393
394         assert( LBER_VALID( ber ) );
395
396         return ber_put_ostring( ber, str, strlen( str ), tag );
397 }
398
399 int
400 ber_put_bitstring(
401         BerElement *ber,
402         LDAP_CONST char *str,
403         ber_len_t blen /* in bits */,
404         ber_tag_t tag )
405 {
406         int                             taglen, lenlen;
407         ber_len_t               len;
408         unsigned char   unusedbits;
409
410         assert( ber != NULL );
411         assert( str != NULL );
412
413         assert( LBER_VALID( ber ) );
414
415         if ( tag == LBER_DEFAULT ) {
416                 tag = LBER_BITSTRING;
417         }
418
419         if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) {
420                 return -1;
421         }
422
423         len = ( blen + 7 ) / 8;
424         unusedbits = (unsigned char) ((len * 8) - blen);
425         if ( (lenlen = ber_put_len( ber, len + 1, 0 )) == -1 ) {
426                 return -1;
427         }
428
429         if ( ber_write( ber, (char *)&unusedbits, 1, 0 ) != 1 ) {
430                 return -1;
431         }
432
433         if ( (ber_len_t) ber_write( ber, str, len, 0 ) != len ) {
434                 return -1;
435         }
436
437         /* return length of tag + length + unused bit count + contents */
438         return taglen + 1 + lenlen + len;
439 }
440
441 int
442 ber_put_null( BerElement *ber, ber_tag_t tag )
443 {
444         int     taglen;
445
446         assert( ber != NULL );
447         assert( LBER_VALID( ber ) );
448
449         if ( tag == LBER_DEFAULT ) {
450                 tag = LBER_NULL;
451         }
452
453         if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) {
454                 return -1;
455         }
456
457         if ( ber_put_len( ber, 0, 0 ) != 1 ) {
458                 return -1;
459         }
460
461         return taglen + 1;
462 }
463
464 int
465 ber_put_boolean(
466         BerElement *ber,
467         ber_int_t boolval,
468         ber_tag_t tag )
469 {
470         int                             taglen;
471         unsigned char   c;
472
473         assert( ber != NULL );
474         assert( LBER_VALID( ber ) );
475
476         if ( tag == LBER_DEFAULT )
477                 tag = LBER_BOOLEAN;
478
479         if ( (taglen = ber_put_tag( ber, tag, 0 )) == -1 ) {
480                 return -1;
481         }
482
483         if ( ber_put_len( ber, 1, 0 ) != 1 ) {
484                 return -1;
485         }
486
487         c = boolval ? (unsigned char) ~0U : (unsigned char) 0U;
488
489         if ( ber_write( ber, (char *) &c, 1, 0 ) != 1 ) {
490                 return -1;
491         }
492
493         return taglen + 2;
494 }
495
496 #define FOUR_BYTE_LEN   5
497
498 static int
499 ber_start_seqorset(
500         BerElement *ber,
501         ber_tag_t tag )
502 {
503         Seqorset        *new;
504
505         assert( ber != NULL );
506         assert( LBER_VALID( ber ) );
507
508         new = (Seqorset *) ber_memcalloc_x( 1, sizeof(Seqorset), ber->ber_memctx );
509
510         if ( new == NULL ) {
511                 return -1;
512         }
513
514         new->sos_ber = ber;
515         if ( ber->ber_sos == NULL ) {
516                 new->sos_first = ber->ber_ptr;
517         } else {
518                 new->sos_first = ber->ber_sos->sos_ptr;
519         }
520
521         /* Set aside room for a 4 byte length field */
522         new->sos_ptr = new->sos_first + ber_calc_taglen( tag ) + FOUR_BYTE_LEN;
523         new->sos_tag = tag;
524
525         new->sos_next = ber->ber_sos;
526         ber->ber_sos = new;
527
528         return 0;
529 }
530
531 int
532 ber_start_seq( BerElement *ber, ber_tag_t tag )
533 {
534         assert( ber != NULL );
535         assert( LBER_VALID( ber ) );
536
537         if ( tag == LBER_DEFAULT ) {
538                 tag = LBER_SEQUENCE;
539         }
540
541         return ber_start_seqorset( ber, tag );
542 }
543
544 int
545 ber_start_set( BerElement *ber, ber_tag_t tag )
546 {
547         assert( ber != NULL );
548         assert( LBER_VALID( ber ) );
549
550         if ( tag == LBER_DEFAULT ) {
551                 tag = LBER_SET;
552         }
553
554         return ber_start_seqorset( ber, tag );
555 }
556
557 static int
558 ber_put_seqorset( BerElement *ber )
559 {
560         int rc;
561         ber_len_t       len;
562         unsigned char netlen[sizeof(ber_len_t)];
563         int                     taglen;
564         ber_len_t       lenlen;
565         unsigned char   ltag = 0x80U + FOUR_BYTE_LEN - 1;
566         Seqorset        *next;
567         Seqorset        **sos = &ber->ber_sos;
568
569         assert( ber != NULL );
570         assert( LBER_VALID( ber ) );
571
572         if( *sos == NULL ) return -1;
573
574         /*
575          * If this is the toplevel sequence or set, we need to actually
576          * write the stuff out.  Otherwise, it's already been put in
577          * the appropriate buffer and will be written when the toplevel
578          * one is written.  In this case all we need to do is update the
579          * length and tag.
580          */
581
582         len = (*sos)->sos_clen;
583
584         if ( sizeof(ber_len_t) > 4 && len > 0xffffffffUL ) {
585                 return -1;
586         }
587
588         if ( ber->ber_options & LBER_USE_DER ) {
589                 lenlen = ber_calc_lenlen( len );
590
591         } else {
592                 lenlen = FOUR_BYTE_LEN;
593         }
594
595         if( lenlen > 1 ) {
596                 int i;
597                 ber_len_t j = len;
598                 for( i=lenlen-2; i >= 0; i-- ) {
599                         netlen[i] = j & 0xffU;
600                         j >>= 8;
601                 }
602         } else {
603                 netlen[0] = (unsigned char)(len & 0x7fU);
604         }
605
606         if ( (next = (*sos)->sos_next) == NULL ) {
607                 /* write the tag */
608                 if ( (taglen = ber_put_tag( ber, (*sos)->sos_tag, 1 )) == -1 ) {
609                         return( -1 );
610                 }
611
612                 if ( ber->ber_options & LBER_USE_DER ) {
613                         /* Write the length in the minimum # of octets */
614                         if ( ber_put_len( ber, len, 1 ) == -1 ) {
615                                 return -1;
616                         }
617
618                         if (lenlen != FOUR_BYTE_LEN) {
619                                 /*
620                                  * We set aside FOUR_BYTE_LEN bytes for
621                                  * the length field.  Move the data if
622                                  * we don't actually need that much
623                                  */
624                                 AC_MEMCPY( (*sos)->sos_first + taglen +
625                                     lenlen, (*sos)->sos_first + taglen +
626                                     FOUR_BYTE_LEN, len );
627                         }
628                 } else {
629                         /* Fill FOUR_BYTE_LEN bytes for length field */
630                         /* one byte of length length */
631                         if ( ber_write( ber, (char *)&ltag, 1, 1 ) != 1 ) {
632                                 return -1;
633                         }
634
635                         /* the length itself */
636                         rc  = ber_write( ber, (char *) netlen, FOUR_BYTE_LEN-1, 1 );
637
638                         if( rc != FOUR_BYTE_LEN - 1 ) {
639                                 return -1;
640                         }
641                 }
642                 /* The ber_ptr is at the set/seq start - move it to the end */
643                 (*sos)->sos_ber->ber_ptr += len;
644
645         } else {
646                 int i;
647                 unsigned char nettag[sizeof(ber_tag_t)];
648                 ber_tag_t tmptag = (*sos)->sos_tag;
649
650                 if( ber->ber_sos->sos_ptr > ber->ber_end ) {
651                         /* The sos_ptr exceeds the end of the BerElement
652                          * this can happen, for example, when the sos_ptr
653                          * is near the end and no data was written for the
654                          * 'V'.  We must realloc the BerElement to ensure
655                          * we don't overwrite the buffer when writing
656                          * the tag and length fields.
657                          */
658                         ber_len_t ext = ber->ber_sos->sos_ptr - ber->ber_end;
659
660                         if( ber_realloc( ber,  ext ) != 0 ) {
661                                 return -1;
662                         }
663                 }
664
665                 /* the tag */
666                 taglen = ber_calc_taglen( tmptag );
667
668                 for( i = taglen-1; i >= 0; i-- ) {
669                         nettag[i] = (unsigned char)(tmptag & 0xffU);
670                         tmptag >>= 8;
671                 }
672
673                 AC_FMEMCPY( (*sos)->sos_first, nettag, taglen );
674
675                 if ( ber->ber_options & LBER_USE_DER ) {
676                         ltag = (lenlen == 1)
677                                 ? (unsigned char) len
678                                 : (unsigned char) (0x80U + (lenlen - 1));
679                 }
680
681                 /* one byte of length length */
682                 (*sos)->sos_first[1] = ltag;
683
684                 if ( ber->ber_options & LBER_USE_DER ) {
685                         if (lenlen > 1) {
686                                 /* Write the length itself */
687                                 AC_FMEMCPY( (*sos)->sos_first + 2, netlen, lenlen - 1 );
688                         }
689                         if (lenlen != FOUR_BYTE_LEN) {
690                                 /*
691                                  * We set aside FOUR_BYTE_LEN bytes for
692                                  * the length field.  Move the data if
693                                  * we don't actually need that much
694                                  */
695                                 AC_FMEMCPY( (*sos)->sos_first + taglen +
696                                     lenlen, (*sos)->sos_first + taglen +
697                                     FOUR_BYTE_LEN, len );
698                         }
699                 } else {
700                         /* the length itself */
701                         AC_FMEMCPY( (*sos)->sos_first + taglen + 1,
702                             netlen, FOUR_BYTE_LEN - 1 );
703                 }
704
705                 next->sos_clen += (taglen + lenlen + len);
706                 next->sos_ptr += (taglen + lenlen + len);
707         }
708
709         /* we're done with this seqorset, so free it up */
710         ber_memfree_x( (char *) (*sos), ber->ber_memctx );
711         *sos = next;
712
713         return taglen + lenlen + len;
714 }
715
716 int
717 ber_put_seq( BerElement *ber )
718 {
719         assert( ber != NULL );
720         assert( LBER_VALID( ber ) );
721
722         return ber_put_seqorset( ber );
723 }
724
725 int
726 ber_put_set( BerElement *ber )
727 {
728         assert( ber != NULL );
729         assert( LBER_VALID( ber ) );
730
731         return ber_put_seqorset( ber );
732 }
733
734 /* N tag */
735 static ber_tag_t lber_int_null = 0;
736
737 /* VARARGS */
738 int
739 ber_printf( BerElement *ber, LDAP_CONST char *fmt, ... )
740 {
741         va_list         ap;
742         char            *s, **ss;
743         struct berval   *bv, **bvp;
744         int             rc;
745         ber_int_t       i;
746         ber_len_t       len;
747
748         assert( ber != NULL );
749         assert( fmt != NULL );
750
751         assert( LBER_VALID( ber ) );
752
753         va_start( ap, fmt );
754
755         for ( rc = 0; *fmt && rc != -1; fmt++ ) {
756                 switch ( *fmt ) {
757                 case '!': { /* hook */
758                                 BEREncodeCallback *f;
759                                 void *p;
760
761                                 f = va_arg( ap, BEREncodeCallback * );
762                                 p = va_arg( ap, void * );
763
764                                 rc = (*f)( ber, p );
765                         } break;
766
767                 case 'b':       /* boolean */
768                         i = va_arg( ap, ber_int_t );
769                         rc = ber_put_boolean( ber, i, ber->ber_tag );
770                         break;
771
772                 case 'i':       /* int */
773                         i = va_arg( ap, ber_int_t );
774                         rc = ber_put_int( ber, i, ber->ber_tag );
775                         break;
776
777                 case 'e':       /* enumeration */
778                         i = va_arg( ap, ber_int_t );
779                         rc = ber_put_enum( ber, i, ber->ber_tag );
780                         break;
781
782                 case 'n':       /* null */
783                         rc = ber_put_null( ber, ber->ber_tag );
784                         break;
785
786                 case 'N':       /* Debug NULL */
787                         if( lber_int_null != 0 ) {
788                                 /* Insert NULL to ensure peer ignores unknown tags */
789                                 rc = ber_put_null( ber, lber_int_null );
790                         } else {
791                                 rc = 0;
792                         }
793                         break;
794
795                 case 'o':       /* octet string (non-null terminated) */
796                         s = va_arg( ap, char * );
797                         len = va_arg( ap, ber_len_t );
798                         rc = ber_put_ostring( ber, s, len, ber->ber_tag );
799                         break;
800
801                 case 'O':       /* berval octet string */
802                         bv = va_arg( ap, struct berval * );
803                         if( bv == NULL ) break;
804                         rc = ber_put_berval( ber, bv, ber->ber_tag );
805                         break;
806
807                 case 's':       /* string */
808                         s = va_arg( ap, char * );
809                         rc = ber_put_string( ber, s, ber->ber_tag );
810                         break;
811
812                 case 'B':       /* bit string */
813                 case 'X':       /* bit string (deprecated) */
814                         s = va_arg( ap, char * );
815                         len = va_arg( ap, int );        /* in bits */
816                         rc = ber_put_bitstring( ber, s, len, ber->ber_tag );
817                         break;
818
819                 case 't':       /* tag for the next element */
820                         ber->ber_tag = va_arg( ap, ber_tag_t );
821                         ber->ber_usertag = 1;
822                         break;
823
824                 case 'v':       /* vector of strings */
825                         if ( (ss = va_arg( ap, char ** )) == NULL )
826                                 break;
827                         for ( i = 0; ss[i] != NULL; i++ ) {
828                                 if ( (rc = ber_put_string( ber, ss[i],
829                                     ber->ber_tag )) == -1 )
830                                         break;
831                         }
832                         break;
833
834                 case 'V':       /* sequences of strings + lengths */
835                         if ( (bvp = va_arg( ap, struct berval ** )) == NULL )
836                                 break;
837                         for ( i = 0; bvp[i] != NULL; i++ ) {
838                                 if ( (rc = ber_put_berval( ber, bvp[i],
839                                     ber->ber_tag )) == -1 )
840                                         break;
841                         }
842                         break;
843
844                 case 'W':       /* BerVarray */
845                         if ( (bv = va_arg( ap, BerVarray )) == NULL )
846                                 break;
847                         for ( i = 0; bv[i].bv_val != NULL; i++ ) {
848                                 if ( (rc = ber_put_berval( ber, &bv[i],
849                                     ber->ber_tag )) == -1 )
850                                         break;
851                         }
852                         break;
853
854                 case '{':       /* begin sequence */
855                         rc = ber_start_seq( ber, ber->ber_tag );
856                         break;
857
858                 case '}':       /* end sequence */
859                         rc = ber_put_seqorset( ber );
860                         break;
861
862                 case '[':       /* begin set */
863                         rc = ber_start_set( ber, ber->ber_tag );
864                         break;
865
866                 case ']':       /* end set */
867                         rc = ber_put_seqorset( ber );
868                         break;
869
870                 default:
871                         if( ber->ber_debug ) {
872                                 ber_log_printf( LDAP_DEBUG_ANY, ber->ber_debug,
873                                         "ber_printf: unknown fmt %c\n", *fmt );
874                         }
875                         rc = -1;
876                         break;
877                 }
878
879                 if ( ber->ber_usertag == 0 ) {
880                         ber->ber_tag = LBER_DEFAULT;
881                 } else {
882                         ber->ber_usertag = 0;
883                 }
884         }
885
886         va_end( ap );
887
888         return rc;
889 }