xref: /netbsd-src/external/bsd/openldap/dist/libraries/liblber/io.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: io.c,v 1.1.1.7 2018/02/06 01:53:08 christos Exp $	*/
2 
3 /* io.c - ber general i/o routines */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 1998-2017 The OpenLDAP Foundation.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* Portions Copyright (c) 1990 Regents of the University of Michigan.
19  * All rights reserved.
20  *
21  * Redistribution and use in source and binary forms are permitted
22  * provided that this notice is preserved and that due credit is given
23  * to the University of Michigan at Ann Arbor. The name of the University
24  * may not be used to endorse or promote products derived from this
25  * software without specific prior written permission. This software
26  * is provided ``as is'' without express or implied warranty.
27  */
28 /* ACKNOWLEDGEMENTS:
29  * This work was originally developed by the University of Michigan
30  * (as part of U-MICH LDAP).
31  */
32 
33 #include <sys/cdefs.h>
34 __RCSID("$NetBSD: io.c,v 1.1.1.7 2018/02/06 01:53:08 christos Exp $");
35 
36 #include "portable.h"
37 
38 #include <stdio.h>
39 
40 #include <ac/stdlib.h>
41 
42 #include <ac/ctype.h>
43 #include <ac/errno.h>
44 #include <ac/socket.h>
45 #include <ac/string.h>
46 #include <ac/unistd.h>
47 
48 #ifdef HAVE_IO_H
49 #include <io.h>
50 #endif
51 
52 #include "lber-int.h"
53 #include "ldap_log.h"
54 
55 ber_slen_t
56 ber_skip_data(
57 	BerElement *ber,
58 	ber_len_t len )
59 {
60 	ber_len_t	actuallen, nleft;
61 
62 	assert( ber != NULL );
63 	assert( LBER_VALID( ber ) );
64 
65 	nleft = ber_pvt_ber_remaining( ber );
66 	actuallen = nleft < len ? nleft : len;
67 	ber->ber_ptr += actuallen;
68 	ber->ber_tag = *(unsigned char *)ber->ber_ptr;
69 
70 	return( (ber_slen_t) actuallen );
71 }
72 
73 /*
74  * Read from the ber buffer.  The caller must maintain ber->ber_tag.
75  * Do not use to read whole tags.  See ber_get_tag() and ber_skip_data().
76  */
77 ber_slen_t
78 ber_read(
79 	BerElement *ber,
80 	char *buf,
81 	ber_len_t len )
82 {
83 	ber_len_t	actuallen, nleft;
84 
85 	assert( ber != NULL );
86 	assert( buf != NULL );
87 	assert( LBER_VALID( ber ) );
88 
89 	nleft = ber_pvt_ber_remaining( ber );
90 	actuallen = nleft < len ? nleft : len;
91 
92 	AC_MEMCPY( buf, ber->ber_ptr, actuallen );
93 
94 	ber->ber_ptr += actuallen;
95 
96 	return( (ber_slen_t) actuallen );
97 }
98 
99 /*
100  * Write to the ber buffer.
101  * Note that ber_start_seqorset/ber_put_seqorset() bypass ber_write().
102  */
103 ber_slen_t
104 ber_write(
105 	BerElement *ber,
106 	LDAP_CONST char *buf,
107 	ber_len_t len,
108 	int zero )	/* nonzero is unsupported from OpenLDAP 2.4.18 */
109 {
110 	char **p;
111 
112 	assert( ber != NULL );
113 	assert( buf != NULL );
114 	assert( LBER_VALID( ber ) );
115 
116 	if ( zero != 0 ) {
117 		ber_log_printf( LDAP_DEBUG_ANY, ber->ber_debug, "%s",
118 			"ber_write: nonzero 4th argument not supported\n" );
119 		return( -1 );
120 	}
121 
122 	p = ber->ber_sos_ptr == NULL ? &ber->ber_ptr : &ber->ber_sos_ptr;
123 	if ( len > (ber_len_t) (ber->ber_end - *p) ) {
124 		if ( ber_realloc( ber, len ) != 0 ) return( -1 );
125 	}
126 	AC_MEMCPY( *p, buf, len );
127 	*p += len;
128 
129 	return( (ber_slen_t) len );
130 }
131 
132 /* Resize the ber buffer */
133 int
134 ber_realloc( BerElement *ber, ber_len_t len )
135 {
136 	ber_len_t	total, offset, sos_offset;
137 	char		*buf;
138 
139 	assert( ber != NULL );
140 	assert( LBER_VALID( ber ) );
141 
142 	/* leave room for ber_flatten() to \0-terminate ber_buf */
143 	if ( ++len == 0 ) {
144 		return( -1 );
145 	}
146 
147 	total = ber_pvt_ber_total( ber );
148 
149 #define LBER_EXBUFSIZ	4060 /* a few words less than 2^N for binary buddy */
150 #if defined( LBER_EXBUFSIZ ) && LBER_EXBUFSIZ > 0
151 # ifndef notdef
152 	/* don't realloc by small amounts */
153 	total += len < LBER_EXBUFSIZ ? LBER_EXBUFSIZ : len;
154 # else
155 	{	/* not sure what value this adds.  reduce fragmentation? */
156 		ber_len_t have = (total + (LBER_EXBUFSIZE - 1)) / LBER_EXBUFSIZ;
157 		ber_len_t need = (len + (LBER_EXBUFSIZ - 1)) / LBER_EXBUFSIZ;
158 		total = ( have + need ) * LBER_EXBUFSIZ;
159 	}
160 # endif
161 #else
162 	total += len;	/* realloc just what's needed */
163 #endif
164 
165 	if ( total < len || total > (ber_len_t)-1 / 2 /* max ber_slen_t */ ) {
166 		return( -1 );
167 	}
168 
169 	buf = ber->ber_buf;
170 	offset = ber->ber_ptr - buf;
171 	sos_offset = ber->ber_sos_ptr ? ber->ber_sos_ptr - buf : 0;
172 	/* if ber_sos_ptr != NULL, it is > ber_buf so that sos_offset > 0 */
173 
174 	buf = (char *) ber_memrealloc_x( buf, total, ber->ber_memctx );
175 	if ( buf == NULL ) {
176 		return( -1 );
177 	}
178 
179 	ber->ber_buf = buf;
180 	ber->ber_end = buf + total;
181 	ber->ber_ptr = buf + offset;
182 	if ( sos_offset )
183 		ber->ber_sos_ptr = buf + sos_offset;
184 
185 	return( 0 );
186 }
187 
188 void
189 ber_free_buf( BerElement *ber )
190 {
191 	assert( LBER_VALID( ber ) );
192 
193 	if ( ber->ber_buf) ber_memfree_x( ber->ber_buf, ber->ber_memctx );
194 
195 	ber->ber_buf = NULL;
196 	ber->ber_sos_ptr = NULL;
197 	ber->ber_valid = LBER_UNINITIALIZED;
198 }
199 
200 void
201 ber_free( BerElement *ber, int freebuf )
202 {
203 	if( ber == NULL ) {
204 		LDAP_MEMORY_DEBUG_ASSERT( ber != NULL );
205 		return;
206 	}
207 
208 	if( freebuf ) ber_free_buf( ber );
209 
210 	ber_memfree_x( (char *) ber, ber->ber_memctx );
211 }
212 
213 int
214 ber_flush( Sockbuf *sb, BerElement *ber, int freeit )
215 {
216 	return ber_flush2( sb, ber,
217 		freeit ? LBER_FLUSH_FREE_ON_SUCCESS
218 			: LBER_FLUSH_FREE_NEVER );
219 }
220 
221 int
222 ber_flush2( Sockbuf *sb, BerElement *ber, int freeit )
223 {
224 	ber_len_t	towrite;
225 	ber_slen_t	rc;
226 
227 	assert( sb != NULL );
228 	assert( ber != NULL );
229 	assert( SOCKBUF_VALID( sb ) );
230 	assert( LBER_VALID( ber ) );
231 
232 	if ( ber->ber_rwptr == NULL ) {
233 		ber->ber_rwptr = ber->ber_buf;
234 	}
235 	towrite = ber->ber_ptr - ber->ber_rwptr;
236 
237 	if ( sb->sb_debug ) {
238 		ber_log_printf( LDAP_DEBUG_TRACE, sb->sb_debug,
239 			"ber_flush2: %ld bytes to sd %ld%s\n",
240 			towrite, (long) sb->sb_fd,
241 			ber->ber_rwptr != ber->ber_buf ?  " (re-flush)" : "" );
242 		ber_log_bprint( LDAP_DEBUG_BER, sb->sb_debug,
243 			ber->ber_rwptr, towrite );
244 	}
245 
246 	while ( towrite > 0 ) {
247 #ifdef LBER_TRICKLE
248 		sleep(1);
249 		rc = ber_int_sb_write( sb, ber->ber_rwptr, 1 );
250 #else
251 		rc = ber_int_sb_write( sb, ber->ber_rwptr, towrite );
252 #endif
253 		if ( rc <= 0 ) {
254 			if ( freeit & LBER_FLUSH_FREE_ON_ERROR ) ber_free( ber, 1 );
255 			return -1;
256 		}
257 		towrite -= rc;
258 		ber->ber_rwptr += rc;
259 	}
260 
261 	if ( freeit & LBER_FLUSH_FREE_ON_SUCCESS ) ber_free( ber, 1 );
262 
263 	return 0;
264 }
265 
266 BerElement *
267 ber_alloc_t( int options )
268 {
269 	BerElement	*ber;
270 
271 	ber = (BerElement *) LBER_CALLOC( 1, sizeof(BerElement) );
272 
273 	if ( ber == NULL ) {
274 		return NULL;
275 	}
276 
277 	ber->ber_valid = LBER_VALID_BERELEMENT;
278 	ber->ber_tag = LBER_DEFAULT;
279 	ber->ber_options = options;
280 	ber->ber_debug = ber_int_debug;
281 
282 	assert( LBER_VALID( ber ) );
283 	return ber;
284 }
285 
286 BerElement *
287 ber_alloc( void )	/* deprecated */
288 {
289 	return ber_alloc_t( 0 );
290 }
291 
292 BerElement *
293 der_alloc( void )	/* deprecated */
294 {
295 	return ber_alloc_t( LBER_USE_DER );
296 }
297 
298 BerElement *
299 ber_dup( BerElement *ber )
300 {
301 	BerElement	*new;
302 
303 	assert( ber != NULL );
304 	assert( LBER_VALID( ber ) );
305 
306 	if ( (new = ber_alloc_t( ber->ber_options )) == NULL ) {
307 		return NULL;
308 	}
309 
310 	*new = *ber;
311 
312 	assert( LBER_VALID( new ) );
313 	return( new );
314 }
315 
316 
317 void
318 ber_init2( BerElement *ber, struct berval *bv, int options )
319 {
320 	assert( ber != NULL );
321 
322 	(void) memset( (char *)ber, '\0', sizeof( BerElement ));
323 	ber->ber_valid = LBER_VALID_BERELEMENT;
324 	ber->ber_tag = LBER_DEFAULT;
325 	ber->ber_options = (char) options;
326 	ber->ber_debug = ber_int_debug;
327 
328 	if ( bv != NULL ) {
329 		ber->ber_buf = bv->bv_val;
330 		ber->ber_ptr = ber->ber_buf;
331 		ber->ber_end = ber->ber_buf + bv->bv_len;
332 	}
333 
334 	assert( LBER_VALID( ber ) );
335 }
336 
337 /* OLD U-Mich ber_init() */
338 void
339 ber_init_w_nullc( BerElement *ber, int options )
340 {
341 	ber_init2( ber, NULL, options );
342 }
343 
344 /* New C-API ber_init() */
345 /* This function constructs a BerElement containing a copy
346 ** of the data in the bv argument.
347 */
348 BerElement *
349 ber_init( struct berval *bv )
350 {
351 	BerElement *ber;
352 
353 	assert( bv != NULL );
354 
355 	if ( bv == NULL ) {
356 		return NULL;
357 	}
358 
359 	ber = ber_alloc_t( 0 );
360 
361 	if( ber == NULL ) {
362 		/* allocation failed */
363 		return NULL;
364 	}
365 
366 	/* copy the data */
367 	if ( ((ber_len_t) ber_write ( ber, bv->bv_val, bv->bv_len, 0 ))
368 		!= bv->bv_len )
369 	{
370 		/* write failed, so free and return NULL */
371 		ber_free( ber, 1 );
372 		return NULL;
373 	}
374 
375 	ber_reset( ber, 1 );	/* reset the pointer to the start of the buffer */
376 	return ber;
377 }
378 
379 /* New C-API ber_flatten routine */
380 /* This routine allocates a struct berval whose contents are a BER
381 ** encoding taken from the ber argument.  The bvPtr pointer points to
382 ** the returned berval.
383 **
384 ** ber_flatten2 is the same, but uses a struct berval passed by
385 ** the caller. If alloc is 0 the returned bv uses the ber buf directly.
386 */
387 int ber_flatten2(
388 	BerElement *ber,
389 	struct berval *bv,
390 	int alloc )
391 {
392 	assert( bv != NULL );
393 
394 	if ( bv == NULL ) {
395 		return -1;
396 	}
397 
398 	if ( ber == NULL ) {
399 		/* ber is null, create an empty berval */
400 		bv->bv_val = NULL;
401 		bv->bv_len = 0;
402 
403 	} else if ( ber->ber_sos_ptr != NULL ) {
404 		/* unmatched "{" and "}" */
405 		return -1;
406 
407 	} else {
408 		/* copy the berval */
409 		ber_len_t len = ber_pvt_ber_write( ber );
410 
411 		if ( alloc ) {
412 			bv->bv_val = (char *) ber_memalloc_x( len + 1, ber->ber_memctx );
413 			if ( bv->bv_val == NULL ) {
414 				return -1;
415 			}
416 			AC_MEMCPY( bv->bv_val, ber->ber_buf, len );
417 			bv->bv_val[len] = '\0';
418 		} else if ( ber->ber_buf != NULL ) {
419 			bv->bv_val = ber->ber_buf;
420 			bv->bv_val[len] = '\0';
421 		} else {
422 			bv->bv_val = "";
423 		}
424 		bv->bv_len = len;
425 	}
426 	return 0;
427 }
428 
429 int ber_flatten(
430 	BerElement *ber,
431 	struct berval **bvPtr)
432 {
433 	struct berval *bv;
434 	int rc;
435 
436 	assert( bvPtr != NULL );
437 
438 	if(bvPtr == NULL) {
439 		return -1;
440 	}
441 
442 	bv = ber_memalloc_x( sizeof(struct berval), ber->ber_memctx );
443 	if ( bv == NULL ) {
444 		return -1;
445 	}
446 	rc = ber_flatten2(ber, bv, 1);
447 	if (rc == -1) {
448 		ber_memfree_x(bv, ber->ber_memctx);
449 	} else {
450 		*bvPtr = bv;
451 	}
452 	return rc;
453 }
454 
455 void
456 ber_reset( BerElement *ber, int was_writing )
457 {
458 	assert( ber != NULL );
459 	assert( LBER_VALID( ber ) );
460 
461 	if ( was_writing ) {
462 		ber->ber_end = ber->ber_ptr;
463 		ber->ber_ptr = ber->ber_buf;
464 
465 	} else {
466 		ber->ber_ptr = ber->ber_end;
467 	}
468 
469 	ber->ber_rwptr = NULL;
470 }
471 
472 /*
473  * A rewrite of ber_get_next that can safely be called multiple times
474  * for the same packet. It will simply continue where it stopped until
475  * a full packet is read.
476  */
477 
478 #define LENSIZE	4
479 
480 ber_tag_t
481 ber_get_next(
482 	Sockbuf *sb,
483 	ber_len_t *len,
484 	BerElement *ber )
485 {
486 	assert( sb != NULL );
487 	assert( len != NULL );
488 	assert( ber != NULL );
489 	assert( SOCKBUF_VALID( sb ) );
490 	assert( LBER_VALID( ber ) );
491 
492 	if ( ber->ber_debug & LDAP_DEBUG_TRACE ) {
493 		ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug,
494 			"ber_get_next\n" );
495 	}
496 
497 	/*
498 	 * Any ber element looks like this: tag length contents.
499 	 * Assuming everything's ok, we return the tag byte (we
500 	 * can assume a single byte), return the length in len,
501 	 * and the rest of the undecoded element in buf.
502 	 *
503 	 * Assumptions:
504 	 *	1) small tags (less than 128)
505 	 *	2) definite lengths
506 	 *	3) primitive encodings used whenever possible
507 	 *
508 	 * The code also handles multi-byte tags. The first few bytes
509 	 * of the message are read to check for multi-byte tags and
510 	 * lengths. These bytes are temporarily stored in the ber_tag,
511 	 * ber_len, and ber_usertag fields of the berelement until
512 	 * tag/len parsing is complete. After this parsing, any leftover
513 	 * bytes and the rest of the message are copied into the ber_buf.
514 	 *
515 	 * We expect tag and len to be at most 32 bits wide.
516 	 */
517 
518 	if (ber->ber_rwptr == NULL) {
519 		assert( ber->ber_buf == NULL );
520 		ber->ber_rwptr = (char *) &ber->ber_len-1;
521 		ber->ber_ptr = ber->ber_rwptr;
522 		ber->ber_tag = 0;
523 	}
524 
525 	while (ber->ber_rwptr > (char *)&ber->ber_tag && ber->ber_rwptr <
526 		(char *)&ber->ber_len + LENSIZE*2) {
527 		ber_slen_t sblen;
528 		char buf[sizeof(ber->ber_len)-1];
529 		ber_len_t tlen = 0;
530 
531 		/* The tag & len can be at most 9 bytes; we try to read up to 8 here */
532 		sock_errset(0);
533 		sblen=((char *)&ber->ber_len + LENSIZE*2 - 1)-ber->ber_rwptr;
534 		/* Trying to read the last len byte of a 9 byte tag+len */
535 		if (sblen<1)
536 			sblen = 1;
537 		sblen=ber_int_sb_read( sb, ber->ber_rwptr, sblen );
538 		if (sblen<=0) return LBER_DEFAULT;
539 		ber->ber_rwptr += sblen;
540 
541 		/* We got at least one byte, try to parse the tag. */
542 		if (ber->ber_ptr == (char *)&ber->ber_len-1) {
543 			ber_tag_t tag;
544 			unsigned char *p = (unsigned char *)ber->ber_ptr;
545 			tag = *p++;
546 			if ((tag & LBER_BIG_TAG_MASK) == LBER_BIG_TAG_MASK) {
547 				ber_len_t i;
548 				for (i=1; (char *)p<ber->ber_rwptr; i++) {
549 					tag <<= 8;
550 					tag |= *p++;
551 					if (!(tag & LBER_MORE_TAG_MASK))
552 						break;
553 					/* Is the tag too big? */
554 					if (i == sizeof(ber_tag_t)-1) {
555 						sock_errset(ERANGE);
556 						return LBER_DEFAULT;
557 					}
558 				}
559 				/* Did we run out of bytes? */
560 				if ((char *)p == ber->ber_rwptr) {
561 					sock_errset(EWOULDBLOCK);
562 					return LBER_DEFAULT;
563 				}
564 			}
565 			ber->ber_tag = tag;
566 			ber->ber_ptr = (char *)p;
567 		}
568 
569 		if ( ber->ber_ptr == ber->ber_rwptr ) {
570 			sock_errset(EWOULDBLOCK);
571 			return LBER_DEFAULT;
572 		}
573 
574 		/* Now look for the length */
575 		if (*ber->ber_ptr & 0x80) {	/* multi-byte */
576 			int i;
577 			unsigned char *p = (unsigned char *)ber->ber_ptr;
578 			int llen = *p++ & 0x7f;
579 			if (llen > LENSIZE) {
580 				sock_errset(ERANGE);
581 				return LBER_DEFAULT;
582 			}
583 			/* Not enough bytes? */
584 			if (ber->ber_rwptr - (char *)p < llen) {
585 				sock_errset(EWOULDBLOCK);
586 				return LBER_DEFAULT;
587 			}
588 			for (i=0; i<llen; i++) {
589 				tlen <<=8;
590 				tlen |= *p++;
591 			}
592 			ber->ber_ptr = (char *)p;
593 		} else {
594 			tlen = *(unsigned char *)ber->ber_ptr++;
595 		}
596 
597 		/* Are there leftover data bytes inside ber->ber_len? */
598 		if (ber->ber_ptr < (char *)&ber->ber_usertag) {
599 			if (ber->ber_rwptr < (char *)&ber->ber_usertag) {
600 				sblen = ber->ber_rwptr - ber->ber_ptr;
601 			} else {
602 				sblen = (char *)&ber->ber_usertag - ber->ber_ptr;
603 			}
604 			AC_MEMCPY(buf, ber->ber_ptr, sblen);
605 			ber->ber_ptr += sblen;
606 		} else {
607 			sblen = 0;
608 		}
609 		ber->ber_len = tlen;
610 
611 		/* now fill the buffer. */
612 
613 		/* make sure length is reasonable */
614 		if ( ber->ber_len == 0 ) {
615 			sock_errset(ERANGE);
616 			return LBER_DEFAULT;
617 		}
618 
619 		if ( sb->sb_max_incoming && ber->ber_len > sb->sb_max_incoming ) {
620 			ber_log_printf( LDAP_DEBUG_CONNS, ber->ber_debug,
621 				"ber_get_next: sockbuf_max_incoming exceeded "
622 				"(%ld > %ld)\n", ber->ber_len, sb->sb_max_incoming );
623 			sock_errset(ERANGE);
624 			return LBER_DEFAULT;
625 		}
626 
627 		if (ber->ber_buf==NULL) {
628 			ber_len_t l = ber->ber_rwptr - ber->ber_ptr;
629 			/* ber->ber_ptr is always <= ber->ber->ber_rwptr.
630 			 * make sure ber->ber_len agrees with what we've
631 			 * already read.
632 			 */
633 			if ( ber->ber_len < sblen + l ) {
634 				sock_errset(ERANGE);
635 				return LBER_DEFAULT;
636 			}
637 			ber->ber_buf = (char *) ber_memalloc_x( ber->ber_len + 1, ber->ber_memctx );
638 			if (ber->ber_buf==NULL) {
639 				return LBER_DEFAULT;
640 			}
641 			ber->ber_end = ber->ber_buf + ber->ber_len;
642 			if (sblen) {
643 				AC_MEMCPY(ber->ber_buf, buf, sblen);
644 			}
645 			if (l > 0) {
646 				AC_MEMCPY(ber->ber_buf + sblen, ber->ber_ptr, l);
647 				sblen += l;
648 			}
649 			*ber->ber_end = '\0';
650 			ber->ber_ptr = ber->ber_buf;
651 			ber->ber_usertag = 0;
652 			if ((ber_len_t)sblen == ber->ber_len) {
653 				goto done;
654 			}
655 			ber->ber_rwptr = ber->ber_buf + sblen;
656 		}
657 	}
658 
659 	if ((ber->ber_rwptr>=ber->ber_buf) && (ber->ber_rwptr<ber->ber_end)) {
660 		ber_slen_t res;
661 		ber_slen_t to_go;
662 
663 		to_go = ber->ber_end - ber->ber_rwptr;
664 		/* unsigned/signed overflow */
665 		if (to_go<0) return LBER_DEFAULT;
666 
667 		sock_errset(0);
668 		res = ber_int_sb_read( sb, ber->ber_rwptr, to_go );
669 		if (res<=0) return LBER_DEFAULT;
670 		ber->ber_rwptr+=res;
671 
672 		if (res<to_go) {
673 			sock_errset(EWOULDBLOCK);
674 			return LBER_DEFAULT;
675 		}
676 done:
677 		ber->ber_rwptr = NULL;
678 		*len = ber->ber_len;
679 		if ( ber->ber_debug ) {
680 			ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug,
681 				"ber_get_next: tag 0x%lx len %ld contents:\n",
682 				ber->ber_tag, ber->ber_len );
683 			ber_log_dump( LDAP_DEBUG_BER, ber->ber_debug, ber, 1 );
684 		}
685 		return (ber->ber_tag);
686 	}
687 
688 	/* invalid input */
689 	return LBER_DEFAULT;
690 }
691 
692 char *
693 ber_start( BerElement* ber )
694 {
695 	return ber->ber_buf;
696 }
697 
698 int
699 ber_len( BerElement* ber )
700 {
701 	return ( ber->ber_end - ber->ber_buf );
702 }
703 
704 int
705 ber_ptrlen( BerElement* ber )
706 {
707 	return ( ber->ber_ptr - ber->ber_buf );
708 }
709 
710 void
711 ber_rewind ( BerElement * ber )
712 {
713 	ber->ber_rwptr = NULL;
714 	ber->ber_sos_ptr = NULL;
715 	ber->ber_end = ber->ber_ptr;
716 	ber->ber_ptr = ber->ber_buf;
717 #if 0	/* TODO: Should we add this? */
718 	ber->ber_tag = LBER_DEFAULT;
719 	ber->ber_usertag = 0;
720 #endif
721 }
722 
723 int
724 ber_remaining( BerElement * ber )
725 {
726 	return ber_pvt_ber_remaining( ber );
727 }
728