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