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