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

RE: ldapsearch problems (ITS#2490)



Hi

Because I was growing up with C++ I didn't write plain C code. Howard
already gave me some feedback and I made some corrections. You'll find
the corrected version below.

Any feedback is welcome.

Patrick

> -----Original Message-----
> From: Howard Chu [mailto:hyc@highlandsun.com] 
> Sent: Mittwoch, 21. Mai 2003 02:35
> To: openldap-devel@OpenLDAP.org
> Subject: FW: ldapsearch problems (ITS#2490)
> 
> 
> I'm a bit pressed for time, can someone else look this over/comment?
>
> -----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 = *(unsigned char*)ber->ber_rwptr;

      /* tag is only one byte long */
      if ((*(unsigned char*)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 |=  *(unsigned char*)ber->ber_rwptr;
      } while ((*(unsigned char*)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 ((*(unsigned char*)ber->ber_rwptr & 0x80) == 0) {
        ber->ber_len = *(unsigned char*)ber->ber_rwptr++;
        ber->ber_ptr = (char*)&ber->ber_usertag;
      }
      else {
        ber->ber_len = *(unsigned char*)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 |=  *(unsigned char*)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 (ber->ber_rwptr == ber->ber_end) {
    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 */