[Date Prev][Date Next] [Chronological] [Thread] [Top]

RE: ldapsearch problems (ITS#2490)



> A fix has been committed to CVS HEAD. Please try the attached patch to
libraries/liblber/io.c and let us know your results.

It doesn't :-(

Below you find my solution for ber_get_next(), which works but is not
optimized yet to read multiple bytes at once during reading the tag and
length instead reading one after one.
As you'll see, the big difference is, that if I do not process it as a
failure if ber_int_sb_read() returns with zero. And my solution doesn't
have any problems to leave and being recalled if no data was currently
available.

Please try my solution and give me some feedpack - only positiv ones are
welcome :-))

Patrick

================================================================
ber_tag_t
ber_get_next(
  Sockbuf *sb,
  ber_len_t *len,
  BerElement *ber )
{
  ber_slen_t sblen;

  assert( sb != NULL );
  assert( len != NULL );
  assert( ber != NULL );

  assert( SOCKBUF_VALID( sb ) );
  assert( LBER_VALID( ber ) );

#ifdef NEW_LOGGING
  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" );
#endif

  /*
   * Any ber element looks like this: tag length contents.
   * Assuming everything's ok, we return the tag byte (we can assume a
single
   * byte), return the length in len, and the rest of the undecoded
element in
   * buf.
   *
   * Assumptions:
   *  1) tag and len at most 32 bits wide
   *  2) definite lengths
   *  3) primitive encodings used whenever possible
   *
   * The code also handles multi-byte tags. The first few bytes of the
message
   * are read to check for multi-byte tags and lengths. These bytes are
   * temporarily stored in the ber_tag, ber_len, and ber_usertag fields
of the
   * berelement until tag/len parsing is complete. After this parsing,
any
   * leftover bytes and the rest of the message are copied into the
ber_buf.
   */

  // we start reading the next element
  if (ber->ber_rwptr == NULL) {
    #if 0
      /* XXYYZ - dtest does like this assert.  */
      assert( ber->ber_buf == NULL );
    #endif
    ber->ber_tag     = 0;
    ber->ber_usertag = 0;
    ber->ber_rwptr = (char*)&ber->ber_tag;
  }

  // reading tag as long as ber_rwptr points inside ber_tag
  if ((char*)&ber->ber_tag <= ber->ber_rwptr
      && ber->ber_rwptr < (char*)&ber->ber_len) {
    // first byte
    if (ber->ber_usertag == 0) {
      sblen = ber_int_sb_read(sb, ber->ber_rwptr, 1);
      if (sblen <= 0) {
        if (sblen == 0) {
          #if defined(EWOULDBLOCK)
            errno = EWOULDBLOCK;
          #elif defined(EAGAIN)
            errno = EAGAIN;
          #endif      
        }
        return LBER_DEFAULT;
      }

      // is only one byte long
      if ((*ber->ber_rwptr & LBER_BIG_TAG_MASK) != LBER_BIG_TAG_MASK) {
        ber->ber_tag     = *ber->ber_rwptr;
        ber->ber_usertag = 0;
        ber->ber_rwptr   = (char*)&ber->ber_len;
      }

      // otherwise multi byte
      else
        ber->ber_usertag = *ber->ber_rwptr++;
    } // first byte

    // more bytes
    if (ber->ber_usertag != 0) {
      do {
        // tag too big
        if (ber->ber_rwptr == (char*)&ber->ber_len) {
          #if defined(EWOULDBLOCK)
            errno = EWOULDBLOCK;
          #elif defined(EAGAIN)
            errno = EAGAIN;
          #endif      
          return LBER_DEFAULT;
        }

        sblen = ber_int_sb_read(sb, ber->ber_rwptr, 1);
        if (sblen <= 0) {
          if (sblen == 0) {
            #if defined(EWOULDBLOCK)
              errno = EWOULDBLOCK;
            #elif defined(EAGAIN)
              errno = EAGAIN;
            #endif      
          }
          return LBER_DEFAULT;
        }

        ber->ber_usertag <<= 8;
        ber->ber_usertag |=  *ber->ber_rwptr;
      } while((*ber->ber_rwptr++ & LBER_MORE_TAG_MASK) ==
LBER_MORE_TAG_MASK);

      ber->ber_tag     = ber->ber_usertag;
      ber->ber_usertag = 0;
      ber->ber_rwptr   = (char*)&ber->ber_len;
    }
  } // reading tag

  // reading length as long as ber_rwptr points inside ber_len
  if ((char*)ber->ber_len <= ber->ber_rwptr
      && ber->ber_rwptr < (char*)&ber->ber_usertag) {
    // first byte
    if (ber->ber_usertag == 0) {
      sblen = ber_int_sb_read(sb, ber->ber_rwptr, 1);
      if (sblen <= 0) {
        if (sblen == 0) {
          #if defined(EWOULDBLOCK)
            errno = EWOULDBLOCK;
          #elif defined(EAGAIN)
            errno = EAGAIN;
          #endif      
        }
        return LBER_DEFAULT;
      }

      // is only one byte long
      if ((*ber->ber_rwptr & 0x80) == 0) {
        ber->ber_len     = *ber->ber_rwptr;
        ber->ber_usertag = 0;
      }

      // otherwise multi byte
      else {
        ber->ber_usertag = *ber->ber_rwptr++ & 0x7f;

        // length too big
        if (ber->ber_usertag > sizeof(ber->ber_len)) {
          errno = ERANGE;
          return LBER_DEFAULT;
        }
      }
    } // first byte

    // more bytes
    for (; ber->ber_usertag > 0; --ber->ber_usertag, ++ber->ber_rwptr) {
      char buf;
      sblen = ber_int_sb_read(sb, &buf, 1);
      if (sblen <= 0) {
        if (sblen == 0) {
          #if defined(EWOULDBLOCK)
            errno = EWOULDBLOCK;
          #elif defined(EAGAIN)
            errno = EAGAIN;
          #endif      
        }
        return LBER_DEFAULT;
      }

      ber->ber_len <<= 8;
      ber->ber_len |=  buf;
    } // more bytes

    // be sure we have to read at least one byte of data
    if (ber->ber_len == 0) {
      errno = ERANGE;
      return LBER_DEFAULT;
    }

    // be sure sockbuf_max_incoming didn't exceed
    if  (sb->sb_max_incoming > 0 && ber->ber_len > sb->sb_max_incoming)
{
      #ifdef NEW_LOGGING
        LDAP_LOG( BER, ERR, 
          "ber_get_next: sockbuf_max_incoming exceeded "
          "(%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 exceeded "
          "(%ld > %ld)\n", ber->ber_len, sb->sb_max_incoming );
      #endif
      errno = ERANGE;
      return LBER_DEFAULT;
    }

    ber->ber_rwptr = (char*)&ber->ber_usertag;
  } // reading length

  // allocate buffer
  if (ber->ber_buf == NULL) {
    ber->ber_buf = LBER_MALLOC(ber->ber_len+1);
    if (ber->ber_buf == NULL)
      return LBER_DEFAULT;

    ber->ber_ptr   = ber->ber_buf;
    ber->ber_end   = ber->ber_buf + ber->ber_len;
    ber->ber_rwptr = ber->ber_buf;
  }

  // reading data
  if (ber->ber_buf <= ber->ber_rwptr && ber->ber_rwptr < ber->ber_end) {
    sblen = ber_int_sb_read(sb, ber->ber_rwptr, ber->ber_end -
ber->ber_rwptr);
    if (sblen <= 0) {
      if (sblen == 0) {
        #if defined(EWOULDBLOCK)
          errno = EWOULDBLOCK;
        #elif defined(EAGAIN)
          errno = EAGAIN;
        #endif      
      }
      return LBER_DEFAULT;
    }

    ber->ber_rwptr += sblen;

    // more data
    if (ber->ber_rwptr < ber->ber_end) {
      #if defined(EWOULDBLOCK)
        errno = EWOULDBLOCK;
      #elif defined(EAGAIN)
        errno = EAGAIN;
      #endif      
      return LBER_DEFAULT;
    }

    // whole element is read
    ber->ber_rwptr = NULL;
    *len           = ber->ber_len;

    // dump if debugging is switched on
    if (ber->ber_debug) {
      #ifdef NEW_LOGGING
        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",
          ber->ber_tag, ber->ber_len );
        ber_log_dump( LDAP_DEBUG_BER, ber->ber_debug, ber, 1 );
      #endif
    }

    return ber->ber_tag;
  }

  // the ber structure is messed up if we reach this point
  assert(0);
  return LBER_DEFAULT;
} // ber_get_next