]> git.sur5r.net Git - openldap/blob - libraries/liblber/io.c
95dc49b79e32dbf4a0b9c29d367f93a46233afce
[openldap] / libraries / liblber / io.c
1 /* io.c - ber general i/o routines */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7 /* Portions
8  * Copyright (c) 1990 Regents of the University of Michigan.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms are permitted
12  * provided that this notice is preserved and that due credit is given
13  * to the University of Michigan at Ann Arbor. The name of the University
14  * may not be used to endorse or promote products derived from this
15  * software without specific prior written permission. This software
16  * is provided ``as is'' without express or implied warranty.
17  */
18
19 #include "portable.h"
20
21 #include <stdio.h>
22
23 #include <ac/stdlib.h>
24
25 #include <ac/ctype.h>
26 #include <ac/errno.h>
27 #include <ac/socket.h>
28 #include <ac/string.h>
29 #include <ac/unistd.h>
30
31 #ifdef HAVE_IO_H
32 #include <io.h>
33 #endif
34
35 #include "lber-int.h"
36
37 ber_slen_t
38 ber_read(
39         BerElement *ber,
40         char *buf,
41         ber_len_t len )
42 {
43         ber_len_t       actuallen, nleft;
44
45         assert( ber != NULL );
46         assert( buf != NULL );
47
48         assert( LBER_VALID( ber ) );
49
50         nleft = ber_pvt_ber_remaining( ber );
51         actuallen = nleft < len ? nleft : len;
52
53         AC_MEMCPY( buf, ber->ber_ptr, actuallen );
54
55         ber->ber_ptr += actuallen;
56
57         return( (ber_slen_t) actuallen );
58 }
59
60 ber_slen_t
61 ber_write(
62         BerElement *ber,
63         LDAP_CONST char *buf,
64         ber_len_t len,
65         int nosos )
66 {
67         assert( ber != NULL );
68         assert( buf != NULL );
69
70         assert( LBER_VALID( ber ) );
71
72         if ( nosos || ber->ber_sos == NULL ) {
73                 if ( ber->ber_ptr + len > ber->ber_end ) {
74                         if ( ber_realloc( ber, len ) != 0 )
75                                 return( -1 );
76                 }
77                 AC_MEMCPY( ber->ber_ptr, buf, (size_t)len );
78                 ber->ber_ptr += len;
79                 return( (ber_slen_t) len );
80
81         } else {
82                 if ( ber->ber_sos->sos_ptr + len > ber->ber_end ) {
83                         if ( ber_realloc( ber, len ) != 0 )
84                                 return( -1 );
85                 }
86                 AC_MEMCPY( ber->ber_sos->sos_ptr, buf, (size_t)len );
87                 ber->ber_sos->sos_ptr += len;
88                 ber->ber_sos->sos_clen += len;
89                 return( (ber_slen_t) len );
90         }
91 }
92
93 int
94 ber_realloc( BerElement *ber, ber_len_t len )
95 {
96         ber_len_t       total;
97         Seqorset        *s;
98         long            off;
99         char            *oldbuf;
100
101         assert( ber != NULL );
102         assert( len > 0 );
103         assert( LBER_VALID( ber ) );
104
105         total = ber_pvt_ber_total( ber );
106
107 #define LBER_EXBUFSIZ   1000 /* a few words less than 2^N for binary buddy */
108 #if defined( LBER_EXBUFSIZ ) && LBER_EXBUFSIZ > 0
109 # ifndef notdef
110         /* don't realloc by small amounts */
111         total += len < LBER_EXBUFSIZ ? LBER_EXBUFSIZ : len;
112 # else
113         {       /* not sure what value this adds */
114                 ber_len_t have = (total + (LBER_EXBUFSIZE - 1)) / LBER_EXBUFSIZ;
115                 ber_len_t need = (len + (LBER_EXBUFSIZ - 1)) / LBER_EXBUFSIZ;
116                 total = ( have + need ) * LBER_EXBUFSIZ;
117         }
118 # endif
119 #else
120         total += len;   /* realloc just what's needed */
121 #endif
122
123         oldbuf = ber->ber_buf;
124
125         ber->ber_buf = (char *) LBER_REALLOC( oldbuf, total );
126         
127         if ( ber->ber_buf == NULL ) {
128                 ber->ber_buf = oldbuf;
129                 return( -1 );
130         }
131
132         ber->ber_end = ber->ber_buf + total;
133
134         /*
135          * If the stinking thing was moved, we need to go through and
136          * reset all the sos and ber pointers.  Offsets would've been
137          * a better idea... oh well.
138          */
139
140         if ( ber->ber_buf != oldbuf ) {
141                 ber->ber_ptr = ber->ber_buf + (ber->ber_ptr - oldbuf);
142
143                 for ( s = ber->ber_sos; s != NULL; s = s->sos_next ) {
144                         off = s->sos_first - oldbuf;
145                         s->sos_first = ber->ber_buf + off;
146
147                         off = s->sos_ptr - oldbuf;
148                         s->sos_ptr = ber->ber_buf + off;
149                 }
150         }
151
152         return( 0 );
153 }
154
155 void
156 ber_free_buf( BerElement *ber )
157 {
158         Seqorset *s, *next;
159
160         assert( LBER_VALID( ber ) );
161
162         if ( ber->ber_buf) LBER_FREE( ber->ber_buf );
163
164         for( s = ber->ber_sos ; s != NULL ; s = next ) {
165                 next = s->sos_next;
166                 LBER_FREE( s );
167         }
168
169         ber->ber_buf = NULL;
170         ber->ber_sos = NULL;
171         ber->ber_valid = LBER_UNINITIALIZED;
172 }
173
174 void
175 ber_free( BerElement *ber, int freebuf )
176 {
177 #ifdef LDAP_MEMORY_DEBUG
178         assert( ber != NULL );
179 #endif
180
181         if( ber == NULL ) {
182                 return;
183         }
184
185         if( freebuf )
186                 ber_free_buf( ber );
187
188         LBER_FREE( (char *) ber );
189 }
190
191 int
192 ber_flush( Sockbuf *sb, BerElement *ber, int freeit )
193 {
194         ber_len_t       nwritten, towrite;
195         ber_slen_t      rc;     
196
197         assert( sb != NULL );
198         assert( ber != NULL );
199
200         assert( SOCKBUF_VALID( sb ) );
201         assert( LBER_VALID( ber ) );
202
203         if ( ber->ber_rwptr == NULL ) {
204                 ber->ber_rwptr = ber->ber_buf;
205         }
206         towrite = ber->ber_ptr - ber->ber_rwptr;
207
208         if ( sb->sb_debug ) {
209 #ifdef NEW_LOGGING
210                 LDAP_LOG(( "liblber", LDAP_LEVEL_DETAIL1,
211                            "ber_flush: %ld bytes to sd %ld%s\n",
212                            towrite, (long)sb->sb_fd,
213                            ber->ber_rwptr != ber->ber_buf ? " (re-flush)" : "" ));
214                 BER_DUMP(( "liblber", LDAP_LEVEL_DETAIL2, ber, 1 ));
215 #else
216                 ber_log_printf( LDAP_DEBUG_ANY, sb->sb_debug,
217                         "ber_flush: %ld bytes to sd %ld%s\n",
218                         towrite, (long) sb->sb_fd,
219                         ber->ber_rwptr != ber->ber_buf ?  " (re-flush)" : "" );
220                 ber_log_bprint( LDAP_DEBUG_PACKETS, sb->sb_debug,
221                         ber->ber_rwptr, towrite );
222 #endif
223         }
224
225         nwritten = 0;
226         do {
227                 rc = ber_int_sb_write( sb, ber->ber_rwptr, towrite );
228                 if (rc<=0) {
229                         return -1;
230                 }
231                 towrite -= rc;
232                 nwritten += rc;
233                 ber->ber_rwptr += rc;
234         } while ( towrite > 0 );
235
236         if ( freeit )
237                 ber_free( ber, 1 );
238
239         return( 0 );
240 }
241
242 BerElement *
243 ber_alloc_t( int options )
244 {
245         BerElement      *ber;
246
247     ber_int_options.lbo_valid = LBER_INITIALIZED;
248
249         ber = (BerElement *) LBER_CALLOC( 1, sizeof(BerElement) );
250
251         if ( ber == NULL ) {
252                 return NULL;
253         }
254
255         ber->ber_valid = LBER_VALID_BERELEMENT;
256         ber->ber_tag = LBER_DEFAULT;
257         ber->ber_options = options;
258         ber->ber_debug = ber_int_debug;
259
260         assert( LBER_VALID( ber ) );
261         return ber;
262 }
263
264 BerElement *
265 ber_alloc( void )       /* deprecated */
266 {
267         return ber_alloc_t( 0 );
268 }
269
270 BerElement *
271 der_alloc( void )       /* deprecated */
272 {
273         return ber_alloc_t( LBER_USE_DER );
274 }
275
276 BerElement *
277 ber_dup( BerElement *ber )
278 {
279         BerElement      *new;
280
281         assert( ber != NULL );
282         assert( LBER_VALID( ber ) );
283
284         if ( (new = ber_alloc_t( ber->ber_options )) == NULL ) {
285                 return NULL;
286         }
287
288         *new = *ber;
289
290         assert( LBER_VALID( new ) );
291         return( new );
292 }
293
294
295 void
296 ber_init2( BerElement *ber, struct berval *bv, int options )
297 {
298         assert( ber != NULL );
299
300         ber_int_options.lbo_valid = LBER_INITIALIZED;
301
302         (void) memset( (char *)ber, '\0', sizeof( BerElement ));
303         ber->ber_valid = LBER_VALID_BERELEMENT;
304         ber->ber_tag = LBER_DEFAULT;
305         ber->ber_options = (char) options;
306         ber->ber_debug = ber_int_debug;
307
308         if ( bv != NULL ) {
309                 ber->ber_buf = bv->bv_val;
310                 ber->ber_ptr = ber->ber_buf;
311                 ber->ber_end = ber->ber_buf + bv->bv_len;
312         }
313
314         assert( LBER_VALID( ber ) );
315 }
316
317 /* OLD U-Mich ber_init() */
318 void
319 ber_init_w_nullc( BerElement *ber, int options )
320 {
321         ber_init2( ber, NULL, options );
322 }
323
324 /* New C-API ber_init() */
325 /* This function constructs a BerElement containing a copy
326 ** of the data in the bv argument.
327 */
328 BerElement *
329 ber_init( struct berval *bv )
330 {
331         BerElement *ber;
332
333         assert( bv != NULL );
334
335     ber_int_options.lbo_valid = LBER_INITIALIZED;
336
337         if ( bv == NULL ) {
338                 return NULL;
339         }
340
341         ber = ber_alloc_t( 0 );
342
343         if( ber == NULL ) {
344                 /* allocation failed */
345                 return NULL;
346         }
347
348         /* copy the data */
349         if ( ((ber_len_t) ber_write ( ber, bv->bv_val, bv->bv_len, 0 ))
350                 != bv->bv_len )
351         {
352                 /* write failed, so free and return NULL */
353                 ber_free( ber, 1 );
354                 return NULL;
355         }
356
357         ber_reset( ber, 1 );    /* reset the pointer to the start of the buffer */
358         return ber;
359 }
360
361 /* New C-API ber_flatten routine */
362 /* This routine allocates a struct berval whose contents are a BER
363 ** encoding taken from the ber argument.  The bvPtr pointer pointers to
364 ** the returned berval.
365 */
366 int ber_flatten(
367         BerElement *ber,
368         struct berval **bvPtr)
369 {
370         struct berval *bv;
371  
372         assert( bvPtr != NULL );
373
374     ber_int_options.lbo_valid = LBER_INITIALIZED;
375
376         if(bvPtr == NULL) {
377                 return -1;
378         }
379
380         bv = LBER_MALLOC( sizeof(struct berval) );
381         if ( bv == NULL ) {
382                 return -1;
383         }
384
385         if ( ber == NULL ) {
386                 /* ber is null, create an empty berval */
387                 bv->bv_val = NULL;
388                 bv->bv_len = 0;
389
390         } else {
391                 /* copy the berval */
392                 ber_len_t len = ber_pvt_ber_write( ber );
393
394                 bv->bv_val = (char *) LBER_MALLOC( len + 1 );
395                 if ( bv->bv_val == NULL ) {
396                         LBER_FREE( bv );
397                         return -1;
398                 }
399
400                 AC_MEMCPY( bv->bv_val, ber->ber_buf, len );
401                 bv->bv_val[len] = '\0';
402                 bv->bv_len = len;
403         }
404     
405         *bvPtr = bv;
406         return 0;
407 }
408
409 void
410 ber_reset( BerElement *ber, int was_writing )
411 {
412         assert( ber != NULL );
413         assert( LBER_VALID( ber ) );
414
415         if ( was_writing ) {
416                 ber->ber_end = ber->ber_ptr;
417                 ber->ber_ptr = ber->ber_buf;
418
419         } else {
420                 ber->ber_ptr = ber->ber_end;
421         }
422
423         ber->ber_rwptr = NULL;
424 }
425
426 /*
427  * A rewrite of ber_get_next that can safely be called multiple times 
428  * for the same packet. It will simply continue where it stopped until
429  * a full packet is read.
430  */
431
432 ber_tag_t
433 ber_get_next(
434         Sockbuf *sb,
435         ber_len_t *len,
436         BerElement *ber )
437 {
438         assert( sb != NULL );
439         assert( len != NULL );
440         assert( ber != NULL );
441
442         assert( SOCKBUF_VALID( sb ) );
443         assert( LBER_VALID( ber ) );
444
445 #ifdef NEW_LOGGING
446         LDAP_LOG(( "liblber", LDAP_LEVEL_ENTRY, "ber_get_next: enter\n" ));
447 #else
448         ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug,
449                 "ber_get_next\n" );
450 #endif
451
452         /*
453          * Any ber element looks like this: tag length contents.
454          * Assuming everything's ok, we return the tag byte (we
455          * can assume a single byte), return the length in len,
456          * and the rest of the undecoded element in buf.
457          *
458          * Assumptions:
459          *      1) small tags (less than 128)
460          *      2) definite lengths
461          *      3) primitive encodings used whenever possible
462          */
463
464         if (ber->ber_rwptr == NULL) {
465                 /* XXYYZ
466                  * dtest does like this assert.
467                  */
468                 /* assert( ber->ber_buf == NULL ); */
469                 ber->ber_rwptr = (char *) &ber->ber_len-1;
470                 ber->ber_ptr = ber->ber_rwptr;
471                 ber->ber_tag = 0;
472         }
473
474         while (ber->ber_rwptr > (char *)&ber->ber_tag && ber->ber_rwptr <
475                 (char *)(&ber->ber_usertag + 1)) {
476                 ber_slen_t i;
477                 char buf[sizeof(ber->ber_len)-1];
478                 ber_len_t tlen = 0;
479
480                 if ((i=ber_int_sb_read( sb, ber->ber_rwptr,
481                         (char *)(&ber->ber_usertag+1)-ber->ber_rwptr))<=0) {
482                         return LBER_DEFAULT;
483                 }
484
485                 ber->ber_rwptr += i;
486
487                 /* We got at least one byte, try to parse the tag. */
488                 if (ber->ber_ptr == (char *)&ber->ber_len-1) {
489                         ber_tag_t tag;
490                         unsigned char *p = (unsigned char *)ber->ber_ptr;
491                         tag = *p++;
492                         if ((tag & LBER_BIG_TAG_MASK) == LBER_BIG_TAG_MASK) {
493                                 for (i=1; (char *)p<ber->ber_rwptr; i++,p++) {
494                                         tag <<= 8;
495                                         tag |= *p;
496                                         if (!(*p & LBER_MORE_TAG_MASK))
497                                                 break;
498                                         /* Is the tag too big? */
499                                         if (i == sizeof(ber_tag_t)-1) {
500                                                 errno = ERANGE;
501                                                 return LBER_DEFAULT;
502                                         }
503                                 }
504                                 /* Did we run out of bytes? */
505                                 if ((char *)p == ber->ber_rwptr) {
506                                         return LBER_DEFAULT;
507                                 }
508                                 p++;
509                         }
510                         ber->ber_tag = tag;
511                         ber->ber_ptr = (char *)p;
512                 }
513
514                 /* Now look for the length */
515                 if (*ber->ber_ptr & 0x80) {     /* multi-byte */
516                         int llen = *(unsigned char *)ber->ber_ptr++ & 0x7f;
517                         if (llen > (int)sizeof(ber_len_t)) {
518                                 errno = ERANGE;
519                                 return LBER_DEFAULT;
520                         }
521                         /* Not enough bytes? */
522                         if (ber->ber_rwptr - ber->ber_ptr < llen) {
523                                 return LBER_DEFAULT;
524                         }
525                         for (i=0; i<llen && ber->ber_ptr<ber->ber_rwptr; i++,ber->ber_ptr++) {
526                                 tlen <<=8;
527                                 tlen |= *(unsigned char *)ber->ber_ptr;
528                         }
529                 } else {
530                         tlen = *(unsigned char *)ber->ber_ptr++;
531                 }
532                 /* Are there leftover data bytes inside ber->ber_len? */
533                 if (ber->ber_ptr < (char *)&ber->ber_usertag) {
534                         if (ber->ber_rwptr < (char *)&ber->ber_usertag)
535                                 i = ber->ber_rwptr - ber->ber_ptr;
536                         else
537                                 i = (char *)&ber->ber_usertag - ber->ber_ptr;
538                         AC_MEMCPY(buf, ber->ber_ptr, i);
539                         ber->ber_ptr += i;
540                 } else {
541                         i = 0;
542                 }
543                 ber->ber_len = tlen;
544
545                 /* now fill the buffer. */
546
547                 /* make sure length is reasonable */
548                 if ( ber->ber_len == 0 ) {
549                         errno = ERANGE;
550                         return LBER_DEFAULT;
551                 } else if ( sb->sb_max_incoming && ber->ber_len > sb->sb_max_incoming ) {
552 #ifdef NEW_LOGGING
553                         LDAP_LOG(( "liblber", LDAP_LEVEL_ERR, 
554                                 "ber_get_next: sockbuf_max_incoming limit hit "
555                                 "(%d > %d)\n", ber->ber_len, sb->sb_max_incoming ));
556 #else
557                         ber_log_printf( LDAP_DEBUG_CONNS, ber->ber_debug,
558                                 "ber_get_next: sockbuf_max_incoming limit hit "
559                                 "(%ld > %ld)\n", ber->ber_len, sb->sb_max_incoming );
560 #endif
561                         errno = ERANGE;
562                         return LBER_DEFAULT;
563                 }
564
565                 if (ber->ber_buf==NULL) {
566                         ber_len_t l = ber->ber_rwptr - ber->ber_ptr;
567                         /* ber->ber_ptr is always <= ber->ber->ber_rwptr.
568                          * make sure ber->ber_len agrees with what we've
569                          * already read.
570                          */
571                         if ( ber->ber_len < i + l ) {
572                                 errno = ERANGE;
573                                 return LBER_DEFAULT;
574                         }
575                         ber->ber_buf = (char *) LBER_MALLOC( ber->ber_len + 1 );
576                         if (ber->ber_buf==NULL) {
577                                 return LBER_DEFAULT;
578                         }
579                         ber->ber_end = ber->ber_buf + ber->ber_len;
580                         if (i) {
581                                 AC_MEMCPY(ber->ber_buf, buf, i);
582                         }
583                         if (l > 0) {
584                                 AC_MEMCPY(ber->ber_buf + i, ber->ber_ptr, l);
585                                 i += l;
586                         }
587                         ber->ber_ptr = ber->ber_buf;
588                         ber->ber_usertag = 0;
589                         if ((ber_len_t)i == ber->ber_len) {
590                                 goto done;
591                         }
592                         ber->ber_rwptr = ber->ber_buf + i;
593                 }
594         }
595
596         if ((ber->ber_rwptr>=ber->ber_buf) && (ber->ber_rwptr<ber->ber_end)) {
597                 ber_slen_t res;
598                 ber_slen_t to_go;
599                 
600                 to_go = ber->ber_end - ber->ber_rwptr;
601                 assert( to_go > 0 );
602                 
603                 res = ber_int_sb_read( sb, ber->ber_rwptr, to_go );
604                 if (res<=0)
605                         return LBER_DEFAULT;
606                 ber->ber_rwptr+=res;
607                 
608                 if (res<to_go) {
609 #if defined( EWOULDBLOCK )
610                         errno = EWOULDBLOCK;
611 #elif defined( EAGAIN )
612                         errno = EAGAIN;
613 #endif                  
614                         return LBER_DEFAULT;
615                 }
616 done:
617                 ber->ber_rwptr = NULL;
618                 *len = ber->ber_len;
619                 if ( ber->ber_debug ) {
620 #ifdef NEW_LOGGING
621                         LDAP_LOG(( "liblber", LDAP_LEVEL_DETAIL1,
622                                 "ber_get_next: tag 0x%lx len %ld\n",
623                                 ber->ber_tag, ber->ber_len ));
624                         BER_DUMP(( "liblber", LDAP_LEVEL_DETAIL2, ber, 1 ));
625 #else
626                         ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug,
627                                 "ber_get_next: tag 0x%lx len %ld contents:\n",
628                                 ber->ber_tag, ber->ber_len );
629                         ber_log_dump( LDAP_DEBUG_BER, ber->ber_debug, ber, 1 );
630 #endif
631                 }
632                 return (ber->ber_tag);
633         }
634
635         assert( 0 ); /* ber structure is messed up ?*/
636         return LBER_DEFAULT;
637 }