]> git.sur5r.net Git - openldap/blobdiff - libraries/liblber/io.c
Fix ITS#2142
[openldap] / libraries / liblber / io.c
index b54f828cdc88b93b4ffefd2fff060526280cc84e..74abb24fd4d22b1516e1b5dc0b5dfd41af1a75e2 100644 (file)
@@ -1,7 +1,7 @@
 /* io.c - ber general i/o routines */
 /* $OpenLDAP$ */
 /*
- * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
  */
 /* Portions
 #endif
 
 #include "lber-int.h"
-
-static ber_slen_t
-BerRead(
-       Sockbuf *sb,
-       unsigned char *buf,
-       ber_len_t len )
-{
-       ber_slen_t      c;
-       ber_slen_t      nread = 0;
-
-       assert( sb != NULL );
-       assert( buf != NULL );
-
-       assert( SOCKBUF_VALID( sb ) );
-
-       while ( len > 0 ) {
-               if ( (c = ber_int_sb_read( sb, buf, len )) <= 0 ) {
-                       if ( nread > 0 )
-                               break;
-                       return( c );
-               }
-               buf+=c;
-               nread+=c;
-               len-=c;
-       }
-
-       return( nread );
-}
+#include "ldap_log.h"
 
 ber_slen_t
 ber_read(
@@ -132,7 +105,7 @@ ber_realloc( BerElement *ber, ber_len_t len )
 
        total = ber_pvt_ber_total( ber );
 
-#define LBER_EXBUFSIZ  1000 /* a few words less than 2^N for binary buddy */
+#define LBER_EXBUFSIZ  4060 /* a few words less than 2^N for binary buddy */
 #if defined( LBER_EXBUFSIZ ) && LBER_EXBUFSIZ > 0
 # ifndef notdef
        /* don't realloc by small amounts */
@@ -181,31 +154,37 @@ ber_realloc( BerElement *ber, ber_len_t len )
 }
 
 void
-ber_free( BerElement *ber, int freebuf )
+ber_free_buf( BerElement *ber )
 {
-#ifdef LDAP_MEMORY_DEBUG
-       assert( ber != NULL );
-#endif
-
-       if( ber == NULL ) {
-               return;
-       }
+       Seqorset *s, *next;
 
        assert( LBER_VALID( ber ) );
 
-       if ( freebuf ) {
-               Seqorset *s, *next;
-               LBER_FREE( ber->ber_buf );
+       if ( ber->ber_buf) LBER_FREE( ber->ber_buf );
 
-               for( s = ber->ber_sos ; s != NULL ; s = next ) {
-                       next = s->sos_next;
-                       LBER_FREE( s );
-               }
+       for( s = ber->ber_sos ; s != NULL ; s = next ) {
+               next = s->sos_next;
+               LBER_FREE( s );
        }
 
        ber->ber_buf = NULL;
        ber->ber_sos = NULL;
        ber->ber_valid = LBER_UNINITIALIZED;
+}
+
+void
+ber_free( BerElement *ber, int freebuf )
+{
+#ifdef LDAP_MEMORY_DEBUG
+       assert( ber != NULL );
+#endif
+
+       if( ber == NULL ) {
+               return;
+       }
+
+       if( freebuf )
+               ber_free_buf( ber );
 
        LBER_FREE( (char *) ber );
 }
@@ -213,7 +192,7 @@ ber_free( BerElement *ber, int freebuf )
 int
 ber_flush( Sockbuf *sb, BerElement *ber, int freeit )
 {
-       ber_len_t       nwritten, towrite;
+       ber_len_t       towrite;
        ber_slen_t      rc;     
 
        assert( sb != NULL );
@@ -229,11 +208,14 @@ ber_flush( Sockbuf *sb, BerElement *ber, int freeit )
 
        if ( sb->sb_debug ) {
 #ifdef NEW_LOGGING
-               LDAP_LOG(( "liblber", LDAP_LEVEL_DETAIL1,
+               LDAP_LOG( BER, DETAIL1,
                           "ber_flush: %ld bytes to sd %ld%s\n",
                           towrite, (long)sb->sb_fd,
-                          ber->ber_rwptr != ber->ber_buf ? " (re-flush)" : "" ));
-               BER_DUMP(( "liblber", LDAP_LEVEL_DETAIL2, ber, 1 ));
+                          ber->ber_rwptr != ber->ber_buf ? " (re-flush)" : "" );
+
+               if(LDAP_LOGS_TEST(BER, DETAIL2))
+                               BER_DUMP(( "liblber", LDAP_LEVEL_DETAIL2, ber, 1 ));
+
 #else
                ber_log_printf( LDAP_DEBUG_ANY, sb->sb_debug,
                        "ber_flush: %ld bytes to sd %ld%s\n",
@@ -244,16 +226,14 @@ ber_flush( Sockbuf *sb, BerElement *ber, int freeit )
 #endif
        }
 
-       nwritten = 0;
-       do {
+       while ( towrite > 0 ) {
                rc = ber_int_sb_write( sb, ber->ber_rwptr, towrite );
                if (rc<=0) {
                        return -1;
                }
                towrite -= rc;
-               nwritten += rc;
                ber->ber_rwptr += rc;
-       } while ( towrite > 0 );
+       } 
 
        if ( freeit )
                ber_free( ber, 1 );
@@ -314,13 +294,12 @@ ber_dup( BerElement *ber )
 }
 
 
-/* OLD U-Mich ber_init() */
 void
-ber_init_w_nullc( BerElement *ber, int options )
+ber_init2( BerElement *ber, struct berval *bv, int options )
 {
        assert( ber != NULL );
 
-    ber_int_options.lbo_valid = LBER_INITIALIZED;
+       ber_int_options.lbo_valid = LBER_INITIALIZED;
 
        (void) memset( (char *)ber, '\0', sizeof( BerElement ));
        ber->ber_valid = LBER_VALID_BERELEMENT;
@@ -328,9 +307,22 @@ ber_init_w_nullc( BerElement *ber, int options )
        ber->ber_options = (char) options;
        ber->ber_debug = ber_int_debug;
 
+       if ( bv != NULL ) {
+               ber->ber_buf = bv->bv_val;
+               ber->ber_ptr = ber->ber_buf;
+               ber->ber_end = ber->ber_buf + bv->bv_len;
+       }
+
        assert( LBER_VALID( ber ) );
 }
 
+/* OLD U-Mich ber_init() */
+void
+ber_init_w_nullc( BerElement *ber, int options )
+{
+       ber_init2( ber, NULL, options );
+}
+
 /* New C-API ber_init() */
 /* This function constructs a BerElement containing a copy
 ** of the data in the bv argument.
@@ -453,7 +445,7 @@ ber_get_next(
        assert( LBER_VALID( ber ) );
 
 #ifdef NEW_LOGGING
-       LDAP_LOG(( "liblber", LDAP_LEVEL_ENTRY, "ber_get_next: enter\n" ));
+       LDAP_LOG( BER, ENTRY, "ber_get_next: enter\n", 0, 0, 0 );
 #else
        ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug,
                "ber_get_next\n" );
@@ -470,121 +462,139 @@ ber_get_next(
         *      2) definite lengths
         *      3) primitive encodings used whenever possible
         */
-       
+
        if (ber->ber_rwptr == NULL) {
                /* XXYYZ
                 * dtest does like this assert.
                 */
                /* assert( ber->ber_buf == NULL ); */
-               ber->ber_rwptr = (char *) &ber->ber_tag;
+               ber->ber_rwptr = (char *) &ber->ber_len-1;
+               ber->ber_ptr = ber->ber_rwptr;
                ber->ber_tag = 0;
        }
 
-#undef PTR_IN_VAR
-#define PTR_IN_VAR( ptr, var ) \
-       (((ptr)>=(char *) &(var)) && ((ptr)< (char *) &(var)+sizeof(var)))
-       
-       if (PTR_IN_VAR(ber->ber_rwptr, ber->ber_tag)) {
-               if (ber->ber_rwptr == (char *) &ber->ber_tag) {
-                       if (ber_int_sb_read( sb, ber->ber_rwptr, 1)<=0)
-                               return LBER_DEFAULT;
+       while (ber->ber_rwptr > (char *)&ber->ber_tag && ber->ber_rwptr <
+               (char *)(&ber->ber_usertag + 1)) {
+               ber_slen_t i;
+               char buf[sizeof(ber->ber_len)-1];
+               ber_len_t tlen = 0;
 
-                       if ((ber->ber_rwptr[0] & LBER_BIG_TAG_MASK)
-                               != LBER_BIG_TAG_MASK)
-                       {
-                               ber->ber_tag = ber->ber_rwptr[0];
-                               ber->ber_rwptr = (char *) &ber->ber_usertag;
-                               goto get_lenbyte;
-                       }
-                       ber->ber_rwptr++;
+               if ((i=ber_int_sb_read( sb, ber->ber_rwptr,
+                       (char *)(&ber->ber_usertag+1)-ber->ber_rwptr))<=0) {
+                       return LBER_DEFAULT;
                }
-               do {
-                       /* reading the tag... */
-                       if (ber_int_sb_read( sb, ber->ber_rwptr, 1)<=0) {
-                               return LBER_DEFAULT;
-                       }
 
-                       if (! (ber->ber_rwptr[0] & LBER_MORE_TAG_MASK) ) {
-                               ber->ber_tag>>=sizeof(ber->ber_tag) -
-                                 ((char *) &ber->ber_tag - ber->ber_rwptr);
-                               ber->ber_rwptr = (char *) &ber->ber_usertag;
-                               goto get_lenbyte;
+               ber->ber_rwptr += i;
+
+               /* We got at least one byte, try to parse the tag. */
+               if (ber->ber_ptr == (char *)&ber->ber_len-1) {
+                       ber_tag_t tag;
+                       unsigned char *p = (unsigned char *)ber->ber_ptr;
+                       tag = *p++;
+                       if ((tag & LBER_BIG_TAG_MASK) == LBER_BIG_TAG_MASK) {
+                               for (i=1; (char *)p<ber->ber_rwptr; i++,p++) {
+                                       tag <<= 8;
+                                       tag |= *p;
+                                       if (!(*p & LBER_MORE_TAG_MASK))
+                                               break;
+                                       /* Is the tag too big? */
+                                       if (i == sizeof(ber_tag_t)-1) {
+                                               errno = ERANGE;
+                                               return LBER_DEFAULT;
+                                       }
+                               }
+                               /* Did we run out of bytes? */
+                               if ((char *)p == ber->ber_rwptr) {
+                                       return LBER_DEFAULT;
+                               }
+                               p++;
                        }
-               } while( PTR_IN_VAR(ber->ber_rwptr, ber->ber_tag ));
-
-               errno = ERANGE; /* this is a serious error. */
-               return LBER_DEFAULT;
-       }
-
-get_lenbyte:
-       if (ber->ber_rwptr==(char *) &ber->ber_usertag) {
-               unsigned char c;
-               if (ber_int_sb_read( sb, (char *) &c, 1)<=0) {
-                       return LBER_DEFAULT;
+                       ber->ber_tag = tag;
+                       ber->ber_ptr = (char *)p;
                }
 
-               if (c & 0x80U) {
-                       int len = c & 0x7fU;
-                       if ( (len==0) || ( len>sizeof( ber->ber_len ) ) ) {
+               if (i == 1) continue;
+
+               /* Now look for the length */
+               if (*ber->ber_ptr & 0x80) {     /* multi-byte */
+                       int llen = *(unsigned char *)ber->ber_ptr++ & 0x7f;
+                       if (llen > (int)sizeof(ber_len_t)) {
                                errno = ERANGE;
                                return LBER_DEFAULT;
                        }
-
-                       ber->ber_rwptr = (char *) &ber->ber_len +
-                               sizeof(ber->ber_len) - len;
-                       ber->ber_len = 0;
-
+                       /* Not enough bytes? */
+                       if (ber->ber_rwptr - ber->ber_ptr < llen) {
+                               return LBER_DEFAULT;
+                       }
+                       for (i=0; i<llen && ber->ber_ptr<ber->ber_rwptr; i++,ber->ber_ptr++) {
+                               tlen <<=8;
+                               tlen |= *(unsigned char *)ber->ber_ptr;
+                       }
                } else {
-                       ber->ber_len = c;
-                       goto fill_buffer;
+                       tlen = *(unsigned char *)ber->ber_ptr++;
                }
-       }
-
-       if (PTR_IN_VAR(ber->ber_rwptr, ber->ber_len)) {
-               unsigned char netlen[sizeof(ber_len_t)];
-
-               ber_slen_t res;
-               ber_slen_t to_go;
-               to_go = (char *) &ber->ber_len + sizeof( ber->ber_len ) -
-                       ber->ber_rwptr;
-               assert( to_go > 0 );
-
-               res = BerRead( sb, netlen, to_go );
-               if (res <= 0) {
-                       return LBER_DEFAULT;
+               /* Are there leftover data bytes inside ber->ber_len? */
+               if (ber->ber_ptr < (char *)&ber->ber_usertag) {
+                       if (ber->ber_rwptr < (char *)&ber->ber_usertag)
+                               i = ber->ber_rwptr - ber->ber_ptr;
+                       else
+                               i = (char *)&ber->ber_usertag - ber->ber_ptr;
+                       AC_MEMCPY(buf, ber->ber_ptr, i);
+                       ber->ber_ptr += i;
+               } else {
+                       i = 0;
                }
-               ber->ber_rwptr += res;
+               ber->ber_len = tlen;
 
-               /* convert length. */
-               for( to_go = 0; to_go < res ; to_go++ ) {
-                       ber->ber_len <<= 8;
-                       ber->ber_len |= netlen[to_go];
-               }
+               /* now fill the buffer. */
 
-               if (PTR_IN_VAR(ber->ber_rwptr, ber->ber_len)) {
+               /* make sure length is reasonable */
+               if ( ber->ber_len == 0 ) {
+                       errno = ERANGE;
+                       return LBER_DEFAULT;
+               } else if ( sb->sb_max_incoming && ber->ber_len > sb->sb_max_incoming ) {
+#ifdef NEW_LOGGING
+                       LDAP_LOG( BER, ERR, 
+                               "ber_get_next: sockbuf_max_incoming limit hit "
+                               "(%d > %d)\n", ber->ber_len, sb->sb_max_incoming, 0 );
+#else
+                       ber_log_printf( LDAP_DEBUG_CONNS, ber->ber_debug,
+                               "ber_get_next: sockbuf_max_incoming limit hit "
+                               "(%ld > %ld)\n", ber->ber_len, sb->sb_max_incoming );
+#endif
+                       errno = ERANGE;
                        return LBER_DEFAULT;
                }
-       }
-
-fill_buffer:   
-       /* now fill the buffer. */
-
-       /* make sure length is reasonable */
-       if ( ber->ber_len == 0 ||
-               ( sb->sb_max_incoming && ber->ber_len > sb->sb_max_incoming ))
-       {
-               errno = ERANGE;
-               return LBER_DEFAULT;
-       }
 
-       if (ber->ber_buf==NULL) {
-               ber->ber_buf = (char *) LBER_MALLOC( ber->ber_len );
                if (ber->ber_buf==NULL) {
-                       return LBER_DEFAULT;
+                       ber_len_t l = ber->ber_rwptr - ber->ber_ptr;
+                       /* ber->ber_ptr is always <= ber->ber->ber_rwptr.
+                        * make sure ber->ber_len agrees with what we've
+                        * already read.
+                        */
+                       if ( ber->ber_len < i + l ) {
+                               errno = ERANGE;
+                               return LBER_DEFAULT;
+                       }
+                       ber->ber_buf = (char *) LBER_MALLOC( ber->ber_len + 1 );
+                       if (ber->ber_buf==NULL) {
+                               return LBER_DEFAULT;
+                       }
+                       ber->ber_end = ber->ber_buf + ber->ber_len;
+                       if (i) {
+                               AC_MEMCPY(ber->ber_buf, buf, i);
+                       }
+                       if (l > 0) {
+                               AC_MEMCPY(ber->ber_buf + i, ber->ber_ptr, l);
+                               i += l;
+                       }
+                       ber->ber_ptr = ber->ber_buf;
+                       ber->ber_usertag = 0;
+                       if ((ber_len_t)i == ber->ber_len) {
+                               goto done;
+                       }
+                       ber->ber_rwptr = ber->ber_buf + i;
                }
-               ber->ber_rwptr = ber->ber_buf;
-               ber->ber_ptr = ber->ber_buf;
-               ber->ber_end = ber->ber_buf + ber->ber_len;
        }
 
        if ((ber->ber_rwptr>=ber->ber_buf) && (ber->ber_rwptr<ber->ber_end)) {
@@ -607,15 +617,16 @@ fill_buffer:
 #endif                 
                        return LBER_DEFAULT;
                }
-               
+done:
                ber->ber_rwptr = NULL;
                *len = ber->ber_len;
                if ( ber->ber_debug ) {
 #ifdef NEW_LOGGING
-                       LDAP_LOG(( "liblber", LDAP_LEVEL_DETAIL1,
-                                  "ber_get_next: tag 0x%lx len %ld\n",
-                                  ber->ber_tag, ber->ber_len ));
-                       BER_DUMP(( "liblber", LDAP_LEVEL_DETAIL2, ber, 1 ));
+                       LDAP_LOG( BER, DETAIL1, 
+                               "ber_get_next: tag 0x%lx len %ld\n", 
+                               ber->ber_tag, ber->ber_len, 0  );
+                       if(LDAP_LOGS_TEST(BER, DETAIL2))
+                                       BER_DUMP(( "liblber", LDAP_LEVEL_DETAIL2, ber, 1 ));
 #else
                        ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug,
                                "ber_get_next: tag 0x%lx len %ld contents:\n",