xref: /netbsd-src/external/bsd/openldap/dist/libraries/liblber/io.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1*549b59edSchristos /*	$NetBSD: io.c,v 1.3 2021/08/14 16:14:55 christos Exp $	*/
24e6df137Slukem 
32de962bdSlukem /* io.c - ber general i/o routines */
4d11b170bStron /* $OpenLDAP$ */
52de962bdSlukem /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
62de962bdSlukem  *
7*549b59edSchristos  * Copyright 1998-2021 The OpenLDAP Foundation.
82de962bdSlukem  * All rights reserved.
92de962bdSlukem  *
102de962bdSlukem  * Redistribution and use in source and binary forms, with or without
112de962bdSlukem  * modification, are permitted only as authorized by the OpenLDAP
122de962bdSlukem  * Public License.
132de962bdSlukem  *
142de962bdSlukem  * A copy of this license is available in the file LICENSE in the
152de962bdSlukem  * top-level directory of the distribution or, alternatively, at
162de962bdSlukem  * <http://www.OpenLDAP.org/license.html>.
172de962bdSlukem  */
182de962bdSlukem /* Portions Copyright (c) 1990 Regents of the University of Michigan.
192de962bdSlukem  * All rights reserved.
202de962bdSlukem  *
212de962bdSlukem  * Redistribution and use in source and binary forms are permitted
222de962bdSlukem  * provided that this notice is preserved and that due credit is given
232de962bdSlukem  * to the University of Michigan at Ann Arbor. The name of the University
242de962bdSlukem  * may not be used to endorse or promote products derived from this
252de962bdSlukem  * software without specific prior written permission. This software
262de962bdSlukem  * is provided ``as is'' without express or implied warranty.
272de962bdSlukem  */
282de962bdSlukem /* ACKNOWLEDGEMENTS:
292de962bdSlukem  * This work was originally developed by the University of Michigan
302de962bdSlukem  * (as part of U-MICH LDAP).
312de962bdSlukem  */
322de962bdSlukem 
33376af7d7Schristos #include <sys/cdefs.h>
34*549b59edSchristos __RCSID("$NetBSD: io.c,v 1.3 2021/08/14 16:14:55 christos Exp $");
35376af7d7Schristos 
362de962bdSlukem #include "portable.h"
372de962bdSlukem 
382de962bdSlukem #include <stdio.h>
392de962bdSlukem 
402de962bdSlukem #include <ac/stdlib.h>
412de962bdSlukem 
422de962bdSlukem #include <ac/ctype.h>
432de962bdSlukem #include <ac/errno.h>
442de962bdSlukem #include <ac/socket.h>
452de962bdSlukem #include <ac/string.h>
462de962bdSlukem #include <ac/unistd.h>
472de962bdSlukem 
482de962bdSlukem #ifdef HAVE_IO_H
492de962bdSlukem #include <io.h>
502de962bdSlukem #endif
512de962bdSlukem 
522de962bdSlukem #include "lber-int.h"
532de962bdSlukem #include "ldap_log.h"
542de962bdSlukem 
552de962bdSlukem ber_slen_t
ber_skip_data(BerElement * ber,ber_len_t len)562de962bdSlukem ber_skip_data(
572de962bdSlukem 	BerElement *ber,
582de962bdSlukem 	ber_len_t len )
592de962bdSlukem {
602de962bdSlukem 	ber_len_t	actuallen, nleft;
612de962bdSlukem 
622de962bdSlukem 	assert( ber != NULL );
632de962bdSlukem 	assert( LBER_VALID( ber ) );
642de962bdSlukem 
652de962bdSlukem 	nleft = ber_pvt_ber_remaining( ber );
662de962bdSlukem 	actuallen = nleft < len ? nleft : len;
672de962bdSlukem 	ber->ber_ptr += actuallen;
682de962bdSlukem 	ber->ber_tag = *(unsigned char *)ber->ber_ptr;
692de962bdSlukem 
702de962bdSlukem 	return( (ber_slen_t) actuallen );
712de962bdSlukem }
722de962bdSlukem 
734e6df137Slukem /*
744e6df137Slukem  * Read from the ber buffer.  The caller must maintain ber->ber_tag.
754e6df137Slukem  * Do not use to read whole tags.  See ber_get_tag() and ber_skip_data().
764e6df137Slukem  */
772de962bdSlukem ber_slen_t
ber_read(BerElement * ber,char * buf,ber_len_t len)782de962bdSlukem ber_read(
792de962bdSlukem 	BerElement *ber,
802de962bdSlukem 	char *buf,
812de962bdSlukem 	ber_len_t len )
822de962bdSlukem {
832de962bdSlukem 	ber_len_t	actuallen, nleft;
842de962bdSlukem 
852de962bdSlukem 	assert( ber != NULL );
862de962bdSlukem 	assert( buf != NULL );
872de962bdSlukem 	assert( LBER_VALID( ber ) );
882de962bdSlukem 
892de962bdSlukem 	nleft = ber_pvt_ber_remaining( ber );
902de962bdSlukem 	actuallen = nleft < len ? nleft : len;
912de962bdSlukem 
922de962bdSlukem 	AC_MEMCPY( buf, ber->ber_ptr, actuallen );
932de962bdSlukem 
942de962bdSlukem 	ber->ber_ptr += actuallen;
952de962bdSlukem 
962de962bdSlukem 	return( (ber_slen_t) actuallen );
972de962bdSlukem }
982de962bdSlukem 
994e6df137Slukem /*
1004e6df137Slukem  * Write to the ber buffer.
1014e6df137Slukem  * Note that ber_start_seqorset/ber_put_seqorset() bypass ber_write().
1024e6df137Slukem  */
1032de962bdSlukem ber_slen_t
ber_write(BerElement * ber,LDAP_CONST char * buf,ber_len_t len,int zero)1042de962bdSlukem ber_write(
1052de962bdSlukem 	BerElement *ber,
1062de962bdSlukem 	LDAP_CONST char *buf,
1072de962bdSlukem 	ber_len_t len,
1084e6df137Slukem 	int zero )	/* nonzero is unsupported from OpenLDAP 2.4.18 */
1092de962bdSlukem {
1104e6df137Slukem 	char **p;
1114e6df137Slukem 
1122de962bdSlukem 	assert( ber != NULL );
1132de962bdSlukem 	assert( buf != NULL );
1142de962bdSlukem 	assert( LBER_VALID( ber ) );
1152de962bdSlukem 
1164e6df137Slukem 	if ( zero != 0 ) {
1174e6df137Slukem 		ber_log_printf( LDAP_DEBUG_ANY, ber->ber_debug, "%s",
1184e6df137Slukem 			"ber_write: nonzero 4th argument not supported\n" );
1194e6df137Slukem 		return( -1 );
1202de962bdSlukem 	}
1212de962bdSlukem 
1224e6df137Slukem 	p = ber->ber_sos_ptr == NULL ? &ber->ber_ptr : &ber->ber_sos_ptr;
1234e6df137Slukem 	if ( len > (ber_len_t) (ber->ber_end - *p) ) {
1244e6df137Slukem 		if ( ber_realloc( ber, len ) != 0 ) return( -1 );
1254e6df137Slukem 	}
1264e6df137Slukem 	AC_MEMCPY( *p, buf, len );
1274e6df137Slukem 	*p += len;
1284e6df137Slukem 
1294e6df137Slukem 	return( (ber_slen_t) len );
1304e6df137Slukem }
1314e6df137Slukem 
1324e6df137Slukem /* Resize the ber buffer */
1332de962bdSlukem int
ber_realloc(BerElement * ber,ber_len_t len)1342de962bdSlukem ber_realloc( BerElement *ber, ber_len_t len )
1352de962bdSlukem {
1364e27b3e8Schristos 	ber_len_t	total, offset, sos_offset, rw_offset;
1374e6df137Slukem 	char		*buf;
1382de962bdSlukem 
1392de962bdSlukem 	assert( ber != NULL );
1402de962bdSlukem 	assert( LBER_VALID( ber ) );
1412de962bdSlukem 
1424e6df137Slukem 	/* leave room for ber_flatten() to \0-terminate ber_buf */
1434e6df137Slukem 	if ( ++len == 0 ) {
1444e6df137Slukem 		return( -1 );
1454e6df137Slukem 	}
1464e6df137Slukem 
1472de962bdSlukem 	total = ber_pvt_ber_total( ber );
1482de962bdSlukem 
1492de962bdSlukem #define LBER_EXBUFSIZ	4060 /* a few words less than 2^N for binary buddy */
1502de962bdSlukem #if defined( LBER_EXBUFSIZ ) && LBER_EXBUFSIZ > 0
1512de962bdSlukem # ifndef notdef
1522de962bdSlukem 	/* don't realloc by small amounts */
1532de962bdSlukem 	total += len < LBER_EXBUFSIZ ? LBER_EXBUFSIZ : len;
1542de962bdSlukem # else
1554e6df137Slukem 	{	/* not sure what value this adds.  reduce fragmentation? */
1562de962bdSlukem 		ber_len_t have = (total + (LBER_EXBUFSIZE - 1)) / LBER_EXBUFSIZ;
1572de962bdSlukem 		ber_len_t need = (len + (LBER_EXBUFSIZ - 1)) / LBER_EXBUFSIZ;
1582de962bdSlukem 		total = ( have + need ) * LBER_EXBUFSIZ;
1592de962bdSlukem 	}
1602de962bdSlukem # endif
1612de962bdSlukem #else
1622de962bdSlukem 	total += len;	/* realloc just what's needed */
1632de962bdSlukem #endif
1642de962bdSlukem 
1654e6df137Slukem 	if ( total < len || total > (ber_len_t)-1 / 2 /* max ber_slen_t */ ) {
1662de962bdSlukem 		return( -1 );
1672de962bdSlukem 	}
1682de962bdSlukem 
1694e6df137Slukem 	buf = ber->ber_buf;
1704e6df137Slukem 	offset = ber->ber_ptr - buf;
1714e6df137Slukem 	sos_offset = ber->ber_sos_ptr ? ber->ber_sos_ptr - buf : 0;
1724e6df137Slukem 	/* if ber_sos_ptr != NULL, it is > ber_buf so that sos_offset > 0 */
1734e27b3e8Schristos 	rw_offset = ber->ber_rwptr ? ber->ber_rwptr - buf : 0;
1742de962bdSlukem 
1754e6df137Slukem 	buf = (char *) ber_memrealloc_x( buf, total, ber->ber_memctx );
1764e6df137Slukem 	if ( buf == NULL ) {
1774e6df137Slukem 		return( -1 );
1782de962bdSlukem 	}
1794e6df137Slukem 
1804e6df137Slukem 	ber->ber_buf = buf;
1814e6df137Slukem 	ber->ber_end = buf + total;
1824e6df137Slukem 	ber->ber_ptr = buf + offset;
1834e6df137Slukem 	if ( sos_offset )
1844e6df137Slukem 		ber->ber_sos_ptr = buf + sos_offset;
1854e27b3e8Schristos 	if ( ber->ber_rwptr )
1864e27b3e8Schristos 		ber->ber_rwptr = buf + rw_offset;
1872de962bdSlukem 
1882de962bdSlukem 	return( 0 );
1892de962bdSlukem }
1902de962bdSlukem 
1912de962bdSlukem void
ber_free_buf(BerElement * ber)1922de962bdSlukem ber_free_buf( BerElement *ber )
1932de962bdSlukem {
1942de962bdSlukem 	assert( LBER_VALID( ber ) );
1952de962bdSlukem 
1962de962bdSlukem 	if ( ber->ber_buf) ber_memfree_x( ber->ber_buf, ber->ber_memctx );
1972de962bdSlukem 
1982de962bdSlukem 	ber->ber_buf = NULL;
1994e6df137Slukem 	ber->ber_sos_ptr = NULL;
2002de962bdSlukem 	ber->ber_valid = LBER_UNINITIALIZED;
2012de962bdSlukem }
2022de962bdSlukem 
2032de962bdSlukem void
ber_free(BerElement * ber,int freebuf)2042de962bdSlukem ber_free( BerElement *ber, int freebuf )
2052de962bdSlukem {
2062de962bdSlukem 	if( ber == NULL ) {
2072de962bdSlukem 		LDAP_MEMORY_DEBUG_ASSERT( ber != NULL );
2082de962bdSlukem 		return;
2092de962bdSlukem 	}
2102de962bdSlukem 
2112de962bdSlukem 	if( freebuf ) ber_free_buf( ber );
2122de962bdSlukem 
2132de962bdSlukem 	ber_memfree_x( (char *) ber, ber->ber_memctx );
2142de962bdSlukem }
2152de962bdSlukem 
2162de962bdSlukem int
ber_flush(Sockbuf * sb,BerElement * ber,int freeit)2172de962bdSlukem ber_flush( Sockbuf *sb, BerElement *ber, int freeit )
2182de962bdSlukem {
2192de962bdSlukem 	return ber_flush2( sb, ber,
2202de962bdSlukem 		freeit ? LBER_FLUSH_FREE_ON_SUCCESS
2212de962bdSlukem 			: LBER_FLUSH_FREE_NEVER );
2222de962bdSlukem }
2232de962bdSlukem 
2242de962bdSlukem int
ber_flush2(Sockbuf * sb,BerElement * ber,int freeit)2252de962bdSlukem ber_flush2( Sockbuf *sb, BerElement *ber, int freeit )
2262de962bdSlukem {
2272de962bdSlukem 	ber_len_t	towrite;
2282de962bdSlukem 	ber_slen_t	rc;
2292de962bdSlukem 
2302de962bdSlukem 	assert( sb != NULL );
2312de962bdSlukem 	assert( ber != NULL );
2322de962bdSlukem 	assert( SOCKBUF_VALID( sb ) );
2332de962bdSlukem 	assert( LBER_VALID( ber ) );
2342de962bdSlukem 
2352de962bdSlukem 	if ( ber->ber_rwptr == NULL ) {
2362de962bdSlukem 		ber->ber_rwptr = ber->ber_buf;
2372de962bdSlukem 	}
2382de962bdSlukem 	towrite = ber->ber_ptr - ber->ber_rwptr;
2392de962bdSlukem 
2402de962bdSlukem 	if ( sb->sb_debug ) {
2412de962bdSlukem 		ber_log_printf( LDAP_DEBUG_TRACE, sb->sb_debug,
2422de962bdSlukem 			"ber_flush2: %ld bytes to sd %ld%s\n",
2432de962bdSlukem 			towrite, (long) sb->sb_fd,
2442de962bdSlukem 			ber->ber_rwptr != ber->ber_buf ?  " (re-flush)" : "" );
2454e6df137Slukem 		ber_log_bprint( LDAP_DEBUG_BER, sb->sb_debug,
2462de962bdSlukem 			ber->ber_rwptr, towrite );
2472de962bdSlukem 	}
2482de962bdSlukem 
2492de962bdSlukem 	while ( towrite > 0 ) {
2502de962bdSlukem #ifdef LBER_TRICKLE
2512de962bdSlukem 		sleep(1);
2522de962bdSlukem 		rc = ber_int_sb_write( sb, ber->ber_rwptr, 1 );
2532de962bdSlukem #else
2542de962bdSlukem 		rc = ber_int_sb_write( sb, ber->ber_rwptr, towrite );
2552de962bdSlukem #endif
2562de962bdSlukem 		if ( rc <= 0 ) {
2572de962bdSlukem 			if ( freeit & LBER_FLUSH_FREE_ON_ERROR ) ber_free( ber, 1 );
2582de962bdSlukem 			return -1;
2592de962bdSlukem 		}
2602de962bdSlukem 		towrite -= rc;
2612de962bdSlukem 		ber->ber_rwptr += rc;
2622de962bdSlukem 	}
2632de962bdSlukem 
2642de962bdSlukem 	if ( freeit & LBER_FLUSH_FREE_ON_SUCCESS ) ber_free( ber, 1 );
2652de962bdSlukem 
2662de962bdSlukem 	return 0;
2672de962bdSlukem }
2682de962bdSlukem 
2692de962bdSlukem BerElement *
ber_alloc_t(int options)2702de962bdSlukem ber_alloc_t( int options )
2712de962bdSlukem {
2722de962bdSlukem 	BerElement	*ber;
2732de962bdSlukem 
2742de962bdSlukem 	ber = (BerElement *) LBER_CALLOC( 1, sizeof(BerElement) );
2752de962bdSlukem 
2762de962bdSlukem 	if ( ber == NULL ) {
2772de962bdSlukem 		return NULL;
2782de962bdSlukem 	}
2792de962bdSlukem 
2802de962bdSlukem 	ber->ber_valid = LBER_VALID_BERELEMENT;
2812de962bdSlukem 	ber->ber_tag = LBER_DEFAULT;
2822de962bdSlukem 	ber->ber_options = options;
2832de962bdSlukem 	ber->ber_debug = ber_int_debug;
2842de962bdSlukem 
2852de962bdSlukem 	assert( LBER_VALID( ber ) );
2862de962bdSlukem 	return ber;
2872de962bdSlukem }
2882de962bdSlukem 
2892de962bdSlukem BerElement *
ber_alloc(void)2902de962bdSlukem ber_alloc( void )	/* deprecated */
2912de962bdSlukem {
2922de962bdSlukem 	return ber_alloc_t( 0 );
2932de962bdSlukem }
2942de962bdSlukem 
2952de962bdSlukem BerElement *
der_alloc(void)2962de962bdSlukem der_alloc( void )	/* deprecated */
2972de962bdSlukem {
2982de962bdSlukem 	return ber_alloc_t( LBER_USE_DER );
2992de962bdSlukem }
3002de962bdSlukem 
3012de962bdSlukem BerElement *
ber_dup(BerElement * ber)3022de962bdSlukem ber_dup( BerElement *ber )
3032de962bdSlukem {
3042de962bdSlukem 	BerElement	*new;
3052de962bdSlukem 
3062de962bdSlukem 	assert( ber != NULL );
3072de962bdSlukem 	assert( LBER_VALID( ber ) );
3082de962bdSlukem 
3092de962bdSlukem 	if ( (new = ber_alloc_t( ber->ber_options )) == NULL ) {
3102de962bdSlukem 		return NULL;
3112de962bdSlukem 	}
3122de962bdSlukem 
3132de962bdSlukem 	*new = *ber;
3142de962bdSlukem 
3152de962bdSlukem 	assert( LBER_VALID( new ) );
3162de962bdSlukem 	return( new );
3172de962bdSlukem }
3182de962bdSlukem 
3192de962bdSlukem 
3202de962bdSlukem void
ber_init2(BerElement * ber,struct berval * bv,int options)3212de962bdSlukem ber_init2( BerElement *ber, struct berval *bv, int options )
3222de962bdSlukem {
3232de962bdSlukem 	assert( ber != NULL );
3242de962bdSlukem 
3252de962bdSlukem 	(void) memset( (char *)ber, '\0', sizeof( BerElement ));
3262de962bdSlukem 	ber->ber_valid = LBER_VALID_BERELEMENT;
3272de962bdSlukem 	ber->ber_tag = LBER_DEFAULT;
3282de962bdSlukem 	ber->ber_options = (char) options;
3292de962bdSlukem 	ber->ber_debug = ber_int_debug;
3302de962bdSlukem 
3312de962bdSlukem 	if ( bv != NULL ) {
3322de962bdSlukem 		ber->ber_buf = bv->bv_val;
3332de962bdSlukem 		ber->ber_ptr = ber->ber_buf;
3342de962bdSlukem 		ber->ber_end = ber->ber_buf + bv->bv_len;
3352de962bdSlukem 	}
3362de962bdSlukem 
3372de962bdSlukem 	assert( LBER_VALID( ber ) );
3382de962bdSlukem }
3392de962bdSlukem 
3402de962bdSlukem /* OLD U-Mich ber_init() */
3412de962bdSlukem void
ber_init_w_nullc(BerElement * ber,int options)3422de962bdSlukem ber_init_w_nullc( BerElement *ber, int options )
3432de962bdSlukem {
3442de962bdSlukem 	ber_init2( ber, NULL, options );
3452de962bdSlukem }
3462de962bdSlukem 
3472de962bdSlukem /* New C-API ber_init() */
3482de962bdSlukem /* This function constructs a BerElement containing a copy
3492de962bdSlukem ** of the data in the bv argument.
3502de962bdSlukem */
3512de962bdSlukem BerElement *
ber_init(struct berval * bv)3522de962bdSlukem ber_init( struct berval *bv )
3532de962bdSlukem {
3542de962bdSlukem 	BerElement *ber;
3552de962bdSlukem 
3562de962bdSlukem 	assert( bv != NULL );
3572de962bdSlukem 
3582de962bdSlukem 	if ( bv == NULL ) {
3592de962bdSlukem 		return NULL;
3602de962bdSlukem 	}
3612de962bdSlukem 
3622de962bdSlukem 	ber = ber_alloc_t( 0 );
3632de962bdSlukem 
3642de962bdSlukem 	if( ber == NULL ) {
3652de962bdSlukem 		/* allocation failed */
3662de962bdSlukem 		return NULL;
3672de962bdSlukem 	}
3682de962bdSlukem 
3692de962bdSlukem 	/* copy the data */
3702de962bdSlukem 	if ( ((ber_len_t) ber_write ( ber, bv->bv_val, bv->bv_len, 0 ))
3712de962bdSlukem 		!= bv->bv_len )
3722de962bdSlukem 	{
3732de962bdSlukem 		/* write failed, so free and return NULL */
3742de962bdSlukem 		ber_free( ber, 1 );
3752de962bdSlukem 		return NULL;
3762de962bdSlukem 	}
3772de962bdSlukem 
3782de962bdSlukem 	ber_reset( ber, 1 );	/* reset the pointer to the start of the buffer */
3792de962bdSlukem 	return ber;
3802de962bdSlukem }
3812de962bdSlukem 
3822de962bdSlukem /* New C-API ber_flatten routine */
3832de962bdSlukem /* This routine allocates a struct berval whose contents are a BER
3842de962bdSlukem ** encoding taken from the ber argument.  The bvPtr pointer points to
3852de962bdSlukem ** the returned berval.
3862de962bdSlukem **
3872de962bdSlukem ** ber_flatten2 is the same, but uses a struct berval passed by
3882de962bdSlukem ** the caller. If alloc is 0 the returned bv uses the ber buf directly.
3892de962bdSlukem */
ber_flatten2(BerElement * ber,struct berval * bv,int alloc)3902de962bdSlukem int ber_flatten2(
3912de962bdSlukem 	BerElement *ber,
3922de962bdSlukem 	struct berval *bv,
3932de962bdSlukem 	int alloc )
3942de962bdSlukem {
3952de962bdSlukem 	assert( bv != NULL );
3962de962bdSlukem 
3972de962bdSlukem 	if ( bv == NULL ) {
3982de962bdSlukem 		return -1;
3992de962bdSlukem 	}
4002de962bdSlukem 
4012de962bdSlukem 	if ( ber == NULL ) {
4022de962bdSlukem 		/* ber is null, create an empty berval */
4032de962bdSlukem 		bv->bv_val = NULL;
4042de962bdSlukem 		bv->bv_len = 0;
4052de962bdSlukem 
406d11b170bStron 	} else if ( ber->ber_sos_ptr != NULL ) {
407d11b170bStron 		/* unmatched "{" and "}" */
408d11b170bStron 		return -1;
409d11b170bStron 
4102de962bdSlukem 	} else {
4112de962bdSlukem 		/* copy the berval */
4122de962bdSlukem 		ber_len_t len = ber_pvt_ber_write( ber );
4132de962bdSlukem 
4142de962bdSlukem 		if ( alloc ) {
4152de962bdSlukem 			bv->bv_val = (char *) ber_memalloc_x( len + 1, ber->ber_memctx );
4162de962bdSlukem 			if ( bv->bv_val == NULL ) {
4172de962bdSlukem 				return -1;
4182de962bdSlukem 			}
4192de962bdSlukem 			AC_MEMCPY( bv->bv_val, ber->ber_buf, len );
4202de962bdSlukem 			bv->bv_val[len] = '\0';
4214e6df137Slukem 		} else if ( ber->ber_buf != NULL ) {
4224e6df137Slukem 			bv->bv_val = ber->ber_buf;
4234e6df137Slukem 			bv->bv_val[len] = '\0';
4244e6df137Slukem 		} else {
4254e6df137Slukem 			bv->bv_val = "";
4264e6df137Slukem 		}
4272de962bdSlukem 		bv->bv_len = len;
4282de962bdSlukem 	}
4292de962bdSlukem 	return 0;
4302de962bdSlukem }
4312de962bdSlukem 
ber_flatten(BerElement * ber,struct berval ** bvPtr)4322de962bdSlukem int ber_flatten(
4332de962bdSlukem 	BerElement *ber,
4342de962bdSlukem 	struct berval **bvPtr)
4352de962bdSlukem {
4362de962bdSlukem 	struct berval *bv;
4372de962bdSlukem 	int rc;
4382de962bdSlukem 
4392de962bdSlukem 	assert( bvPtr != NULL );
4402de962bdSlukem 
4412de962bdSlukem 	if(bvPtr == NULL) {
4422de962bdSlukem 		return -1;
4432de962bdSlukem 	}
4442de962bdSlukem 
4452de962bdSlukem 	bv = ber_memalloc_x( sizeof(struct berval), ber->ber_memctx );
4462de962bdSlukem 	if ( bv == NULL ) {
4472de962bdSlukem 		return -1;
4482de962bdSlukem 	}
4492de962bdSlukem 	rc = ber_flatten2(ber, bv, 1);
4502de962bdSlukem 	if (rc == -1) {
4512de962bdSlukem 		ber_memfree_x(bv, ber->ber_memctx);
4522de962bdSlukem 	} else {
4532de962bdSlukem 		*bvPtr = bv;
4542de962bdSlukem 	}
4552de962bdSlukem 	return rc;
4562de962bdSlukem }
4572de962bdSlukem 
4582de962bdSlukem void
ber_reset(BerElement * ber,int was_writing)4592de962bdSlukem ber_reset( BerElement *ber, int was_writing )
4602de962bdSlukem {
4612de962bdSlukem 	assert( ber != NULL );
4622de962bdSlukem 	assert( LBER_VALID( ber ) );
4632de962bdSlukem 
4642de962bdSlukem 	if ( was_writing ) {
4652de962bdSlukem 		ber->ber_end = ber->ber_ptr;
4662de962bdSlukem 		ber->ber_ptr = ber->ber_buf;
4672de962bdSlukem 
4682de962bdSlukem 	} else {
4692de962bdSlukem 		ber->ber_ptr = ber->ber_end;
4702de962bdSlukem 	}
4712de962bdSlukem 
4722de962bdSlukem 	ber->ber_rwptr = NULL;
4732de962bdSlukem }
4742de962bdSlukem 
4752de962bdSlukem /*
4762de962bdSlukem  * A rewrite of ber_get_next that can safely be called multiple times
4772de962bdSlukem  * for the same packet. It will simply continue where it stopped until
4782de962bdSlukem  * a full packet is read.
4792de962bdSlukem  */
4802de962bdSlukem 
4812de962bdSlukem #define LENSIZE	4
4822de962bdSlukem 
4832de962bdSlukem ber_tag_t
ber_get_next(Sockbuf * sb,ber_len_t * len,BerElement * ber)4842de962bdSlukem ber_get_next(
4852de962bdSlukem 	Sockbuf *sb,
4862de962bdSlukem 	ber_len_t *len,
4872de962bdSlukem 	BerElement *ber )
4882de962bdSlukem {
4892de962bdSlukem 	assert( sb != NULL );
4902de962bdSlukem 	assert( len != NULL );
4912de962bdSlukem 	assert( ber != NULL );
4922de962bdSlukem 	assert( SOCKBUF_VALID( sb ) );
4932de962bdSlukem 	assert( LBER_VALID( ber ) );
4942de962bdSlukem 
4952de962bdSlukem 	if ( ber->ber_debug & LDAP_DEBUG_TRACE ) {
4962de962bdSlukem 		ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug,
4972de962bdSlukem 			"ber_get_next\n" );
4982de962bdSlukem 	}
4992de962bdSlukem 
5002de962bdSlukem 	/*
5012de962bdSlukem 	 * Any ber element looks like this: tag length contents.
5022de962bdSlukem 	 * Assuming everything's ok, we return the tag byte (we
5032de962bdSlukem 	 * can assume a single byte), return the length in len,
5042de962bdSlukem 	 * and the rest of the undecoded element in buf.
5052de962bdSlukem 	 *
5062de962bdSlukem 	 * Assumptions:
5072de962bdSlukem 	 *	1) small tags (less than 128)
5082de962bdSlukem 	 *	2) definite lengths
5092de962bdSlukem 	 *	3) primitive encodings used whenever possible
5102de962bdSlukem 	 *
5112de962bdSlukem 	 * The code also handles multi-byte tags. The first few bytes
5122de962bdSlukem 	 * of the message are read to check for multi-byte tags and
5132de962bdSlukem 	 * lengths. These bytes are temporarily stored in the ber_tag,
5142de962bdSlukem 	 * ber_len, and ber_usertag fields of the berelement until
5152de962bdSlukem 	 * tag/len parsing is complete. After this parsing, any leftover
5162de962bdSlukem 	 * bytes and the rest of the message are copied into the ber_buf.
5172de962bdSlukem 	 *
5182de962bdSlukem 	 * We expect tag and len to be at most 32 bits wide.
5192de962bdSlukem 	 */
5202de962bdSlukem 
5212de962bdSlukem 	if (ber->ber_rwptr == NULL) {
5222de962bdSlukem 		assert( ber->ber_buf == NULL );
5232de962bdSlukem 		ber->ber_rwptr = (char *) &ber->ber_len-1;
5242de962bdSlukem 		ber->ber_ptr = ber->ber_rwptr;
5252de962bdSlukem 		ber->ber_tag = 0;
5262de962bdSlukem 	}
5272de962bdSlukem 
5282de962bdSlukem 	while (ber->ber_rwptr > (char *)&ber->ber_tag && ber->ber_rwptr <
529bb30016cSlukem 		(char *)&ber->ber_len + LENSIZE*2) {
5302de962bdSlukem 		ber_slen_t sblen;
5312de962bdSlukem 		char buf[sizeof(ber->ber_len)-1];
5322de962bdSlukem 		ber_len_t tlen = 0;
5332de962bdSlukem 
534bb30016cSlukem 		/* The tag & len can be at most 9 bytes; we try to read up to 8 here */
5352de962bdSlukem 		sock_errset(0);
536bb30016cSlukem 		sblen=((char *)&ber->ber_len + LENSIZE*2 - 1)-ber->ber_rwptr;
537bb30016cSlukem 		/* Trying to read the last len byte of a 9 byte tag+len */
538bb30016cSlukem 		if (sblen<1)
539bb30016cSlukem 			sblen = 1;
540bb30016cSlukem 		sblen=ber_int_sb_read( sb, ber->ber_rwptr, sblen );
5412de962bdSlukem 		if (sblen<=0) return LBER_DEFAULT;
5422de962bdSlukem 		ber->ber_rwptr += sblen;
5432de962bdSlukem 
5442de962bdSlukem 		/* We got at least one byte, try to parse the tag. */
5452de962bdSlukem 		if (ber->ber_ptr == (char *)&ber->ber_len-1) {
5462de962bdSlukem 			ber_tag_t tag;
5472de962bdSlukem 			unsigned char *p = (unsigned char *)ber->ber_ptr;
5482de962bdSlukem 			tag = *p++;
5492de962bdSlukem 			if ((tag & LBER_BIG_TAG_MASK) == LBER_BIG_TAG_MASK) {
5502de962bdSlukem 				ber_len_t i;
5512de962bdSlukem 				for (i=1; (char *)p<ber->ber_rwptr; i++) {
5522de962bdSlukem 					tag <<= 8;
5532de962bdSlukem 					tag |= *p++;
5542de962bdSlukem 					if (!(tag & LBER_MORE_TAG_MASK))
5552de962bdSlukem 						break;
5562de962bdSlukem 					/* Is the tag too big? */
5572de962bdSlukem 					if (i == sizeof(ber_tag_t)-1) {
5582de962bdSlukem 						sock_errset(ERANGE);
5592de962bdSlukem 						return LBER_DEFAULT;
5602de962bdSlukem 					}
5612de962bdSlukem 				}
5622de962bdSlukem 				/* Did we run out of bytes? */
5632de962bdSlukem 				if ((char *)p == ber->ber_rwptr) {
5642de962bdSlukem 					sock_errset(EWOULDBLOCK);
5652de962bdSlukem 					return LBER_DEFAULT;
5662de962bdSlukem 				}
5672de962bdSlukem 			}
5682de962bdSlukem 			ber->ber_tag = tag;
5692de962bdSlukem 			ber->ber_ptr = (char *)p;
5702de962bdSlukem 		}
5712de962bdSlukem 
5722de962bdSlukem 		if ( ber->ber_ptr == ber->ber_rwptr ) {
5732de962bdSlukem 			sock_errset(EWOULDBLOCK);
5742de962bdSlukem 			return LBER_DEFAULT;
5752de962bdSlukem 		}
5762de962bdSlukem 
5772de962bdSlukem 		/* Now look for the length */
5782de962bdSlukem 		if (*ber->ber_ptr & 0x80) {	/* multi-byte */
5792de962bdSlukem 			int i;
5802de962bdSlukem 			unsigned char *p = (unsigned char *)ber->ber_ptr;
5812de962bdSlukem 			int llen = *p++ & 0x7f;
582bb30016cSlukem 			if (llen > LENSIZE) {
5832de962bdSlukem 				sock_errset(ERANGE);
5842de962bdSlukem 				return LBER_DEFAULT;
5852de962bdSlukem 			}
5862de962bdSlukem 			/* Not enough bytes? */
5872de962bdSlukem 			if (ber->ber_rwptr - (char *)p < llen) {
5882de962bdSlukem 				sock_errset(EWOULDBLOCK);
5892de962bdSlukem 				return LBER_DEFAULT;
5902de962bdSlukem 			}
5912de962bdSlukem 			for (i=0; i<llen; i++) {
5922de962bdSlukem 				tlen <<=8;
5932de962bdSlukem 				tlen |= *p++;
5942de962bdSlukem 			}
5952de962bdSlukem 			ber->ber_ptr = (char *)p;
5962de962bdSlukem 		} else {
5972de962bdSlukem 			tlen = *(unsigned char *)ber->ber_ptr++;
5982de962bdSlukem 		}
5992de962bdSlukem 
6002de962bdSlukem 		/* Are there leftover data bytes inside ber->ber_len? */
6012de962bdSlukem 		if (ber->ber_ptr < (char *)&ber->ber_usertag) {
6022de962bdSlukem 			if (ber->ber_rwptr < (char *)&ber->ber_usertag) {
6032de962bdSlukem 				sblen = ber->ber_rwptr - ber->ber_ptr;
6042de962bdSlukem 			} else {
6052de962bdSlukem 				sblen = (char *)&ber->ber_usertag - ber->ber_ptr;
6062de962bdSlukem 			}
6072de962bdSlukem 			AC_MEMCPY(buf, ber->ber_ptr, sblen);
6082de962bdSlukem 			ber->ber_ptr += sblen;
6092de962bdSlukem 		} else {
6102de962bdSlukem 			sblen = 0;
6112de962bdSlukem 		}
6122de962bdSlukem 		ber->ber_len = tlen;
6132de962bdSlukem 
6142de962bdSlukem 		/* now fill the buffer. */
6152de962bdSlukem 
6162de962bdSlukem 		/* make sure length is reasonable */
6172de962bdSlukem 		if ( ber->ber_len == 0 ) {
6182de962bdSlukem 			sock_errset(ERANGE);
6192de962bdSlukem 			return LBER_DEFAULT;
6202de962bdSlukem 		}
6212de962bdSlukem 
6222de962bdSlukem 		if ( sb->sb_max_incoming && ber->ber_len > sb->sb_max_incoming ) {
6232de962bdSlukem 			ber_log_printf( LDAP_DEBUG_CONNS, ber->ber_debug,
6242de962bdSlukem 				"ber_get_next: sockbuf_max_incoming exceeded "
6252de962bdSlukem 				"(%ld > %ld)\n", ber->ber_len, sb->sb_max_incoming );
6262de962bdSlukem 			sock_errset(ERANGE);
6272de962bdSlukem 			return LBER_DEFAULT;
6282de962bdSlukem 		}
6292de962bdSlukem 
6302de962bdSlukem 		if (ber->ber_buf==NULL) {
6312de962bdSlukem 			ber_len_t l = ber->ber_rwptr - ber->ber_ptr;
6322de962bdSlukem 			/* ber->ber_ptr is always <= ber->ber->ber_rwptr.
6332de962bdSlukem 			 * make sure ber->ber_len agrees with what we've
6342de962bdSlukem 			 * already read.
6352de962bdSlukem 			 */
6362de962bdSlukem 			if ( ber->ber_len < sblen + l ) {
6372de962bdSlukem 				sock_errset(ERANGE);
6382de962bdSlukem 				return LBER_DEFAULT;
6392de962bdSlukem 			}
6402de962bdSlukem 			ber->ber_buf = (char *) ber_memalloc_x( ber->ber_len + 1, ber->ber_memctx );
6412de962bdSlukem 			if (ber->ber_buf==NULL) {
6422de962bdSlukem 				return LBER_DEFAULT;
6432de962bdSlukem 			}
6442de962bdSlukem 			ber->ber_end = ber->ber_buf + ber->ber_len;
6452de962bdSlukem 			if (sblen) {
6462de962bdSlukem 				AC_MEMCPY(ber->ber_buf, buf, sblen);
6472de962bdSlukem 			}
6482de962bdSlukem 			if (l > 0) {
6492de962bdSlukem 				AC_MEMCPY(ber->ber_buf + sblen, ber->ber_ptr, l);
6502de962bdSlukem 				sblen += l;
6512de962bdSlukem 			}
6522de962bdSlukem 			*ber->ber_end = '\0';
6532de962bdSlukem 			ber->ber_ptr = ber->ber_buf;
6542de962bdSlukem 			ber->ber_usertag = 0;
6552de962bdSlukem 			if ((ber_len_t)sblen == ber->ber_len) {
6562de962bdSlukem 				goto done;
6572de962bdSlukem 			}
6582de962bdSlukem 			ber->ber_rwptr = ber->ber_buf + sblen;
6592de962bdSlukem 		}
6602de962bdSlukem 	}
6612de962bdSlukem 
6622de962bdSlukem 	if ((ber->ber_rwptr>=ber->ber_buf) && (ber->ber_rwptr<ber->ber_end)) {
6632de962bdSlukem 		ber_slen_t res;
6642de962bdSlukem 		ber_slen_t to_go;
6652de962bdSlukem 
6662de962bdSlukem 		to_go = ber->ber_end - ber->ber_rwptr;
667376af7d7Schristos 		/* unsigned/signed overflow */
668376af7d7Schristos 		if (to_go<0) return LBER_DEFAULT;
6692de962bdSlukem 
6702de962bdSlukem 		sock_errset(0);
6712de962bdSlukem 		res = ber_int_sb_read( sb, ber->ber_rwptr, to_go );
6722de962bdSlukem 		if (res<=0) return LBER_DEFAULT;
6732de962bdSlukem 		ber->ber_rwptr+=res;
6742de962bdSlukem 
6752de962bdSlukem 		if (res<to_go) {
6762de962bdSlukem 			sock_errset(EWOULDBLOCK);
6772de962bdSlukem 			return LBER_DEFAULT;
6782de962bdSlukem 		}
6792de962bdSlukem done:
6802de962bdSlukem 		ber->ber_rwptr = NULL;
6812de962bdSlukem 		*len = ber->ber_len;
6822de962bdSlukem 		if ( ber->ber_debug ) {
6832de962bdSlukem 			ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug,
6842de962bdSlukem 				"ber_get_next: tag 0x%lx len %ld contents:\n",
6852de962bdSlukem 				ber->ber_tag, ber->ber_len );
6862de962bdSlukem 			ber_log_dump( LDAP_DEBUG_BER, ber->ber_debug, ber, 1 );
6872de962bdSlukem 		}
6882de962bdSlukem 		return (ber->ber_tag);
6892de962bdSlukem 	}
6902de962bdSlukem 
691376af7d7Schristos 	/* invalid input */
6922de962bdSlukem 	return LBER_DEFAULT;
6932de962bdSlukem }
6942de962bdSlukem 
6952de962bdSlukem char *
ber_start(BerElement * ber)6962de962bdSlukem ber_start( BerElement* ber )
6972de962bdSlukem {
6982de962bdSlukem 	return ber->ber_buf;
6992de962bdSlukem }
7002de962bdSlukem 
7012de962bdSlukem int
ber_len(BerElement * ber)7022de962bdSlukem ber_len( BerElement* ber )
7032de962bdSlukem {
7042de962bdSlukem 	return ( ber->ber_end - ber->ber_buf );
7052de962bdSlukem }
7062de962bdSlukem 
7072de962bdSlukem int
ber_ptrlen(BerElement * ber)7082de962bdSlukem ber_ptrlen( BerElement* ber )
7092de962bdSlukem {
7102de962bdSlukem 	return ( ber->ber_ptr - ber->ber_buf );
7112de962bdSlukem }
7122de962bdSlukem 
7132de962bdSlukem void
ber_rewind(BerElement * ber)7142de962bdSlukem ber_rewind ( BerElement * ber )
7152de962bdSlukem {
7162de962bdSlukem 	ber->ber_rwptr = NULL;
7174e6df137Slukem 	ber->ber_sos_ptr = NULL;
7182de962bdSlukem 	ber->ber_end = ber->ber_ptr;
7192de962bdSlukem 	ber->ber_ptr = ber->ber_buf;
7204e6df137Slukem #if 0	/* TODO: Should we add this? */
7214e6df137Slukem 	ber->ber_tag = LBER_DEFAULT;
7224e6df137Slukem 	ber->ber_usertag = 0;
7234e6df137Slukem #endif
7242de962bdSlukem }
7252de962bdSlukem 
7262de962bdSlukem int
ber_remaining(BerElement * ber)7272de962bdSlukem ber_remaining( BerElement * ber )
7282de962bdSlukem {
7292de962bdSlukem 	return ber_pvt_ber_remaining( ber );
7302de962bdSlukem }
731