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