]> git.sur5r.net Git - openldap/blob - libraries/liblber/io.c
b57714765f749fb327201c3326e4be3b3692ffb3
[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_int_options.lbo_valid = LBER_INITIALIZED;
246
247         ber = (BerElement *) LBER_CALLOC( 1, sizeof(BerElement) );
248
249         if ( ber == NULLBER )
250                 return( NULLBER );
251
252         ber->ber_valid = LBER_VALID_BERELEMENT;
253         ber->ber_tag = LBER_DEFAULT;
254         ber->ber_options = options;
255         ber->ber_debug = ber_int_debug;
256
257         assert( BER_VALID( ber ) );
258         return( ber );
259 }
260
261 BerElement *
262 ber_alloc( void )       /* deprecated */
263 {
264         return( ber_alloc_t( 0 ) );
265 }
266
267 BerElement *
268 der_alloc( void )       /* deprecated */
269 {
270         return( ber_alloc_t( LBER_USE_DER ) );
271 }
272
273 BerElement *
274 ber_dup( LDAP_CONST BerElement *ber )
275 {
276         BerElement      *new;
277
278         assert( ber != NULL );
279         assert( BER_VALID( ber ) );
280
281         if ( (new = ber_alloc_t( ber->ber_options )) == NULL ) {
282                 return( NULL );
283         }
284
285         *new = *ber;
286
287         assert( BER_VALID( new ) );
288         return( new );
289 }
290
291
292 /* OLD U-Mich ber_init() */
293 void
294 ber_init_w_nullc( BerElement *ber, int options )
295 {
296         assert( ber != NULL );
297
298     ber_int_options.lbo_valid = LBER_INITIALIZED;
299
300         (void) memset( (char *)ber, '\0', sizeof( BerElement ));
301         ber->ber_valid = LBER_VALID_BERELEMENT;
302         ber->ber_tag = LBER_DEFAULT;
303         ber->ber_options = (char) options;
304         ber->ber_debug = ber_int_debug;
305
306         assert( BER_VALID( ber ) );
307 }
308
309 /* New C-API ber_init() */
310 /* This function constructs a BerElement containing a copy
311 ** of the data in the bv argument.
312 */
313 BerElement *
314 ber_init( struct berval *bv )
315 {
316         BerElement *ber;
317
318         assert( bv != NULL );
319
320     ber_int_options.lbo_valid = LBER_INITIALIZED;
321
322         if ( bv == NULL ) {
323                 return NULL;
324         }
325
326         ber = ber_alloc_t( 0 );
327
328         if( ber == NULLBER ) {
329                 /* allocation failed */
330                 return ( NULL );
331         }
332
333         /* copy the data */
334         if ( ( (unsigned int) ber_write ( ber, bv->bv_val, bv->bv_len, 0 )) != bv->bv_len ) {
335                 /* write failed, so free and return NULL */
336                 ber_free( ber, 1 );
337                 return( NULL );
338         }
339
340         ber_reset( ber, 1 );    /* reset the pointer to the start of the buffer */
341
342         return ( ber );
343 }
344
345 /* New C-API ber_flatten routine */
346 /* This routine allocates a struct berval whose contents are a BER
347 ** encoding taken from the ber argument.  The bvPtr pointer pointers to
348 ** the returned berval.
349 */
350 int ber_flatten(
351         LDAP_CONST BerElement *ber,
352         struct berval **bvPtr)
353 {
354         struct berval *bv;
355  
356         assert( bvPtr != NULL );
357
358     ber_int_options.lbo_valid = LBER_INITIALIZED;
359
360         if(bvPtr == NULL) {
361                 return( -1 );
362         }
363
364         if ( (bv = LBER_MALLOC( sizeof(struct berval))) == NULL ) {
365                 return( -1 );
366         }
367
368         if ( ber == NULL ) {
369                 /* ber is null, create an empty berval */
370                 bv->bv_val = NULL;
371                 bv->bv_len = 0;
372
373         } else {
374                 /* copy the berval */
375                 ptrdiff_t len = ber->ber_ptr - ber->ber_buf;
376
377                 if ( (bv->bv_val = (char *) LBER_MALLOC( len + 1 )) == NULL ) {
378                         ber_bvfree( bv );
379                         return( -1 );
380                 }
381
382                 SAFEMEMCPY( bv->bv_val, ber->ber_buf, (size_t)len );
383                 bv->bv_val[len] = '\0';
384                 bv->bv_len = len;
385         }
386     
387         *bvPtr = bv;
388         return( 0 );
389 }
390
391 void
392 ber_reset( BerElement *ber, int was_writing )
393 {
394         assert( ber != NULL );
395         assert( BER_VALID( ber ) );
396
397         if ( was_writing ) {
398                 ber->ber_end = ber->ber_ptr;
399                 ber->ber_ptr = ber->ber_buf;
400         } else {
401                 ber->ber_ptr = ber->ber_end;
402         }
403
404         ber->ber_rwptr = NULL;
405 }
406
407 #if 0
408 /* return the tag - LBER_DEFAULT returned means trouble */
409 static unsigned long
410 get_tag( Sockbuf *sb )
411 {
412         unsigned char   xbyte;
413         unsigned long   tag;
414         char            *tagp;
415         unsigned int    i;
416
417         assert( sb != NULL );
418         assert( SOCKBUF_VALID( sb ) );
419
420         if ( ber_pvt_sb_read( sb, (char *) &xbyte, 1 ) != 1 )
421                 return( LBER_DEFAULT );
422
423         if ( (xbyte & LBER_BIG_TAG_MASK) != LBER_BIG_TAG_MASK )
424                 return( (unsigned long) xbyte );
425
426         tagp = (char *) &tag;
427         tagp[0] = xbyte;
428         for ( i = 1; i < sizeof(long); i++ ) {
429                 if ( ber_pvt_sb_read( sb, (char *) &xbyte, 1 ) != 1 )
430                         return( LBER_DEFAULT );
431
432                 tagp[i] = xbyte;
433
434                 if ( ! (xbyte & LBER_MORE_TAG_MASK) )
435                         break;
436         }
437
438         /* tag too big! */
439         if ( i == sizeof(long) )
440                 return( LBER_DEFAULT );
441
442         /* want leading, not trailing 0's */
443         return( tag >> (sizeof(long) - i - 1) );
444 }
445 #endif
446
447 /*
448  * A rewrite of ber_get_next that can safely be called multiple times 
449  * for the same packet. It will simply continue were it stopped until
450  * a full packet is read.
451  */
452
453 unsigned long
454 ber_get_next( Sockbuf *sb, unsigned long *len, BerElement *ber )
455 {
456         assert( sb != NULL );
457         assert( len != NULL );
458         assert( ber != NULL );
459
460         assert( SOCKBUF_VALID( sb ) );
461         assert( BER_VALID( ber ) );
462
463         ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug,
464                 "ber_get_next\n" );
465
466         /*
467          * Any ber element looks like this: tag length contents.
468          * Assuming everything's ok, we return the tag byte (we
469          * can assume a single byte), return the length in len,
470          * and the rest of the undecoded element in buf.
471          *
472          * Assumptions:
473          *      1) small tags (less than 128)
474          *      2) definite lengths
475          *      3) primitive encodings used whenever possible
476          */
477         
478         if (ber->ber_rwptr == NULL) {
479                 /* XXYYZ
480                  * dtest does like this assert.
481                  */
482                 /* assert( ber->ber_buf == NULL ); */
483                 ber->ber_rwptr = (char *) &ber->ber_tag;
484                 ber->ber_tag = 0;
485         }
486
487 #define PTR_IN_VAR( ptr, var )\
488 (((ptr)>=(char *) &(var)) && ((ptr)< (char *) &(var)+sizeof(var)))
489         
490         if (PTR_IN_VAR(ber->ber_rwptr, ber->ber_tag)) {
491                 if (ber->ber_rwptr == (char *) &ber->ber_tag) {
492                         if (ber_pvt_sb_read( sb, ber->ber_rwptr, 1)<=0)
493                                 return LBER_DEFAULT;
494                         if ((ber->ber_rwptr[0] & LBER_BIG_TAG_MASK)
495                                 != LBER_BIG_TAG_MASK) {
496                                 ber->ber_tag = ber->ber_rwptr[0];
497                                 ber->ber_rwptr = (char *) &ber->ber_usertag;
498                                 goto get_lenbyte;
499                         }
500                         ber->ber_rwptr++;
501                 }
502                 do {
503                         /* reading the tag... */
504                         if (ber_pvt_sb_read( sb, ber->ber_rwptr, 1)<=0)
505                                 return LBER_DEFAULT;
506                         if (! (ber->ber_rwptr[0] & LBER_MORE_TAG_MASK) ) {
507                                 ber->ber_tag>>=sizeof(ber->ber_tag) -
508                                   ((char *) &ber->ber_tag - ber->ber_rwptr);
509                                 ber->ber_rwptr = (char *) &ber->ber_usertag;
510                                 goto get_lenbyte;
511                         }
512                 } while (PTR_IN_VAR(ber->ber_rwptr,ber->ber_tag));
513                 errno = ERANGE; /* this is a serious error. */
514                 return LBER_DEFAULT;
515         }
516 get_lenbyte:
517         if (ber->ber_rwptr==(char *) &ber->ber_usertag) {
518                 unsigned char c;
519                 if (ber_pvt_sb_read( sb, (char *) &c, 1)<=0)
520                         return LBER_DEFAULT;
521                 if (c & 0x80U) {
522                         int len = c & 0x7fU;
523                         if ( (len==0) || ((unsigned) len>sizeof( ber->ber_len ) ) ) {
524                                 errno = ERANGE;
525                                 return LBER_DEFAULT;
526                         }
527                         ber->ber_rwptr = (char *) &ber->ber_len +
528                                 sizeof(ber->ber_len) - len;
529                         ber->ber_len = 0;
530                 } else {
531                         ber->ber_len = c;
532                         goto fill_buffer;
533                 }
534         }
535         if (PTR_IN_VAR(ber->ber_rwptr, ber->ber_len)) {
536                 int res;
537                 int to_go;
538                 to_go = (char *) &ber->ber_len + sizeof( ber->ber_len ) -
539                         ber->ber_rwptr;
540                 assert( to_go > 0 );
541                 res = ber_pvt_sb_read( sb, ber->ber_rwptr, to_go );
542                 if (res <=0)
543                         return LBER_DEFAULT;
544                 ber->ber_rwptr += res;
545                 if (res==to_go) {
546                         /* convert length. */
547                         ber->ber_len = AC_NTOHL( ber->ber_len );
548                         goto fill_buffer;
549                 } else {
550 #if defined( EWOULDBLOCK )
551                         errno = EWOULDBLOCK;
552 #elif defined( EAGAIN )
553                         errno = EAGAIN;
554 #endif                  
555                         return LBER_DEFAULT;
556                 }
557         }
558 fill_buffer:    
559         /* now fill the buffer. */
560         if (ber->ber_buf==NULL) {
561                 if (ber->ber_len > MAX_BERBUFSIZE) {
562                         errno = ERANGE;
563                         return LBER_DEFAULT;
564                 }
565                 ber->ber_buf = (char *) LBER_MALLOC( ber->ber_len );
566                 if (ber->ber_buf==NULL)
567                         return LBER_DEFAULT;
568                 ber->ber_rwptr = ber->ber_buf;
569                 ber->ber_ptr = ber->ber_buf;
570                 ber->ber_end = ber->ber_buf + ber->ber_len;
571         }
572         if ((ber->ber_rwptr>=ber->ber_buf) && (ber->ber_rwptr<ber->ber_end)) {
573                 int res;
574                 int to_go;
575                 
576                 to_go = ber->ber_end - ber->ber_rwptr;
577                 assert( to_go > 0 );
578                 
579                 res = ber_pvt_sb_read( sb, ber->ber_rwptr, to_go );
580                 if (res<=0)
581                         return LBER_DEFAULT;
582                 ber->ber_rwptr+=res;
583                 
584                 if (res<to_go) {
585 #if defined( EWOULDBLOCK )
586                         errno = EWOULDBLOCK;
587 #elif defined( EAGAIN )
588                         errno = EAGAIN;
589 #endif                  
590                         return LBER_DEFAULT;
591                 }
592                 
593                 ber->ber_rwptr = NULL;
594                 *len = ber->ber_len;
595                 if ( ber->ber_debug ) {
596                         ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug,
597                                 "ber_get_next: tag 0x%lx len %ld contents:\n",
598                                 ber->ber_tag, ber->ber_len );
599                         ber_log_dump( LDAP_DEBUG_BER, ber->ber_debug, ber, 1 );
600                 }
601                 return (ber->ber_tag);
602         }
603         assert( 0 ); /* ber structure is messed up ?*/
604         return LBER_DEFAULT;
605 }