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