diff --git a/include/lber.h b/include/lber.h
index 515b45b19b929fc82c0205f4866cbe5424460b51..fb49db32b2b0af90c83b97a26f441bb9ab8253e0 100644
--- a/include/lber.h
+++ b/include/lber.h
@@ -123,11 +123,26 @@ typedef struct lber_memory_fns {
 	BER_MEMFREE_FN bmf_free;
 } BerMemoryFunctions;
 
-/* LBER Sockbuf options */ 
-#define LBER_TO_FILE           0x01	/* to a file referenced by sb_fd   */
-#define LBER_TO_FILE_ONLY      0x02	/* only write to file, not network */
-#define LBER_MAX_INCOMING_SIZE 0x04	/* impose limit on incoming stuff  */
-#define LBER_NO_READ_AHEAD     0x08	/* read only as much as requested  */
+/* LBER Sockbuf_IO options */ 
+#define LBER_SB_OPT_GET_FD		1
+#define LBER_SB_OPT_SET_FD		2
+#define LBER_SB_OPT_HAS_IO		3
+#define LBER_SB_OPT_SET_NONBLOCK	4
+#define LBER_SB_OPT_UDP_GET_SRC		5
+#define LBER_SB_OPT_UDP_SET_DST		6
+#define LBER_SB_OPT_GET_SSL		7
+#define LBER_SB_OPT_DATA_READY		8
+#define LBER_SB_OPT_SET_READAHEAD	9
+#define LBER_SB_OPT_DRAIN		10
+#define LBER_SB_OPT_NEEDS_READ		11
+#define LBER_SB_OPT_NEEDS_WRITE		12
+/* Largest option used by the library */
+#define LBER_SB_OPT_OPT_MAX		12
+
+/* LBER IO operations stacking levels */
+#define LBER_SBIOD_LEVEL_PROVIDER	10
+#define LBER_SBIOD_LEVEL_TRANSPORT	20
+#define LBER_SBIOD_LEVEL_APPLICATION	30
 
 /* get/set options for Sockbuf */
 #define LBER_OPT_SOCKBUF_DESC		0x1000
@@ -145,6 +160,43 @@ typedef struct berelement BerElement;
 typedef struct sockbuf Sockbuf;
 typedef struct seqorset Seqorset;
 
+typedef struct sockbuf_io Sockbuf_IO;
+
+/* Structure for LBER IO operarion descriptor */
+typedef struct sockbuf_io_desc {
+	int			sbiod_level;
+	Sockbuf			*sbiod_sb;
+	Sockbuf_IO		*sbiod_io;
+	void 			*sbiod_pvt;
+	struct sockbuf_io_desc	*sbiod_next;
+} Sockbuf_IO_Desc;
+
+/* Structure for LBER IO operation functions */
+struct sockbuf_io {
+	int (*sbi_setup)( Sockbuf_IO_Desc *sbiod, void *arg );
+	int (*sbi_remove)( Sockbuf_IO_Desc *sbiod );
+	int (*sbi_ctrl)( Sockbuf_IO_Desc *sbiod, int opt, void *arg);
+	
+	ber_slen_t (*sbi_read)( Sockbuf_IO_Desc *sbiod, void *buf,
+		ber_len_t len );
+	ber_slen_t (*sbi_write)( Sockbuf_IO_Desc *sbiod, void *buf,
+		ber_len_t len );
+	
+	int (*sbi_close)( Sockbuf_IO_Desc *sbiod );
+};
+
+/* Helper macros for LBER IO functions */
+#define LBER_SBIOD_READ_NEXT( sbiod, buf, len ) \
+	( (sbiod)->sbiod_next->sbiod_io->sbi_read( (sbiod)->sbiod_next, \
+		buf, len ) )
+#define LBER_SBIOD_WRITE_NEXT( sbiod, buf, len ) \
+	( (sbiod)->sbiod_next->sbiod_io->sbi_write( (sbiod)->sbiod_next, \
+		buf, len ) )
+#define LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg ) \
+	( (sbiod)->sbiod_next ? \
+		( (sbiod)->sbiod_next->sbiod_io->sbi_ctrl( \
+		(sbiod)->sbiod_next, opt, arg ) ) : 0 )
+
 /* structure for returning a sequence of octet strings + length */
 typedef struct berval {
 	ber_len_t	bv_len;
@@ -428,16 +480,38 @@ ber_set_option LDAP_P((
  * LBER sockbuf.c
  */
 
-LIBLBER_F( Sockbuf * )
-ber_sockbuf_alloc( void );
-
 LIBLBER_F( Sockbuf *  )
-ber_sockbuf_alloc_fd(
-	ber_socket_t fd );
+ber_sockbuf_alloc LDAP_P((
+	void ));
 
 LIBLBER_F( void )
-ber_sockbuf_free(
-	Sockbuf *sb );
+ber_sockbuf_free LDAP_P((
+	Sockbuf *sb ));
+
+LIBLBER_F( int )
+ber_sockbuf_add_io LDAP_P((
+	Sockbuf *sb,
+	Sockbuf_IO *sbio,
+	int layer,
+	void *arg ));
+
+LIBLBER_F( int )
+ber_sockbuf_remove_io LDAP_P((
+	Sockbuf *sb,
+	Sockbuf_IO *sbio,
+	int layer ));
+
+LIBLBER_F( int )
+ber_sockbuf_ctrl LDAP_P((
+	Sockbuf *sb,
+	int opt,
+	void *arg ));
+
+LIBLBER_F( Sockbuf_IO ) ber_sockbuf_io_tcp;
+LIBLBER_F( Sockbuf_IO ) ber_sockbuf_io_udp;
+LIBLBER_F( Sockbuf_IO ) ber_sockbuf_io_readahead;
+LIBLBER_F( Sockbuf_IO ) ber_sockbuf_io_fd;
+LIBLBER_F( Sockbuf_IO ) ber_sockbuf_io_debug;
 
 /*
  * LBER memory.c
diff --git a/include/lber_pvt.h b/include/lber_pvt.h
index 2a57ba11c642146fe49cabee5bf330587eccc2d5..e314205f4781fb096aff0f1516ea97357b3f0b0b 100644
--- a/include/lber_pvt.h
+++ b/include/lber_pvt.h
@@ -20,6 +20,13 @@
 
 LDAP_BEGIN_DECL
 
+typedef struct sockbuf_buf {
+	ber_len_t		buf_size;
+	ber_len_t		buf_ptr;
+	ber_len_t		buf_end;
+	char			*buf_base;
+} Sockbuf_Buf;
+
 /*
  * bprint.c
  */
@@ -32,6 +39,27 @@ ber_pvt_log_printf LDAP_P((
 	const char *fmt,
 	... )) LDAP_GCCATTR((format(printf, 3, 4)));
 
+/*
+ * sockbuf.c
+ */
+LIBLBER_F( ber_slen_t )
+ber_pvt_sb_do_write LDAP_P(( Sockbuf_IO_Desc *sbiod, Sockbuf_Buf *buf_out ));
+
+LIBLBER_F( void )
+ber_pvt_sb_buf_init LDAP_P(( Sockbuf_Buf *buf ));
+
+LIBLBER_F( void )
+ber_pvt_sb_buf_destroy LDAP_P(( Sockbuf_Buf *buf ));
+
+LIBLBER_F( int )
+ber_pvt_sb_grow_buffer LDAP_P(( Sockbuf_Buf *buf, ber_len_t minsize ));
+
+LIBLBER_F( ber_len_t )
+ber_pvt_sb_copy_out LDAP_P(( Sockbuf_Buf *sbb, char *buf, ber_len_t len ));
+
+LIBLBER_F( int )
+ber_pvt_socket_set_nonblock LDAP_P(( ber_socket_t sd, int nb ));
+
 LDAP_END_DECL
 
 #endif
diff --git a/libraries/liblber/dtest.c b/libraries/liblber/dtest.c
index 144445ece92cc9d5a9eec1b1a177cfb23c418b20..633d42ed7edaaf8ffa3652fbc46af920dee91132 100644
--- a/libraries/liblber/dtest.c
+++ b/libraries/liblber/dtest.c
@@ -47,6 +47,7 @@ main( int argc, char **argv )
 
 	BerElement	*ber;
 	Sockbuf		*sb;
+	int		fd;
 
 	/* enable debugging */
 	int ival = -1;
@@ -62,7 +63,10 @@ main( int argc, char **argv )
 	cshow( stdout );
 #endif
 
-	sb = ber_sockbuf_alloc_fd( fileno(stdin) );
+	sb = ber_sockbuf_alloc();
+	fd = fileno( stdin );
+	ber_sockbuf_add_io( sb, &ber_sockbuf_io_fd, LBER_SBIOD_LEVEL_PROVIDER,
+		(void *)&fd );
 
 	if( (ber = ber_alloc_t(LBER_USE_DER)) == NULL ) {
 		perror( "ber_alloc_t" );
diff --git a/libraries/liblber/etest.c b/libraries/liblber/etest.c
index 0dcf7d71117c780ad42e990dcac3183cf2cf54fb..01c10e99071c2a7ef43d51e90d817b82afc28d2d 100644
--- a/libraries/liblber/etest.c
+++ b/libraries/liblber/etest.c
@@ -75,7 +75,9 @@ main( int argc, char **argv )
 	fd = fileno(stdout);
 #endif
 
-	sb = ber_sockbuf_alloc_fd( fd );
+	sb = ber_sockbuf_alloc();
+	ber_sockbuf_add_io( sb, &ber_sockbuf_io_fd, LBER_SBIOD_LEVEL_PROVIDER,
+		(void *)&fd );
 
 	if( sb == NULL ) {
 		perror( "ber_sockbuf_alloc_fd" );
diff --git a/libraries/liblber/io.c b/libraries/liblber/io.c
index a31d23daf52e79766b44a6cd80c05066e14eda48..859b70c13726b03a809acf1f8b8d3e07e1445ed5 100644
--- a/libraries/liblber/io.c
+++ b/libraries/liblber/io.c
@@ -64,7 +64,7 @@ BerRead(
 	assert( SOCKBUF_VALID( sb ) );
 
 	while ( len > 0 ) {
-		if ( (c = ber_pvt_sb_read( sb, buf, len )) <= 0 ) {
+		if ( (c = ber_int_sb_read( sb, buf, len )) <= 0 ) {
 			if ( nread > 0 )
 				break;
 			return( c );
@@ -232,26 +232,15 @@ ber_flush( Sockbuf *sb, BerElement *ber, int freeit )
 	if ( sb->sb_debug ) {
 		ber_log_printf( LDAP_DEBUG_ANY, sb->sb_debug,
 			"ber_flush: %ld bytes to sd %ld%s\n", towrite,
-		    (long) sb->sb_sd, ber->ber_rwptr != ber->ber_buf ? " (re-flush)"
-		    : "" );
+		    (long) sb->sb_fd, ber->ber_rwptr != ber->ber_buf ?
+		    " (re-flush)" : "" );
 		ber_log_bprint( LDAP_DEBUG_PACKETS, sb->sb_debug,
 			ber->ber_rwptr, towrite );
 	}
 
-#if HAVE_WRITE
-	if ( sb->sb_options & (LBER_TO_FILE | LBER_TO_FILE_ONLY) ) {
-		rc = write( sb->sb_fd, ber->ber_rwptr, towrite );
-		if ( sb->sb_options & LBER_TO_FILE_ONLY ) {
-			if ( freeit )
-				ber_free( ber, 1 );
-			return( (int)rc );
-		}
-	}
-#endif
-	
 	nwritten = 0;
 	do {
-		rc = ber_pvt_sb_write( sb, ber->ber_rwptr, towrite );
+		rc = ber_int_sb_write( sb, ber->ber_rwptr, towrite );
 		if (rc<=0) {
 			return -1;
 		}
@@ -446,7 +435,7 @@ get_tag( Sockbuf *sb )
 	assert( sb != NULL );
 	assert( SOCKBUF_VALID( sb ) );
 
-	if ( ber_pvt_sb_read( sb, (char *) &xbyte, 1 ) != 1 )
+	if ( ber_int_sb_read( sb, (char *) &xbyte, 1 ) != 1 )
 		return( LBER_DEFAULT );
 
 	if ( (xbyte & LBER_BIG_TAG_MASK) != LBER_BIG_TAG_MASK )
@@ -455,7 +444,7 @@ get_tag( Sockbuf *sb )
 	tagp = (char *) &tag;
 	tagp[0] = xbyte;
 	for ( i = 1; i < sizeof(ber_tag_t); i++ ) {
-		if ( ber_pvt_sb_read( sb, (char *) &xbyte, 1 ) != 1 )
+		if ( ber_int_sb_read( sb, (char *) &xbyte, 1 ) != 1 )
 			return( LBER_DEFAULT );
 
 		tagp[i] = xbyte;
@@ -522,7 +511,7 @@ ber_get_next(
 	
 	if (PTR_IN_VAR(ber->ber_rwptr, ber->ber_tag)) {
 		if (ber->ber_rwptr == (char *) &ber->ber_tag) {
-			if (ber_pvt_sb_read( sb, ber->ber_rwptr, 1)<=0)
+			if (ber_int_sb_read( sb, ber->ber_rwptr, 1)<=0)
 				return LBER_DEFAULT;
 			if ((ber->ber_rwptr[0] & LBER_BIG_TAG_MASK)
 				!= LBER_BIG_TAG_MASK) {
@@ -534,7 +523,7 @@ ber_get_next(
 		}
 		do {
 			/* reading the tag... */
-			if (ber_pvt_sb_read( sb, ber->ber_rwptr, 1)<=0)
+			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) -
@@ -550,7 +539,7 @@ ber_get_next(
 get_lenbyte:
 	if (ber->ber_rwptr==(char *) &ber->ber_usertag) {
 		unsigned char c;
-		if (ber_pvt_sb_read( sb, (char *) &c, 1)<=0)
+		if (ber_int_sb_read( sb, (char *) &c, 1)<=0)
 			return LBER_DEFAULT;
 		if (c & 0x80U) {
 			int len = c & 0x7fU;
@@ -611,7 +600,7 @@ fill_buffer:
 		to_go = ber->ber_end - ber->ber_rwptr;
 		assert( to_go > 0 );
 		
-		res = ber_pvt_sb_read( sb, ber->ber_rwptr, to_go );
+		res = ber_int_sb_read( sb, ber->ber_rwptr, to_go );
 		if (res<=0)
 			return LBER_DEFAULT;
 		ber->ber_rwptr+=res;
diff --git a/libraries/liblber/lber-int.h b/libraries/liblber/lber-int.h
index 10b8b2cbd7410cc2d83edfee79ad5e2ed710aafd..70d10c044778d6a3f0c5334ac56c7d9cf4b0fd9d 100644
--- a/libraries/liblber/lber-int.h
+++ b/libraries/liblber/lber-int.h
@@ -28,15 +28,15 @@ LIBLBER_F (BER_ERRNO_FN) ber_int_errno_fn;
 
 struct lber_options {
 	short lbo_valid;
+	unsigned short		lbo_options;
+	int			lbo_debug;
+};
+
 #define LBER_UNINITIALIZED		0x0
 #define LBER_INITIALIZED		0x1
 #define LBER_VALID_BERELEMENT	0x2
 #define LBER_VALID_SOCKBUF		0x3
 
-	unsigned short lbo_options;
-	int lbo_debug;
-};
-
 LIBLBER_F (struct lber_options) ber_int_options;
 #define ber_int_debug ber_int_options.lbo_debug
 
@@ -62,106 +62,21 @@ struct berelement {
 };
 #define BER_VALID(ber)	((ber)->ber_valid==LBER_VALID_BERELEMENT)
 
-
 #define ber_pvt_ber_bytes(ber)		((ber)->ber_ptr - (ber)->ber_buf)
 #define ber_pvt_ber_remaining(ber)	((ber)->ber_end - (ber)->ber_ptr)
 
-struct sockbuf;
-
-struct sockbuf_io {
-	int	(*sbi_setup)( struct sockbuf * sb, void *arg );
-	int	(*sbi_remove)( struct sockbuf *sb );
-	
-	ber_slen_t	(*sbi_read)( struct sockbuf *sb, void *buf, ber_len_t len );
-	ber_slen_t	(*sbi_write)( struct sockbuf *sb, void *buf, ber_len_t len );
-	int	(*sbi_close)( struct sockbuf *sb );
-};
-
-struct sockbuf_sec {
-	int	(*sbs_setup)( struct sockbuf * sb, void *arg );
-	int	(*sbs_remove)( struct sockbuf *sb );
-   
-	long	(*sbs_protect)( struct sockbuf *sb, char *in, long *ilen,
-			        char *out, long olen );
-	long	(*sbs_release)( struct sockbuf *sb, char *in, long ilen,
-			       char *out0, long olen0, char *out1, long olen1 );
-};
-
-struct sockbuf_buf {
-	ber_len_t	buf_size;
-	ber_len_t	buf_ptr;
-	ber_len_t	buf_end;
-	char	*buf_base;
-};
-
-typedef struct sockbuf_io Sockbuf_IO;
-typedef struct sockbuf_sec Sockbuf_Sec;
-typedef struct sockbuf_buf Sockbuf_Buf;
-
-LIBLBER_F( Sockbuf_IO ) ber_pvt_sb_io_tcp;
-LIBLBER_F( Sockbuf_IO ) ber_pvt_sb_io_udp;
-
-
 struct sockbuf {
 	struct lber_options sb_opts;
+	Sockbuf_IO_Desc		*sb_iod;		/* I/O functions */
 #define	sb_valid		sb_opts.lbo_valid
 #define	sb_options		sb_opts.lbo_options
 #define	sb_debug		sb_opts.lbo_debug
-
-	int		sb_non_block:1;	
-	int		sb_read_ahead:1;
-   
-	int		sb_buf_ready:1;
-	int		sb_trans_ready:1;
-   	int		sb_sec_ready:1;
-      
-   	/* these bits indicate if the transport layer 
-	 * needs to read or write 
-	 */
+	ber_socket_t		sb_fd;
    	int		sb_trans_needs_read:1;
    	int		sb_trans_needs_write:1;
-
-   	int		sb_fd;
-   
-	void		*sb_iodata;	/* transport-layer data pointer */
-	Sockbuf_IO	*sb_io;		/* I/O functions */
-   
-#ifdef LDAP_SASL
-   	void		*sb_sdata;	/* security-layer data pointer */
-	Sockbuf_Sec	*sb_sec;
-#endif	
-
-	ber_socket_t	sb_sd;
-
-#ifdef DEADWOOD
-	long		sb_max_incoming;
-#endif
-	Sockbuf_Buf	sb_buf;
-#ifdef LDAP_SASL   
-	Sockbuf_Buf	sb_sec_buf_in;
-	Sockbuf_Buf	sb_sec_buf_out;
-	ber_len_t	sb_sec_prev_len;
-#endif   
 };
-#define SOCKBUF_VALID(ber)	((sb)->sb_valid==LBER_VALID_SOCKBUF)
 
-/* these should be internal ie: ber_int_* */
-#define	ber_pvt_sb_get_desc( sb ) ((sb)->sb_sd)
-#define ber_pvt_sb_set_desc( sb, val ) ((sb)->sb_sd =(val))
-
-#define ber_pvt_sb_in_use( sb ) ((sb)->sb_sd != AC_SOCKET_INVALID)
-
-#ifdef USE_SASL
-#define ber_pvt_sb_data_ready( sb ) \
-(((sb)->sb_buf_ready) || ((sb)->sb_trans_ready) || ((sb)->sb_sec_ready))
-#else
-#define ber_pvt_sb_data_ready( sb ) \
-(((sb)->sb_buf_ready) || ((sb)->sb_trans_ready))
-#endif
-#define ber_pvt_sb_needs_read( sb ) \
-((sb)->sb_trans_needs_read)
-#define ber_pvt_sb_needs_write( sb ) \
-((sb)->sb_trans_needs_write)
+#define SOCKBUF_VALID( sb )	( (sb)->sb_valid == LBER_VALID_SOCKBUF )
 
 #define READBUFSIZ	8192
 
@@ -245,51 +160,20 @@ LIBLBER_F (BerMemoryFunctions *)	ber_int_memory_fns;
 
 /* sockbuf.c */
 
-/* these should be ber_int*() functions */
-
-LIBLBER_F( int )
-ber_pvt_sb_init LDAP_P(( Sockbuf *sb ));
-
 LIBLBER_F(	int )
-ber_pvt_sb_destroy LDAP_P(( Sockbuf *sb ));
+ber_int_sb_init LDAP_P(( Sockbuf *sb ));
 
-#ifdef USE_SASL
 LIBLBER_F( int )
-ber_pvt_sb_set_sec LDAP_P(( Sockbuf *sb, Sockbuf_Sec *sec, void *arg ));
-
-LIBLBER_F( int )
-ber_pvt_sb_clear_sec LDAP_P(( Sockbuf *sb ));
-#endif
+ber_int_sb_close LDAP_P(( Sockbuf *sb ));
 
 LIBLBER_F(	int )
-ber_pvt_sb_set_io LDAP_P(( Sockbuf *sb, Sockbuf_IO *layer, void *arg ));
-
-LIBLBER_F(	int )
-ber_pvt_sb_clear_io LDAP_P(( Sockbuf *sb ));
-
-LIBLBER_F(	int )
-ber_pvt_sb_close LDAP_P((Sockbuf *sb ));
-
-LIBLBER_F( int )
-ber_pvt_sb_set_nonblock LDAP_P(( Sockbuf *sb, int nb ));
-
-LIBLBER_F( int )
-ber_pvt_sb_set_readahead LDAP_P(( Sockbuf *sb, int rh ));
+ber_int_sb_destroy LDAP_P(( Sockbuf *sb ));
 
 LIBLBER_F( ber_slen_t )
-ber_pvt_sb_read LDAP_P(( Sockbuf *sb, void *buf, ber_len_t len ));
+ber_int_sb_read LDAP_P(( Sockbuf *sb, void *buf, ber_len_t len ));
 
 LIBLBER_F( ber_slen_t )
-ber_pvt_sb_write LDAP_P(( Sockbuf *sb, void *buf, ber_len_t len ));
-
-LIBLBER_F(	int )
-ber_pvt_sb_udp_set_dst LDAP_P((Sockbuf *sb, void *addr ));
-
-LIBLBER_F(	void * )
-ber_pvt_sb_udp_get_src LDAP_P((Sockbuf *sb ));
-
-LIBLBER_F( int )
-ber_pvt_socket_set_nonblock LDAP_P(( ber_socket_t sd, int nb ));
+ber_int_sb_write LDAP_P(( Sockbuf *sb, void *buf, ber_len_t len ));
 
 LDAP_END_DECL
 
diff --git a/libraries/liblber/sockbuf.c b/libraries/liblber/sockbuf.c
index 2b9bb8799a4cba647ec07956b296c81223f6a974..5cdda86ad93c7a8363a6d7b0cc2f219702debad5 100644
--- a/libraries/liblber/sockbuf.c
+++ b/libraries/liblber/sockbuf.c
@@ -29,630 +29,266 @@
 
 #include "lber-int.h"
 
-#ifdef LDAP_TEST
-#undef TEST_PARTIAL_READ
-#undef TEST_PARTIAL_WRITE
-#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) ))
+#define MIN_BUFF_SIZE		4096
+#define MAX_BUFF_SIZE		65536
+#define DEFAULT_READAHEAD	16384
 
-#define sockbuf_io_read( sb, buf, len ) \
-((sb)->sb_io->sbi_read( (sb), (buf), (len) ))
+Sockbuf *
+ber_sockbuf_alloc( void )
+{
+	Sockbuf			*sb;
 
-static ber_slen_t have_no_read( Sockbuf *sb, void *buf, ber_len_t len );
-static ber_slen_t have_no_write( Sockbuf *sb, void *buf, ber_len_t len );
-static int have_no_close( Sockbuf *sb );
+	ber_int_options.lbo_valid = LBER_INITIALIZED;
 
-static Sockbuf_IO sb_IO_None=
-{
-	NULL,	/* sbi_setup */
-	NULL,	/* sbi_release */
-	have_no_read,	/* sbi_read */
-	have_no_write,	/* sbi_write */
-	have_no_close	/* sbi_close */
-};
+	sb = LBER_CALLOC( 1, sizeof( Sockbuf ) );
 
-static void
-update_status( Sockbuf *sb )
-{
-	assert( sb != NULL );
-	assert( SOCKBUF_VALID( sb ) );
+	if( sb == NULL )
+		return NULL;
 
-   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;
-#ifdef USE_SASL
-	int osr;
-#endif
-
 	assert( sb != NULL );
 	assert( SOCKBUF_VALID( sb ) );
 
-	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;
+	ber_int_sb_close( sb );
+	ber_int_sb_destroy( sb );
+	LBER_FREE( sb );
 }
-#endif
 
-#ifdef USE_SASL
-static ber_len_t
-packet_length( Sockbuf *sb, const char *buf )
+/* 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 )
 {
-   ber_len_t size;
+	Sockbuf_IO_Desc		*p;
+	int			ret = 0;
+	char			buf[4096];
 
-   assert( buf != NULL );
+	assert( sb != NULL );
 
-   size = (((ber_len_t)buf[0])<<24)|
-     (((ber_len_t)buf[1])<<16)|
-     (((ber_len_t)buf[2])<<8)|
-     (((ber_len_t)buf[3]));
-   
-   if ( size > MAX_BUF_SIZE ) {
-      /* somebody is trying to mess me up. */
-      ber_log_printf( LDAP_DEBUG_SASL, sb->sb_debug,
-		      "SASL: received packet length of %lu bytes\n",
-		      (unsigned long) size );      
-      size = 16; /* this should lead to an error. */
+	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,
+				(int)arg ) ? -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
+			 */
+			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;
+		default:
+			ret = sb->sb_iod->sbiod_io->sbi_ctrl( sb->sb_iod,
+				opt, arg );
+			break;
    }
-   
-   return size + 4; /* include the size !!! */
-}
-#endif
-
-static int
-grow_buffer( Sockbuf_Buf * buf, ber_len_t minsize )
-{
-   ber_len_t pw;;
-   
-   assert( buf != NULL );
 
-   for( pw=MIN_BUF_SIZE; pw<minsize; pw<<=1 ) {
-      if (pw > MAX_BUF_SIZE) {
-	 /* this could mean that somebody is trying to crash us. */
-	 return -1;
-      }
-   }
-   minsize = pw;
-
-   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)
-	   LBER_FREE( buf->buf_base );
-	 assert( buf->buf_ptr==0 );
-	 assert( buf->buf_end==0 );
-	 buf->buf_base = LBER_MALLOC( minsize );
-	 if (buf->buf_base==NULL)
-	   return -1;
-      } else {
-	 char *nb;
-	 nb = LBER_REALLOC( buf->buf_base, minsize );
-	 if (nb==NULL)
-	   return -1;
-	 buf->buf_base = nb;
-      }
-      buf->buf_size = minsize;
-   }
-   return 0;
+	return ret;
 }
 
-#ifdef USE_SASL
-static ber_slen_t
-sockbuf_sec_release( Sockbuf *sb, char *buf, ber_len_t len )
+int
+ber_sockbuf_add_io( Sockbuf *sb, Sockbuf_IO *sbio, int layer, void *arg )
 {
-   /* 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;
+	Sockbuf_IO_Desc		*d, *p, **q;
    
-    assert( buf != NULL );
 	assert( sb != NULL );
 	assert( SOCKBUF_VALID( sb ) );
-
-   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 );
-
-   assert( status_is_ok(sb) );
    
-   total = 0;
+	if ( sbio == NULL )
+		return -1;
    
-   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;
+	q = &sb->sb_iod;
+	p = *q;
+	while ( p && p->sbiod_level > layer ) {
+		q = &p->sbiod_next;
+		p = *q;
+	}
    
-   sb->sb_sec_ready = 1;
+	d = LBER_MALLOC( sizeof( *d ) );
+	if ( d == NULL )
+		return -1;
    
-   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;
+	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;
       
-      if (ptr+4<=end)
-	size = packet_length( sb, ptr ); 
-      /* size is always at least 4, so the loop condition is always OK !!*/
-      assert( size>=4 );
+	if ( sbio->sbi_setup != NULL && ( sbio->sbi_setup( d, arg ) < 0 ) )
+		return -1;
       
-      if (rlen<len) {
-	 len-=rlen;
-	 buf+=rlen;
-      } else {
-	 sb->sb_buf_ready = (sb->sb_buf.buf_end = rlen - len) ? 1 : 0;
-	 break;
-      }
+	return 0;
    }
    
-   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;
-}
-
-static long
-sockbuf_sec_protect( Sockbuf *sb, char *buf, long len )
+int
+ber_sockbuf_remove_io( Sockbuf *sb, Sockbuf_IO *sbio, int layer )
 {
-   long ret;
-   long blen;
-   long total;
-   
-   assert( buf != NULL );
+	Sockbuf_IO_Desc		*p, **q;
 
    assert( sb != NULL );
 	assert( SOCKBUF_VALID( sb ) );
-
-   assert( sb->sb_sec_out.buf_end == 0 );
-   assert( sb->sb_sec_out.buf_ptr == 0 );
-   
-   assert( sb->sb_sec );
-   assert( sb->sb_sec->sbs_protect );
    
-   assert( status_is_ok(sb) );
-   
-   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) */
+	if ( sb->sb_iod == NULL )
+		return -1;
+   
+	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;
-      }
-      sb->sb_sec_out.buf_end += ret;
-      len -= blen;
-      total += blen;
    }
-   assert( status_is_ok(sb) );
-   return total;
+		q = &p->sbiod_next;
 }
-#endif
-
-static ber_len_t 
-sockbuf_copy_out( Sockbuf *sb, char **buf, ber_len_t len )
-{
-   ber_len_t blen = (sb->sb_buf.buf_end - sb->sb_buf.buf_ptr );
 
-   assert( buf != NULL );
-
-   assert( sb != NULL );
-	assert( SOCKBUF_VALID( sb ) );
-   assert( status_is_ok(sb) );
-
-   if (blen) {
-      ber_len_t 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;
+	return 0;
 }
 
-Sockbuf *ber_sockbuf_alloc( void )
+void
+ber_pvt_sb_buf_init( Sockbuf_Buf *buf )
 {
-	Sockbuf *sb;
-
-	ber_int_options.lbo_valid = LBER_INITIALIZED;
-
-	sb = LBER_CALLOC(1, sizeof(Sockbuf));
-
-	if( sb == NULL ) return NULL;
-
-	ber_pvt_sb_init( sb );
-	return sb;
+	buf->buf_base = NULL;
+	buf->buf_ptr = 0;
+	buf->buf_end = 0;
+	buf->buf_size = 0;
 }
 
-Sockbuf *ber_sockbuf_alloc_fd( ber_socket_t fd )
+void
+ber_pvt_sb_buf_destroy( Sockbuf_Buf *buf )
 {
-	Sockbuf *sb = ber_sockbuf_alloc();
-
-	if( sb == NULL ) return NULL;
-
-	ber_pvt_sb_set_desc( sb, fd );
-   	ber_pvt_sb_set_io( sb, &ber_pvt_sb_io_tcp, NULL );
-	return sb;
-}
+	assert( buf != NULL);
 
-void ber_sockbuf_free( Sockbuf *sb )
-{
-	assert(sb != NULL);
-	assert( SOCKBUF_VALID( sb ) );
-	ber_pvt_sb_destroy( sb );
-	LBER_FREE(sb);
+	if (buf->buf_base)
+		LBER_FREE( buf->buf_base );
+	ber_pvt_sb_buf_init( buf );
 }
 
-ber_slen_t 
-ber_pvt_sb_read( Sockbuf *sb, void *buf_arg, ber_len_t len )
+int
+ber_pvt_sb_grow_buffer( Sockbuf_Buf *buf, ber_len_t minsize )
 {
-   char *buf;
-   ber_slen_t ret;
+	ber_len_t		pw;
+	char			*p;
    
-   assert( buf_arg != NULL );
-   assert( sb != NULL );
-   assert( SOCKBUF_VALID( sb ) );
-   assert( status_is_ok(sb) );
-
-   /* slapd might have problems with this */
-   assert( ber_pvt_sb_in_use( sb ) );
+	assert( buf != NULL );
 
-#ifdef TEST_PARTIAL_READ
-   if ((rand() & 3)==1) { /* 1 out of 4 */
-      errno = EWOULDBLOCK;
+	for ( pw = MIN_BUFF_SIZE; pw < minsize; pw <<= 1 ) {
+		if (pw > MAX_BUFF_SIZE)
       return -1;
    }
 
-   if( len > 0 )
-	   len = (rand() % len)+1;
-#endif   
-   
-   buf = (char *) buf_arg;
-
-   if (sb->sb_buf.buf_ptr!=sb->sb_buf.buf_end) {
-      len = sockbuf_copy_out( sb, &buf, len );
-      if (len==0) {
-	 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;
    }
-
-#ifdef USE_SASL
-   if (sb->sb_sec) {
-      ber_slen_t 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 */
-	 for(;;) {
-	    ret = sockbuf_io_read( sb, sb->sb_sec_buf_in.buf_base +
-				  sb->sb_sec_buf_in.buf_ptr, max );
-#ifdef EINTR
-	    if ((ret<0) && (errno==EINTR))
-	      continue;
-#endif
-	    break;
-	 }
-	 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->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->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) {
-	 ber_slen_t max;
-	 max = sb->sb_buf.buf_size - sb->sb_buf.buf_end;
-	 if (max> (ber_slen_t) len) {
-	    for(;;) {
-	       ret = sockbuf_io_read( sb, 
-				     sb->sb_buf.buf_base +
-				     sb->sb_buf.buf_end,
-				     max );
-#ifdef EINTR	       
-	       if ((ret<0) && (errno==EINTR))
-		 continue;
-#endif
-	       break;
-	    }
-	    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. */
-      for(;;) {
-	 ret = sockbuf_io_read( sb, buf, len );
-#ifdef EINTR	 
-	 if ((ret<0) && (errno==EINTR))
-	   continue;
-#endif
-	 break;
-      }
-      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));
+	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;
-   ber_slen_t   ret;
+	ber_len_t		max;
 
-   assert( sb != NULL );
-	assert( SOCKBUF_VALID( sb ) );
-
-   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... */
-   for(;;) {
-      ret = sockbuf_io_write( sb, sb->sb_sec_out.buf_base+
-			     sb->sb_sec_out.buf_ptr, to_go );
-#ifdef EINTR
-      if ((ret<0) && (errno==EINTR))
-	continue;
-#endif
-      break;
+	assert( buf != NULL );
+	assert( sbb != NULL );
+	assert( sbb->buf_size > 0 );
+
+	max = sbb->buf_end - sbb->buf_ptr;
+	max = ( max < len) ? max : len;
+	if ( max ) {
+		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;
    }
-   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;
+	return max;
 }
-#endif
 
-ber_slen_t ber_pvt_sb_write( Sockbuf *sb, void *buf, ber_len_t len_arg )
+ber_slen_t
+ber_pvt_sb_do_write( Sockbuf_IO_Desc *sbiod, Sockbuf_Buf *buf_out )
 {
+	ber_len_t		to_go;
    ber_slen_t ret;
-   ber_len_t len = len_arg;
 
-	assert( buf != NULL );
-	assert( sb != NULL );
-	assert( SOCKBUF_VALID( sb ) );
-   assert( status_is_ok(sb) );
-
-   /* slapd might have problems with this */
-   assert( ber_pvt_sb_in_use( sb ) );
-
-#ifdef TEST_PARTIAL_WRITE
-   if ((rand() & 3)==1) { /* 1 out of 4 */
-      errno = EWOULDBLOCK;
-      return -1;
-   }
+	assert( sbiod != NULL );
+	assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
 
-   len_arg = (rand() % len_arg)+1;
-   len = len_arg;
-#endif   
+	to_go = buf_out->buf_end - buf_out->buf_ptr;
+	assert( to_go > 0 );
    
-#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 {
-#endif
       for(;;) {
-	 ret = sockbuf_io_write( sb, buf, len );
+		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;
       }
-#ifdef USE_SASL      
-   }
-#endif
 
+	if ( ret <= 0 )
    return ret;
-}
-     
-int ber_pvt_sb_close( Sockbuf *sb )
-{
-   int ret;
-
-   assert( sb != NULL );
-   assert( SOCKBUF_VALID( sb ) );
-   assert( sb->sb_io );
-   assert( sb->sb_io->sbi_close );
-   assert( status_is_ok(sb) );
-   assert( ber_pvt_sb_in_use( sb ) );
    
-   ret = sb->sb_io->sbi_close( sb );
-   ber_pvt_sb_set_desc( sb, -1 );
-
+	buf_out->buf_ptr += ret;
+	if (buf_out->buf_ptr == buf_out->buf_end)
+		buf_out->buf_end = buf_out->buf_ptr = 0;
+	if ( ret < to_go )
+		/* not enough data, so pretend no data was sent. */
+		return -1;
    return ret;
 }
 
-int ber_pvt_sb_set_readahead( Sockbuf *sb, int rh )
-{
-   assert( sb != NULL );
-   assert( SOCKBUF_VALID( sb ) );
-   assert( status_is_ok(sb) );
-   sb->sb_read_ahead = (rh!=0);
-   return 0;
-}
-
-int ber_pvt_socket_set_nonblock( ber_socket_t sd, int nb )
+int
+ber_pvt_socket_set_nonblock( ber_socket_t sd, int nb )
 {
 #if HAVE_FCNTL
-	int flags = fcntl(ber_pvt_sb_get_desc(sb), F_GETFL);
-	if( nb ) {
+	int flags = fcntl( sd, F_GETFL);
+	if( nb )
 		flags |= O_NONBLOCK;
-	} else {
+	else
 		flags &= ~O_NONBLOCK;
-	}
-	return fcntl( ber_pvt_sb_get_desc(sb), F_SETFL, flags );
+	return fcntl( sd, F_SETFL, flags );
 		
 #elif defined( FIONBIO )
 	ioctl_t status = nb ? 1 : 0;
@@ -660,201 +296,100 @@ int ber_pvt_socket_set_nonblock( ber_socket_t sd, int nb )
 #endif
 }
 
-#define USE_NONBLOCK
-#ifdef USE_NONBLOCK
-int ber_pvt_sb_set_nonblock( Sockbuf *sb, int nb )
-{
-   assert( sb != NULL );
-   assert( SOCKBUF_VALID( sb ) );
-   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;
-#endif
-   }
-	if (ber_pvt_sb_in_use(sb)) {
-		return ber_pvt_socket_set_nonblock(
-			ber_pvt_sb_get_desc(sb), nb );
-	}
-	return 0;
-}
-#endif
-	 
-#define sockbuf_buf_init( bb ) do { \
-		Sockbuf_Buf *sbb = (bb); \
-		sbb->buf_base = NULL; \
-		sbb->buf_ptr = 0; \
-		sbb->buf_end = 0; \
-		sbb->buf_size = 0; \
-	} while(0)
-
-static int 
-sockbuf_buf_destroy( Sockbuf_Buf *buf )
-{
-	assert( buf != NULL);
-
-   if (buf->buf_base)
-     LBER_FREE( buf->buf_base );
-   sockbuf_buf_init( buf );
-   return 0;
-}
-
-int ber_pvt_sb_init( Sockbuf *sb )
+int
+ber_int_sb_init( Sockbuf *sb )
 {
 	assert( sb != NULL);
 
-	ber_int_options.lbo_valid = LBER_INITIALIZED;
-
    sb->sb_valid=LBER_VALID_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 = 1; /* test */
-   sb->sb_non_block = 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;
-   sb->sb_fd = -1;
-   sb->sb_iodata = NULL;
-   sb->sb_io = &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 
    
    assert( SOCKBUF_VALID( sb ) );
    return 0;
 }
    
-int ber_pvt_sb_destroy( Sockbuf *sb )
+int
+ber_int_sb_close( Sockbuf *sb )
 {
-	assert( sb != NULL);
-	assert( SOCKBUF_VALID(sb) );
-#ifdef USE_SASL
-   ber_pvt_sb_clear_sec(sb);
-   sockbuf_buf_destroy( &(sb->sb_sec_buf_in) );
-   sockbuf_buf_destroy( &(sb->sb_sec_buf_out) );
-#endif
-   ber_pvt_sb_clear_io(sb);
-   sockbuf_buf_destroy( &(sb->sb_buf) );
-   return ber_pvt_sb_init( sb );
-}
+	Sockbuf_IO_Desc		*p;
 
-#ifdef USE_SASL
-int ber_pvt_sb_set_sec( Sockbuf *sb, Sockbuf_Sec * sec, void *arg )
-{
-   int len;
 	assert( sb != NULL);
-	assert( SOCKBUF_VALID( *sb ) );
-   if ((sb->sb_sec) || (sec==NULL))
-     return -1;
-   
-   sb->sb_sec = sec;
    
-   if ((sec->sbs_setup) && (sec->sbs_setup( sb, arg)<0)) {
+	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->sb_sec_buf_in ) : 0;
-      sb->sb_buf.buf_ptr = sb->sb_buf.buf_end = 0;
-   }
-   update_status( sb );
    return 0;
 }
 
-int ber_pvt_sb_clear_sec( Sockbuf *sb )
+int
+ber_int_sb_destroy( Sockbuf *sb )
 {
+	Sockbuf_IO_Desc		*p;
+
 	assert( sb != NULL);
 	assert( SOCKBUF_VALID( 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;
-   
-   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 ber_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 )
 {
+	ber_slen_t		ret;
+
+	assert( buf != NULL );
 	assert( sb != NULL);
+	assert( sb->sb_iod != NULL );
 	assert( SOCKBUF_VALID( sb ) );
-   assert( sb->sb_io == &sb_IO_None );
 
-   if (trans==NULL)
-     return -1;
-   
-   sb->sb_io = trans;
-   
-   if ((trans->sbi_setup) && (trans->sbi_setup( sb, arg)<0))
-     return -1;
-   
-   return 0;
+	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 ber_pvt_sb_clear_io( Sockbuf *sb )
+ber_slen_t
+ber_int_sb_write( Sockbuf *sb, void *buf, ber_len_t len )
 {
+	ber_slen_t		ret;
+
+	assert( buf != NULL );
 	assert( sb != NULL);
+	assert( sb->sb_iod != NULL );
 	assert( SOCKBUF_VALID( sb ) );
 
-   if (sb->sb_io==&sb_IO_None)
-     return -1;
-   
-   if ((sb->sb_io->sbi_remove) && (sb->sb_io->sbi_remove( sb )<0))
-     return -1;
-
-   sb->sb_io = &sb_IO_None;
-   
-   sb->sb_trans_ready = 0;
-   sb->sb_trans_needs_read = 0;
-   sb->sb_trans_needs_write = 0;
-
-   return 0;
+	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;
 }
 
 /*
@@ -862,16 +397,16 @@ int ber_pvt_sb_clear_io( Sockbuf *sb )
  */
 
 static ber_slen_t
-stream_read( Sockbuf *sb, void *buf, ber_len_t len )
+sb_stream_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
 {
-	assert( sb != NULL);
-	assert( SOCKBUF_VALID( sb ) );
+	assert( sbiod != NULL);
+	assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
 
 #if defined(MACOS)
 /*
  * MacTCP/OpenTransport
  */
-   return tcpread( ber_pvt_sb_get_desc(sb), 0, (unsigned char *)buf, 
+	return tcpread( sbiod->sbiod_sb->sb_fd, 0, (unsigned char *)buf,
 		   len, NULL );
 
 #elif defined( HAVE_PCNFS ) || \
@@ -887,7 +422,8 @@ stream_read( Sockbuf *sb, void *buf, ber_len_t len )
  */
    {
    int rc;
-   rc = recv( ber_pvt_sb_get_desc(sb), buf, len, 0 );
+
+		rc = recv( sbiod->sbiod_sb->sb_fd, buf, len, 0 );
 
 #ifdef HAVE_WINSOCK
    if ( rc < 0 )
@@ -905,26 +441,25 @@ stream_read( Sockbuf *sb, void *buf, ber_len_t len )
 /*
  * NCSA Telnet TCP/IP stack (under DOS)
  */
-   return nread( ber_pvt_sb_get_desc(sb), buf, len );
+	return nread( sbiod->sbiod_sb->sb_fd, buf, len );
 
 #else
-   return read( ber_pvt_sb_get_desc(sb), buf, len );
+	return read( sbiod->sbiod_sb->sb_fd, buf, len );
 #endif
 }
 
 static ber_slen_t
-stream_write( Sockbuf *sb, void *buf, ber_len_t len )
+sb_stream_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
 {
-	assert( sb != NULL);
-	assert( SOCKBUF_VALID( sb ) );
+	assert( sbiod != NULL);
+	assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
 
 #if defined(MACOS) 
 /*
  * MacTCP/OpenTransport
  */
 #define MAX_WRITE	65535
-   return tcpwrite( ber_pvt_sb_get_desc(sb),
-		    (unsigned char *)(buf), 
+	return tcpwrite( sbiod->sbiod_sb->sb_fd, (unsigned char *)buf,
 		    (len<MAX_WRITE)? len : MAX_WRITE );
 
 #elif defined( HAVE_PCNFS) \
@@ -941,11 +476,13 @@ stream_write( Sockbuf *sb, void *buf, ber_len_t len )
 
    {
    int rc;
-   rc = send( ber_pvt_sb_get_desc(sb), buf, len, 0 );
+	
+		rc = send( sbiod->sbiod_sb->sb_fd, buf, len, 0 );
 #ifdef HAVE_WINSOCK
    if ( rc < 0 )
    {
      int err;
+		
      err = WSAGetLastError();
      errno = err;
    }
@@ -954,36 +491,53 @@ stream_write( Sockbuf *sb, void *buf, ber_len_t len )
    }
 
 #elif defined(HAVE_NCSA)
-   return netwrite( ber_pvt_sb_get_desc(sb), buf, len );
+	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( ber_pvt_sb_get_desc(sb), buf, 
+	return write( sbiod->sbiod_sb->sb_fd, buf,
 		 (len<MAX_WRITE)? len : MAX_WRITE);
 #else
-   return write( ber_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 )
 {
-	assert( sb != NULL);
-	assert( SOCKBUF_VALID( sb ) );
-   tcp_close( ber_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 ber_pvt_sb_io_tcp=
+/* 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 =
 {
-	NULL,	/* sbi_setup */
-	NULL,	/* sbi_release */
-	stream_read,	/* sbi_read */
-	stream_write,	/* sbi_write */
-	stream_close,	/* sbi_close */
+	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 */
 };
 
 /*
@@ -997,53 +551,52 @@ struct dgram_data
 };
 
 static int 
-dgram_setup( Sockbuf *sb, void *arg )
+sb_dgram_setup( Sockbuf_IO_Desc *sbiod, void *arg )
 {
-	assert( sb != NULL);
-	assert( SOCKBUF_VALID( sb ) );
+	struct dgram_data	*p;
+
+	assert( sbiod != NULL);
+	assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
 
-   sb->sb_iodata = LBER_MALLOC( sizeof( struct dgram_data ) );
-   if (sb->sb_iodata==NULL)
+	p = LBER_MALLOC( sizeof( *p ) );
+	if ( p == NULL )
      return -1;
-   sb->sb_read_ahead = 1; /* important since udp is packet based. */
+	memset( p, 0, sizeof( *p ) );
+	sbiod->sbiod_pvt = (void *)p;
+	if ( arg != NULL )
+		sbiod->sbiod_sb->sb_fd = *((int *)arg);
    return 0;
 }
 
 static int 
-dgram_release( Sockbuf *sb )
+sb_dgram_release( Sockbuf_IO_Desc *sbiod )
 {
-	assert( sb != NULL);
-	assert( SOCKBUF_VALID( sb ) );
+	assert( sbiod != NULL);
+	assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
 
-   LBER_FREE( sb->sb_iodata );
+	LBER_FREE( sbiod->sbiod_pvt );
+	sbiod->sbiod_pvt = NULL;
    return 0;
 }
 
 static ber_slen_t
-dgram_read( Sockbuf *sb, void *buf, ber_len_t len )
+sb_dgram_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
 {
 #ifdef LDAP_CONNECTIONLESS
    ber_slen_t rc;
    socklen_t  addrlen;
-   struct dgram_data *dd;
+	struct dgram_data	*p;
    
-	assert( sb != NULL );
-	assert( SOCKBUF_VALID( sb ) );
+	assert( sbiod != NULL );
+	assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
 	assert( buf != NULL );
 
-   dd = (struct dgram_data *)(sb->sb_iodata);
+	p = (struct dgram_data *)sbiod->sbiod_pvt;
    
    addrlen = sizeof( struct sockaddr );
-   rc=recvfrom( ber_pvt_sb_get_desc(sb), buf, len, 0, &(dd->src), &addrlen );
+	rc = recvfrom( sbiod->sbiod_sb->sb_fd, buf, len, 0, &p->src,
+		&addrlen );
    
-   if ( sb->sb_debug ) {
-      ber_log_printf( LDAP_DEBUG_ANY, sb->sb_debug,
-		      "dgram_read udp_read %ld bytes\n",
-		      (long) rc );
-      if ( rc > 0 )
-	ber_log_bprint( LDAP_DEBUG_PACKETS, sb->sb_debug,
-			buf, rc );
-   }
    return rc;
 # else /* LDAP_CONNECTIONLESS */
    return -1;
@@ -1051,30 +604,30 @@ dgram_read( Sockbuf *sb, void *buf, ber_len_t len )
 }
 
 static ber_slen_t 
-dgram_write( Sockbuf *sb, void *buf, ber_len_t len )
+sb_dgram_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
 {
 #ifdef LDAP_CONNECTIONLESS
    ber_slen_t rc;
-   struct dgram_data *dd;
+	struct dgram_data	*p;
    
-	assert( sb != NULL );
-	assert( SOCKBUF_VALID( sb ) );
+	assert( sbiod != NULL );
+	assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
 	assert( buf != NULL );
 
-   dd = (struct dgram_data *)(sb->sb_iodata);
+	p = (struct dgram_data *)sbiod->sbiod_pvt;
    
-   rc=sendto( ber_pvt_sb_get_desc(sb), buf, len, 0, &(dd->dst),
+	rc = sendto( sbiod->sbiod_sb->sb_fd, buf, len, 0, &p->dst,
 	     sizeof( struct sockaddr ) );
 
    if ( rc <= 0 )
-       return( -1 );
+		return -1;
    
    /* fake error if write was not atomic */
    if (rc < len) {
 # ifdef EMSGSIZE
       errno = EMSGSIZE;
 # endif
-      return( -1 );
+		return -1;
    }
    return rc;
 #else
@@ -1083,84 +636,325 @@ dgram_write( Sockbuf *sb, void *buf, ber_len_t len )
 }
 
 static int 
-dgram_close( Sockbuf *sb )
+sb_dgram_close( Sockbuf_IO_Desc *sbiod )
 {
-	assert( sb != NULL );
-	assert( SOCKBUF_VALID( sb ) );
+	assert( sbiod != NULL );
+	assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
 
-	tcp_close( ber_pvt_sb_get_desc(sb) );
+	tcp_close( sbiod->sbiod_sb->sb_fd );
 	return 0;
 }
 
-Sockbuf_IO ber_pvt_sb_io_udp=
+static int
+sb_dgram_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 */
+	struct dgram_data	*p;
+
+	assert( sbiod != NULL );
+	assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+	p = (struct dgram_data *)sbiod->sbiod_pvt;
+
+	if ( opt == LBER_SB_OPT_UDP_SET_DST ) {
+		memcpy( &p->dst, arg, sizeof( struct sockaddr ) );
+		return 1;
+	}
+	else if ( opt == LBER_SB_OPT_UDP_GET_SRC ) {
+		*(( struct sockaddr **)arg) = &p->src;
+		return 1;
+	}
+	/* This is an end IO descriptor */
+	return 0;
+}
+
+Sockbuf_IO ber_sockbuf_io_udp =
+{
+	sb_dgram_setup,		/* sbi_setup */
+	sb_dgram_release,	/* sbi_release */
+	sb_dgram_ctrl,		/* sbi_ctrl */
+	sb_dgram_read,		/* sbi_read */
+	sb_dgram_write,		/* sbi_write */
+	sb_dgram_close		/* sbi_close */
 };
 
-int ber_pvt_sb_udp_set_dst(Sockbuf *sb, void *addr )
+/*
+ * Support for readahead (UDP needs it)
+ */
+
+static int
+sb_rdahead_setup( Sockbuf_IO_Desc *sbiod, void *arg )
 {
-   struct dgram_data *dd;
-	assert( sb != NULL );
-	assert( SOCKBUF_VALID( sb ) );
-   assert( sb->sb_io == &ber_pvt_sb_io_udp );
-   dd = (struct dgram_data *) (sb->sb_iodata);
-   memcpy( &(dd->dst), addr, sizeof( struct sockaddr ) );
+	Sockbuf_Buf		*p;
+
+	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, DEFAULT_READAHEAD );
+	else
+		ber_pvt_sb_grow_buffer( p, *((int *)arg) );
+	sbiod->sbiod_pvt = p;
    return 0;
 }
 
-void *ber_pvt_sb_udp_get_src( Sockbuf *sb )
+static int
+sb_rdahead_remove( Sockbuf_IO_Desc *sbiod )
 {
-   struct dgram_data *dd;
+	Sockbuf_Buf		*p;
 
-	assert( sb != NULL );
-	assert( SOCKBUF_VALID( sb ) );
-   assert( sb->sb_io == &ber_pvt_sb_io_udp );
-   dd = (struct dgram_data *) (sb->sb_iodata);
-   return &(dd->src);
+	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 ber_slen_t
+sb_rdahead_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
+{
+	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, buf + bufptr, len );
+	return bufptr;
+}
+
+static ber_slen_t
+sb_rdahead_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
+{
+	assert( sbiod != NULL );
+	assert( sbiod->sbiod_next != NULL );
+
+	return LBER_SBIOD_WRITE_NEXT( sbiod, buf, len );
+}
+
+static int
+sb_rdahead_close( Sockbuf_IO_Desc *sbiod )
+{
+	assert( sbiod != NULL );
+
+	/* Just erase the buffer */
+	ber_pvt_sb_buf_destroy((Sockbuf_Buf *)sbiod->sbiod_pvt);
+	return 0;
+}
+
+static int
+sb_rdahead_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
+{
+	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 */
+};
+
 /*
- * debug routines.
- * 
- * BUGS:
- * These routines should really call abort, but at the moment that would
- * break the servers.
+ * Support for simple file IO
  */
 
 static ber_slen_t
-have_no_read( Sockbuf *sb, void *buf, ber_len_t len )
+sb_fd_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
 {
-	assert( sb != NULL );
-	assert( SOCKBUF_VALID( sb ) );
+	assert( sbiod != NULL);
+	assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
 
-   ber_log_printf( LDAP_DEBUG_ANY, ber_int_debug,
-		   "warning: reading from uninitialized sockbuf\n");
-   errno =  EBADF;
-   return -1;
+	return read( sbiod->sbiod_sb->sb_fd, buf, len );
 }
 
 static ber_slen_t
-have_no_write( Sockbuf *sb, void *buf, ber_len_t len )
+sb_fd_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
 {
-	assert( sb != NULL );
-	assert( SOCKBUF_VALID( sb ) );
+	assert( sbiod != NULL);
+	assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
 
-   ber_log_printf( LDAP_DEBUG_ANY, ber_int_debug,
-		   "warning: writing to uninitialized sockbuf\n");
-   errno =  EBADF;
-   return -1;
+	return write( sbiod->sbiod_sb->sb_fd, buf, len );
 }
 
 static int 
-have_no_close( Sockbuf *sb )
+sb_fd_close( Sockbuf_IO_Desc *sbiod )
 {   
-	assert( sb != NULL );
-	assert( SOCKBUF_VALID( sb ) );
+	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 */
+};
+
+/*
+ * Debugging layer
+ */
 
-   assert( 0 );
+static int
+sb_debug_setup( Sockbuf_IO_Desc *sbiod, void *arg )
+{
+	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 int
+sb_debug_remove( Sockbuf_IO_Desc *sbiod )
+{
+	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 ( 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 );
+	}
+	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 ( 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 );
+	}
+	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 */
+};
+
diff --git a/libraries/libldap/abandon.c b/libraries/libldap/abandon.c
index b2b24b2f58bbad038b03e4afc6d7ccd2aa124c08..3ce621f7aa3685d11dc1149512b56d40897cec89 100644
--- a/libraries/libldap/abandon.c
+++ b/libraries/libldap/abandon.c
@@ -180,7 +180,7 @@ do_abandon(
 				if ( lr != NULL ) {
 					sb = lr->lr_conn->lconn_sb;
 				} else {
-					sb = &ld->ld_sb;
+					sb = ld->ld_sb;
 				}
 
 				if ( ber_flush( sb, ber, 1 ) != 0 ) {
diff --git a/libraries/libldap/cldap.c b/libraries/libldap/cldap.c
index 0b03b2ad8d72c47906ee3fa82410b0f7ae171631..9a4eac20ff5e32927480ed186b98239c0bb32f30 100644
--- a/libraries/libldap/cldap.c
+++ b/libraries/libldap/cldap.c
@@ -94,10 +94,20 @@ cldap_open( LDAP_CONST char *host, int port )
     ld->ld_cldapnaddr = 0;
     ld->ld_cldapaddrs = NULL;
 
-    if (ber_pvt_sb_set_io( &(ld->ld_sb), &ber_pvt_sb_io_udp, NULL )<0) {
+    if ( ber_sockbuf_add_io( ld->ld_sb, &ber_sockbuf_io_udp,
+	    LBER_SBIOD_LEVEL_PROVIDER, (void *)&s ) < 0 ) {
        ldap_ld_free(ld, 1, NULL, NULL );
+	tcp_close( s );
        return NULL;
     }
+    if ( ber_sockbuf_add_io( ld->ld_sb, &ber_sockbuf_io_readahead,
+	    LBER_SBIOD_LEVEL_PROVIDER, NULL ) < 0 ) {
+	ldap_ld_free( ld, 1, NULL, NULL );
+	return NULL;
+    }
+#ifdef LDAP_DEBUG
+    ber_sockbuf_add_io( ld->ld_sb, &ber_sockbuf_io_debug, INT_MAX, NULL );
+#endif
 	
     ld->ld_version = LDAP_VERSION2;
 
@@ -168,7 +178,8 @@ cldap_open( LDAP_CONST char *host, int port )
 	DO_RETURN( NULL );
     }
 
-    ber_pvt_sb_udp_set_dst( &ld->ld_sb, ld->ld_cldapaddrs[0] );
+    ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_UDP_SET_DST,
+	ld->ld_cldapaddrs[0] );
 
     cldap_setretryinfo( ld, 0, 0 );
 
@@ -229,8 +240,8 @@ cldap_search_s( LDAP *ld,
 		--ld->ld_msgid;	/* use same id as before */
 	}
 	    
-	ber_pvt_sb_udp_set_dst( &(ld->ld_sb), 
-			ld->ld_cldapaddrs[ cri.cri_useaddr ] );
+	ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_UDP_SET_DST,
+	    (void *)ld->ld_cldapaddrs[cri.cri_useaddr] );
 
 	Debug( LDAP_DEBUG_TRACE, "cldap_search_s try %d (to %s)\n",
 	    cri.cri_try, inet_ntoa( ((struct sockaddr_in *)
@@ -287,7 +298,6 @@ static int
 cldap_result( LDAP *ld, int msgid, LDAPMessage **res,
 	struct cldap_retinfo *crip, const char *base )
 {
-    Sockbuf 		*sb = &ld->ld_sb;
     BerElement		ber;
     char		*logdn;
     int			ret, fromaddr, i;
@@ -365,7 +375,7 @@ cldap_result( LDAP *ld, int msgid, LDAPMessage **res,
 	     * got a result: determine which server it came from
 	     * decode into ldap message chain
 	     */
-	    src = (struct sockaddr_in *) ber_pvt_sb_udp_get_src( sb );
+	    ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_UDP_GET_SRC, (void *)&src );
 		
 	    for ( fromaddr = 0; fromaddr < ld->ld_cldapnaddr; ++fromaddr ) {
 		    if ( memcmp( &((struct sockaddr_in *)
@@ -401,7 +411,8 @@ cldap_result( LDAP *ld, int msgid, LDAPMessage **res,
 	    if ( i == fromaddr ) {
 		continue;
 	    }
-	    ber_pvt_sb_udp_set_dst( sb, ld->ld_cldapaddrs[i] );
+	    ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_UDP_SET_DST,
+		(void *)ld->ld_cldapaddrs[i] );
 
 	    Debug( LDAP_DEBUG_TRACE, "cldap_result abandoning id %d (to %s)\n",
 		msgid, inet_ntoa( ((struct sockaddr_in *)
diff --git a/libraries/libldap/kbind.c b/libraries/libldap/kbind.c
index 54982ae1a079f41f4badf6231cd022e495d6c2f0..3f6f20cdd1280063e89570666b650cd1a39e581f 100644
--- a/libraries/libldap/kbind.c
+++ b/libraries/libldap/kbind.c
@@ -273,7 +273,7 @@ ldap_get_kerberosv4_credentials(
 		return( NULL );
 	}
 
-	if( ! ber_pvt_sb_in_use( &ld->ld_sb ) ) {
+	if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, NULL ) == -1 ) {
 		/* not connected yet */
 		int rc = ldap_open_defconn( ld );
 
diff --git a/libraries/libldap/ldap-int.h b/libraries/libldap/ldap-int.h
index df499916be17a62f8f4b56bcb7eb8b90e2e1546f..216a9b44e1406170761597d646da9f508c0d69e1 100644
--- a/libraries/libldap/ldap-int.h
+++ b/libraries/libldap/ldap-int.h
@@ -20,12 +20,13 @@
 #include "../liblber/lber-int.h"
 
 #define ldap_debug	(ldap_int_global_options.ldo_debug)
+
+#include "ldap_log.h"
+
 #undef Debug
 #define Debug( level, fmt, arg1, arg2, arg3 ) \
 	ldap_log_printf( NULL, (level), (fmt), (arg1), (arg2), (arg3) )
 
-#include "ldap_log.h"
-
 #include "ldap.h"
 
 #include "ldap_pvt.h"
@@ -230,7 +231,7 @@ typedef struct ldapreqinfo {
  */
 
 struct ldap {
-	Sockbuf		ld_sb;		/* socket descriptor & buffer */
+	Sockbuf		*ld_sb;		/* socket descriptor & buffer */
 
 	struct ldapoptions ld_options;
 
@@ -391,8 +392,6 @@ LIBLDAP_F (int) ldap_int_tblsize;
 LIBLDAP_F (int) ldap_int_timeval_dup( struct timeval **dest, const struct timeval *tm );
 LIBLDAP_F (int) ldap_connect_to_host( LDAP *ld, Sockbuf *sb, const char *host, unsigned long address, int port, int async );
 
-LIBLDAP_F (void) ldap_close_connection( Sockbuf *sb );
-
 #if defined(LDAP_API_FEATURE_X_OPENLDAP_V2_KBIND) || defined(HAVE_TLS) || defined(HAVE_CYRUS_SASL)
 LIBLDAP_F (char *) ldap_host_connected_to( Sockbuf *sb );
 #endif /* LDAP_API_FEATURE_X_OPENLDAP_V2_KBIND */
diff --git a/libraries/libldap/open.c b/libraries/libldap/open.c
index 955733482f3b09ed237bc295214e8d70be253e48..afccb261d7ccfc1d95bba7c8946415c38fe89812 100644
--- a/libraries/libldap/open.c
+++ b/libraries/libldap/open.c
@@ -161,7 +161,13 @@ ldap_create( LDAP **ldp )
 #endif /* LDAP_CHARSET_8859 == LDAP_DEFAULT_CHARSET */
 #endif /* STR_TRANSLATION && LDAP_DEFAULT_CHARSET */
 
-	ber_pvt_sb_init( &(ld->ld_sb) );
+	ld->ld_sb = ber_sockbuf_alloc( );
+	if ( ld->ld_sb == NULL ) {
+		ldap_free_urllist( ld->ld_options.ldo_defludp );
+		LDAP_FREE( (char*) ld );
+		WSACleanup( );
+		return LDAP_NO_MEMORY;
+	}
 
 	*ldp = ld;
 	return LDAP_SUCCESS;
@@ -283,24 +289,41 @@ open_ldap_connection( LDAP *ld, Sockbuf *sb, LDAPURLDesc *srv,
 
 	switch ( srv->lud_protocol ) {
 		case LDAP_PROTO_TCP:
+			rc = ldap_connect_to_host( ld, sb, srv->lud_host,
+				addr, port, async );
+			if ( rc == -1 )
+				return rc;
+			ber_sockbuf_add_io( sb, &ber_sockbuf_io_tcp,
+				LBER_SBIOD_LEVEL_PROVIDER, NULL );
+			break;
 		case LDAP_PROTO_UDP:
-			rc = ldap_connect_to_host( ld, sb, srv->lud_host, addr, port, async );
+			rc = ldap_connect_to_host( ld, sb, srv->lud_host,
+				addr, port, async );
+			if ( rc == -1 )
+				return rc;
+			ber_sockbuf_add_io( sb, &ber_sockbuf_io_udp,
+				LBER_SBIOD_LEVEL_PROVIDER, NULL );
 			break;
 #ifdef LDAP_PF_LOCAL
 		case LDAP_PROTO_LOCAL:
-			rc = ldap_connect_to_path( ld, sb, srv->lud_host, async );
+			rc = ldap_connect_to_path( ld, sb, srv->lud_host,
+				async );
+			if ( rc == -1 )
+				return rc;
+			ber_sockbuf_add_io( sb, &ber_sockbuf_io_fd,
+				LBER_SBIOD_LEVEL_PROVIDER, NULL );
 			break;
 #endif /* LDAP_PF_LOCAL */
 		default:
-			rc = -1;
+			return -1;
 			break;
 	}
 
-	if ( rc == -1 ) {
-		return( rc );
-	}
-   
-   	ber_pvt_sb_set_io( sb, &ber_pvt_sb_io_tcp, NULL );
+	ber_sockbuf_add_io( sb, &ber_sockbuf_io_readahead,
+		LBER_SBIOD_LEVEL_PROVIDER, NULL );
+#ifdef LDAP_DEBUG
+	ber_sockbuf_add_io( sb, &ber_sockbuf_io_debug, INT_MAX, NULL );
+#endif
 
 #ifdef HAVE_TLS
 	if (ld->ld_options.ldo_tls_mode == LDAP_OPT_X_TLS_HARD ||
diff --git a/libraries/libldap/options.c b/libraries/libldap/options.c
index 47e77e624732b8a8aaec1a1570e38c4aa7c5a834..56b39c20f487399ec0094e66ef2b4f3d8027777a 100644
--- a/libraries/libldap/options.c
+++ b/libraries/libldap/options.c
@@ -151,12 +151,12 @@ ldap_get_option(
 		} break;
 
 	case LDAP_OPT_DESC:
-		if(ld == NULL) {
+		if( ld == NULL || ld->ld_sb == NULL ) {
 			/* bad param */
 			break;
 		} 
 
-		* (ber_socket_t *) outvalue = ber_pvt_sb_get_desc( &(ld->ld_sb) );
+		ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, outvalue );
 		return LDAP_OPT_SUCCESS;
 
 	case LDAP_OPT_TIMEOUT:
diff --git a/libraries/libldap/os-ip.c b/libraries/libldap/os-ip.c
index 1033d62c526632c69f4372e62a8ca8ce38f60f20..f150eb4ff6c0043a6239b4aeb8eb102dc0bbc9f3 100644
--- a/libraries/libldap/os-ip.c
+++ b/libraries/libldap/os-ip.c
@@ -337,7 +337,7 @@ ldap_connect_to_host(LDAP *ld, Sockbuf *sb, const char *host,
 		rc = ldap_pvt_connect(ld, s, &sin, async);
    
 		if ( (rc == 0) || (rc == -2) ) {
-			ber_pvt_sb_set_desc( sb, s );
+			ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_FD, &s );
 			break;
 		}
 
@@ -350,13 +350,6 @@ ldap_connect_to_host(LDAP *ld, Sockbuf *sb, const char *host,
 	return rc;
 }
 
-void
-ldap_close_connection( Sockbuf *sb )
-{
-	ber_pvt_sb_close( sb );
-}
-
-
 #if defined( LDAP_API_FEATURE_X_OPENLDAP_V2_KBIND ) || defined( HAVE_TLS ) || defined( HAVE_CYRUS_SASL )
 char *
 ldap_host_connected_to( Sockbuf *sb )
@@ -369,12 +362,14 @@ ldap_host_connected_to( Sockbuf *sb )
    	struct hostent		he_buf;
         int			local_h_errno;
    	char			*ha_buf=NULL;
+	ber_socket_t		sd;
 #define DO_RETURN(x) if (ha_buf) LDAP_FREE(ha_buf); return (x);
    
 	(void)memset( (char *)&sin, 0, sizeof( struct sockaddr_in ));
 	len = sizeof( sin );
 
-	if ( getpeername( ber_pvt_sb_get_desc(sb), (struct sockaddr *)&sin, &len ) == -1 ) {
+	ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+	if ( getpeername( sd, (struct sockaddr *)&sin, &len ) == -1 ) {
 		return( NULL );
 	}
 
@@ -414,11 +409,13 @@ void
 ldap_mark_select_write( LDAP *ld, Sockbuf *sb )
 {
 	struct selectinfo	*sip;
+	ber_socket_t		sd;
 
 	sip = (struct selectinfo *)ld->ld_selectinfo;
 	
-	if ( !FD_ISSET( ber_pvt_sb_get_desc(sb), &sip->si_writefds )) {
-		FD_SET( (u_int) sb->sb_sd, &sip->si_writefds );
+	ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+	if ( !FD_ISSET( sd, &sip->si_writefds )) {
+		FD_SET( sd, &sip->si_writefds );
 	}
 }
 
@@ -427,11 +424,13 @@ void
 ldap_mark_select_read( LDAP *ld, Sockbuf *sb )
 {
 	struct selectinfo	*sip;
+	ber_socket_t		sd;
 
 	sip = (struct selectinfo *)ld->ld_selectinfo;
 
-	if ( !FD_ISSET( ber_pvt_sb_get_desc(sb), &sip->si_readfds )) {
-		FD_SET( (u_int) sb->sb_sd, &sip->si_readfds );
+	ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+	if ( !FD_ISSET( sd, &sip->si_readfds )) {
+		FD_SET( sd, &sip->si_readfds );
 	}
 }
 
@@ -440,11 +439,13 @@ void
 ldap_mark_select_clear( LDAP *ld, Sockbuf *sb )
 {
 	struct selectinfo	*sip;
+	ber_socket_t		sd;
 
 	sip = (struct selectinfo *)ld->ld_selectinfo;
 
-	FD_CLR( (u_int) ber_pvt_sb_get_desc(sb), &sip->si_writefds );
-	FD_CLR( (u_int) ber_pvt_sb_get_desc(sb), &sip->si_readfds );
+	ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+	FD_CLR( sd, &sip->si_writefds );
+	FD_CLR( sd, &sip->si_readfds );
 }
 
 
@@ -452,10 +453,12 @@ int
 ldap_is_write_ready( LDAP *ld, Sockbuf *sb )
 {
 	struct selectinfo	*sip;
+	ber_socket_t		sd;
 
 	sip = (struct selectinfo *)ld->ld_selectinfo;
 
-	return( FD_ISSET( ber_pvt_sb_get_desc(sb), &sip->si_use_writefds ));
+	ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+	return( FD_ISSET( sd, &sip->si_use_writefds ));
 }
 
 
@@ -463,10 +466,12 @@ int
 ldap_is_read_ready( LDAP *ld, Sockbuf *sb )
 {
 	struct selectinfo	*sip;
+	ber_socket_t		sd;
 
 	sip = (struct selectinfo *)ld->ld_selectinfo;
 
-	return( FD_ISSET( ber_pvt_sb_get_desc(sb), &sip->si_use_readfds ));
+	ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+	return( FD_ISSET( sd, &sip->si_use_readfds ));
 }
 
 
diff --git a/libraries/libldap/os-local.c b/libraries/libldap/os-local.c
index baae3da352fb89e668c9479b424f8bd0fdee782a..67da7e0328a09a834d3216cbacb63cffa8594c05 100644
--- a/libraries/libldap/os-local.c
+++ b/libraries/libldap/os-local.c
@@ -208,7 +208,7 @@ ldap_connect_to_path(LDAP *ld, Sockbuf *sb, const char *path, int async)
 	rc = ldap_pvt_connect(ld, s, &server, async);
 
 	if (rc == 0) {
-		ber_pvt_sb_set_desc( sb, s );
+		ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_FD, (void *)&s );
 	} else {
 		ldap_pvt_close_socket(ld, s);
 	}
diff --git a/libraries/libldap/request.c b/libraries/libldap/request.c
index 87d6c84bfac115832d66c01372a85cbf99bb7e60..bb6c9df78dc71ceefec03e8f60cf6d531dfd9344 100644
--- a/libraries/libldap/request.c
+++ b/libraries/libldap/request.c
@@ -100,7 +100,7 @@ ldap_send_initial_request(
 
 	Debug( LDAP_DEBUG_TRACE, "ldap_send_initial_request\n", 0, 0, 0 );
 
-	if ( ! ber_pvt_sb_in_use(&ld->ld_sb ) ) {
+	if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, NULL ) == -1 ) {
 		/* not connected yet */
 		int rc = ldap_open_defconn( ld );
 
@@ -293,7 +293,7 @@ ldap_new_connection( LDAP *ld, LDAPURLDesc *srvlist, int use_ldsb,
 		return( NULL );
 	}
 
-	lc->lconn_sb = ( use_ldsb ) ? &ld->ld_sb : sb;
+	lc->lconn_sb = ( use_ldsb ) ? ld->ld_sb : sb;
 
 	if ( connect ) {
 		for ( srv = srvlist; srv != NULL; srv = srv->lud_next ) {
@@ -433,10 +433,6 @@ ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind )
 			}
 		}
 
-		/* force closure */
-		ldap_close_connection( lc->lconn_sb );
-		ber_pvt_sb_destroy( lc->lconn_sb );
-
 		if( lc->lconn_ber != NULL ) {
 			ber_free( lc->lconn_ber, 1 );
 		}
@@ -458,7 +454,7 @@ ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind )
 		if ( lc->lconn_krbinstance != NULL ) {
 			LDAP_FREE( lc->lconn_krbinstance );
 		}
-		if ( lc->lconn_sb != &ld->ld_sb ) {
+		if ( lc->lconn_sb != ld->ld_sb ) {
 			ber_sockbuf_free( lc->lconn_sb );
 		}
 		if( lc->lconn_rebind_queue != NULL) {
@@ -493,7 +489,7 @@ ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all )
 			    ( lc->lconn_server->lud_host == NULL ) ? "(null)"
 			    : lc->lconn_server->lud_host,
 			    lc->lconn_server->lud_port, ( lc->lconn_sb ==
-			    &ld->ld_sb ) ? "  (default)" : "" );
+			    ld->ld_sb ) ? "  (default)" : "" );
 		}
 		fprintf( stderr, "  refcnt: %d  status: %s\n", lc->lconn_refcnt,
 		    ( lc->lconn_status == LDAP_CONNST_NEEDSOCKET ) ?
diff --git a/libraries/libldap/result.c b/libraries/libldap/result.c
index 3ceb1fa70d46335347dc4326640a62eecef99ec7..475effc9d5c062caa9b4e015e625a0eb752f01c3 100644
--- a/libraries/libldap/result.c
+++ b/libraries/libldap/result.c
@@ -218,7 +218,8 @@ wait4msg(
 		}
 #endif /* LDAP_DEBUG */
 		for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
-			if ( ber_pvt_sb_data_ready(lc->lconn_sb) ) {
+			if ( ber_sockbuf_ctrl( lc->lconn_sb,
+					LBER_SB_OPT_DATA_READY, NULL ) ) {
 				rc = try_read1msg( ld, msgid, all, lc->lconn_sb,
 				    lc, result );
 				break;
@@ -888,14 +889,16 @@ cldap_getmsg( LDAP *ld, struct timeval *timeout, BerElement *ber )
 	int	rc;
 	ber_tag_t	tag;
 	ber_len_t	len;
+	ber_socket_t	sd;
 
-	if ( ! ber_pvt_sb_data_ready(&ld->ld_sb) ) {
+	ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd );
+	if ( sd != AC_SOCKET_INVALID ) {
 		/* restored from ldap_select1() in result.c version 1.24 */
 		fd_set	readfds;
 		if ( ldap_int_tblsize == 0 )
 			ldap_int_ip_init();
 		FD_ZERO( &readfds );
-		FD_SET( ber_pvt_sb_get_desc(&ld->ld_sb), &readfds );
+		FD_SET( sd, &readfds );
 		rc = select( ldap_int_tblsize, &readfds, 0, 0, timeout );
 
 		if ( rc == -1 || rc == 0 ) {
@@ -906,7 +909,7 @@ cldap_getmsg( LDAP *ld, struct timeval *timeout, BerElement *ber )
 	}
 
 	/* get the next message */
-	if ( (tag = ber_get_next( &ld->ld_sb, &len, ber ))
+	if ( (tag = ber_get_next( ld->ld_sb, &len, ber ))
 	    != LDAP_TAG_MESSAGE ) {
 		ld->ld_errno = (tag == LBER_DEFAULT ? LDAP_SERVER_DOWN :
 		    LDAP_LOCAL_ERROR);
diff --git a/libraries/libldap/sasl.c b/libraries/libldap/sasl.c
index 00656001d9cbec9f2bb8331e2984d07f3bb89eb0..e162b04a5389db52da14aee698485d26b9c82b47 100644
--- a/libraries/libldap/sasl.c
+++ b/libraries/libldap/sasl.c
@@ -32,6 +32,7 @@
 #include <ac/socket.h>
 #include <ac/string.h>
 #include <ac/time.h>
+#include <ac/errno.h>
 
 #include "ldap-int.h"
 
@@ -349,22 +350,8 @@ ldap_parse_sasl_bind_result(
 * Various Cyrus SASL related stuff.
 */
 
-static int sasl_setup( Sockbuf *sb, void *arg );
-static int sasl_remove( Sockbuf *sb );
-static ber_slen_t sasl_read( Sockbuf *sb, void *buf, ber_len_t len );
-static ber_slen_t sasl_write( Sockbuf *sb, void *buf, ber_len_t len );
-static int sasl_close( Sockbuf *sb );
-
-static Sockbuf_IO sasl_io=
-{
-sasl_setup,
-sasl_remove,
-sasl_read,
-sasl_write,
-sasl_close
-}; 
-
-#define HAS_SASL( sb ) ((sb)->sb_io==&sasl_io)
+#define MAX_BUFF_SIZE	65536
+#define MIN_BUFF_SIZE	4096
 
 static char *
 array2str( char **a )
@@ -424,79 +411,259 @@ int ldap_pvt_sasl_init( void )
 	return -1;
 }
 
-int ldap_pvt_sasl_install( Sockbuf *sb, void *ctx_arg )
+/*
+ * SASL encryption support for LBER Sockbufs
+ */
+
+struct sb_sasl_data {
+	sasl_conn_t		*sasl_context;
+	Sockbuf_Buf		sec_buf_in;
+	Sockbuf_Buf		buf_in;
+	Sockbuf_Buf		buf_out;
+};
+
+static int
+sb_sasl_setup( Sockbuf_IO_Desc *sbiod, void *arg )
 {
-	/* don't install the stuff unless security has been negotiated */
+	struct sb_sasl_data	*p;
 
-	if ( !HAS_SASL( sb ) ) {
-		ber_pvt_sb_clear_io( sb );
-		ber_pvt_sb_set_io( sb, &sasl_io, ctx_arg );
+	assert( sbiod != NULL );
+
+	p = LBER_MALLOC( sizeof( *p ) );
+	if ( p == NULL )
+		return -1;
+	p->sasl_context = (sasl_conn_t *)arg;
+	ber_pvt_sb_buf_init( &p->sec_buf_in );
+	ber_pvt_sb_buf_init( &p->buf_in );
+	ber_pvt_sb_buf_init( &p->buf_out );
+	if ( ber_pvt_sb_grow_buffer( &p->sec_buf_in, MIN_BUFF_SIZE ) < 0 ) {
+		errno = ENOMEM;
+		return -1;
 	}
 
+	sbiod->sbiod_pvt = p;
+
 	return 0;
 }
 
-static int sasl_setup( Sockbuf *sb, void *arg )
+static int
+sb_sasl_remove( Sockbuf_IO_Desc *sbiod )
 {
-	sb->sb_iodata = arg;
+	struct sb_sasl_data	*p;
+
+	assert( sbiod != NULL );
+	
+	p = (struct sb_sasl_data *)sbiod->sbiod_pvt;
+	ber_pvt_sb_buf_destroy( &p->sec_buf_in );
+	ber_pvt_sb_buf_destroy( &p->buf_in );
+	ber_pvt_sb_buf_destroy( &p->buf_out );
+	LBER_FREE( p );
+	sbiod->sbiod_pvt = NULL;
 	return 0;
 }
 
-static int sasl_remove( Sockbuf *sb )
+static ber_len_t
+sb_sasl_pkt_length( const char *buf, int debuglevel )
 {
-	return 0;
+	ber_len_t		size;
+	long			tmp;
+
+	assert( buf != NULL );
+
+	tmp = *((long *)buf);
+	size = ntohl( tmp );
+   
+	if ( size > MAX_BUFF_SIZE ) {
+		/* somebody is trying to mess me up. */
+		ber_log_printf( LDAP_DEBUG_ANY, debuglevel,
+			"sb_sasl_pkt_length: received illegal packet length "
+			"of %lu bytes\n", (unsigned long)size );      
+		size = 16; /* this should lead to an error. */
+}
+
+	return size + 4; /* include the size !!! */
 }
 
-static ber_slen_t sasl_read( Sockbuf *sb, void *buf, ber_len_t buflen )
+/* Drop a processed packet from the input buffer */
+static void
+sb_sasl_drop_packet ( Sockbuf_Buf *sec_buf_in, int debuglevel )
 {
-	char *recv_tok;
-	unsigned recv_tok_len;
-	sasl_conn_t *conn = (sasl_conn_t *)sb->sb_iodata;
+	ber_slen_t			len;
+
+	len = sec_buf_in->buf_ptr - sec_buf_in->buf_end;
+	if ( len > 0 )
+		memmove( sec_buf_in->buf_base, sec_buf_in->buf_base +
+			sec_buf_in->buf_end, len );
+   
+	if ( len >= 4 ) {
+		sec_buf_in->buf_end = sb_sasl_pkt_length( sec_buf_in->buf_base,
+			debuglevel);
+	}
+	else {
+		sec_buf_in->buf_end = 0;
+	}
+	sec_buf_in->buf_ptr = len;
+}
 
-	if ((ber_pvt_sb_io_tcp.sbi_read)( sb, buf, buflen ) != buflen ) {
-		return -1;
+static ber_slen_t
+sb_sasl_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
+{
+	struct sb_sasl_data	*p;
+	ber_slen_t		ret, bufptr;
+   
+	assert( sbiod != NULL );
+	assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+	p = (struct sb_sasl_data *)sbiod->sbiod_pvt;
+
+	/* Are there anything left in the buffer? */
+	ret = ber_pvt_sb_copy_out( &p->buf_in, buf, len );
+	bufptr = ret;
+	len -= ret;
+
+	if ( len == 0 )
+		return bufptr;
+
+	ber_pvt_sb_buf_destroy( &p->buf_in );
+
+	/* Read the length of the packet */
+	while ( p->sec_buf_in.buf_ptr < 4 ) {
+		ret = LBER_SBIOD_READ_NEXT( sbiod, p->sec_buf_in.buf_base,
+			4 - p->sec_buf_in.buf_ptr );
+#ifdef EINTR
+		if ( ( ret < 0 ) && ( errno == EINTR ) )
+			continue;
+#endif
+		if ( ret <= 0 )
+			return ret;
+
+		p->sec_buf_in.buf_ptr += ret;
 	}
 
-	if ( sasl_decode( conn, buf, buflen, &recv_tok, &recv_tok_len ) != SASL_OK ) {
+	/* The new packet always starts at p->sec_buf_in.buf_base */
+	ret = sb_sasl_pkt_length( p->sec_buf_in.buf_base,
+		sbiod->sbiod_sb->sb_debug );
+
+	/* Grow the packet buffer if neccessary */
+	if ( ( p->sec_buf_in.buf_size < ret ) && 
+			ber_pvt_sb_grow_buffer( &p->sec_buf_in, ret ) < 0 ) {
+		errno = ENOMEM;
 		return -1;
 	}
-
-	if ( recv_tok_len > buflen ) {
-		LDAP_FREE( recv_tok );
+	p->sec_buf_in.buf_end = ret;
+
+	/* Did we read the whole encrypted packet? */
+	while ( p->sec_buf_in.buf_ptr < p->sec_buf_in.buf_end ) {
+		/* No, we have got only a part of it */
+		ret = p->sec_buf_in.buf_end - p->sec_buf_in.buf_ptr;
+
+		ret = LBER_SBIOD_READ_NEXT( sbiod, p->sec_buf_in.buf_base +
+			p->sec_buf_in.buf_ptr, ret );
+#ifdef EINTR
+		if ( ( ret < 0 ) && ( errno == EINTR ) )
+			continue;
+#endif
+		if ( ret <= 0 )
+			return ret;
+
+		p->sec_buf_in.buf_ptr += ret;
+   	}
+
+	/* Decode the packet */
+	ret = sasl_decode( p->sasl_context, p->sec_buf_in.buf_base,
+		p->sec_buf_in.buf_end, &p->buf_in.buf_base,
+		(unsigned *)&p->buf_in.buf_end );
+	if ( ret != SASL_OK ) {
+		ber_log_printf( LDAP_DEBUG_ANY, sbiod->sbiod_sb->sb_debug,
+			"sb_sasl_read: failed to decode packet: %s\n",
+			sasl_errstring( ret, NULL, NULL ) );
+		sb_sasl_drop_packet( &p->sec_buf_in,
+			sbiod->sbiod_sb->sb_debug );
+		errno = EIO;
 		return -1;
 	}
+	
+	/* Drop the packet from the input buffer */
+	sb_sasl_drop_packet( &p->sec_buf_in, sbiod->sbiod_sb->sb_debug );
 
-	memcpy( buf, recv_tok, recv_tok_len );	
+	p->buf_in.buf_size = p->buf_in.buf_end;
 
-	LDAP_FREE( recv_tok );
+	bufptr += ber_pvt_sb_copy_out( &p->buf_in, buf + bufptr, len );
 
-	return recv_tok_len;
+	return bufptr;
 }
 
-static ber_slen_t sasl_write( Sockbuf *sb, void *buf, ber_len_t len )
+static ber_slen_t
+sb_sasl_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
 {
-	char *wrapped_tok;
-	unsigned wrapped_tok_len;
-	sasl_conn_t *conn = (sasl_conn_t *)sb->sb_iodata;
+	struct sb_sasl_data	*p;
+	int			ret;
 
-	if ( sasl_encode( conn, (const char *)buf, len,
-		&wrapped_tok, &wrapped_tok_len ) != SASL_OK ) {
-		return -1;
+	assert( sbiod != NULL );
+	assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+	p = (struct sb_sasl_data *)sbiod->sbiod_pvt;
+
+	/* Are there anything left in the buffer? */
+	if ( p->buf_out.buf_ptr != p->buf_out.buf_end ) {
+		ret = ber_pvt_sb_do_write( sbiod, &p->buf_out );
+		if ( ret <= 0 )
+			return ret;
 	}
 
-	if ((ber_pvt_sb_io_tcp.sbi_write)( sb, wrapped_tok, wrapped_tok_len ) != wrapped_tok_len ) {
-		LDAP_FREE( wrapped_tok );
+	/* now encode the next packet. */
+	ber_pvt_sb_buf_destroy( &p->buf_out );
+	ret = sasl_encode( p->sasl_context, buf, len, &p->buf_out.buf_base,
+		(unsigned *)&p->buf_out.buf_size );
+	if ( ret != SASL_OK ) {
+		ber_log_printf( LDAP_DEBUG_ANY, sbiod->sbiod_sb->sb_debug,
+			"sb_sasl_write: failed to encode packet: %s\n",
+			sasl_errstring( ret, NULL, NULL ) );
 		return -1;
 	}
+	p->buf_out.buf_end = p->buf_out.buf_size;
 
-	LDAP_FREE( wrapped_tok );
-
+	ret = ber_pvt_sb_do_write( sbiod, &p->buf_out );
+	if ( ret <= 0 )
+		return ret;
 	return len;
 }
 
-static int sasl_close( Sockbuf *sb )
+static int
+sb_sasl_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
 {
-	return (ber_pvt_sb_io_tcp.sbi_close)( sb );
+	struct sb_sasl_data	*p;
+
+	p = (struct sb_sasl_data *)sbiod->sbiod_pvt;
+
+	if ( opt == LBER_SB_OPT_DATA_READY ) {
+		if ( p->buf_in.buf_ptr != p->buf_in.buf_end )
+			return 1;
+	}
+	
+	return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg );
+}
+
+Sockbuf_IO ldap_pvt_sockbuf_io_sasl =
+{
+	sb_sasl_setup,		/* sbi_setup */
+	sb_sasl_remove,		/* sbi_remove */
+	sb_sasl_ctrl,		/* sbi_ctrl */
+	sb_sasl_read,		/* sbi_read */
+	sb_sasl_write,		/* sbi_write */
+	NULL			/* sbi_close */
+};
+
+int ldap_pvt_sasl_install( Sockbuf *sb, void *ctx_arg )
+{
+	/* don't install the stuff unless security has been negotiated */
+
+	if ( !ber_sockbuf_ctrl( sb, LBER_SB_OPT_HAS_IO,
+			&ldap_pvt_sockbuf_io_sasl ) )
+		ber_sockbuf_add_io( sb, &ldap_pvt_sockbuf_io_sasl,
+			LBER_SBIOD_LEVEL_APPLICATION, ctx_arg );
+
+	return LDAP_SUCCESS;
 }
 
 static int
@@ -598,11 +765,16 @@ ldap_pvt_sasl_bind(
 	LDAPControl		**cctrls )
 {
 	const char *mech;
-	int	saslrc, rc, ssf = 0;
+	int			saslrc, rc;
+	sasl_ssf_t		*ssf = NULL;
 	unsigned credlen;
 	struct berval ccred, *scred;
 	char *host;
 	sasl_interact_t *client_interact = NULL;
+	struct sockaddr_in	sin;
+	socklen_t		len;
+	sasl_security_properties_t	secprops;
+	ber_socket_t		sd;
 
 	Debug( LDAP_DEBUG_TRACE, "ldap_pvt_sasl_bind\n", 0, 0, 0 );
 
@@ -612,15 +784,18 @@ ldap_pvt_sasl_bind(
 		return ld->ld_errno;
 	}
 
-	if( ! ber_pvt_sb_in_use( &ld->ld_sb ) ) {
+	ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd );
+
+	if ( sd == AC_SOCKET_INVALID ) {
  		/* not connected yet */
  		int rc = ldap_open_defconn( ld );
   
 		if( rc < 0 ) return ld->ld_errno;
+		ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd );
 	}   
 
 	/* XXX this doesn't work with PF_LOCAL hosts */
-	host = ldap_host_connected_to( &ld->ld_sb );
+	host = ldap_host_connected_to( ld->ld_sb );
 
 	if ( host == NULL ) {
 		ld->ld_errno = LDAP_UNAVAILABLE;
@@ -631,16 +806,42 @@ ldap_pvt_sasl_bind(
 		sasl_dispose( &ld->ld_sasl_context );
 	}
 
-	saslrc = sasl_client_new( "ldap", host, callbacks, 0, &ld->ld_sasl_context );
+	saslrc = sasl_client_new( "ldap", host, callbacks, SASL_SECURITY_LAYER,
+		&ld->ld_sasl_context );
 
 	LDAP_FREE( host );
 
 	if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
-		ld->ld_errno = sasl_err2ldap( rc );
+		ld->ld_errno = sasl_err2ldap( saslrc );
 		sasl_dispose( &ld->ld_sasl_context );
 		return ld->ld_errno;
 	}
 
+	len = sizeof( sin );
+	if ( getpeername( sd, (struct sockaddr *)&sin, &len ) == -1 ) {
+		Debug( LDAP_DEBUG_ANY, "SASL: can't query remote IP.\n",
+			0, 0, 0 );
+		ld->ld_errno = LDAP_OPERATIONS_ERROR;
+		return ld->ld_errno;
+	}
+	sasl_setprop( ld->ld_sasl_context, SASL_IP_REMOTE, &sin );
+
+	len = sizeof( sin );
+	if ( getsockname( sd, (struct sockaddr *)&sin, &len ) == -1 ) {
+		Debug( LDAP_DEBUG_ANY, "SASL: can't query local IP.\n",
+			0, 0, 0 );
+		ld->ld_errno = LDAP_OPERATIONS_ERROR;
+		return ld->ld_errno;
+	}
+	sasl_setprop( ld->ld_sasl_context, SASL_IP_LOCAL, &sin );
+
+	memset( &secprops, 0, sizeof( secprops ) );
+	secprops.min_ssf = ld->ld_options.ldo_sasl_minssf;
+	secprops.max_ssf = ld->ld_options.ldo_sasl_maxssf;
+	secprops.security_flags = SASL_SECURITY_LAYER;
+	secprops.maxbufsize = 65536;
+	sasl_setprop( ld->ld_sasl_context, SASL_SEC_PROPS, &secprops );
+
 	ccred.bv_val = NULL;
 	ccred.bv_len = 0;
 
@@ -702,8 +903,8 @@ ldap_pvt_sasl_bind(
 	assert ( rc == LDAP_SUCCESS );
 
 	if ( sasl_getprop( ld->ld_sasl_context, SASL_SSF, (void **)&ssf )
-		== SASL_OK && ssf ) {
-		ldap_pvt_sasl_install( &ld->ld_sb, ld->ld_sasl_context );
+		== SASL_OK && ssf && *ssf ) {
+		ldap_pvt_sasl_install( ld->ld_sb, ld->ld_sasl_context );
 	}
 
 	return rc;
diff --git a/libraries/libldap/test.c b/libraries/libldap/test.c
index f32fe6954bd3308f407e5a1befcc4cb54b7755af..cdcadc9fb2a801dae3c03cbee7cfe641fb0f3970 100644
--- a/libraries/libldap/test.c
+++ b/libraries/libldap/test.c
@@ -235,6 +235,9 @@ bind_prompt( LDAP *ld, LDAP_CONST char *url, int request, ber_int_t msgid)
 	char *dnp;
 	int	authmethod;
 
+	printf("rebind for request=%d msgid=%ld url=%s\n",
+		request, (long) msgid, url );
+
 #ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_KBIND
 		getline( dn, sizeof(dn), stdin,
 		    "re-bind method (0->simple, 1->krbv41, 2->krbv42, 3->krbv41&2)? " );
@@ -321,12 +324,12 @@ main( int argc, char **argv )
 
 		case 't':	/* copy ber's to given file */
 			copyfname = strdup( optarg );
-			copyoptions = LBER_TO_FILE;
+/*			copyoptions = LBER_TO_FILE; */
 			break;
 
 		case 'T':	/* only output ber's to given file */
 			copyfname = strdup( optarg );
-			copyoptions = (LBER_TO_FILE | LBER_TO_FILE_ONLY);
+/*			copyoptions = (LBER_TO_FILE | LBER_TO_FILE_ONLY); */
 			break;
 
 		default:
@@ -362,12 +365,12 @@ main( int argc, char **argv )
 	}
 
 	if ( copyfname != NULL ) {
-		if ( (ld->ld_sb.sb_fd = open( copyfname, O_WRONLY | O_CREAT,
+		if ( ( ld->ld_sb->sb_fd = open( copyfname, O_WRONLY | O_CREAT,
 		    0600 ))  == -1 ) {
 			perror( copyfname );
 			exit ( EXIT_FAILURE );
 		}
-		ld->ld_sb.sb_options = copyoptions;
+		ld->ld_sb->sb_options = copyoptions;
 	}
 
 	bound = 0;
diff --git a/libraries/libldap/tls.c b/libraries/libldap/tls.c
index 32a773cfca55c5e5e33dc2c87e001eff0df6957c..ff72666090ee294e78c0b76b54dcde2388197994 100644
--- a/libraries/libldap/tls.c
+++ b/libraries/libldap/tls.c
@@ -41,24 +41,11 @@ static char *tls_opt_cacertdir = NULL;
 static int  tls_opt_require_cert = 0;
 static char *tls_opt_ciphersuite = NULL;
 
-#define HAS_TLS( sb ) ((sb)->sb_io==&tls_io)
+#define HAS_TLS( sb )	ber_sockbuf_ctrl( sb, LBER_SB_OPT_HAS_IO, \
+				(void *)&ldap_pvt_sockbuf_io_tls )
 
-static int tls_setup( Sockbuf *sb, void *arg );
-static int tls_remove( Sockbuf *sb );
-static ber_slen_t tls_read( Sockbuf *sb, void *buf, ber_len_t len );
-static ber_slen_t tls_write( Sockbuf *sb, void *buf, ber_len_t len );
-static int tls_close( Sockbuf *sb );
 static void tls_report_error( void );
 
-static Sockbuf_IO tls_io=
-{
-   tls_setup,
-   tls_remove,
-   tls_read,
-   tls_write,
-   tls_close
-};
-
 static void tls_info_cb( SSL *ssl, int where, int ret );
 static int tls_verify_cb( int ok, X509_STORE_CTX *ctx );
 static RSA * tls_tmp_rsa_cb( SSL *ssl, int is_export, int key_length );
@@ -243,9 +230,8 @@ get_ca_list( char * bundle, char * dir )
 }
 
 static SSL *
-alloc_handle( Sockbuf *sb, void *ctx_arg )
+alloc_handle( void *ctx_arg )
 {
-	int	err;
 	SSL_CTX	*ctx;
 	SSL	*ssl;
 
@@ -266,8 +252,6 @@ alloc_handle( Sockbuf *sb, void *ctx_arg )
 	if ( tls_opt_trace ) {
 		SSL_set_info_callback( ssl, tls_info_cb );
 	}
-	sb->sb_iodata = ssl;
-	SSL_set_fd( ssl, ber_pvt_sb_get_desc( sb ) );
 	return ssl;
 }
 
@@ -293,6 +277,252 @@ update_flags( Sockbuf *sb, SSL * ssl, int rc )
 	return 0;
 }
 
+/*
+ * TLS support for LBER Sockbufs
+ */
+
+struct tls_data {
+	SSL			*ssl;
+	Sockbuf_IO_Desc		*sbiod;
+};
+
+extern BIO_METHOD ldap_pvt_sb_bio_method;
+
+static int
+sb_tls_setup( Sockbuf_IO_Desc *sbiod, void *arg )
+{
+	struct tls_data		*p;
+	BIO			*bio;
+
+	assert( sbiod != NULL );
+
+	p = LBER_MALLOC( sizeof( *p ) );
+	if ( p == NULL )
+		return -1;
+	
+	p->ssl = (SSL *)arg;
+	p->sbiod = sbiod;
+	bio = BIO_new( &ldap_pvt_sb_bio_method );
+	bio->ptr = (void *)p;
+	SSL_set_bio( p->ssl, bio, bio );
+	sbiod->sbiod_pvt = p;
+	return 0;
+}
+
+static int
+sb_tls_remove( Sockbuf_IO_Desc *sbiod )
+{
+	struct tls_data		*p;
+	
+	assert( sbiod != NULL );
+	assert( sbiod->sbiod_pvt != NULL );
+
+	p = (struct tls_data *)sbiod->sbiod_pvt;
+	SSL_free( p->ssl );
+	LBER_FREE( sbiod->sbiod_pvt );
+	sbiod->sbiod_pvt = NULL;
+	return 0;
+}
+
+static int
+sb_tls_close( Sockbuf_IO_Desc *sbiod )
+{
+	struct tls_data		*p;
+	
+	assert( sbiod != NULL );
+	assert( sbiod->sbiod_pvt != NULL );
+
+	p = (struct tls_data *)sbiod->sbiod_pvt;
+	SSL_shutdown( p->ssl );
+	return 0;
+}
+
+static int
+sb_tls_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
+{
+	struct tls_data		*p;
+	
+	assert( sbiod != NULL );
+	assert( sbiod->sbiod_pvt != NULL );
+
+	p = (struct tls_data *)sbiod->sbiod_pvt;
+	
+	if ( opt == LBER_SB_OPT_GET_SSL ) {
+		*((SSL **)arg) = p->ssl;
+		return 1;
+	}
+	
+	return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg );
+}
+
+static ber_slen_t
+sb_tls_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
+{
+	struct tls_data		*p;
+	ber_slen_t		ret;
+	int			err;
+
+	assert( sbiod != NULL );
+	assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+	p = (struct tls_data *)sbiod->sbiod_pvt;
+
+	ret = SSL_read( p->ssl, (char *)buf, len );
+#ifdef HAVE_WINSOCK
+	errno = WSAGetLastError();
+#endif
+	err = SSL_get_error( p->ssl, ret );
+	if (err == SSL_ERROR_WANT_READ ) {
+		sbiod->sbiod_sb->sb_trans_needs_read = 1;
+#ifdef WIN32
+		errno = EWOULDBLOCK;
+#endif
+	}
+	else
+		sbiod->sbiod_sb->sb_trans_needs_read = 0;
+	return ret;
+}
+
+static ber_slen_t
+sb_tls_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
+{
+	struct tls_data		*p;
+	ber_slen_t		ret;
+	int			err;
+
+	assert( sbiod != NULL );
+	assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+	p = (struct tls_data *)sbiod->sbiod_pvt;
+
+	ret = SSL_write( p->ssl, (char *)buf, len );
+#ifdef HAVE_WINSOCK
+	errno = WSAGetLastError();
+#endif
+	err = SSL_get_error( p->ssl, ret );
+	if (err == SSL_ERROR_WANT_WRITE ) {
+		sbiod->sbiod_sb->sb_trans_needs_write = 1;
+#ifdef WIN32
+		errno = EWOULDBLOCK;
+#endif
+	}
+	else
+		sbiod->sbiod_sb->sb_trans_needs_write = 0;
+	return ret;
+}
+
+Sockbuf_IO ldap_pvt_sockbuf_io_tls =
+{
+	sb_tls_setup,		/* sbi_setup */
+	sb_tls_remove,		/* sbi_remove */
+	sb_tls_ctrl,		/* sbi_ctrl */
+	sb_tls_read,		/* sbi_read */
+	sb_tls_write,		/* sbi_write */
+	sb_tls_close		/* sbi_close */
+};
+
+static int
+sb_tls_bio_create( BIO *b ) {
+	b->init = 1;
+	b->num = 0;
+	b->ptr = NULL;
+	b->flags = 0;
+	return 1;
+}
+
+static int
+sb_tls_bio_destroy( BIO *b )
+{
+	if ( b == NULL )
+		return 0;
+
+	b->ptr = NULL;		/* sb_tls_remove() will free it */
+	b->init = 0;
+	b->flags = 0;
+	return 1;
+}
+
+static int
+sb_tls_bio_read( BIO *b, char *buf, int len )
+{
+	struct tls_data		*p;
+	int			ret;
+		
+	if ( buf == NULL || len <= 0 )
+		return 0;
+
+	p = (struct tls_data *)b->ptr;
+
+	if ( p == NULL || p->sbiod == NULL )
+		return 0;
+
+	ret = LBER_SBIOD_READ_NEXT( p->sbiod, buf, len );
+
+	BIO_clear_retry_flags( b );
+	if ( ret < 0 && errno == EWOULDBLOCK )
+		BIO_set_retry_read( b );
+
+	return ret;
+}
+
+static int
+sb_tls_bio_write( BIO *b, char *buf, int len )
+{
+	struct tls_data		*p;
+	int			ret;
+	
+	if ( buf == NULL || len <= 0 )
+		return 0;
+	
+	p = (struct tls_data *)b->ptr;
+
+	if ( p == NULL || p->sbiod == NULL )
+		return 0;
+
+	ret = LBER_SBIOD_WRITE_NEXT( p->sbiod, buf, len );
+
+	BIO_clear_retry_flags( b );
+	if ( ret < 0 && errno == EWOULDBLOCK )
+		BIO_set_retry_write( b );
+
+	return ret;
+}
+
+static long
+sb_tls_bio_ctrl( BIO *b, int cmd, long num, char *ptr )
+{
+	if ( cmd == BIO_CTRL_FLUSH ) {
+		/* The OpenSSL library needs this */
+		return 1;
+	}
+	return 0;
+}
+
+static int
+sb_tls_bio_gets( BIO *b, char *buf, int len )
+{
+	return -1;
+}
+
+static int
+sb_tls_bio_puts( BIO *b, char *str )
+{
+	return sb_tls_bio_write( b, str, strlen( str ) );
+}
+	
+BIO_METHOD ldap_pvt_sb_bio_method =
+{
+	( 100 | 0x400 ),		/* it's a source/sink BIO */
+	"sockbuf glue",
+	sb_tls_bio_write,
+	sb_tls_bio_read,
+	sb_tls_bio_puts,
+	sb_tls_bio_gets,
+	sb_tls_bio_ctrl,
+	sb_tls_bio_create,
+	sb_tls_bio_destroy
+};
+
 /*
  * Call this to do a TLS connect on a sockbuf. ctx_arg can be
  * a SSL_CTX * or NULL, in which case the default ctx is used.
@@ -313,13 +543,17 @@ ldap_pvt_tls_connect( LDAP *ld, Sockbuf *sb, void *ctx_arg )
 	SSL	*ssl;
 
 	if ( HAS_TLS( sb ) ) {
-		ssl = (SSL *) sb->sb_iodata;
+		ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_SSL, (void *)&ssl );
 	} else {
-		ssl = alloc_handle( sb, ctx_arg );
+		ssl = alloc_handle( ctx_arg );
 		if ( ssl == NULL )
 			return -1;
-		ber_pvt_sb_clear_io( sb );
-		ber_pvt_sb_set_io( sb, &tls_io, (void *)ssl );
+#ifdef LDAP_DEBUG
+		ber_sockbuf_add_io( sb, &ber_sockbuf_io_debug,
+			LBER_SBIOD_LEVEL_TRANSPORT, (void *)"tls_" );
+#endif
+		ber_sockbuf_add_io( sb, &ldap_pvt_sockbuf_io_tls,
+			LBER_SBIOD_LEVEL_TRANSPORT, (void *)ssl );
 	}
 
 	err = SSL_connect( ssl );
@@ -335,8 +569,12 @@ ldap_pvt_tls_connect( LDAP *ld, Sockbuf *sb, void *ctx_arg )
 			ld->ld_error = ldap_strdup(ERR_error_string(err, buf));
 		}
 		Debug( LDAP_DEBUG_ANY,"TLS: can't connect.\n",0,0,0);
-		ber_pvt_sb_clear_io( sb );
-		ber_pvt_sb_set_io( sb, &ber_pvt_sb_io_tcp, NULL );
+		ber_sockbuf_remove_io( sb, &ldap_pvt_sockbuf_io_tls,
+			LBER_SBIOD_LEVEL_TRANSPORT );
+#ifdef LDAP_DEBUG
+		ber_sockbuf_remove_io( sb, &ber_sockbuf_io_debug,
+			LBER_SBIOD_LEVEL_TRANSPORT );
+#endif
 		return -1;
 	}
 	return 0;
@@ -353,13 +591,17 @@ ldap_pvt_tls_accept( Sockbuf *sb, void *ctx_arg )
 	SSL	*ssl;
 
 	if ( HAS_TLS( sb ) ) {
-		ssl = (SSL *) sb->sb_iodata;
+		ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_SSL, (void *)&ssl );
 	} else {
-		ssl = alloc_handle( sb, ctx_arg );
+		ssl = alloc_handle( ctx_arg );
 		if ( ssl == NULL )
 			return -1;
-		ber_pvt_sb_clear_io( sb );
-		ber_pvt_sb_set_io( sb, &tls_io, (void *)ssl );
+#ifdef LDAP_DEBUG
+		ber_sockbuf_add_io( sb, &ber_sockbuf_io_debug,
+			LBER_SBIOD_LEVEL_TRANSPORT, (void *)"tls_" );
+#endif
+		ber_sockbuf_add_io( sb, &ldap_pvt_sockbuf_io_tls,
+			LBER_SBIOD_LEVEL_TRANSPORT, (void *)ssl );
 	}
 
 	err = SSL_accept( ssl );
@@ -372,8 +614,12 @@ ldap_pvt_tls_accept( Sockbuf *sb, void *ctx_arg )
 			return 1;
 		Debug( LDAP_DEBUG_ANY,"TLS: can't accept.\n",0,0,0 );
 		tls_report_error();
-		ber_pvt_sb_clear_io( sb );
-		ber_pvt_sb_set_io( sb, &ber_pvt_sb_io_tcp, NULL );
+		ber_sockbuf_remove_io( sb, &ldap_pvt_sockbuf_io_tls,
+			LBER_SBIOD_LEVEL_TRANSPORT );
+#ifdef LDAP_DEBUG
+		ber_sockbuf_remove_io( sb, &ber_sockbuf_io_debug,
+			LBER_SBIOD_LEVEL_TRANSPORT );
+#endif
 		return -1;
 	}
 	return 0;
@@ -390,16 +636,19 @@ ldap_pvt_tls_inplace ( Sockbuf *sb )
 void *
 ldap_pvt_tls_sb_handle( Sockbuf *sb )
 {
-	if (HAS_TLS( sb ))
-		return sb->sb_iodata;
-	else
+	void			*p;
+	
+	if (HAS_TLS( sb )) {
+		ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_SSL, (void *)&p );
+		return p;
+	}
 		return NULL;
 }
 
 void *
 ldap_pvt_tls_get_handle( LDAP *ld )
 {
-	return ldap_pvt_tls_sb_handle(&ld->ld_sb);
+	return ldap_pvt_tls_sb_handle( ld->ld_sb );
 }
 
 const char *
@@ -568,60 +817,6 @@ ldap_pvt_tls_start ( LDAP *ld, Sockbuf *sb, void *ctx_arg )
 	return LDAP_SUCCESS;
 }
 
-
-static int
-tls_setup( Sockbuf *sb, void *arg )
-{
-	sb->sb_iodata = arg;
-	return 0;
-}
-
-static int
-tls_remove( Sockbuf *sb )
-{
-	SSL_free( (SSL *) sb->sb_iodata );
-	return 0;
-}
-
-static ber_slen_t
-tls_write( Sockbuf *sb, void *buf, ber_len_t sz )
-{
-	int ret = SSL_write( (SSL *)sb->sb_iodata, buf, sz );
-
-#ifdef HAVE_WINSOCK
-	errno = WSAGetLastError();
-#endif
-	update_flags(sb, (SSL *)sb->sb_iodata, ret );
-#ifdef WIN32
-	if (sb->sb_trans_needs_write)
-		errno = EWOULDBLOCK;
-#endif
-	return ret;
-}
-
-static ber_slen_t
-tls_read( Sockbuf *sb, void *buf, ber_len_t sz )
-{
-	int ret = SSL_read( (SSL *)sb->sb_iodata, buf, sz );
-
-#ifdef HAVE_WINSOCK
-	errno = WSAGetLastError();
-#endif
-	update_flags(sb, (SSL *)sb->sb_iodata, ret );
-#ifdef WIN32
-	if (sb->sb_trans_needs_read)
-		errno = EWOULDBLOCK;
-#endif
-	return ret;
-}
-
-static int
-tls_close( Sockbuf *sb )
-{
-	tcp_close( ber_pvt_sb_get_desc( sb ) );
-	return 0;
-}
-
 /* Derived from openssl/apps/s_cb.c */
 static void
 tls_info_cb( SSL *ssl, int where, int ret )
diff --git a/libraries/libldap/unbind.c b/libraries/libldap/unbind.c
index e858d6e34317713e1499e93c6f683a24310697dc..7fd116f76048f781cfb36f115ceb949a71a0d2f9 100644
--- a/libraries/libldap/unbind.c
+++ b/libraries/libldap/unbind.c
@@ -145,7 +145,7 @@ ldap_ld_free(
 	}
 #endif 
 
-	ber_pvt_sb_destroy( &(ld->ld_sb) );   
+	ber_sockbuf_free( ld->ld_sb );   
    
 	LDAP_FREE( (char *) ld );
    
diff --git a/servers/ldapd/main.c b/servers/ldapd/main.c
index 7e86443755d8ddf252e0234a274640bf2be55299..8d1d2659c418b2809b6a32d0cb1ae2106e2df9f0 100644
--- a/servers/ldapd/main.c
+++ b/servers/ldapd/main.c
@@ -38,7 +38,6 @@
 #include <quipu/ds_error.h>
 
 #include "lber.h"
-#include "../../libraries/liblber/lber-int.h"	/* get struct sockbuf */
 #include "ldap.h"
 #include "common.h"
 #include "lutil.h"		/* Get lutil_detach() */
@@ -492,7 +491,7 @@ do_queries(
 	fd_set		readfds;
 	int		rc;
 	struct timeval	timeout;
-	Sockbuf		sb;
+	Sockbuf		*sb;
 #ifdef LDAP_CONNECTIONLESS
 	struct sockaddr	saddr, faddr;
 	struct sockaddr *saddrlist[ 1 ];
@@ -515,10 +514,9 @@ do_queries(
 		conn_init();
 	}
 
-   	ber_pvt_sb_init( &sb );
-   	ber_pvt_sb_set_desc( &sb, clientsock );
-	ber_pvt_sb_set_io( &sb, (udp) ? &ber_pvt_sb_io_udp :
-					&ber_pvt_sb_io_tcp, NULL );
+	sb = ber_sockbuf_alloc( );
+	ber_sockbuf_add_io( sb, (udp) ? &ber_sockbuf_io_udp :
+		&ber_sockbuf_io_tcp, (void *)&clientsock );
 	timeout.tv_sec = idletime;
 	timeout.tv_usec = 0;
 	for ( ;; ) {
@@ -547,7 +545,7 @@ do_queries(
 		 * already waiting for us on the client sock.
 		 */
 
-		if ( ! ber_pvt_sb_data_ready( &sb ) ) {
+		if ( ! ber_sockbuf_ctrl( sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
 			if ( (rc = select( dtblsize, &readfds, 0, 0,
 			    udp ? 0 : &timeout )) < 1 ) {
 #ifdef LDAP_DEBUG
@@ -573,9 +571,9 @@ do_queries(
 			}
 		}
 
-		if ( ber_pvt_sb_data_ready( &sb ) ||
+		if ( ber_sockbuf_ctrl( sb, LBER_SB_OPT_DATA_READY, NULL ) ||
 		    FD_ISSET( clientsock, &readfds ) ) {
-			client_request( &sb, conns, udp );
+			client_request( sb, conns, udp );
 		} else {
 			if ( (dsaconn = conn_getfd( &readfds )) == NULL ) {
 				Debug( LDAP_DEBUG_ANY, "No DSA activity!\n",
@@ -583,7 +581,7 @@ do_queries(
 				continue;
 			}
 
-			dsa_response( dsaconn, &sb );
+			dsa_response( dsaconn, sb );
 		}
 	}
 	/* NOT REACHED */
diff --git a/servers/ldapd/request.c b/servers/ldapd/request.c
index 95598ff28a9bae3bba6fe2451f5f2fe06f05e500..8f27d30430cad701aab28be20a38600d5d736818 100644
--- a/servers/ldapd/request.c
+++ b/servers/ldapd/request.c
@@ -28,7 +28,6 @@
 #include <quipu/dua.h>
 
 #include "lber.h"
-#include "../../libraries/liblber/lber-int.h"	/* get struct berelement */
 #include "ldap.h"
 #include "common.h"
 
@@ -89,7 +88,8 @@ client_request(
 
 #ifdef LDAP_CONNECTIONLESS
 	if ( udp && dosyslog ) {
-	   	sai = (struct sockaddr_in *)ber_pvt_sb_udp_get_src( &clientsb );
+		ber_sockbuf_ctrl( clientsb, LBER_SB_OPT_UDP_GET_SRC,
+			(void *)&sai );
 		syslog( LOG_INFO, "UDP request from unknown (%s)",
 			inet_ntoa( sai->sin_addr ) );
 	}
@@ -193,7 +193,7 @@ client_request(
 		free( ber.ber_buf );
 		return;
 	}
-	sai = (struct sockaddr_in *) ber_pvt_sb_udp_get_src( &clientsb );
+	ber_sockbuf_ctrl( clientsb, LBER_SB_OPT_UDP_GET_SRC, (void *)&sai );
    
 	if ( get_cldap_msg( msgid, tag,
 	    (struct sockaddr *)sai ) != NULL ) {
diff --git a/servers/ldapd/result.c b/servers/ldapd/result.c
index 29ebd3e1bb2a9ba7b08d6fa580dcd774bddc3b65..d7a8e1445a77130cf09964a94e65cd93952d49ed 100644
--- a/servers/ldapd/result.c
+++ b/servers/ldapd/result.c
@@ -24,7 +24,6 @@
 #include <quipu/dua.h>
 
 #include "lber.h"
-#include "../../libraries/liblber/lber-int.h"	/* get struct berelement */
 #include "ldap.h"
 #include "common.h"
 
@@ -281,7 +280,8 @@ send_ldap_msgresult(
 {
 #ifdef LDAP_CONNECTIONLESS
 	if ( m->m_cldap ) {
-	   	ber_pvt_sb_udp_set_dst( &sb, &m->m_clientaddr );
+		ber_sockbuf_ctrl( sb, LBER_SB_OPT_UDP_SET_DST,
+		    (void *)&m->m_clientaddr );
 
 		Debug( LDAP_DEBUG_TRACE, "UDP response to %s port %d\n", 
 		    inet_ntoa(((struct sockaddr_in *)
@@ -306,7 +306,7 @@ send_ldap_result(
 	int		rc;
 #ifdef LDAP_CONNECTIONLESS
 	int		cldap;
-	cldap = ( sb->sb_io == &ber_pvt_sb_io_udp );
+	cldap = ber_sockbuf_ctrl( sb, LBER_SB_OPT_HAS_IO, &ber_sockbuf_io_udp );
 #endif
 
 	Debug( LDAP_DEBUG_TRACE, "send_ldap_result\n", 0, 0, 0 );
diff --git a/servers/ldapd/search.c b/servers/ldapd/search.c
index 4ad1a3de8c301303c291818f1a4f2125b4e0f993..8ff3c194f64e6ac23f8cebb10d1cd4414fb6e492 100644
--- a/servers/ldapd/search.c
+++ b/servers/ldapd/search.c
@@ -26,7 +26,6 @@
 #include <quipu/dua.h>
 
 #include "lber.h"
-#include "../../libraries/liblber/lber-int.h"	/* get struct berelement */
 #include "ldap.h"
 #include "common.h"
 
@@ -701,7 +700,8 @@ search_result(
 			    LDAP_OPERATIONS_ERROR, NULL, "ber_printf" );
 			return;
 		}
-	   	ber_pvt_sb_udp_set_dst( sb, &m->m_clientaddr );
+		ber_sockbuf_ctrl( sb, LBER_SB_OPT_UDP_SET_DST,
+		    (void *)&m->m_clientaddr );
 
 		if ( ber_flush( sb, ber, 1 ) != 0 ) {
 		    send_ldap_msgresult( sb, SEARCHRESTAG, m, 
diff --git a/servers/slapd/connection.c b/servers/slapd/connection.c
index a2231eb5ed38bcc81ce5e702ebd891bec9124f3c..9a3b4feb564594fbabbdaa5f8c8a983216f17e1a 100644
--- a/servers/slapd/connection.c
+++ b/servers/slapd/connection.c
@@ -16,9 +16,6 @@
 
 #include "slap.h"
 
-/* we need LBER internals */
-#include "../../libraries/liblber/lber-int.h"
-
 /* protected by connections_mutex */
 static ldap_pvt_thread_mutex_t connections_mutex;
 static Connection *connections = NULL;
@@ -213,9 +210,12 @@ static Connection* connection_get( ber_socket_t s )
 #else
 	c = NULL;
 	{
-		ber_socket_t i;
+		ber_socket_t i, sd;
 
 		for(i=0; i<dtblsize; i++) {
+			ber_sockbuf_ctrl( connections[i].c_sb,
+				LBER_SB_OPT_GET_FD, &sd );
+
 			if( connections[i].c_struct_state == SLAP_C_UNINITIALIZED ) {
 				assert( connections[i].c_conn_state == SLAP_C_INVALID );
 				assert( connections[i].c_sb == 0 );
@@ -224,7 +224,7 @@ static Connection* connection_get( ber_socket_t s )
 
 			if( connections[i].c_struct_state == SLAP_C_UNUSED ) {
 				assert( connections[i].c_conn_state == SLAP_C_INVALID );
-				assert( !ber_pvt_sb_in_use( connections[i].c_sb ) );
+				assert( sd == AC_SOCKET_INVALID );
 				continue;
 			}
 
@@ -232,7 +232,7 @@ static Connection* connection_get( ber_socket_t s )
 			 * so don't assert details here.
 			 */
 
-			if( ber_pvt_sb_get_desc( connections[i].c_sb ) == s ) {
+			if( sd == s ) {
 				c = &connections[i];
 				break;
 			}
@@ -241,13 +241,16 @@ static Connection* connection_get( ber_socket_t s )
 #endif
 
 	if( c != NULL ) {
+		ber_socket_t	sd;
+
 		ldap_pvt_thread_mutex_lock( &c->c_mutex );
 
+		ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_GET_FD, &sd );
 		if( c->c_struct_state != SLAP_C_USED ) {
 			/* connection must have been closed due to resched */
 
 			assert( c->c_conn_state == SLAP_C_INVALID );
-			assert( !ber_pvt_sb_in_use( c->c_sb ) );
+			assert( sd == AC_SOCKET_INVALID );
 
 			Debug( LDAP_DEBUG_TRACE,
 				"connection_get(%d): connection not used\n",
@@ -265,7 +268,7 @@ static Connection* connection_get( ber_socket_t s )
 
 		assert( c->c_struct_state == SLAP_C_USED );
 		assert( c->c_conn_state != SLAP_C_INVALID );
-		assert( ber_pvt_sb_in_use( c->c_sb ) );
+		assert( sd != AC_SOCKET_INVALID );
 
     	c->c_activitytime = slap_get_time();
 	}
@@ -323,6 +326,10 @@ long connection_init(
 		c = NULL;
 
         for( i=0; i < dtblsize; i++) {
+	    ber_socket_t	sd;
+
+	    ber_sockbuf_ctrl( connections[i].c_sb, LBER_SB_OPT_GET_FD, &sd );
+	    
             if( connections[i].c_struct_state == SLAP_C_UNINITIALIZED ) {
                 assert( connections[i].c_sb == 0 );
                 c = &connections[i];
@@ -330,14 +337,14 @@ long connection_init(
             }
 
             if( connections[i].c_struct_state == SLAP_C_UNUSED ) {
-                assert( !ber_pvt_sb_in_use( connections[i].c_sb ));
+                assert( sd == AC_SOCKET_INVALID );
                 c = &connections[i];
                 break;
             }
 
             assert( connections[i].c_struct_state == SLAP_C_USED );
             assert( connections[i].c_conn_state != SLAP_C_INVALID );
-            assert( ber_pvt_sb_in_use( connections[i].c_sb ));
+            assert( sd != AC_SOCKET_INVALID );
         }
 
         if( c == NULL ) {
@@ -418,10 +425,14 @@ long connection_init(
 
     c->c_activitytime = c->c_starttime = slap_get_time();
 
-    ber_pvt_sb_set_desc( c->c_sb, s );
-    ber_pvt_sb_set_io( c->c_sb, &ber_pvt_sb_io_tcp, NULL );
-
-    if( ber_pvt_sb_set_nonblock( c->c_sb, 1 ) < 0 ) {
+    ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_tcp, LBER_SBIOD_LEVEL_PROVIDER,
+	(void *)&s );
+    ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_readahead,
+	LBER_SBIOD_LEVEL_PROVIDER, NULL );
+#ifdef LDAP_DEBUG
+    ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_debug, INT_MAX, NULL );
+#endif
+    if( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_NONBLOCK, (void *)1 ) < 0 ) {
         Debug( LDAP_DEBUG_ANY,
             "connection_init(%d, %s): set nonblocking failed\n",
             s, c->c_peer_name,0 );
@@ -454,6 +465,7 @@ static void
 connection_destroy( Connection *c )
 {
 	/* note: connections_mutex should be locked by caller */
+    ber_socket_t	sd;
 
     assert( connections != NULL );
     assert( c != NULL );
@@ -527,18 +539,17 @@ connection_destroy( Connection *c )
 		c->c_currentber = NULL;
 	}
 
-	if ( ber_pvt_sb_in_use(c->c_sb) ) {
-		int sd = ber_pvt_sb_get_desc(c->c_sb);
-
+	ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_GET_FD, &sd );
+	if ( sd != AC_SOCKET_INVALID ) {
 		slapd_remove( sd, 0 );
-	   	ber_pvt_sb_close( c->c_sb );
 
 		Statslog( LDAP_DEBUG_STATS,
 		    "conn=%ld fd=%d closed\n",
 			c->c_connid, sd, 0, 0, 0 );
 	}
 
-   	ber_pvt_sb_destroy( c->c_sb );
+   	ber_sockbuf_free( c->c_sb );
+	c->c_sb = ber_sockbuf_alloc( );
 
     c->c_conn_state = SLAP_C_INVALID;
     c->c_struct_state = SLAP_C_UNUSED;
@@ -590,27 +601,32 @@ void connection_closing( Connection *c )
 	/* c_mutex must be locked by caller */
 
 	if( c->c_conn_state != SLAP_C_CLOSING ) {
+		ber_socket_t	sd;
+
+		ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_GET_FD, &sd );
 		Debug( LDAP_DEBUG_TRACE,
 			"connection_closing: readying conn=%ld sd=%d for close\n",
-			c->c_connid, ber_pvt_sb_get_desc( c->c_sb ), 0 );
+			c->c_connid, sd, 0 );
 
 		/* update state to closing */
 		c->c_conn_state = SLAP_C_CLOSING;
 
 		/* don't listen on this port anymore */
-		slapd_clr_read( ber_pvt_sb_get_desc( c->c_sb ), 1 );
+		slapd_clr_read( sd, 1 );
 
 		/* abandon active operations */
 		connection_abandon( c );
 
 		/* wake write blocked operations */
-		slapd_clr_write( ber_pvt_sb_get_desc(c->c_sb), 1 );
+		slapd_clr_write( sd, 1 );
 		ldap_pvt_thread_cond_signal( &c->c_write_cv );
 	}
 }
 
 static void connection_close( Connection *c )
 {
+	ber_socket_t	sd;
+
 	assert( connections != NULL );
 	assert( c != NULL );
 	assert( c->c_struct_state == SLAP_C_USED );
@@ -618,16 +634,17 @@ static void connection_close( Connection *c )
 
 	/* note: connections_mutex and c_mutex should be locked by caller */
 
+	ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_GET_FD, &sd );
 	if( c->c_ops != NULL ) {
 		Debug( LDAP_DEBUG_TRACE,
 			"connection_close: deferring conn=%ld sd=%d\n",
-			c->c_connid, ber_pvt_sb_get_desc( c->c_sb ), 0 );
+			c->c_connid, sd, 0 );
 
 		return;
 	}
 
 	Debug( LDAP_DEBUG_TRACE, "connection_close: conn=%ld sd=%d\n",
-		c->c_connid, ber_pvt_sb_get_desc( c->c_sb ), 0 );
+		c->c_connid, sd, 0 );
 
 	connection_destroy( c );
 }
@@ -893,15 +910,14 @@ int connection_read(ber_socket_t s)
 			 * to propagate to client. */
 			FD_ZERO(&rfd);
 			FD_SET(s, &rfd);
-			ber_pvt_sb_set_readahead(c->c_sb, 0);
 			for (rc=1; rc>0;)
 			{
-			    char buf[4096];
 			    tv.tv_sec = 1;
 			    tv.tv_usec = 0;
 			    rc = select(s+1, &rfd, NULL, NULL, &tv);
 			    if (rc == 1)
-				rc = ber_pvt_sb_read(c->c_sb, buf, sizeof(buf));
+				ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_DRAIN,
+				    NULL);
 			}
 			connection_close( c );
 		} else if ( rc == 0 ) {
@@ -916,7 +932,7 @@ int connection_read(ber_socket_t s)
 #define CONNECTION_INPUT_LOOP 1
 
 #ifdef DATA_READY_LOOP
-	while(!rc && ber_pvt_sb_data_ready(&c->c_sb))
+	while( !rc && ber_sockbuf_ctrl( c->c_sb, LBER_SB_DATA_READY, NULL ) )
 #elif CONNECTION_INPUT_LOOP
 	while(!rc)
 #endif
@@ -935,9 +951,9 @@ int connection_read(ber_socket_t s)
 		connection_close( c );
 	}
 
-	if ( ber_pvt_sb_needs_read( c->c_sb ) )
+	if ( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_READ, NULL ) )
 		slapd_set_read( s, 1 );
-	if ( ber_pvt_sb_needs_write( c->c_sb ) )
+	if ( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_WRITE, NULL ) )
 		slapd_set_write( s, 1 );
 	connection_return( c );
 	ldap_pvt_thread_mutex_unlock( &connections_mutex );
@@ -966,16 +982,13 @@ connection_input(
 	    != LDAP_TAG_MESSAGE )
 	{
 		int err = errno;
+		ber_socket_t	sd;
+
+		ber_sockbuf_ctrl( conn->c_sb, LBER_SB_OPT_GET_FD, &sd );
 
 		Debug( LDAP_DEBUG_TRACE,
 			"ber_get_next on fd %d failed errno=%d (%s)\n",
-			ber_pvt_sb_get_desc( conn->c_sb ), err, sock_errstr(err) );
-		Debug( LDAP_DEBUG_TRACE,
-			"\t*** got %ld of %lu so far\n",
-			(long) ( conn->c_currentber->ber_buf
-				?  conn->c_currentber->ber_rwptr - conn->c_currentber->ber_buf
-				: 0 ),
-			(long) conn->c_currentber->ber_len, 0 );
+			sd, err, sock_errstr(err) );
 
 		if ( err != EWOULDBLOCK && err != EAGAIN ) {
 			/* log, close and send error */
@@ -1043,9 +1056,12 @@ connection_resched( Connection *conn )
 	Operation *op;
 
 	if( conn->c_conn_state == SLAP_C_CLOSING ) {
+		ber_socket_t	sd;
+
+		ber_sockbuf_ctrl( conn->c_sb, LBER_SB_OPT_GET_FD, &sd );
 		Debug( LDAP_DEBUG_TRACE,
 			"connection_resched: attempting closing conn=%ld sd=%d\n",
-			conn->c_connid, ber_pvt_sb_get_desc( conn->c_sb ), 0 );
+			conn->c_connid, sd, 0 );
 
 		connection_close( conn );
 		return 0;
@@ -1158,9 +1174,9 @@ int connection_write(ber_socket_t s)
 
 	ldap_pvt_thread_cond_signal( &c->c_write_cv );
 
-	if ( ber_pvt_sb_needs_read( c->c_sb ) )
+	if ( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_READ, NULL ) )
 		slapd_set_read( s, 1 );
-	if ( ber_pvt_sb_needs_write( c->c_sb ) )
+	if ( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_WRITE, NULL ) )
 		slapd_set_write( s, 1 );
 	connection_return( c );
 	ldap_pvt_thread_mutex_unlock( &connections_mutex );
diff --git a/servers/slapd/result.c b/servers/slapd/result.c
index d8bc0ffd18b4fc7245c55345081b46770e1e1e6f..75d30de43f01b29700b4fb598a95a33977eb0cc1 100644
--- a/servers/slapd/result.c
+++ b/servers/slapd/result.c
@@ -19,9 +19,6 @@
 
 #include "slap.h"
 
-/* we need LBER internals */
-#include "../../libraries/liblber/lber-int.h"
-
 static char *v2ref( struct berval **ref, const char *text )
 {
 	size_t len = 0, i = 0;
@@ -196,6 +193,7 @@ static long send_ldap_ber(
 	/* write the pdu */
 	while( 1 ) {
 		int err;
+		ber_socket_t	sd;
 
 		if ( connection_state_closing( conn ) ) {
 			ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
@@ -230,7 +228,8 @@ static long send_ldap_ber(
 
 		/* wait for socket to be write-ready */
 		conn->c_writewaiter = 1;
-		slapd_set_write( ber_pvt_sb_get_desc( conn->c_sb ), 1 );
+		ber_sockbuf_ctrl( conn->c_sb, LBER_SB_OPT_GET_FD, &sd );
+		slapd_set_write( sd, 1 );
 
 		ldap_pvt_thread_cond_wait( &conn->c_write_cv, &conn->c_mutex );
 		conn->c_writewaiter = 0;
@@ -382,7 +381,8 @@ send_ldap_disconnect(
 
 #ifdef LDAP_CONNECTIONLESS
 	if ( op->o_cldap ) {
-		ber_pvt_sb_udp_set_dst( conn->c_sb, &op->o_clientaddr );
+		ber_sockbuf_ctrl( conn->c_sb, LBER_SB_OPT_UDP_SET_DST,
+		    (void *)&op->o_clientaddr );
 		Debug( LDAP_DEBUG_TRACE, "UDP response to %s port %d\n", 
 		    inet_ntoa(((struct sockaddr_in *)
 		    &op->o_clientaddr)->sin_addr ),
@@ -453,7 +453,8 @@ send_ldap_result(
 
 #ifdef LDAP_CONNECTIONLESS
 	if ( op->o_cldap ) {
-		ber_pvt_sb_udp_set_dst( conn->c_sb, &op->o_clientaddr );
+		ber_sockbuf_ctrl( conn->c_sb, LBER_SB_OPT_UDP_SET_DST,
+		    (void *)&op->o_clientaddr );
 		Debug( LDAP_DEBUG_TRACE, "UDP response to %s port %d\n", 
 		    inet_ntoa(((struct sockaddr_in *)
 		    &op->o_clientaddr)->sin_addr ),
@@ -499,7 +500,8 @@ send_ldap_sasl(
 
 #ifdef LDAP_CONNECTIONLESS
 	if ( op->o_cldap ) {
-		ber_pvt_sb_udp_set_dst( conn->c_sb, &op->o_clientaddr );
+		ber_sockbuf_ctrl( conn->c_sb, LBER_SB_OPT_UDP_SET_DST,
+		    (void *)&op->o_clientaddr );
 		Debug( LDAP_DEBUG_TRACE, "UDP response to %s port %d\n", 
 		    inet_ntoa(((struct sockaddr_in *)
 		    &op->o_clientaddr)->sin_addr ),
@@ -540,7 +542,8 @@ send_ldap_extended(
 
 #ifdef LDAP_CONNECTIONLESS
 	if ( op->o_cldap ) {
-		ber_pvt_sb_udp_set_dst( conn->c_sb, &op->o_clientaddr );
+		ber_sockbuf_ctrl( conn->c_sb, LBER_SB_OPT_UDP_SET_DST,
+		    (void *)&op->o_clientaddr );
 		Debug( LDAP_DEBUG_TRACE, "UDP response to %s port %d\n", 
 		    inet_ntoa(((struct sockaddr_in *)
 		    &op->o_clientaddr)->sin_addr ),
@@ -603,7 +606,8 @@ send_search_result(
 
 #ifdef LDAP_CONNECTIONLESS
 	if ( op->o_cldap ) {
-		ber_pvt_sb_udp_set_dst( conn->c_sb, &op->o_clientaddr );
+		ber_sockbuf_ctrl( conn->c_sb, LBER_SB_OPT_UDP_SET_DST,
+		    (void *)&op->o_clientaddr );
 		Debug( LDAP_DEBUG_TRACE, "UDP response to %s port %d\n", 
 		    inet_ntoa(((struct sockaddr_in *)
 		    &op->o_clientaddr)->sin_addr ),