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