]> git.sur5r.net Git - openldap/blobdiff - libraries/liblber/sockbuf.c
ITS#4046 fix from HEAD
[openldap] / libraries / liblber / sockbuf.c
index 8902af162dfc8a3a3cb4ac253766f433d622310d..6747093853cec69bf5254278e16250d3b8ab52b4 100644 (file)
@@ -1,13 +1,24 @@
 /* sockbuf.c - i/o routines with support for adding i/o layers. */
-/*
- * Copyright 1998-1999 The OpenLDAP Foundation, All Rights Reserved.
- * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2005 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
  */
 
 #include "portable.h"
 
 #include <stdio.h>
-#include <stdlib.h>
+
+#include <ac/stdlib.h>
 
 #include <ac/ctype.h>
 #include <ac/errno.h>
 #include <io.h>
 #endif /* HAVE_IO_H */
 
+#if defined( HAVE_FCNTL_H )
+#include <fcntl.h>
+#endif
+
 #if defined( HAVE_SYS_FILIO_H )
 #include <sys/filio.h>
 #elif defined( HAVE_SYS_IOCTL_H )
 
 #include "lber-int.h"
 
-#ifdef LDAP_DEBUG
-#include <assert.h>
-#else
-#define assert( cond )
+#ifndef LBER_MIN_BUFF_SIZE
+#define LBER_MIN_BUFF_SIZE             4096
+#endif
+#ifndef LBER_MAX_BUFF_SIZE
+#define LBER_MAX_BUFF_SIZE             65536
+#endif
+#ifndef LBER_DEFAULT_READAHEAD
+#define LBER_DEFAULT_READAHEAD 16384
 #endif
 
-#define MAX_BUF_SIZE   65535
-#define MIN_BUF_SIZE   4096
-
-#define sockbuf_io_write( sb, buf, len ) \
-((sb)->sb_io->sbi_write( (sb), (buf), (len) ))
+Sockbuf *
+ber_sockbuf_alloc( void )
+{
+       Sockbuf                 *sb;
 
-#define sockbuf_io_read( sb, buf, len ) \
-((sb)->sb_io->sbi_read( (sb), (buf), (len) ))
+       ber_int_options.lbo_valid = LBER_INITIALIZED;
 
-static long have_no_read( Sockbuf *sb, void *buf, long len );
-static long have_no_write( Sockbuf *sb, void *buf, long len );
-static int have_no_close( Sockbuf *sb );
+       sb = LBER_CALLOC( 1, sizeof( Sockbuf ) );
 
-static Sockbuf_IO lber_pvt_sb_IO_None=
-{
-       NULL,   /* sbi_setup */
-       NULL,   /* sbi_release */
-       have_no_read,   /* sbi_read */
-       have_no_write,  /* sbi_write */
-       have_no_close   /* sbi_close */
-};
+       if( sb == NULL ) return NULL;
 
-static void
-update_status( Sockbuf *sb )
-{
-   sb->sb_buf_ready = (sb->sb_buf.buf_ptr < sb->sb_buf.buf_end);
-#ifdef USE_SASL   
-   sb->sb_sec_ready = ((sb->sb_sec_buf_in.buf_end!=0) &&
-                      (sb->sb_sec_buf_in.buf_ptr >= 
-                       sb->sb_sec_buf_in.buf_end));
-#endif   
+       ber_int_sb_init( sb );
+       return sb;
 }
 
-#ifdef LDAP_DEBUG
-static int 
-status_is_ok( Sockbuf *sb )
+void
+ber_sockbuf_free( Sockbuf *sb )
 {
-   int obr;
-   int osr;
-   obr = sb->sb_buf_ready;
-#ifdef USE_SASL
-   osr = sb->sb_sec_ready;
-#endif
-   update_status(sb);
-   if (obr!=sb->sb_buf_ready)
-     return 0;
-#ifdef USE_SASL
-   if (osr!=sb->sb_sec_ready)
-     return 0;
-#endif
-   return 1;
-}
-#endif
+       assert( sb != NULL );
+       assert( SOCKBUF_VALID( sb ) );
 
-#ifdef USE_SASL
-static long
-packet_length( char *buf )
-{
-   long size;
-   size = (((unsigned long)buf[0])<<24)|
-     (((unsigned long)buf[1])<<16)|
-     (((unsigned long)buf[2])<<8)|
-     (((unsigned long)buf[3]));
-   
-   if ((size<0) || (size>MAX_BUF_SIZE))        {
-      /* somebody is trying to mess me up. */
-      lber_log_printf( LDAP_DEBUG_SASL, sb->sb_debug,
-                     "SASL: received packet length of %d bytes\n",
-                     size );      
-      size = 16; /* this should lead to an error. */
-   }
-   
-   return size + 4; /* include the size !!! */
+       ber_int_sb_close( sb );
+       ber_int_sb_destroy( sb );
+       LBER_FREE( sb );
 }
-#endif
 
-static int
-grow_buffer( Sockbuf_Buf * buf, long minsize )
-{
-   /* round to nearest 2k */
-   if (minsize < MIN_BUF_SIZE) {
-      minsize = MIN_BUF_SIZE;
-   }  else {
-      minsize=((minsize-1)|2047)+1;
-      if (minsize > MAX_BUF_SIZE) {
-        /* this could mean that somebody is trying to crash us. */
-        return -1;
-      }
-   }
-   if (buf->buf_size<minsize) {
-      if ((buf->buf_base==NULL) || ((buf->buf_end==0) && (buf->buf_ptr==0))) {
-        /* empty buffer */
-        if (buf->buf_base!=NULL)
-          free( buf->buf_base );
-        assert( buf->buf_ptr==0 );
-        assert( buf->buf_end==0 );
-        buf->buf_base = malloc( minsize );
-        if (buf->buf_base==NULL)
-          return -1;
-      } else {
-        char *nb;
-        nb = realloc( buf->buf_base, minsize );
-        if (nb==NULL)
-          return -1;
-        buf->buf_base = nb;
-      }
-      buf->buf_size = minsize;
+/* Return values: -1: error, 0: no operation performed or the answer is false,
+ * 1: successful operation or the answer is true
+ */
+int
+ber_sockbuf_ctrl( Sockbuf *sb, int opt, void *arg )
+{
+       Sockbuf_IO_Desc         *p;
+       int                     ret = 0;
+
+       assert( sb != NULL );
+       assert( SOCKBUF_VALID( sb ) );
+
+       switch ( opt ) {
+               case LBER_SB_OPT_HAS_IO:
+                       p = sb->sb_iod;
+                       while ( p && p->sbiod_io != (Sockbuf_IO *)arg ) {
+                               p = p->sbiod_next;
+                       }
+   
+                       if ( p ) {
+                               ret = 1;
+                       }
+                       break;
+
+               case LBER_SB_OPT_GET_FD:
+                       if ( arg != NULL ) {
+                               *((int *)arg) = sb->sb_fd;
+                       }
+                       ret = ( sb->sb_fd == AC_SOCKET_INVALID ? -1 : 1);
+                       break;
+
+               case LBER_SB_OPT_SET_FD:
+                       sb->sb_fd = *((int *)arg);
+                       ret = 1;
+                       break;
+
+               case LBER_SB_OPT_SET_NONBLOCK:
+                       ret = ber_pvt_socket_set_nonblock( sb->sb_fd, arg != NULL)
+                               ? -1 : 1;
+                       break;
+
+               case LBER_SB_OPT_DRAIN: {
+                               /* Drain the data source to enable possible errors (e.g.
+                                * TLS) to be propagated to the upper layers
+                                */
+                               char buf[LBER_MIN_BUFF_SIZE];
+
+                               do {
+                                       ret = ber_int_sb_read( sb, buf, sizeof( buf ) );
+                               } while ( ret == sizeof( buf ) );
+
+                               ret = 1;
+                       } break;
+
+               case LBER_SB_OPT_NEEDS_READ:
+                       ret = ( sb->sb_trans_needs_read ? 1 : 0 );
+                       break;
+
+               case LBER_SB_OPT_NEEDS_WRITE:
+                       ret = ( sb->sb_trans_needs_write ? 1 : 0 );
+                       break;
+
+               case LBER_SB_OPT_GET_MAX_INCOMING:
+                       if ( arg != NULL ) {
+                               *((ber_len_t *)arg) = sb->sb_max_incoming;
+                       }
+                       ret = 1;
+                       break;
+
+               case LBER_SB_OPT_SET_MAX_INCOMING:
+                       sb->sb_max_incoming = *((ber_len_t *)arg);
+                       ret = 1;
+                       break;
+
+               default:
+                       ret = sb->sb_iod->sbiod_io->sbi_ctrl( sb->sb_iod, opt, arg );
+                       break;
    }
-   return 0;
-}
 
-#ifdef USE_SASL
-static long
-sockbuf_sec_release( Sockbuf *sb, char *buf, long len )
-{
-   /* when this is called:
-    *  sb->sb_sec_buf_in.buf_base  points to a packet.
-    *  sb->sb_sec_buf_in.buf_ptr   contains the total bytes read.
-    *  sb->sb_sec_end.buf_end   contains the packet length.
-    * 
-    *  sb->sb_buf.buf_ptr == sb->sb_buf.buf_end == 0;
-    */
-   long rlen;
-   long total;
-   char *ptr;
-   char *end;
-   long size;
-   
-   assert( sb->sb_sec );
-   assert( sb->sb_sec->sbs_release );
-   assert( sb->sb_sec_buf_in.sb_ptr >= sb->sb_sec_buf_in.sb_end );
-   
-   assert( sb->sb_buf.sb_ptr == 0 );
-   assert( sb->sb_buf.sb_end == 0 );
+       return ret;
+}
 
-   assert( status_is_ok(sb) );
+int
+ber_sockbuf_add_io( Sockbuf *sb, Sockbuf_IO *sbio, int layer, void *arg )
+{
+       Sockbuf_IO_Desc         *d, *p, **q;
    
-   total = 0;
+       assert( sb != NULL );
+       assert( SOCKBUF_VALID( sb ) );
    
-   ptr = sb->sb_sec_buf_in.buf_base;
-   end = ptr+ sb->sb_sec_buf_in.buf_ptr;
-   size = sb->sb_sec_buf_in.buf_end;
+       if ( sbio == NULL ) {
+               return -1;
+       }
    
-   sb->sb_sec_ready = 1;
+       q = &sb->sb_iod;
+       p = *q;
+       while ( p && p->sbiod_level > layer ) {
+               q = &p->sbiod_next;
+               p = *q;
+       }
    
-   for(;(ptr+size<=end);) {
-      for(;;) {
-        rlen = sb->sb_sec->sbs_release( sb, ptr, size,
-                                       buf, len, 
-                                       sb->sb_buf.buf_base,
-                                       sb->sb_buf.buf_size );
-        if (rlen==0) {
-           /* this means a security violation. */
-           return total; /* total ? total : 0 */
-        }
-        if (rlen<0) {
-           /* this means that the buffer isn't big enough. */
-           if (grow_buffer( &(sb->sb_buf), -rlen )<0)
-             /* memory violation. */
-             return total; /* total ? total : 0 */
-           continue;
-        }
-        /* if (rlen>0) */
-        break;
-      }
-      total+=rlen;
-      
-      /* move to the next packet... */
-      ptr+=size;
-      
-      if (ptr+4<=end)
-       size = packet_length( ptr ); 
-      /* size is always at least 4, so the loop condition is always OK !!*/
-      assert( size>=4 );
-      
-      if (rlen<len) {
-        len-=rlen;
-        buf+=rlen;
-      } else {
-        sb->sb_buf_ready = (sb->sb_buf.buf_end = rlen - len) ? 1 : 0;
-        break;
-      }
-   }
+       d = LBER_MALLOC( sizeof( *d ) );
+       if ( d == NULL ) {
+               return -1;
+       }
    
-   if (ptr+size>end)
-     sb->sb_sec_ready = 0;
-   /* clean up the mess. */
-   if (ptr<end) {
-      /* copy back to beginning of buffer. */
-      SAFEMEMCPY( sb->sb_sec_buf_in.buf_base, ptr, end-ptr );
-      sb->sb_sec_buf_in.buf_ptr = 0;
-      sb->sb_sec_buf_in.buf_end -= (ptr - sb->sb_sec_buf_in.buf_base);
-   }
-   assert( status_is_ok(sb) );
-   return total;
-}
+       d->sbiod_level = layer;
+       d->sbiod_sb = sb;
+       d->sbiod_io = sbio;
+       memset( &d->sbiod_pvt, '\0', sizeof( d->sbiod_pvt ) );
+       d->sbiod_next = p;
+       *q = d;
 
-static long
-sockbuf_sec_protect( Sockbuf *sb, char *buf, long len )
-{
-   long ret;
-   long blen;
-   long total;
-   
-   assert( sb->sb_sec_out.buf_end == 0 );
-   assert( sb->sb_sec_out.buf_ptr == 0 );
+       if ( sbio->sbi_setup != NULL && ( sbio->sbi_setup( d, arg ) < 0 ) ) {
+               return -1;
+       }
+
+       return 0;
+}
    
-   assert( sb->sb_sec );
-   assert( sb->sb_sec->sbs_protect );
+int
+ber_sockbuf_remove_io( Sockbuf *sb, Sockbuf_IO *sbio, int layer )
+{
+       Sockbuf_IO_Desc         *p, **q;
+
+       assert( sb != NULL );
+       assert( SOCKBUF_VALID( sb ) );
    
-   assert( status_is_ok(sb) );
+       if ( sb->sb_iod == NULL ) {
+               return -1;
+       }
    
-   total = 0;
-   for(;(len);) {
-      for(;;) {
-        blen = len;
-        ret = sb->sb_sec->sbs_protect( sb, buf, &blen, 
-                                      sb->sb_sec_out.buf_base+
-                                      sb->sb_sec_out.buf_end, 
-                                      sb->sb_sec_out.buf_size -
-                                      sb->sb_sec_out.buf_end );
-        if (ret==0)
-          /* protection error ? */
-          return total;
-        if (ret<0) {
-           if (grow_buffer( &(sb->sb_sec_out),-ret-sb->sb_sec_out.buf_end )<0)
-             /* memory error */
-             return total;
-           continue;
-        }
-        /* else if (ret>0) */
-        break;
-      }
-      sb->sb_sec_out.buf_end += ret;
-      len -= blen;
-      total += blen;
-   }
-   assert( status_is_ok(sb) );
-   return total;
+       q = &sb->sb_iod;
+       while ( *q != NULL ) {
+               p = *q;
+               if ( layer == p->sbiod_level && p->sbiod_io == sbio ) {
+                       if ( p->sbiod_io->sbi_remove != NULL &&
+                               p->sbiod_io->sbi_remove( p ) < 0 )
+                       {
+                               return -1;
+                       }
+                       *q = p->sbiod_next;
+                       LBER_FREE( p );
+               break;
+               }
+               q = &p->sbiod_next;
+       }
+
+       return 0;
 }
-#endif
 
-static long 
-sockbuf_copy_out( Sockbuf *sb, char **buf, long len )
-{
-   long blen = (sb->sb_buf.buf_end - sb->sb_buf.buf_ptr );
-   assert( status_is_ok(sb) );
-   if (blen) {
-      long rlen = (blen<len) ? blen : len;
-      memcpy( *buf, sb->sb_buf.buf_base + sb->sb_buf.buf_ptr, rlen );
-      sb->sb_buf.buf_ptr+=rlen;
-      *buf+=rlen;
-      len -= rlen;
-      if (sb->sb_buf.buf_ptr >= sb->sb_buf.buf_end) {
-        sb->sb_buf.buf_ptr = sb->sb_buf.buf_end = 0;
-        sb->sb_buf_ready = 0;
-      } else {
-        sb->sb_buf_ready = 1;
-      }
-   }
-   assert( status_is_ok(sb) );
-   return len;
+void
+ber_pvt_sb_buf_init( Sockbuf_Buf *buf )
+{
+       buf->buf_base = NULL;
+       buf->buf_ptr = 0;
+       buf->buf_end = 0;
+       buf->buf_size = 0;
 }
 
+void
+ber_pvt_sb_buf_destroy( Sockbuf_Buf *buf )
+{
+       assert( buf != NULL);
 
-long 
-lber_pvt_sb_read( Sockbuf *sb, void *buf_arg, long len )
+       if (buf->buf_base) {
+               LBER_FREE( buf->buf_base );
+       }
+       ber_pvt_sb_buf_init( buf );
+}
+
+int
+ber_pvt_sb_grow_buffer( Sockbuf_Buf *buf, ber_len_t minsize )
 {
-   char *buf;
-   long ret;
-   
-   assert( status_is_ok(sb) );
-#if 0
-   /* breaks slapd :-) */
-   assert( lber_pvt_sb_in_use( sb ) );
-#endif 
+       ber_len_t               pw;
+       char                    *p;
    
-   buf = (char *) buf_arg;
+       assert( buf != NULL );
 
-   len = sockbuf_copy_out( sb, &buf, len );
-   
-   if (len==0) {
-      return (buf - (char *) buf_arg);
-   }
+       for ( pw = LBER_MIN_BUFF_SIZE; pw < minsize; pw <<= 1 ) {
+               if (pw > LBER_MAX_BUFF_SIZE) return -1;
+       }
 
-#ifdef USE_SASL
-   if (sb->sb_sec) {
-      int max;
-      assert( sb->sb_sec->sbs_release );
-      assert( sb->sb_sec_buf_in.buf_base );
-      if (sb->sb_read_ahead) {
-        max = sb->sb_sec_buf_in.buf_size - sb->sb_sec_buf_in.buf_ptr;
-      } else {
-        max = sb->sb_sec_buf_in.buf_end - sb->sb_sec_buf_in.buf_ptr;
-        if (max<=0) {
-           /* special situation. This means that we need to read the first
-            * four bytes for the packet length.
-            */
-           max += 4;
-        }
-      }
-      for(;;) {
-        /* read from stream into sb_sec_buf_in */
-        ret = sockbuf_io_read( sb, sb->sb_sec_buf_in.buf_base +
-                                 sb->sb_sec_buf_in.buf_ptr, max );
-        if (ret<=0) {
-           /* read error. return */
-           goto do_return;
-        }
-        sb->sb_sec_buf_in.buf_ptr += ret;
-        
-        if (sb->sb_sec_buf_in.buf_ptr < sb->sb_sec_buf_in.buf_end) {
-           /* did not finish a packet. give up. */
-           goto do_return;
-        }
-           
-        if (sb->sb_sec_buf_in.buf_end == 0) {
-           /* Were trying to read the first four bytes... */
-           if (sb->sb_sec_buf_in.buf_ptr < 4) {
-              /* did not read enough for packet length. give up. */
-              goto do_return;
-           }
-           /* calculate the packet length. */
-           sb->sb_sec_buf_in.buf_end = 
-              packet_length(sb->sb_sec_buf_in.buf_base );
-           if ((sb->sb_sec_buf_in.buf_end > sb->sb_sec_buf_in.buf_size) &&
-               (grow_buffer( &(sb->sb_sec_buf_in), sb->sb_sec_buf_in.buf_end)<0)) {
-              /* buffer has to be to big. exit with error. */
-              ret = -1;
-              goto do_return;
-           }
-           if (sb->sb_sec_buf_in.buf_ptr >= sb_sec_buf_in.buf_end) {
-              /* finished packet. decode it. */
-              goto decode_packet;
-           }
-           /* did not finish packet yet. try again ? */
-           if (sb->sb_read_ahead) {
-              /* we were trying to read the max anyway. forget it */
-              goto do_return;
-           }
-        }
-decode_packet:
-        /* we read enough for at least 1 packet */
-        ret = sockbuf_sec_release( sb, buf, len );
-        if (ret<=0) {
-           /* something went wrong... */
-           goto do_return;
-        }
-        buf+=ret;
-        len-=ret;
-        /* we are finished !!! */
-        if ((len==0) || (ret!=max))
-          goto do_return;
-      }
-   } else {
-#endif
-      if (sb->sb_read_ahead) {
-        long max;
-        max = sb->sb_buf.buf_size - sb->sb_buf.buf_end;
-        if (max>len) {
-           ret = sockbuf_io_read( sb, 
-                                    sb->sb_buf.buf_base +
-                                    sb->sb_buf.buf_end,
-                                       max );
-           if (ret<=0) {
-              /* some error occured */
-              goto do_return;
-           }
-           sb->sb_buf.buf_end += ret;
-           /* move out the data... */
-           len = sockbuf_copy_out( sb, &buf, len );
-           goto do_return;
-        }
-      }
-      /* no read_ahead, just try to put the data in the buf. */
-      ret = sockbuf_io_read( sb, buf, len );
-      if (ret>0) {
-        buf+=ret;
-        len-=ret;
-      }
-      /* we might as well return, since there is nothing to do... */
-#ifdef USE_SASL            
-   }
-#endif
-do_return:
-   assert( status_is_ok(sb) );
-   if ((ret<=0) && (buf==buf_arg)) {
-      /* there was an error. */
-      return ret;
-   }
-   return (buf - ((char *) buf_arg));
+       if ( buf->buf_size < pw ) {
+               p = LBER_REALLOC( buf->buf_base, pw );
+               if ( p == NULL ) return -1;
+               buf->buf_base = p;
+               buf->buf_size = pw;
+       }
+       return 0;
 }
 
-#ifdef USE_SASL
-long sockbuf_do_write( Sockbuf *sb )
+ber_len_t
+ber_pvt_sb_copy_out( Sockbuf_Buf *sbb, char *buf, ber_len_t len )
 {
-   long to_go;
-   to_go = sb->sb_sec_out.buf_end - sb->sb_sec_out.buf_ptr;
-   assert( to_go > 0 );
-   /* there is something left of the last time... */
-   ret = sockbuf_io_write( sb, sb->sb_sec_out.buf_base+
-                             sb->sb_sec_out.buf_ptr, to_go );
-   if (ret<=0) /* error */
-     return ret;
-   sb->sb_sec_out.buf_ptr += ret;
-   if (ret<to_go) /* not enough data, so pretend no data was sent. */
-     return -1;
-   return ret;
-}
-#endif
+       ber_len_t               max;
 
-long lber_pvt_sb_write( Sockbuf *sb, void *buf, long len_arg )
-{
-   long ret;
-   long len = len_arg;
-   assert( status_is_ok(sb) );
+       assert( buf != NULL );
+       assert( sbb != NULL );
 #if 0
-   /* unfortunately breaks slapd */
-   assert( lber_pvt_sb_in_use( sb ) );
-#endif   
-#ifdef USE_SASL
-   if (sb->sb_sec) {
-      assert( sb->sb_sec_prev_len <= len );
-      if (sb->sb_sec_prev_len) {
-        ret = sockbuf_do_write( sb );
-        if (ret<=0)
-          return ret;
-        /* finished. */
-        len -= sb->sb_sec_prev_len;
-        sb->sb_sec_prev_len = 0;
-        sb->sb_sec_out.buf_end = sb->sb_sec_out.buf_ptr = 0;
-      }
-      /* now protect the next packet. */
-      ret = sockbuf_sec_protect( sb, buf, len );
-      if (ret<=0)
-       return ret;
-      ret = sockbuf_do_write( sb );
-      if (ret<=0) {
-        sb->sb_sec_prev_len = len;
-        return ret;
-      }
-      return len_arg;
-   } else {
+       assert( sbb->buf_size > 0 );
 #endif
-      return sockbuf_io_write( sb, buf, len );
-#ifdef USE_SASL      
+
+       max = sbb->buf_end - sbb->buf_ptr;
+       max = ( max < len) ? max : len;
+       if ( max ) {
+               AC_MEMCPY( buf, sbb->buf_base + sbb->buf_ptr, max );
+               sbb->buf_ptr += max;
+               if ( sbb->buf_ptr >= sbb->buf_end ) {
+                       sbb->buf_ptr = sbb->buf_end = 0;
+               }
    }
-#endif
+       return max;
 }
-     
-int lber_pvt_sb_close( Sockbuf *sb )
+
+ber_slen_t
+ber_pvt_sb_do_write( Sockbuf_IO_Desc *sbiod, Sockbuf_Buf *buf_out )
 {
-   int ret;
-   assert( sb->sb_io );
-   assert( sb->sb_io->sbi_close );
-   assert( status_is_ok(sb) );
-   assert( lber_pvt_sb_in_use( sb ) );
+       ber_len_t               to_go;
+       ber_slen_t ret;
+
+       assert( sbiod != NULL );
+       assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+       to_go = buf_out->buf_end - buf_out->buf_ptr;
+       assert( to_go > 0 );
    
-   ret = sb->sb_io->sbi_close( sb );
-   lber_pvt_sb_set_desc( sb, -1 );
+       for(;;) {
+               ret = LBER_SBIOD_WRITE_NEXT( sbiod, buf_out->buf_base +
+                       buf_out->buf_ptr, to_go );
+#ifdef EINTR
+               if ((ret<0) && (errno==EINTR)) continue;
+#endif
+               break;
+       }
 
-   return ret;
-}
+       if ( ret <= 0 ) return ret;
+   
+       buf_out->buf_ptr += ret;
+       if (buf_out->buf_ptr == buf_out->buf_end) {
+               buf_out->buf_end = buf_out->buf_ptr = 0;
+       }
 
-int lber_pvt_sb_set_readahead( Sockbuf *sb, int rh )
-{
-   assert( status_is_ok(sb) );
-   sb->sb_read_ahead = (rh!=0);
-   return 0;
+       return ret;
 }
 
-int lber_pvt_sb_set_nonblock( Sockbuf *sb, int nb )
-{
-   assert( status_is_ok(sb) );
-   if (nb) {
-      sb->sb_non_block = 1;
-#if 0      
-      sb->sb_read_ahead = 1;
-#endif
-   } else {
-      sb->sb_non_block = 0;
-#if 0
-      sb->sb_read_ahead = 0;
+int
+ber_pvt_socket_set_nonblock( ber_socket_t sd, int nb )
+{
+#if HAVE_FCNTL
+       int flags = fcntl( sd, F_GETFL);
+       if( nb ) {
+               flags |= O_NONBLOCK;
+       } else {
+               flags &= ~O_NONBLOCK;
+       }
+       return fcntl( sd, F_SETFL, flags );
+               
+#elif defined( FIONBIO )
+       ioctl_t status = nb ? 1 : 0;
+       return ioctl( sd, FIONBIO, &status );
 #endif
-   }
-   if (lber_pvt_sb_in_use(sb)) {
-      int status = (nb!=0);
-      if (ioctl( lber_pvt_sb_get_desc(sb), FIONBIO, (caddr_t)&status ) == -1 ) {
-        return -1;
-      }
-   }
-   return 0;
 }
-        
-#define sockbuf_buf_init( bb ) \
-(bb)->buf_base=NULL;\
-(bb)->buf_ptr = 0;\
-(bb)->buf_end = 0;\
-(bb)->buf_size = 0;
 
-static int 
-sockbuf_buf_destroy( Sockbuf_Buf *buf )
+int
+ber_int_sb_init( Sockbuf *sb )
 {
-   if (buf->buf_base)
-     free( buf->buf_base );
-   sockbuf_buf_init( buf );
-   return 0;
-}
+       assert( sb != NULL);
 
-int lber_pvt_sb_init( Sockbuf *sb )
-{
-   sb->sb_item_type=LBER_ITEM_SOCKBUF;
-   sb->sb_options = 0;
-   sb->sb_debug = 0;
-   sb->sb_trans_ready = 0;
-   sb->sb_buf_ready = 0;
-#ifdef USE_SASL   
-   sb->sb_sec_ready = 0;
-#endif   
-   sb->sb_read_ahead = 0;
-   sb->sb_non_block = 0;
-   sb->sb_fd = -1;
-   sb->sb_iodata = NULL;
-   sb->sb_io = &lber_pvt_sb_IO_None;
-   sb->sb_sd = -1;
-#ifdef DEADWOOD   
-   sb->sb_max_incoming = 0;
-#endif   
-   sockbuf_buf_init( &(sb->sb_buf) );
-#ifdef USE_SASL
-   sockbuf_buf_init( &(sb->sb_sec_buf_in) );
-   sockbuf_buf_init( &(sb->sb_sec_buf_out) );
-   sb->sb_sdata = NULL;
-   sb->sb_sec = NULL;
-   sb->sb_sec_prev_len = 0;
-#endif   
-   return 0;
+       sb->sb_valid=LBER_VALID_SOCKBUF;
+       sb->sb_options = 0;
+       sb->sb_debug = ber_int_debug;
+       sb->sb_fd = AC_SOCKET_INVALID;
+       sb->sb_iod = NULL;
+       sb->sb_trans_needs_read = 0;
+       sb->sb_trans_needs_write = 0;
+   
+       assert( SOCKBUF_VALID( sb ) );
+       return 0;
 }
    
-int lber_pvt_sb_destroy( Sockbuf *sb )
+int
+ber_int_sb_close( Sockbuf *sb )
 {
-#ifdef USE_SASL
-   lber_pvt_sb_clear_sec(sb);
-   sockbuf_buf_destroy( &(sb->sb_sec_buf_in) );
-   sockbuf_buf_destroy( &(sb->sb_sec_buf_out) );
-#endif
-   lber_pvt_sb_clear_io(sb);
-   sockbuf_buf_destroy( &(sb->sb_buf) );
-   return lber_pvt_sb_init( sb );
-}
+       Sockbuf_IO_Desc         *p;
 
-#ifdef USE_SASL
-int lber_pvt_sb_set_sec( Sockbuf *sb, Sockbuf_Sec * sec, void *arg )
-{
-   int len;
-   if ((sb->sb_sec) || (sec==NULL))
-     return -1;
-   
-   sb->sb_sec = sec;
+       assert( sb != NULL);
    
-   if ((sec->sbs_setup) && (sec->sbs_setup( sb, arg)<0)) {
-      return -1;
-   }
+       p = sb->sb_iod;
+       while ( p ) {
+               if ( p->sbiod_io->sbi_close && p->sbiod_io->sbi_close( p ) < 0 ) {
+                       return -1;
+               }
+               p = p->sbiod_next;
+       }
    
-   len = sb->sb_buf.buf_end - sb->sb_buf.buf_ptr;
+       sb->sb_fd = AC_SOCKET_INVALID;
    
-   if (len>0) {
-      /* move this to the security layer. */
-      if (grow_buffer( &(sb->sb_sec_buf_in), len )<0)
-       return -1;
-      memcpy( sb->sb_sec_buf_in.buf_base, 
-            sb->sb_buf.buf_base + sb->sb_buf.buf_ptr, len );
-      sb->sb_sec_buf_in.buf_ptr = len;
-      sb->sb_sec_buf_in.buf_end = (len>4) ? packet_length( sb->sb_sec_buf_in ) : 0;
-      sb->sb_buf.buf_ptr = sb->sb_buf.buf_end = 0;
-   }
-   update_status();
-   return 0;
+       return 0;
 }
 
-int lber_pvt_sb_clear_sec( Sockbuf *sb )
+int
+ber_int_sb_destroy( Sockbuf *sb )
 {
-   if (sb->sb_buf.buf_ptr!=0)
-     return -1;
-   if (sb->sb_sec==NULL)
-     return -1;
-   if ((sb->sb_sec->sbs_remove) && (sb->sb_sec->sbs_remove(sb)<0)) 
-     return -1;
-   
-   sb->sb_sec = NULL;
-   if (sb->sb_sec_buf_in.buf_ptr!=0) {
-      if (grow_buffer( &(sb->sb_buf), 
-                     sb->sb_buf.buf_end + sb->sb_sec_buf_in.buf_ptr)<0)
-       return -1;
-      memcpy( sb->sb_buf.buf_base + sb->sb_buf.buf_end,
-             sb->sb_sec_buf_in.buf_base, sb->sb_sec_buf_in.buf_ptr );
-      sb->sb_buf.buf_end += sb->sb_sec_buf_in.buf_ptr;
-      sb->sb_buf_ready = 1;
-   }
-   sockbuf_buf_destroy( &(sb->sb_sec_buf_in) );
-   assert( sb->sb_sec_buf.buf_end==0 );
-   sockbuf_buf_destroy( &(sb->sb_sec_buf_out) );
-   
-   sb->sb_sec_ready = 0;
+       Sockbuf_IO_Desc         *p;
+
+       assert( sb != NULL);
+       assert( SOCKBUF_VALID( sb ) );
    
-   return 0;
+       while ( sb->sb_iod ) {
+               p = sb->sb_iod->sbiod_next;
+               ber_sockbuf_remove_io( sb, sb->sb_iod->sbiod_io,
+                       sb->sb_iod->sbiod_level );
+               sb->sb_iod = p;
+       }
+
+       return ber_int_sb_init( sb );
 }
-#endif
 
-int lber_pvt_sb_set_io( Sockbuf *sb, Sockbuf_IO *trans, void *arg )
+ber_slen_t
+ber_int_sb_read( Sockbuf *sb, void *buf, ber_len_t len )
 {
-   assert( sb->sb_io == &lber_pvt_sb_IO_None );
+       ber_slen_t              ret;
 
-   if (trans==NULL)
-     return -1;
-   
-   sb->sb_io = trans;
-   
-   if ((trans->sbi_setup) && (trans->sbi_setup( sb, arg)<0))
-     return -1;
-   
-   return 0;
+       assert( buf != NULL );
+       assert( sb != NULL);
+       assert( sb->sb_iod != NULL );
+       assert( SOCKBUF_VALID( sb ) );
+
+       for (;;) {
+               ret = sb->sb_iod->sbiod_io->sbi_read( sb->sb_iod, buf, len );
+
+#ifdef EINTR   
+               if ( ( ret < 0 ) && ( errno == EINTR ) ) continue;
+#endif
+               break;
+       }
+
+       return ret;
 }
 
-int lber_pvt_sb_clear_io( Sockbuf *sb )
+ber_slen_t
+ber_int_sb_write( Sockbuf *sb, void *buf, ber_len_t len )
 {
-   if (sb->sb_io==&lber_pvt_sb_IO_None)
-     return -1;
-   
-   if ((sb->sb_io->sbi_remove) && (sb->sb_io->sbi_remove( sb )<0))
-     return -1;
+       ber_slen_t              ret;
 
-   sb->sb_io = &lber_pvt_sb_IO_None;
-   
-   sb->sb_trans_ready = 0;
-   
-   return 0;
+       assert( buf != NULL );
+       assert( sb != NULL);
+       assert( sb->sb_iod != NULL );
+       assert( SOCKBUF_VALID( sb ) );
+
+       for (;;) {
+               ret = sb->sb_iod->sbiod_io->sbi_write( sb->sb_iod, buf, len );
+
+#ifdef EINTR   
+               if ( ( ret < 0 ) && ( errno == EINTR ) ) continue;
+#endif
+               break;
+       }
+
+       return ret;
 }
 
 /*
  * Support for TCP
  */
 
-static long
-stream_read( Sockbuf *sb, void *buf, long len )
+static ber_slen_t
+sb_stream_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
 {
+       assert( sbiod != NULL);
+       assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
 #if defined(MACOS)
 /*
  * MacTCP/OpenTransport
  */
-   return tcpread( lber_pvt_sb_get_desc(sb), 0, (unsigned char *)buf, 
-                  len, NULL );
-#elif (defined(DOS) && (defined(PCNFS) || defined( WINSOCK))) \
-       || defined( _WIN32)
+       return tcpread( sbiod->sbiod_sb->sb_fd, 0, (unsigned char *)buf,
+               len, NULL );
+
+#elif defined( HAVE_PCNFS ) || \
+   defined( HAVE_WINSOCK ) || defined ( __BEOS__ )
 /*
  * PCNFS (under DOS)
  */
@@ -696,30 +469,50 @@ stream_read( Sockbuf *sb, void *buf, long len )
 /*
  * 32-bit Windows Socket API (under Windows NT or Windows 95)
  */
-   return recv( lber_pvt_sb_get_desc(sb), buf, len, 0 );
-#elif (defined(DOS) && defined( NCSA ))
+       {
+               int rc;
+
+               rc = recv( sbiod->sbiod_sb->sb_fd, buf, len, 0 );
+
+#ifdef HAVE_WINSOCK
+               if ( rc < 0 ) {
+                       int err;
+
+                       err = WSAGetLastError();
+                       errno = err;
+               }
+#endif
+
+               return rc;
+       }
+
+#elif defined( HAVE_NCSA )
 /*
  * NCSA Telnet TCP/IP stack (under DOS)
  */
-   return nread( lber_pvt_sb_get_desc(sb), buf, len );
+       return nread( sbiod->sbiod_sb->sb_fd, buf, len );
+
 #else
-   return read( lber_pvt_sb_get_desc(sb), buf, len );
+       return read( sbiod->sbiod_sb->sb_fd, buf, len );
 #endif
 }
 
-static long
-stream_write( Sockbuf *sb, void *buf, long len )
+static ber_slen_t
+sb_stream_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
 {
+       assert( sbiod != NULL);
+       assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
 #if defined(MACOS) 
 /*
  * MacTCP/OpenTransport
  */
 #define MAX_WRITE      65535
-   return tcpwrite( lber_pvt_sb_get_desc(sb),
-                   (unsigned char *)(buf), 
-                   (len<MAX_WRITE)? len : MAX_WRITE );
-#elif (defined(DOS) && (defined(PCNFS) || defined( WINSOCK))) \
-       || defined( _WIN32)
+       return tcpwrite( sbiod->sbiod_sb->sb_fd, (unsigned char *)buf,
+               (len<MAX_WRITE) ? len : MAX_WRITE );
+
+#elif defined( HAVE_PCNFS) \
+       || defined( HAVE_WINSOCK) || defined ( __BEOS__ )
 /*
  * PCNFS (under DOS)
  */
@@ -729,188 +522,454 @@ stream_write( Sockbuf *sb, void *buf, long len )
 /*
  * 32-bit Windows Socket API (under Windows NT or Windows 95)
  */
-   return send( lber_pvt_sb_get_desc(sb), buf, len, 0 );
-#elif defined(NCSA)
-   return netwrite( lber_pvt_sb_get_desc(sb), buf, len );
+       {
+               int rc = send( sbiod->sbiod_sb->sb_fd, buf, len, 0 );
+
+#ifdef HAVE_WINSOCK
+               if ( rc < 0 ) {
+                       int err;
+                       err = WSAGetLastError();
+                       errno = err;
+               }
+#endif
+               return rc;
+       }
+
+#elif defined(HAVE_NCSA)
+       return netwrite( sbiod->sbiod_sb->sb_fd, buf, len );
+
 #elif defined(VMS)
 /*
  * VMS -- each write must be 64K or smaller
  */
 #define MAX_WRITE 65535
-   return write( lber_pvt_sb_get_desc(sb), buf, 
-                (len<MAX_WRITE)? len : MAX_WRITE);
+       return write( sbiod->sbiod_sb->sb_fd, buf,
+               (len<MAX_WRITE) ? len : MAX_WRITE);
 #else
-   return write( lber_pvt_sb_get_desc(sb), buf, len );
+       return write( sbiod->sbiod_sb->sb_fd, buf, len );
 #endif   
 }   
    
 static int 
-stream_close( Sockbuf *sb )
+sb_stream_close( Sockbuf_IO_Desc *sbiod )
 {
-   tcp_close( lber_pvt_sb_get_desc( sb ) );
+       assert( sbiod != NULL );
+       assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+       tcp_close( sbiod->sbiod_sb->sb_fd );
    return 0;
 }
 
-Sockbuf_IO lber_pvt_sb_io_tcp=
-{
-       NULL,   /* sbi_setup */
-       NULL,   /* sbi_release */
-       stream_read,    /* sbi_read */
-       stream_write,   /* sbi_write */
-       stream_close,   /* sbi_close */
+/* The argument is a pointer to the socket descriptor */
+static int
+sb_stream_setup( Sockbuf_IO_Desc *sbiod, void *arg ) {
+       assert( sbiod != NULL );
+
+       if ( arg != NULL ) {
+               sbiod->sbiod_sb->sb_fd = *((int *)arg);
+       }
+       return 0;
+}
+
+static int
+sb_stream_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) {
+       /* This is an end IO descriptor */
+       return 0;
+}
+
+Sockbuf_IO ber_sockbuf_io_tcp = {
+       sb_stream_setup,        /* sbi_setup */
+       NULL,                           /* sbi_remove */
+       sb_stream_ctrl,         /* sbi_ctrl */
+       sb_stream_read,         /* sbi_read */
+       sb_stream_write,        /* sbi_write */
+       sb_stream_close         /* sbi_close */
 };
 
+
 /*
- * Support for UDP (CLDAP)
+ * Support for readahead (UDP needs it)
  */
 
-struct dgram_data
+static int
+sb_rdahead_setup( Sockbuf_IO_Desc *sbiod, void *arg )
 {
-       struct sockaddr dst;
-       struct sockaddr src;
-};
+       Sockbuf_Buf             *p;
 
-static int 
-dgram_setup( Sockbuf *sb, void *arg )
-{
-   sb->sb_iodata = malloc( sizeof( struct dgram_data ) );
-   if (sb->sb_iodata==NULL)
-     return -1;
-   sb->sb_read_ahead = 1; /* important since udp is packet based. */
-   return 0;
+       assert( sbiod != NULL );
+
+       p = LBER_MALLOC( sizeof( *p ) );
+       if ( p == NULL ) return -1;
+
+       ber_pvt_sb_buf_init( p );
+
+       if ( arg == NULL ) {
+               ber_pvt_sb_grow_buffer( p, LBER_DEFAULT_READAHEAD );
+       } else {
+               ber_pvt_sb_grow_buffer( p, *((int *)arg) );
+       }
+
+       sbiod->sbiod_pvt = p;
+       return 0;
 }
 
-static int 
-dgram_release( Sockbuf *sb )
+static int
+sb_rdahead_remove( Sockbuf_IO_Desc *sbiod )
 {
-   free( sb->sb_iodata );
-   return 0;
+       Sockbuf_Buf             *p;
+
+       assert( sbiod != NULL );
+
+       p = (Sockbuf_Buf *)sbiod->sbiod_pvt;
+
+       if ( p->buf_ptr != p->buf_end ) return -1;
+
+       ber_pvt_sb_buf_destroy( (Sockbuf_Buf *)(sbiod->sbiod_pvt) );
+       LBER_FREE( sbiod->sbiod_pvt );
+       sbiod->sbiod_pvt = NULL;
+
+       return 0;
 }
 
-static long
-dgram_read( Sockbuf *sb, void *buf, long len )
+static ber_slen_t
+sb_rdahead_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
 {
-#ifdef LDAP_CONNECTIONLESS
-   long rc;
-   int addrlen;
-   struct dgram_data *dd;
-   
-   dd = (struct dgram_data *)(sb->sb_iodata);
-   
-# if !defined( MACOS) && !defined(DOS) && !defined( _WIN32)
-   addrlen = sizeof( struct sockaddr );
-   rc=recvfrom( lber_pvt_sb_get_desc(sb), buf, len, 0, &(dd->src), &addrlen );
-# else
-   UDP not supported
-# endif
-   
-   if ( sb->sb_debug ) {
-      lber_log_printf( LDAP_DEBUG_ANY, sb->sb_debug,
-                     "dgram_read udp_read %d bytes\n",
-                     rc );
-      if ( rc > 0 )
-       lber_log_bprint( LDAP_DEBUG_PACKETS, sb->sb_debug,
-                       buf, rc );
-   }
-   return rc;
-# else /* LDAP_CONNECTIONLESS */
-   return -1;
-# endif /* LDAP_CONNECTIONLESS */
+       Sockbuf_Buf             *p;
+       ber_slen_t              bufptr = 0, ret, max;
+
+       assert( sbiod != NULL );
+       assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+       assert( sbiod->sbiod_next != NULL );
+
+       p = (Sockbuf_Buf *)sbiod->sbiod_pvt;
+
+       assert( p->buf_size > 0 );
+
+       /* Are there anything left in the buffer? */
+       ret = ber_pvt_sb_copy_out( p, buf, len );
+       bufptr += ret;
+       len -= ret;
+
+       if ( len == 0 ) return bufptr;
+
+       max = p->buf_size - p->buf_end;
+       ret = 0;
+       while ( max > 0 ) {
+               ret = LBER_SBIOD_READ_NEXT( sbiod, p->buf_base + p->buf_end,
+                       max );
+#ifdef EINTR   
+               if ( ( ret < 0 ) && ( errno == EINTR ) ) continue;
+#endif
+               break;
+       }
+
+       if ( ret < 0 ) {
+               return ( bufptr ? bufptr : ret );
+       }
+
+       p->buf_end += ret;
+       bufptr += ber_pvt_sb_copy_out( p, (char *) buf + bufptr, len );
+       return bufptr;
 }
 
-static long 
-dgram_write( Sockbuf *sb, void *buf, long len )
+static ber_slen_t
+sb_rdahead_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
 {
-#ifdef LDAP_CONNECTIONLESS
-   int rc;
-   struct dgram_data *dd;
-   
-   dd = (struct dgram_data *)(sb->sb_iodata);
-   
-# if !defined( MACOS) && !defined(DOS) && !defined( _WIN32)
-   rc=sendto( lber_pvt_sb_get_desc(sb), buf, len, 0, &(dd->dst),
-            sizeof( struct sockaddr ) );
-# else
-   UDP not supported
-# endif
-     if ( rc <= 0 )
-       return( -1 );
-   
-   /* fake error if write was not atomic */
-   if (rc < len) {
-# ifdef EMSGSIZE
-      errno = EMSGSIZE;
-# endif
-      return( -1 );
-   }
-   return rc;
-#else
-   return -1;
-#endif 
+       assert( sbiod != NULL );
+       assert( sbiod->sbiod_next != NULL );
+
+       return LBER_SBIOD_WRITE_NEXT( sbiod, buf, len );
 }
 
-static int 
-dgram_close( Sockbuf *sb )
+static int
+sb_rdahead_close( Sockbuf_IO_Desc *sbiod )
 {
-       tcp_close( lber_pvt_sb_get_desc(sb) );
+       assert( sbiod != NULL );
+
+       /* Just erase the buffer */
+       ber_pvt_sb_buf_destroy((Sockbuf_Buf *)sbiod->sbiod_pvt);
        return 0;
 }
 
-Sockbuf_IO lber_pvt_sb_io_udp=
+static int
+sb_rdahead_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
 {
-       dgram_setup,    /* sbi_setup */
-       dgram_release,  /* sbi_release */
-       dgram_read,     /* sbi_read */
-       dgram_write,    /* sbi_write */
-       dgram_close,    /* sbi_close */
+       Sockbuf_Buf             *p;
+
+       p = (Sockbuf_Buf *)sbiod->sbiod_pvt;
+
+       if ( opt == LBER_SB_OPT_DATA_READY ) {
+               if ( p->buf_ptr != p->buf_end ) {
+                       return 1;
+               }
+
+       } else if ( opt == LBER_SB_OPT_SET_READAHEAD ) {
+               if ( p->buf_size >= *((ber_len_t *)arg) ) {
+                       return 0;
+               }
+               return ( ber_pvt_sb_grow_buffer( p, *((int *)arg) ) ?
+                       -1 : 1 );
+       }
+
+       return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg );
+}
+
+Sockbuf_IO ber_sockbuf_io_readahead = {
+       sb_rdahead_setup,       /* sbi_setup */
+       sb_rdahead_remove,      /* sbi_remove */
+       sb_rdahead_ctrl,        /* sbi_ctrl */
+       sb_rdahead_read,        /* sbi_read */
+       sb_rdahead_write,       /* sbi_write */
+       sb_rdahead_close        /* sbi_close */
 };
 
-int lber_pvt_sb_udp_set_dst(Sockbuf *sb, void *addr )
+/*
+ * Support for simple file IO
+ */
+
+static ber_slen_t
+sb_fd_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
 {
-   struct dgram_data *dd;
-   assert( sb->sb_io == &lber_pvt_sb_io_udp );
-   dd = (struct dgram_data *) (sb->sb_iodata);
-   memcpy( &(dd->dst), addr, sizeof( struct sockaddr ) );
-   return 0;
+       assert( sbiod != NULL);
+       assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+       return read( sbiod->sbiod_sb->sb_fd, buf, len );
 }
 
-void *lber_pvt_sb_udp_get_src( Sockbuf *sb )
+static ber_slen_t
+sb_fd_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
 {
-   struct dgram_data *dd;
-   assert( sb->sb_io == &lber_pvt_sb_io_udp );
-   dd = (struct dgram_data *) (sb->sb_iodata);
-   return &(dd->src);
+       assert( sbiod != NULL);
+       assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+       return write( sbiod->sbiod_sb->sb_fd, buf, len );
 }
 
+static int 
+sb_fd_close( Sockbuf_IO_Desc *sbiod )
+{   
+       assert( sbiod != NULL );
+       assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+       close( sbiod->sbiod_sb->sb_fd );
+       return 0;
+}
+
+/* The argument is a pointer to the file descriptor */
+static int
+sb_fd_setup( Sockbuf_IO_Desc *sbiod, void *arg ) {
+       assert( sbiod != NULL );
+
+       if ( arg != NULL )
+               sbiod->sbiod_sb->sb_fd = *((int *)arg);
+       return 0;
+}
+
+static int
+sb_fd_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) {
+       /* This is an end IO descriptor */
+       return 0;
+}
+
+Sockbuf_IO ber_sockbuf_io_fd = {
+       sb_fd_setup,    /* sbi_setup */
+       NULL,                   /* sbi_remove */
+       sb_fd_ctrl,             /* sbi_ctrl */
+       sb_fd_read,             /* sbi_read */
+       sb_fd_write,            /* sbi_write */
+       sb_fd_close             /* sbi_close */
+};
+
 /*
- * debug routines.
- * 
- * BUGS:
- * These routines should really call abort, but at the moment that would
- * break the servers.
+ * Debugging layer
  */
 
-static long
-have_no_read( Sockbuf *sb, void *buf, long len )
+static int
+sb_debug_setup( Sockbuf_IO_Desc *sbiod, void *arg )
 {
-   lber_log_printf( LDAP_DEBUG_ANY, lber_int_debug,
-                  "warning: reading from uninitialized sockbuf\n");
-   errno =  EBADF;
-   return -1;
+       assert( sbiod != NULL );
+       
+       if ( arg == NULL ) arg = "sockbuf_";
+
+       sbiod->sbiod_pvt = LBER_MALLOC( strlen( arg ) + 1 );
+       if ( sbiod->sbiod_pvt == NULL ) return -1;
+
+       strcpy( (char *)sbiod->sbiod_pvt, (char *)arg );
+       return 0;
 }
 
-static long
-have_no_write( Sockbuf *sb, void *buf, long len )
+static int
+sb_debug_remove( Sockbuf_IO_Desc *sbiod )
 {
-   lber_log_printf( LDAP_DEBUG_ANY, lber_int_debug,
-                  "warning: writing to uninitialized sockbuf\n");
-   errno =  EBADF;
-   return -1;
+       assert( sbiod != NULL );
+       assert( sbiod->sbiod_pvt != NULL );
+
+       LBER_FREE( sbiod->sbiod_pvt );
+       sbiod->sbiod_pvt = NULL;
+       return 0;
+}
+
+static int
+sb_debug_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
+{
+       return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg );
+}
+
+static ber_slen_t
+sb_debug_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
+{
+       ber_slen_t              ret;
+
+       ret = LBER_SBIOD_READ_NEXT( sbiod, buf, len );
+       if (sbiod->sbiod_sb->sb_debug & LDAP_DEBUG_PACKETS) {
+               int err = errno;
+               if ( ret < 0 ) {
+                       ber_log_printf( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug,
+                               "%sread: want=%ld error=%s\n", (char *)sbiod->sbiod_pvt,
+                               (long)len, STRERROR( errno ) );
+               } else {
+                       ber_log_printf( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug,
+                               "%sread: want=%ld, got=%ld\n", (char *)sbiod->sbiod_pvt,
+                               (long)len, (long)ret );
+                       ber_log_bprint( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug,
+                               (const char *)buf, ret );
+               }
+               errno = err;
+       }
+       return ret;
+}
+
+static ber_slen_t
+sb_debug_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
+{
+       ber_slen_t              ret;
+
+       ret = LBER_SBIOD_WRITE_NEXT( sbiod, buf, len );
+       if (sbiod->sbiod_sb->sb_debug & LDAP_DEBUG_PACKETS) {
+               int err = errno;
+               if ( ret < 0 ) {
+                       ber_log_printf( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug,
+                               "%swrite: want=%ld error=%s\n",
+                               (char *)sbiod->sbiod_pvt, (long)len,
+                               STRERROR( errno ) );
+               } else {
+                       ber_log_printf( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug,
+                               "%swrite: want=%ld, written=%ld\n",
+                               (char *)sbiod->sbiod_pvt, (long)len, (long)ret );
+                       ber_log_bprint( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug,
+                               (const char *)buf, ret );
+               }
+               errno = err;
+       }
+
+       return ret;
 }
 
+Sockbuf_IO ber_sockbuf_io_debug = {
+       sb_debug_setup,         /* sbi_setup */
+       sb_debug_remove,        /* sbi_remove */
+       sb_debug_ctrl,          /* sbi_ctrl */
+       sb_debug_read,          /* sbi_read */
+       sb_debug_write,         /* sbi_write */
+       NULL                            /* sbi_close */
+};
+
+#ifdef LDAP_CONNECTIONLESS
+
+/*
+ * Support for UDP (CLDAP)
+ *
+ * All I/O at this level must be atomic. For ease of use, the sb_readahead
+ * must be used above this module. All data reads and writes are prefixed
+ * with a sockaddr containing the address of the remote entity. Upper levels
+ * must read and write this sockaddr before doing the usual ber_printf/scanf
+ * operations on LDAP messages.
+ */
+
 static int 
-have_no_close( Sockbuf *sb )
-{   
-   assert( 0 );
-   return -1;
+sb_dgram_setup( Sockbuf_IO_Desc *sbiod, void *arg )
+{
+       assert( sbiod != NULL);
+       assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+       if ( arg != NULL ) sbiod->sbiod_sb->sb_fd = *((int *)arg);
+       return 0;
+}
+
+static ber_slen_t
+sb_dgram_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
+{
+       ber_slen_t rc;
+       socklen_t  addrlen;
+       struct sockaddr *src;
+   
+       assert( sbiod != NULL );
+       assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+       assert( buf != NULL );
+
+       addrlen = sizeof( struct sockaddr );
+       src = buf;
+       buf += addrlen;
+       len -= addrlen;
+       rc = recvfrom( sbiod->sbiod_sb->sb_fd, buf, len, 0, src, &addrlen );
+
+       return rc > 0 ? rc+sizeof(struct sockaddr) : rc;
 }
+
+static ber_slen_t 
+sb_dgram_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
+{
+       ber_slen_t rc;
+       struct sockaddr *dst;
+   
+       assert( sbiod != NULL );
+       assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+       assert( buf != NULL );
+
+       dst = buf;
+       buf += sizeof( struct sockaddr );
+       len -= sizeof( struct sockaddr );
+   
+       rc = sendto( sbiod->sbiod_sb->sb_fd, buf, len, 0, dst,
+               sizeof( struct sockaddr ) );
+
+       if ( rc < 0 ) return -1;
+   
+       /* fake error if write was not atomic */
+       if (rc < len) {
+# ifdef EMSGSIZE
+               errno = EMSGSIZE;
+# endif
+               return -1;
+       }
+       rc = len + sizeof(struct sockaddr);
+       return rc;
+}
+
+static int 
+sb_dgram_close( Sockbuf_IO_Desc *sbiod )
+{
+       assert( sbiod != NULL );
+       assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+       tcp_close( sbiod->sbiod_sb->sb_fd );
+       return 0;
+}
+
+static int
+sb_dgram_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
+{
+       /* This is an end IO descriptor */
+       return 0;
+}
+
+Sockbuf_IO ber_sockbuf_io_udp =
+{
+       sb_dgram_setup,         /* sbi_setup */
+       NULL,                   /* sbi_remove */
+       sb_dgram_ctrl,          /* sbi_ctrl */
+       sb_dgram_read,          /* sbi_read */
+       sb_dgram_write,         /* sbi_write */
+       sb_dgram_close          /* sbi_close */
+};
+
+#endif /* LDAP_CONNECTIONLESS */