]> git.sur5r.net Git - openldap/blob - libraries/liblber/io.c
Add LBER_ and LDAP_ memory allocators/deallocators for internal
[openldap] / libraries / liblber / io.c
1 /* io.c - ber general i/o 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/ctype.h>
24 #include <ac/errno.h>
25 #include <ac/socket.h>
26 #include <ac/string.h>
27 #include <ac/unistd.h>
28
29 #ifdef HAVE_IO_H
30 #include <io.h>
31 #endif
32
33 #include "lber-int.h"
34
35 static long BerRead LDAP_P(( Sockbuf *sb, char *buf, long len ));
36 static int ber_realloc LDAP_P(( BerElement *ber, unsigned long len ));
37
38 #define EXBUFSIZ        1024
39
40 /* probably far too large... */
41 #define MAX_BERBUFSIZE  (128*1024)
42
43 #if defined( DOS ) && !defined( _WIN32 ) && (MAX_BERBUFSIZE > 65535)
44 # undef MAX_BERBUFSIZE
45 # define MAX_BERBUFSIZE 65535
46 #endif
47
48 static long
49 BerRead( Sockbuf *sb, char *buf, long len )
50 {
51         int     c;
52         long    nread = 0;
53
54         assert( sb != NULL );
55         assert( buf != NULL );
56
57         assert( SOCKBUF_VALID( sb ) );
58
59         while ( len > 0 ) {
60                 if ( (c = ber_pvt_sb_read( sb, buf, len )) <= 0 ) {
61                         if ( nread > 0 )
62                                 break;
63                         return( c );
64                 }
65                 buf+= c;
66                 nread+=c;
67                 len-=c;
68         }
69
70         return( nread );
71 }
72
73 long
74 ber_read( BerElement *ber, char *buf, unsigned long len )
75 {
76         unsigned long   actuallen, nleft;
77
78         assert( ber != NULL );
79         assert( buf != NULL );
80
81         assert( BER_VALID( ber ) );
82
83         nleft = ber->ber_end - ber->ber_ptr;
84         actuallen = nleft < len ? nleft : len;
85
86         SAFEMEMCPY( buf, ber->ber_ptr, (size_t)actuallen );
87
88         ber->ber_ptr += actuallen;
89
90         return( (long)actuallen );
91 }
92
93 long
94 ber_write(
95         BerElement *ber,
96         LDAP_CONST char *buf,
97         unsigned long len,
98         int nosos )
99 {
100         assert( ber != NULL );
101         assert( buf != NULL );
102
103         assert( BER_VALID( ber ) );
104
105         if ( nosos || ber->ber_sos == NULL ) {
106                 if ( ber->ber_ptr + len > ber->ber_end ) {
107                         if ( ber_realloc( ber, len ) != 0 )
108                                 return( -1 );
109                 }
110                 SAFEMEMCPY( ber->ber_ptr, buf, (size_t)len );
111                 ber->ber_ptr += len;
112                 return( len );
113         } else {
114                 if ( ber->ber_sos->sos_ptr + len > ber->ber_end ) {
115                         if ( ber_realloc( ber, len ) != 0 )
116                                 return( -1 );
117                 }
118                 SAFEMEMCPY( ber->ber_sos->sos_ptr, buf, (size_t)len );
119                 ber->ber_sos->sos_ptr += len;
120                 ber->ber_sos->sos_clen += len;
121                 return( len );
122         }
123 }
124
125 static int
126 ber_realloc( BerElement *ber, unsigned long len )
127 {
128         unsigned long   need, have, total;
129         Seqorset        *s;
130         long            off;
131         char            *oldbuf;
132
133         assert( ber != NULL );
134         assert( len > 0 );
135
136         assert( BER_VALID( ber ) );
137
138         have = (ber->ber_end - ber->ber_buf) / EXBUFSIZ;
139         need = (len < EXBUFSIZ ? 1 : (len + (EXBUFSIZ - 1)) / EXBUFSIZ);
140         total = have * EXBUFSIZ + need * EXBUFSIZ;
141
142         oldbuf = ber->ber_buf;
143
144         ber->ber_buf = (char *) LBER_REALLOC( ber->ber_buf, total );
145         
146         if ( ber->ber_buf == NULL ) {
147                 ber->ber_buf = oldbuf;
148                 return( -1 );
149         }
150
151         ber->ber_end = ber->ber_buf + total;
152
153         /*
154          * If the stinking thing was moved, we need to go through and
155          * reset all the sos and ber pointers.  Offsets would've been
156          * a better idea... oh well.
157          */
158
159         if ( ber->ber_buf != oldbuf ) {
160                 ber->ber_ptr = ber->ber_buf + (ber->ber_ptr - oldbuf);
161
162                 for ( s = ber->ber_sos; s != NULLSEQORSET; s = s->sos_next ) {
163                         off = s->sos_first - oldbuf;
164                         s->sos_first = ber->ber_buf + off;
165
166                         off = s->sos_ptr - oldbuf;
167                         s->sos_ptr = ber->ber_buf + off;
168                 }
169         }
170
171         return( 0 );
172 }
173
174 void
175 ber_free( BerElement *ber, int freebuf )
176 {
177         assert( ber != NULL );
178         assert( BER_VALID( ber ) );
179
180         if ( freebuf && ber->ber_buf != NULL )
181                 LBER_FREE( ber->ber_buf );
182
183         ber->ber_buf = NULL;
184         ber->ber_valid = LBER_UNINITIALIZED;
185
186         LBER_FREE( (char *) ber );
187 }
188
189 int
190 ber_flush( Sockbuf *sb, BerElement *ber, int freeit )
191 {
192         long    nwritten, towrite, rc;  
193
194         assert( sb != NULL );
195         assert( ber != NULL );
196
197         assert( SOCKBUF_VALID( ber ) );
198         assert( BER_VALID( ber ) );
199
200         if ( ber->ber_rwptr == NULL ) {
201                 ber->ber_rwptr = ber->ber_buf;
202         }
203         towrite = ber->ber_ptr - ber->ber_rwptr;
204
205         if ( sb->sb_debug ) {
206                 ber_log_printf( LDAP_DEBUG_ANY, sb->sb_debug,
207                         "ber_flush: %ld bytes to sd %ld%s\n", towrite,
208                     (long) sb->sb_sd, ber->ber_rwptr != ber->ber_buf ? " (re-flush)"
209                     : "" );
210                 ber_log_bprint( LDAP_DEBUG_PACKETS, sb->sb_debug,
211                         ber->ber_rwptr, towrite );
212         }
213
214 #if !defined(MACOS) && !defined(DOS)
215         if ( sb->sb_options & (LBER_TO_FILE | LBER_TO_FILE_ONLY) ) {
216                 rc = write( sb->sb_fd, ber->ber_rwptr, towrite );
217                 if ( sb->sb_options & LBER_TO_FILE_ONLY ) {
218                         return( (int)rc );
219                 }
220         }
221 #endif
222         
223         nwritten = 0;
224         do {
225                 rc = ber_pvt_sb_write( sb, ber->ber_rwptr, towrite );
226                 if (rc<=0) {
227                         return -1;
228                 }
229                 towrite -= rc;
230                 nwritten += rc;
231                 ber->ber_rwptr += rc;
232         } while ( towrite > 0 );
233
234         if ( freeit )
235                 ber_free( ber, 1 );
236
237         return( 0 );
238 }
239
240 BerElement *
241 ber_alloc_t( int options )
242 {
243         BerElement      *ber;
244
245         ber = (BerElement *) LBER_CALLOC( 1, sizeof(BerElement) );
246
247         if ( ber == NULLBER )
248                 return( NULLBER );
249
250         ber->ber_valid = LBER_VALID_BERELEMENT;
251         ber->ber_tag = LBER_DEFAULT;
252         ber->ber_options = options;
253         ber->ber_debug = ber_int_debug;
254
255         assert( BER_VALID( ber ) );
256         return( ber );
257 }
258
259 BerElement *
260 ber_alloc( void )       /* deprecated */
261 {
262         return( ber_alloc_t( 0 ) );
263 }
264
265 BerElement *
266 der_alloc( void )       /* deprecated */
267 {
268         return( ber_alloc_t( LBER_USE_DER ) );
269 }
270
271 BerElement *
272 ber_dup( LDAP_CONST BerElement *ber )
273 {
274         BerElement      *new;
275
276         assert( ber != NULL );
277         assert( BER_VALID( ber ) );
278
279         if ( (new = ber_alloc_t( ber->ber_options )) == NULL ) {
280                 return( NULL );
281         }
282
283         *new = *ber;
284
285         assert( BER_VALID( new ) );
286         return( new );
287 }
288
289
290 /* OLD U-Mich ber_init() */
291 void
292 ber_init_w_nullc( BerElement *ber, int options )
293 {
294         assert( ber != NULL );
295
296         (void) memset( (char *)ber, '\0', sizeof( BerElement ));
297         ber->ber_valid = LBER_VALID_BERELEMENT;
298         ber->ber_tag = LBER_DEFAULT;
299         ber->ber_options = (char) options;
300         ber->ber_debug = ber_int_debug;
301
302         assert( BER_VALID( ber ) );
303 }
304
305 /* New C-API ber_init() */
306 /* This function constructs a BerElement containing a copy
307 ** of the data in the bv argument.
308 */
309 BerElement *
310 ber_init( struct berval *bv )
311 {
312         BerElement *ber;
313
314         assert( bv != NULL );
315
316         if ( bv == NULL ) {
317                 return NULL;
318         }
319
320         ber = ber_alloc_t( 0 );
321
322         if( ber == NULLBER ) {
323                 /* allocation failed */
324                 return ( NULL );
325         }
326
327         /* copy the data */
328         if ( ( (unsigned int) ber_write ( ber, bv->bv_val, bv->bv_len, 0 )) != bv->bv_len ) {
329                 /* write failed, so free and return NULL */
330                 ber_free( ber, 1 );
331                 return( NULL );
332         }
333
334         ber_reset( ber, 1 );    /* reset the pointer to the start of the buffer */
335
336         return ( ber );
337 }
338
339 /* New C-API ber_flatten routine */
340 /* This routine allocates a struct berval whose contents are a BER
341 ** encoding taken from the ber argument.  The bvPtr pointer pointers to
342 ** the returned berval.
343 */
344 int ber_flatten(
345         LDAP_CONST BerElement *ber,
346         struct berval **bvPtr)
347 {
348         struct berval *bv;
349  
350         assert( bvPtr != NULL );
351
352         if(bvPtr == NULL) {
353                 return( -1 );
354         }
355
356         if ( (bv = LBER_MALLOC( sizeof(struct berval))) == NULL ) {
357                 return( -1 );
358         }
359
360         if ( ber == NULL ) {
361                 /* ber is null, create an empty berval */
362                 bv->bv_val = NULL;
363                 bv->bv_len = 0;
364
365         } else {
366                 /* copy the berval */
367                 ptrdiff_t len = ber->ber_ptr - ber->ber_buf;
368
369                 if ( (bv->bv_val = (char *) LBER_MALLOC( len + 1 )) == NULL ) {
370                         ber_bvfree( bv );
371                         return( -1 );
372                 }
373
374                 SAFEMEMCPY( bv->bv_val, ber->ber_buf, (size_t)len );
375                 bv->bv_val[len] = '\0';
376                 bv->bv_len = len;
377         }
378     
379         *bvPtr = bv;
380         return( 0 );
381 }
382
383 void
384 ber_reset( BerElement *ber, int was_writing )
385 {
386         assert( ber != NULL );
387         assert( BER_VALID( ber ) );
388
389         if ( was_writing ) {
390                 ber->ber_end = ber->ber_ptr;
391                 ber->ber_ptr = ber->ber_buf;
392         } else {
393                 ber->ber_ptr = ber->ber_end;
394         }
395
396         ber->ber_rwptr = NULL;
397 }
398
399 #if 0
400 /* return the tag - LBER_DEFAULT returned means trouble */
401 static unsigned long
402 get_tag( Sockbuf *sb )
403 {
404         unsigned char   xbyte;
405         unsigned long   tag;
406         char            *tagp;
407         unsigned int    i;
408
409         assert( sb != NULL );
410         assert( SOCKBUF_VALID( sb ) );
411
412         if ( ber_pvt_sb_read( sb, (char *) &xbyte, 1 ) != 1 )
413                 return( LBER_DEFAULT );
414
415         if ( (xbyte & LBER_BIG_TAG_MASK) != LBER_BIG_TAG_MASK )
416                 return( (unsigned long) xbyte );
417
418         tagp = (char *) &tag;
419         tagp[0] = xbyte;
420         for ( i = 1; i < sizeof(long); i++ ) {
421                 if ( ber_pvt_sb_read( sb, (char *) &xbyte, 1 ) != 1 )
422                         return( LBER_DEFAULT );
423
424                 tagp[i] = xbyte;
425
426                 if ( ! (xbyte & LBER_MORE_TAG_MASK) )
427                         break;
428         }
429
430         /* tag too big! */
431         if ( i == sizeof(long) )
432                 return( LBER_DEFAULT );
433
434         /* want leading, not trailing 0's */
435         return( tag >> (sizeof(long) - i - 1) );
436 }
437 #endif
438
439 /*
440  * A rewrite of ber_get_next that can safely be called multiple times 
441  * for the same packet. It will simply continue were it stopped until
442  * a full packet is read.
443  */
444
445 unsigned long
446 ber_get_next( Sockbuf *sb, unsigned long *len, BerElement *ber )
447 {
448         assert( sb != NULL );
449         assert( len != NULL );
450         assert( ber != NULL );
451
452         assert( SOCKBUF_VALID( sb ) );
453         assert( BER_VALID( ber ) );
454
455         ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug,
456                 "ber_get_next\n" );
457
458         /*
459          * Any ber element looks like this: tag length contents.
460          * Assuming everything's ok, we return the tag byte (we
461          * can assume a single byte), return the length in len,
462          * and the rest of the undecoded element in buf.
463          *
464          * Assumptions:
465          *      1) small tags (less than 128)
466          *      2) definite lengths
467          *      3) primitive encodings used whenever possible
468          */
469         
470         if (ber->ber_rwptr == NULL) {
471                 /* XXYYZ
472                  * dtest does like this assert.
473                  */
474                 /* assert( ber->ber_buf == NULL ); */
475                 ber->ber_rwptr = (char *) &ber->ber_tag;
476                 ber->ber_tag = 0;
477         }
478
479 #define PTR_IN_VAR( ptr, var )\
480 (((ptr)>=(char *) &(var)) && ((ptr)< (char *) &(var)+sizeof(var)))
481         
482         if (PTR_IN_VAR(ber->ber_rwptr, ber->ber_tag)) {
483                 if (ber->ber_rwptr == (char *) &ber->ber_tag) {
484                         if (ber_pvt_sb_read( sb, ber->ber_rwptr, 1)<=0)
485                                 return LBER_DEFAULT;
486                         if ((ber->ber_rwptr[0] & LBER_BIG_TAG_MASK)
487                                 != LBER_BIG_TAG_MASK) {
488                                 ber->ber_tag = ber->ber_rwptr[0];
489                                 ber->ber_rwptr = (char *) &ber->ber_usertag;
490                                 goto get_lenbyte;
491                         }
492                         ber->ber_rwptr++;
493                 }
494                 do {
495                         /* reading the tag... */
496                         if (ber_pvt_sb_read( sb, ber->ber_rwptr, 1)<=0)
497                                 return LBER_DEFAULT;
498                         if (! (ber->ber_rwptr[0] & LBER_MORE_TAG_MASK) ) {
499                                 ber->ber_tag>>=sizeof(ber->ber_tag) -
500                                   ((char *) &ber->ber_tag - ber->ber_rwptr);
501                                 ber->ber_rwptr = (char *) &ber->ber_usertag;
502                                 goto get_lenbyte;
503                         }
504                 } while (PTR_IN_VAR(ber->ber_rwptr,ber->ber_tag));
505                 errno = ERANGE; /* this is a serious error. */
506                 return LBER_DEFAULT;
507         }
508 get_lenbyte:
509         if (ber->ber_rwptr==(char *) &ber->ber_usertag) {
510                 unsigned char c;
511                 if (ber_pvt_sb_read( sb, (char *) &c, 1)<=0)
512                         return LBER_DEFAULT;
513                 if (c & 0x80U) {
514                         int len = c & 0x7fU;
515                         if ( (len==0) || ((unsigned) len>sizeof( ber->ber_len ) ) ) {
516                                 errno = ERANGE;
517                                 return LBER_DEFAULT;
518                         }
519                         ber->ber_rwptr = (char *) &ber->ber_len +
520                                 sizeof(ber->ber_len) - len;
521                         ber->ber_len = 0;
522                 } else {
523                         ber->ber_len = c;
524                         goto fill_buffer;
525                 }
526         }
527         if (PTR_IN_VAR(ber->ber_rwptr, ber->ber_len)) {
528                 int res;
529                 int to_go;
530                 to_go = (char *) &ber->ber_len + sizeof( ber->ber_len ) -
531                         ber->ber_rwptr;
532                 assert( to_go > 0 );
533                 res = ber_pvt_sb_read( sb, ber->ber_rwptr, to_go );
534                 if (res <=0)
535                         return LBER_DEFAULT;
536                 ber->ber_rwptr += res;
537                 if (res==to_go) {
538                         /* convert length. */
539                         ber->ber_len = AC_NTOHL( ber->ber_len );
540                         goto fill_buffer;
541                 } else {
542 #if defined( EWOULDBLOCK )
543                         errno = EWOULDBLOCK;
544 #elif defined( EAGAIN )
545                         errno = EAGAIN;
546 #endif                  
547                         return LBER_DEFAULT;
548                 }
549         }
550 fill_buffer:    
551         /* now fill the buffer. */
552         if (ber->ber_buf==NULL) {
553                 if (ber->ber_len > MAX_BERBUFSIZE) {
554                         errno = ERANGE;
555                         return LBER_DEFAULT;
556                 }
557                 ber->ber_buf = (char *) LBER_MALLOC( ber->ber_len );
558                 if (ber->ber_buf==NULL)
559                         return LBER_DEFAULT;
560                 ber->ber_rwptr = ber->ber_buf;
561                 ber->ber_ptr = ber->ber_buf;
562                 ber->ber_end = ber->ber_buf + ber->ber_len;
563         }
564         if ((ber->ber_rwptr>=ber->ber_buf) && (ber->ber_rwptr<ber->ber_end)) {
565                 int res;
566                 int to_go;
567                 
568                 to_go = ber->ber_end - ber->ber_rwptr;
569                 assert( to_go > 0 );
570                 
571                 res = ber_pvt_sb_read( sb, ber->ber_rwptr, to_go );
572                 if (res<=0)
573                         return LBER_DEFAULT;
574                 ber->ber_rwptr+=res;
575                 
576                 if (res<to_go) {
577 #if defined( EWOULDBLOCK )
578                         errno = EWOULDBLOCK;
579 #elif defined( EAGAIN )
580                         errno = EAGAIN;
581 #endif                  
582                         return LBER_DEFAULT;
583                 }
584                 
585                 ber->ber_rwptr = NULL;
586                 *len = ber->ber_len;
587                 if ( ber->ber_debug ) {
588                         ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug,
589                                 "ber_get_next: tag 0x%lx len %ld contents:\n",
590                                 ber->ber_tag, ber->ber_len );
591                         ber_log_dump( LDAP_DEBUG_BER, ber->ber_debug, ber, 1 );
592                 }
593                 return (ber->ber_tag);
594         }
595         assert( 0 ); /* ber structure is messed up ?*/
596         return LBER_DEFAULT;
597 }