]> git.sur5r.net Git - openldap/blob - libraries/liblber/io.c
4a687de4830faf0392f5149b7f0fd70acd4a1299
[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 /* OLD U-Mich ber_init() */
296 void
297 ber_init_w_nullc( BerElement *ber, int options )
298 {
299         assert( ber != NULL );
300
301     ber_int_options.lbo_valid = LBER_INITIALIZED;
302
303         (void) memset( (char *)ber, '\0', sizeof( BerElement ));
304         ber->ber_valid = LBER_VALID_BERELEMENT;
305         ber->ber_tag = LBER_DEFAULT;
306         ber->ber_options = (char) options;
307         ber->ber_debug = ber_int_debug;
308
309         assert( LBER_VALID( ber ) );
310 }
311
312 /* New C-API ber_init() */
313 /* This function constructs a BerElement containing a copy
314 ** of the data in the bv argument.
315 */
316 BerElement *
317 ber_init( struct berval *bv )
318 {
319         BerElement *ber;
320
321         assert( bv != NULL );
322
323     ber_int_options.lbo_valid = LBER_INITIALIZED;
324
325         if ( bv == NULL ) {
326                 return NULL;
327         }
328
329         ber = ber_alloc_t( 0 );
330
331         if( ber == NULL ) {
332                 /* allocation failed */
333                 return NULL;
334         }
335
336         /* copy the data */
337         if ( ((ber_len_t) ber_write ( ber, bv->bv_val, bv->bv_len, 0 ))
338                 != bv->bv_len )
339         {
340                 /* write failed, so free and return NULL */
341                 ber_free( ber, 1 );
342                 return NULL;
343         }
344
345         ber_reset( ber, 1 );    /* reset the pointer to the start of the buffer */
346         return ber;
347 }
348
349 /* New C-API ber_flatten routine */
350 /* This routine allocates a struct berval whose contents are a BER
351 ** encoding taken from the ber argument.  The bvPtr pointer pointers to
352 ** the returned berval.
353 */
354 int ber_flatten(
355         BerElement *ber,
356         struct berval **bvPtr)
357 {
358         struct berval *bv;
359  
360         assert( bvPtr != NULL );
361
362     ber_int_options.lbo_valid = LBER_INITIALIZED;
363
364         if(bvPtr == NULL) {
365                 return -1;
366         }
367
368         bv = LBER_MALLOC( sizeof(struct berval) );
369         if ( bv == NULL ) {
370                 return -1;
371         }
372
373         if ( ber == NULL ) {
374                 /* ber is null, create an empty berval */
375                 bv->bv_val = NULL;
376                 bv->bv_len = 0;
377
378         } else {
379                 /* copy the berval */
380                 ber_len_t len = ber_pvt_ber_write( ber );
381
382                 bv->bv_val = (char *) LBER_MALLOC( len + 1 );
383                 if ( bv->bv_val == NULL ) {
384                         LBER_FREE( bv );
385                         return -1;
386                 }
387
388                 AC_MEMCPY( bv->bv_val, ber->ber_buf, len );
389                 bv->bv_val[len] = '\0';
390                 bv->bv_len = len;
391         }
392     
393         *bvPtr = bv;
394         return 0;
395 }
396
397 void
398 ber_reset( BerElement *ber, int was_writing )
399 {
400         assert( ber != NULL );
401         assert( LBER_VALID( ber ) );
402
403         if ( was_writing ) {
404                 ber->ber_end = ber->ber_ptr;
405                 ber->ber_ptr = ber->ber_buf;
406
407         } else {
408                 ber->ber_ptr = ber->ber_end;
409         }
410
411         ber->ber_rwptr = NULL;
412 }
413
414 /*
415  * A rewrite of ber_get_next that can safely be called multiple times 
416  * for the same packet. It will simply continue where it stopped until
417  * a full packet is read.
418  */
419
420 ber_tag_t
421 ber_get_next(
422         Sockbuf *sb,
423         ber_len_t *len,
424         BerElement *ber )
425 {
426         assert( sb != NULL );
427         assert( len != NULL );
428         assert( ber != NULL );
429
430         assert( SOCKBUF_VALID( sb ) );
431         assert( LBER_VALID( ber ) );
432
433 #ifdef NEW_LOGGING
434         LDAP_LOG(( "liblber", LDAP_LEVEL_ENTRY, "ber_get_next: enter\n" ));
435 #else
436         ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug,
437                 "ber_get_next\n" );
438 #endif
439
440         /*
441          * Any ber element looks like this: tag length contents.
442          * Assuming everything's ok, we return the tag byte (we
443          * can assume a single byte), return the length in len,
444          * and the rest of the undecoded element in buf.
445          *
446          * Assumptions:
447          *      1) small tags (less than 128)
448          *      2) definite lengths
449          *      3) primitive encodings used whenever possible
450          */
451
452         if (ber->ber_rwptr == NULL) {
453                 /* XXYYZ
454                  * dtest does like this assert.
455                  */
456                 /* assert( ber->ber_buf == NULL ); */
457                 ber->ber_rwptr = (char *) &ber->ber_len-1;
458                 ber->ber_ptr = ber->ber_rwptr;
459                 ber->ber_tag = 0;
460         }
461
462         while (ber->ber_rwptr > (char *)&ber->ber_tag && ber->ber_rwptr <
463                 (char *)(&ber->ber_usertag + 1)) {
464                 ber_slen_t i;
465                 char buf[sizeof(ber->ber_len)-1];
466                 ber_len_t tlen = 0;
467
468                 if ((i=ber_int_sb_read( sb, ber->ber_rwptr,
469                         (char *)(&ber->ber_usertag+1)-ber->ber_rwptr))<=0) {
470                         return LBER_DEFAULT;
471                 }
472
473                 ber->ber_rwptr += i;
474
475                 /* We got at least one byte, try to parse the tag. */
476                 if (ber->ber_ptr == (char *)&ber->ber_len-1) {
477                         ber_tag_t tag;
478                         unsigned char *p = (unsigned char *)ber->ber_ptr;
479                         tag = *p++;
480                         if ((tag & LBER_BIG_TAG_MASK) == LBER_BIG_TAG_MASK) {
481                                 for (i=1; (char *)p<ber->ber_rwptr; i++,p++) {
482                                         tag <<= 8;
483                                         tag |= *p;
484                                         if (!(*p & LBER_MORE_TAG_MASK))
485                                                 break;
486                                         /* Is the tag too big? */
487                                         if (i == sizeof(ber_tag_t)-1) {
488                                                 errno = ERANGE;
489                                                 return LBER_DEFAULT;
490                                         }
491                                 }
492                                 /* Did we run out of bytes? */
493                                 if ((char *)p == ber->ber_rwptr) {
494                                         return LBER_DEFAULT;
495                                 }
496                                 p++;
497                         }
498                         ber->ber_tag = tag;
499                         ber->ber_ptr = (char *)p;
500                 }
501
502                 /* Now look for the length */
503                 if (*ber->ber_ptr & 0x80) {     /* multi-byte */
504                         int llen = *(unsigned char *)ber->ber_ptr++ & 0x7f;
505                         if (llen > sizeof(ber_len_t)) {
506                                 errno = ERANGE;
507                                 return LBER_DEFAULT;
508                         }
509                         /* Not enough bytes? */
510                         if (ber->ber_rwptr - ber->ber_ptr < llen) {
511                                 return LBER_DEFAULT;
512                         }
513                         for (i=0; i<llen && ber->ber_ptr<ber->ber_rwptr; i++,ber->ber_ptr++) {
514                                 tlen <<=8;
515                                 tlen |= *(unsigned char *)ber->ber_ptr;
516                         }
517                 } else {
518                         tlen = *(unsigned char *)ber->ber_ptr++;
519                 }
520                 /* Are there leftover data bytes inside ber->ber_len? */
521                 if (ber->ber_ptr < (char *)&ber->ber_usertag) {
522                         i = (char *)&ber->ber_usertag - ber->ber_ptr;
523                         AC_MEMCPY(buf, ber->ber_ptr, i);
524                         ber->ber_ptr += i;
525                 } else {
526                         i = 0;
527                 }
528                 ber->ber_len = tlen;
529
530                 /* now fill the buffer. */
531
532                 /* make sure length is reasonable */
533                 if ( ber->ber_len == 0 ) {
534                         errno = ERANGE;
535                         return LBER_DEFAULT;
536                 } else if ( sb->sb_max_incoming && ber->ber_len > sb->sb_max_incoming ) {
537 #ifdef NEW_LOGGING
538                         LDAP_LOG(( "liblber", LDAP_LEVEL_ERR, 
539                                 "ber_get_next: sockbuf_max_incoming limit hit "
540                                 "(%d > %d)\n", ber->ber_len, sb->sb_max_incoming ));
541 #else
542                         ber_log_printf( LDAP_DEBUG_CONNS, ber->ber_debug,
543                                 "ber_get_next: sockbuf_max_incoming limit hit "
544                                 "(%ld > %ld)\n", ber->ber_len, sb->sb_max_incoming );
545 #endif
546                         errno = ERANGE;
547                         return LBER_DEFAULT;
548                 }
549
550                 if (ber->ber_buf==NULL) {
551                         ber_len_t l = ber->ber_rwptr - ber->ber_ptr;
552                         /* ber->ber_ptr is always <= ber->ber->ber_rwptr.
553                          * make sure ber->ber_len agrees with what we've
554                          * already read.
555                          */
556                         if ( ber->ber_len < i + l ) {
557                                 errno = ERANGE;
558                                 return LBER_DEFAULT;
559                         }
560                         ber->ber_buf = (char *) LBER_MALLOC( ber->ber_len + 1 );
561                         if (ber->ber_buf==NULL) {
562                                 return LBER_DEFAULT;
563                         }
564                         ber->ber_end = ber->ber_buf + ber->ber_len;
565                         if (i) {
566                                 AC_MEMCPY(ber->ber_buf, buf, i);
567                         }
568                         if (l > 0) {
569                                 AC_MEMCPY(ber->ber_buf + i, ber->ber_ptr, l);
570                                 i += l;
571                         }
572                         ber->ber_ptr = ber->ber_buf;
573                         ber->ber_usertag = 0;
574                         if ((ber_len_t)i == ber->ber_len) {
575                                 goto done;
576                         }
577                         ber->ber_rwptr = ber->ber_buf + i;
578                 }
579         }
580
581         if ((ber->ber_rwptr>=ber->ber_buf) && (ber->ber_rwptr<ber->ber_end)) {
582                 ber_slen_t res;
583                 ber_slen_t to_go;
584                 
585                 to_go = ber->ber_end - ber->ber_rwptr;
586                 assert( to_go > 0 );
587                 
588                 res = ber_int_sb_read( sb, ber->ber_rwptr, to_go );
589                 if (res<=0)
590                         return LBER_DEFAULT;
591                 ber->ber_rwptr+=res;
592                 
593                 if (res<to_go) {
594 #if defined( EWOULDBLOCK )
595                         errno = EWOULDBLOCK;
596 #elif defined( EAGAIN )
597                         errno = EAGAIN;
598 #endif                  
599                         return LBER_DEFAULT;
600                 }
601 done:
602                 ber->ber_rwptr = NULL;
603                 *len = ber->ber_len;
604                 if ( ber->ber_debug ) {
605 #ifdef NEW_LOGGING
606                         LDAP_LOG(( "liblber", LDAP_LEVEL_DETAIL1,
607                                 "ber_get_next: tag 0x%lx len %ld\n",
608                                 ber->ber_tag, ber->ber_len ));
609                         BER_DUMP(( "liblber", LDAP_LEVEL_DETAIL2, ber, 1 ));
610 #else
611                         ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug,
612                                 "ber_get_next: tag 0x%lx len %ld contents:\n",
613                                 ber->ber_tag, ber->ber_len );
614                         ber_log_dump( LDAP_DEBUG_BER, ber->ber_debug, ber, 1 );
615 #endif
616                 }
617                 return (ber->ber_tag);
618         }
619
620         assert( 0 ); /* ber structure is messed up ?*/
621         return LBER_DEFAULT;
622 }