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

FW: ldapsearch problems (ITS#2490)



I'm a bit pressed for time, can someone else look this over/comment?

  -- Howard Chu
  Chief Architect, Symas Corp.       Director, Highland Sun
  http://www.symas.com               http://highlandsun.com/hyc
  Symas: Premier OpenSource Development and Support

-----Original Message-----
From: Patrick Dreyer, SY-UCP [mailto:Patrick.Dreyer@swisscom-ucp.com]
Sent: Tuesday, May 20, 2003 1:32 AM
To: Howard Chu; openldap-its@OpenLDAP.org
Subject: RE: ldapsearch problems (ITS#2490)


Hi

> Thanks for this, but it has some obvious problems. Setting errno when
read() returns 0 is incorrect behavior. This will
> cause ber_get_next() to treat an EOF/close as a retry.

Yes, you're right. I corrected that in my second solution below.

> Also, even if your patch were valid, you have used C++ style comments in
it, which is not acceptable.

Sorry for this. It seams the MS compiler is too dump to tell me that, beside
I should have known it.

As I told you, I want to optimize my first solution and below it is, hopping
my comments are kind of help...
Give me your feedpack one more time please and tell me if I should have
forgotten to take out any C++ code.

Patrick


====================================================

/*
 * A rewrite of ber_get_next that can safely be called multiple times for the
 * same packet and will simply continue where it stopped last time until a
full
 * packet is read.
 *
 * 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) multi-byte tag and len at most 32 bits wide
 *  2) definite lengths
 *  3) primitive encodings used whenever possible
 *
 * 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_usertag and ber_buf
 * fields, called temp buffer, of the berelement until tag/length parsing is
 * complete.
 * ber_ptr is used to indicate if we are parsing the tag or the length and if
 * they are multi-byte to know which byte we are reading.
 * ber_rwptr points to the next byte in the temp buffer and ber_end points to
 * the byte one after the last in the temp buffer.
 *
 * The diagram below shows you how it looks like if we could fill the whole
 * temp buffer before we begin parsing the tag:
 *
 * +-------------+-------------+-------------+-------------+-------------+---
 * |   ber_tag   |   ber_len   | ber_usertag |   ber_buf   |   ber_ptr   | ..
 * +-------------+-------------+-------------+-------------+-------------+--
 * ^                           ^                           ^
 * ber_ptr                  ber_rwptr                   ber_end
 *
 *
 * The following diagram shows you how it could look like if we parsed the
tag
 * and the length is a multi-byte two bytes long:
 *
 * +-------------+-------------+-------------+-------------+-------------+---
 * |   ber_tag   |   ber_len   | ber_usertag |   ber_buf   |   ber_ptr   | ..
 * +-------------+-------------+-------------+-------------+-------------+--
 *                      ^             ^                    ^
 *                    ber_ptr      ber_rwptr            ber_end
 *
 *
 * After tag/length parsing, any leftover bytes and the rest of the message
is
 * copied into ber_buf.
 */
ber_tag_t
ber_get_next(
  Sockbuf *sb,
  ber_len_t *len,
  BerElement *ber )
{
  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

  /* 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_len   = 0;
    ber->ber_ptr   = (char*)&ber->ber_tag;
    ber->ber_end   = (char*)&ber->ber_usertag;
    ber->ber_rwptr = (char*)&ber->ber_usertag;
  }

  /* parse the tag and length */
  if ((char*)&ber->ber_tag <= ber->ber_ptr
      && ber->ber_ptr < (char*)&ber->ber_usertag) {
    /* read more data if we have free space in the temp buffer */
    {
      ber_len_t sblen = (char*)&ber->ber_ptr - ber->ber_end;

      /* jump to the beginning of the temp buffer if it is empty and we
         reached the end */
      if (sblen == 0 && ber->ber_rwptr == ber->ber_end) {
        ber->ber_end = (char*)&ber->ber_usertag;
        sblen        = (char*)&ber->ber_ptr - ber->ber_end;
      }

      /* read as much bytes as we have space left in the temp buffer */
      if (sblen > 0) {
        sblen = ber_int_sb_read(sb, ber->ber_end, sblen);
        if (sblen <= 0)
          return LBER_DEFAULT;

        ber->ber_end += sblen;

        /* be sure we have at least one available byte in the temp buffer */
        if (ber->ber_rwptr == ber->ber_end) {
          #if defined(EWOULDBLOCK)
            errno = EWOULDBLOCK;
          #elif defined(EAGAIN)
            errno = EAGAIN;
          #endif
          return LBER_DEFAULT;
        }
      }
    } /* read more data */

    /* parse the first tag byte */
    if (ber->ber_ptr == (char*)&ber->ber_tag) {
      ber->ber_tag = *ber->ber_rwptr;

      /* tag is only one byte long */
      if ((*ber->ber_rwptr++ & LBER_BIG_TAG_MASK) != LBER_BIG_TAG_MASK)
        ber->ber_ptr = (char*)&ber->ber_len;
      else
        ++ber->ber_ptr;
    }

    /* parse more tag bytes */
    if (ber->ber_ptr < (char*)&ber->ber_len) {
      do {
        /* be sure the temp buffer is not empty */
        if (ber->ber_rwptr == ber->ber_end) {
          #if defined(EWOULDBLOCK)
            errno = EWOULDBLOCK;
          #elif defined(EAGAIN)
            errno = EAGAIN;
          #endif
          return LBER_DEFAULT;
        }

        /* be sure the tag is not too big */
        if (ber->ber_ptr++ == (char*)&ber->ber_len) {
          errno = ERANGE;
          return LBER_DEFAULT;
        }

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

      ber->ber_ptr = (char*)&ber->ber_len;
    } /* parse more tag bytes */

    /* parse the first length byte */
    if (ber->ber_ptr == (char*)&ber->ber_len) {
      /* be sure the temp buffer is not empty */
      if (ber->ber_rwptr == ber->ber_end) {
        #if defined(EWOULDBLOCK)
          errno = EWOULDBLOCK;
        #elif defined(EAGAIN)
          errno = EAGAIN;
        #endif
        return LBER_DEFAULT;
      }

      /* length is only one byte long */
      if ((*ber->ber_rwptr & 0x80) == 0) {
        ber->ber_len = *ber->ber_rwptr++;
        ber->ber_ptr = (char*)&ber->ber_usertag;
      }
      else {
        ber->ber_len = *ber->ber_rwptr++ & 0x7f;

        /* be sure the length is not too big */
        if (ber->ber_len > sizeof(ber->ber_len)) {
          errno = ERANGE;
          return LBER_DEFAULT;
        }

        ber->ber_ptr += sizeof(ber->ber_len) - ber->ber_len;
      }
    } /* parse the first length byte */

    /* parse more length bytes */
    for (; ber->ber_ptr < (char*)&ber->ber_usertag; ++ber->ber_ptr) {
      /* be sure the temp buffer is not empty */
      if (ber->ber_rwptr == ber->ber_end) {
        #if defined(EWOULDBLOCK)
          errno = EWOULDBLOCK;
        #elif defined(EAGAIN)
          errno = EAGAIN;
        #endif
        return LBER_DEFAULT;
      }

      ber->ber_len <<= 8;
      ber->ber_len |=  *ber->ber_rwptr++;
    }

    /* 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;
    }

    /* allocate data buffer */
    {
      char*     buffer;
      ber_len_t left_over;

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

      /* copy left over bytes from temp to data buffer */
      left_over = ber->ber_end - ber->ber_rwptr;
      if (left_over > 0)
        AC_MEMCPY(buffer, ber->ber_rwptr, left_over);

      ber->ber_buf   = buffer;
      ber->ber_ptr   = buffer;
      ber->ber_end   = buffer + ber->ber_len;
      ber->ber_rwptr = buffer + left_over;
    }
  } /* parse the tag and length */

  /* read the data */
  if (ber->ber_buf <= ber->ber_rwptr && ber->ber_rwptr < ber->ber_end) {
    ber_len_t sblen = ber_int_sb_read(sb, ber->ber_rwptr,
                        ber->ber_end - ber->ber_rwptr);
    if (sblen <= 0)
      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;
    }

    /* the whole element is read if we reach this point */
    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 */