1 /*
2 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 #pragma ident "%Z%%M% %I% %E% SMI"
7
8 /* io.c - ber general i/o routines */
9 /*
10 * Copyright (c) 1990 Regents of the University of Michigan.
11 * All rights reserved.
12 *
13 * Redistribution and use in source and binary forms are permitted
14 * provided that this notice is preserved and that due credit is given
15 * to the University of Michigan at Ann Arbor. The name of the University
16 * may not be used to endorse or promote products derived from this
17 * software without specific prior written permission. This software
18 * is provided ``as is'' without express or implied warranty.
19 */
20
21 #include <stdio.h>
22 #include <ctype.h>
23 #include <unistd.h>
24 #include <poll.h>
25
26 #if defined( DOS ) || defined( _WIN32 )
27 #include "msdos.h"
28 #endif /* DOS || _WIN32 */
29
30 #ifdef MACOS
31 #include <stdlib.h>
32 #include "macos.h"
33 #else /* MACOS */
34 #if defined(NeXT) || defined(VMS)
35 #include <stdlib.h>
36 #else /* next || vms */
37 #include <malloc.h>
38 #endif /* next || vms */
39 #include <errno.h>
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #ifdef PCNFS
44 #include <tklib.h>
45 #endif /* PCNFS */
46 #endif /* MACOS */
47
48 #ifdef SUN
49 #include <unistd.h>
50 #endif
51
52 #ifndef VMS
53 #include <memory.h>
54 #endif
55 #include <string.h>
56 #include "lber.h"
57 #include "ldap.h"
58 #include "ldap-private.h"
59 #include "ldap-int.h"
60
61 #ifdef _WIN32
62 #include <winsock.h>
63 #include <io.h>
64 #endif /* _WIN32 */
65
66 #ifdef NEEDPROTOS
67 int ber_realloc(BerElement *ber, unsigned int len);
68 static int ber_filbuf(Sockbuf *sb, int len);
69 static int BerRead(Sockbuf *sb, char *buf, int len);
70 #ifdef PCNFS
71 static int BerWrite( Sockbuf *sb, char *buf, int len );
72 #endif /* PCNFS */
73 #else
74 int ber_filbuf();
75 int BerRead();
76 int ber_realloc();
77 #endif /* NEEDPROTOS */
78
79 #define bergetc( sb, len ) ( sb->sb_ber.ber_end > sb->sb_ber.ber_ptr ? \
80 (unsigned char)*sb->sb_ber.ber_ptr++ : \
81 ber_filbuf( sb, len ))
82
83 #ifdef MACOS
84 /*
85 * MacTCP/OpenTransport
86 */
87 #define read( s, b, l ) tcpread( s, 0, (unsigned char *)b, l, NULL )
88 #define MAX_WRITE 65535
89 #define BerWrite( sb, b, l ) tcpwrite( sb->sb_sd, (unsigned char *)(b), (l<MAX_WRITE)? l : MAX_WRITE )
90 #else /* MACOS */
91 #ifdef DOS
92 #ifdef PCNFS
93 /*
94 * PCNFS (under DOS)
95 */
96 #define read( s, b, l ) recv( s, b, l, 0 )
97 #define BerWrite( s, b, l ) send( s->sb_sd, b, (int) l, 0 )
98 #endif /* PCNFS */
99 #ifdef NCSA
100 /*
101 * NCSA Telnet TCP/IP stack (under DOS)
102 */
103 #define read( s, b, l ) nread( s, b, l )
104 #define BerWrite( s, b, l ) netwrite( s->sb_sd, b, l )
105 #endif /* NCSA */
106 #ifdef WINSOCK
107 /*
108 * Windows Socket API (under DOS/Windows 3.x)
109 */
110 #define read( s, b, l ) recv( s, b, l, 0 )
111 #define BerWrite( s, b, l ) send( s->sb_sd, b, l, 0 )
112 #endif /* WINSOCK */
113 #else /* DOS */
114 #ifdef _WIN32
115 /*
116 * 32-bit Windows Socket API (under Windows NT or Windows 95)
117 */
118 #define read( s, b, l ) recv( s, b, l, 0 )
119 #define BerWrite( s, b, l ) send( s->sb_sd, b, l, 0 )
120 #else /* _WIN32 */
121 #ifdef VMS
122 /*
123 * VMS -- each write must be 64K or smaller
124 */
125 #define MAX_WRITE 65535
126 #define BerWrite( sb, b, l ) write( sb->sb_sd, b, (l<MAX_WRITE)? l : MAX_WRITE)
127 #else /* VMS */
128 /*
129 * everything else (Unix/BSD 4.3 socket API)
130 */
131 #define BerWrite( sb, b, l ) write( sb->sb_sd, b, l )
132 #endif /* VMS */
133 #define udp_read( sb, b, l, al ) recvfrom(sb->sb_sd, (char *)b, l, 0, \
134 (struct sockaddr *)sb->sb_fromaddr, \
135 (al = sizeof(struct sockaddr), &al))
136 #define udp_write( sb, b, l ) sendto(sb->sb_sd, (char *)(b), l, 0, \
137 (struct sockaddr *)sb->sb_useaddr, sizeof(struct sockaddr))
138 #endif /* _WIN32 */
139 #endif /* DOS */
140 #endif /* MACOS */
141
142 #ifndef udp_read
143 #define udp_read( sb, b, l, al ) CLDAP NOT SUPPORTED
144 #define udp_write( sb, b, l ) CLDAP NOT SUPPORTED
145 #endif /* udp_read */
146
147 #define EXBUFSIZ 1024
148
149 int
ber_filbuf(Sockbuf * sb,int len)150 ber_filbuf( Sockbuf *sb, int len )
151 {
152 ssize_t rc;
153 #ifdef CLDAP
154 int addrlen;
155 #endif /* CLDAP */
156
157 if ( sb->sb_ber.ber_buf == NULL ) {
158 if ( (sb->sb_ber.ber_buf = (char *) malloc( READBUFSIZ )) ==
159 NULL )
160 return( -1 );
161 sb->sb_ber.ber_ptr = sb->sb_ber.ber_buf;
162 sb->sb_ber.ber_end = sb->sb_ber.ber_buf;
163 }
164
165 if ( sb->sb_naddr > 0 ) {
166 #ifdef CLDAP
167 rc = udp_read(sb, sb->sb_ber.ber_buf, READBUFSIZ, addrlen );
168 #ifdef LDAP_DEBUG
169 if ( lber_debug ) {
170 (void) fprintf( stderr, catgets(slapdcat, 1, 75, "ber_filbuf udp_read %d bytes\n"),
171 (int)rc );
172 if ( lber_debug > 1 && rc > 0 )
173 lber_bprint( sb->sb_ber.ber_buf, (int)rc );
174 }
175 #endif /* LDAP_DEBUG */
176 #else /* CLDAP */
177 rc = -1;
178 #endif /* CLDAP */
179 #ifdef LDAP_SSL
180 } else if ( sb->sb_ssl != NULL ) {
181 rc = SSL_read(sb->sb_ssl,(u_char *)sb->sb_ber.ber_buf,
182 ((sb->sb_options & LBER_NO_READ_AHEAD) &&
183 (len < READBUFSIZ)) ?
184 len : READBUFSIZ );
185 #endif /* LDAP_SSL */
186 } else {
187 int loop=2;
188 while (loop>0) {
189 --loop;
190 rc = read( sb->sb_sd, sb->sb_ber.ber_buf,
191 ((sb->sb_options & LBER_NO_READ_AHEAD) &&
192 (len < READBUFSIZ)) ?
193 len : READBUFSIZ );
194 /*
195 * if packet not here yet, wait 10 seconds to let it arrive
196 */
197 if ( rc <= 0 && (errno==EWOULDBLOCK || errno==EAGAIN) ) {
198 struct pollfd poll_tab[1];
199 poll_tab[0].fd = sb->sb_sd;
200 poll_tab[0].events = POLLIN;
201 poll_tab[0].revents = 0;
202 if ( poll(poll_tab, 1, 10000) <= 0) {
203 /* nothing received or error, just abandon the read */
204 break;
205 } /* end if */
206 } else {
207 break;
208 } /* end if */
209 } /* end while */
210 }
211
212 if ( rc > 0 ) {
213 sb->sb_ber.ber_ptr = sb->sb_ber.ber_buf + 1;
214 sb->sb_ber.ber_end = sb->sb_ber.ber_buf + rc;
215 return( (unsigned char)*sb->sb_ber.ber_buf );
216 }
217
218 return( -1 );
219 }
220
221
222 int
BerRead(Sockbuf * sb,char * buf,int len)223 BerRead( Sockbuf *sb, char *buf, int len )
224 {
225 int c;
226 int nread = 0;
227
228 while ( len > 0 ) {
229 if ( (c = bergetc( sb, len )) < 0 ) {
230 if ( nread > 0 )
231 break;
232 return( c );
233 }
234 *buf++ = (char)c;
235 nread++;
236 len--;
237 }
238
239 return( nread );
240 }
241
242
243 int
ber_read(BerElement * ber,char * buf,unsigned int len)244 ber_read( BerElement *ber, char *buf, unsigned int len )
245 {
246 unsigned int actuallen, nleft;
247
248 nleft = (int)(ber->ber_end - ber->ber_ptr);
249 actuallen = nleft < len ? nleft : len;
250
251 SAFEMEMCPY( buf, ber->ber_ptr, (size_t)actuallen );
252
253 ber->ber_ptr += actuallen;
254
255 return( (int)actuallen );
256 }
257
258 int
ber_write(BerElement * ber,char * buf,unsigned int len,int nosos)259 ber_write( BerElement *ber, char *buf, unsigned int len, int nosos )
260 {
261 if ( nosos || ber->ber_sos == NULL ) {
262 if ( ber->ber_ptr + len > ber->ber_end ) {
263 if ( ber_realloc( ber, len ) != 0 )
264 return( -1 );
265 }
266 (void) SAFEMEMCPY( ber->ber_ptr, buf, (size_t)len );
267 ber->ber_ptr += len;
268 return( len );
269 } else {
270 if ( ber->ber_sos->sos_ptr + len > ber->ber_end ) {
271 if ( ber_realloc( ber, len ) != 0 )
272 return( -1 );
273 }
274 (void) SAFEMEMCPY( ber->ber_sos->sos_ptr, buf, (size_t)len );
275 ber->ber_sos->sos_ptr += len;
276 ber->ber_sos->sos_clen += len;
277 return( len );
278 }
279 }
280
281 int
ber_realloc(BerElement * ber,unsigned int len)282 ber_realloc(BerElement *ber, unsigned int len)
283 {
284 size_t need, have, total;
285 Seqorset *s;
286 ssize_t off;
287 char *oldbuf;
288
289 have = (ber->ber_end - ber->ber_buf) / EXBUFSIZ;
290 need = (len < EXBUFSIZ ? 1 : (len + (EXBUFSIZ - 1)) / EXBUFSIZ);
291 total = have * EXBUFSIZ + need * EXBUFSIZ;
292
293 oldbuf = ber->ber_buf;
294
295 if ( ber->ber_buf == NULL ) {
296 if ( (ber->ber_buf = (char *) malloc( (size_t)total )) == NULL )
297 return( -1 );
298 } else if ( (ber->ber_buf = (char *) realloc( ber->ber_buf,
299 (size_t)total )) == NULL )
300 return( -1 );
301
302 ber->ber_end = ber->ber_buf + total;
303
304 /*
305 * If the stinking thing was moved, we need to go through and
306 * reset all the sos and ber pointers. Offsets would've been
307 * a better idea... oh well.
308 */
309
310 if ( ber->ber_buf != oldbuf ) {
311 ber->ber_ptr = ber->ber_buf + (ber->ber_ptr - oldbuf);
312
313 for ( s = ber->ber_sos; s != NULLSEQORSET; s = s->sos_next ) {
314 off = s->sos_first - oldbuf;
315 s->sos_first = ber->ber_buf + off;
316
317 off = s->sos_ptr - oldbuf;
318 s->sos_ptr = ber->ber_buf + off;
319 }
320 }
321
322 return( 0 );
323 }
324
325 void
ber_free(BerElement * ber,int freebuf)326 ber_free(BerElement *ber, int freebuf)
327 {
328 if (NULL != ber) {
329 if (freebuf && ber->ber_buf != NULL)
330 free(ber->ber_buf);
331 free((char *)ber);
332 }
333 }
334
335 int
ber_flush(Sockbuf * sb,BerElement * ber,int freeit)336 ber_flush( Sockbuf *sb, BerElement *ber, int freeit )
337 {
338 ssize_t nwritten, towrite, rc;
339
340 if ( ber->ber_rwptr == NULL ) {
341 ber->ber_rwptr = ber->ber_buf;
342 }
343 towrite = ber->ber_ptr - ber->ber_rwptr;
344
345 #ifdef LDAP_DEBUG
346 if ( lber_debug ) {
347 (void) fprintf( stderr, catgets(slapdcat, 1, 76, "ber_flush: %1$ld bytes to sd %2$ld%s\n"), towrite,
348 sb->sb_sd, ber->ber_rwptr != ber->ber_buf ? " (re-flush)"
349 : "" );
350 if ( lber_debug > 1 )
351 lber_bprint( ber->ber_rwptr, towrite );
352 }
353 #endif
354 #if !defined(MACOS) && !defined(DOS)
355 if ( sb->sb_options & (LBER_TO_FILE | LBER_TO_FILE_ONLY) ) {
356 #ifdef LDAP_SSL
357 if (sb->sb_ssl) {
358 rc = SSL_write( sb->sb_ssl, (u_char *)ber->ber_buf, towrite );
359 if ( rc < 0 ) {
360 fprintf( stderr, SSL_strerr(SSL_errno(sb->sb_ssl)));
361 }
362 } else {
363 #endif /* LDAP_SSL */
364 rc = write( sb->sb_fd, ber->ber_buf, towrite );
365 if ( sb->sb_options & LBER_TO_FILE_ONLY ) {
366 return( (int)rc );
367 }
368 #ifdef LDAP_SSL
369 }
370 #endif /* LDAP_SSL */
371 }
372 #endif
373
374 nwritten = 0;
375 do {
376 if (sb->sb_naddr > 0) {
377 #ifdef CLDAP
378 rc = udp_write( sb, ber->ber_buf + nwritten,
379 (size_t)towrite );
380 #else /* CLDAP */
381 rc = -1;
382 #endif /* CLDAP */
383 if ( rc <= 0 )
384 return( -1 );
385 /* fake error if write was not atomic */
386 if (rc < towrite) {
387 #if !defined( MACOS ) && !defined( DOS )
388 errno = EMSGSIZE;
389 #endif
390 return( -1 );
391 }
392 } else {
393 #ifdef LDAP_SSL
394 if (sb->sb_ssl) {
395 if ( (rc = SSL_write( sb->sb_ssl, (u_char *)ber->ber_rwptr,
396 (size_t) towrite )) <= 0 ) {
397 return( -1 );
398 }
399 } else
400 #endif /* LDAP_SSL */
401 if ( (rc = BerWrite( sb, ber->ber_rwptr,
402 (size_t) towrite )) <= 0 ) {
403 return( -1 );
404 }
405 }
406 towrite -= rc;
407 nwritten += rc;
408 ber->ber_rwptr += rc;
409 } while ( towrite > 0 );
410
411 if ( freeit )
412 ber_free( ber, 1 );
413
414 return( 0 );
415 }
416
417 BerElement *
ber_alloc_t(int options)418 ber_alloc_t( int options )
419 {
420 BerElement *ber;
421
422 if ( (ber = (BerElement *) calloc( (size_t) 1, sizeof(BerElement) )) == NULLBER )
423 return( NULLBER );
424 ber->ber_tag = LBER_DEFAULT;
425 ber->ber_options = (char) options;
426
427 return( ber );
428 }
429
430 BerElement *
ber_alloc()431 ber_alloc()
432 {
433 return( ber_alloc_t( 0 ) );
434 }
435
436 BerElement *
der_alloc()437 der_alloc()
438 {
439 return( ber_alloc_t( LBER_USE_DER ) );
440 }
441
442 BerElement *
ber_dup(BerElement * ber)443 ber_dup( BerElement *ber )
444 {
445 BerElement *new;
446
447 if ( (new = ber_alloc()) == NULLBER )
448 return( NULLBER );
449
450 *new = *ber;
451
452 return( new );
453 }
454
ber_init(struct berval * bv)455 BerElement *ber_init(struct berval *bv)
456 {
457 BerElement *new;
458
459 if (bv == NULL)
460 return (NULLBER);
461
462 if ((new = ber_alloc()) == NULLBER)
463 return (NULLBER);
464 if ((new->ber_buf = (char *)malloc(bv->bv_len + 1)) == NULL){
465 free(new);
466 return (NULLBER);
467 }
468 SAFEMEMCPY(new->ber_buf, bv->bv_val, bv->bv_len);
469 new->ber_end = new->ber_buf + bv->bv_len;
470 new->ber_ptr = new->ber_buf;
471 new->ber_len = bv->bv_len;
472 return (new);
473 }
474
475 void
ber_zero_init(BerElement * ber,int options)476 ber_zero_init( BerElement *ber, int options )
477 {
478 (void) memset( (char *)ber, '\0', sizeof( BerElement ));
479 ber->ber_tag = LBER_DEFAULT;
480 ber->ber_options = options;
481 }
482
483
484 void
ber_reset(BerElement * ber,int was_writing)485 ber_reset( BerElement *ber, int was_writing )
486 {
487 if ( was_writing ) {
488 ber->ber_end = ber->ber_ptr;
489 ber->ber_ptr = ber->ber_buf;
490 } else {
491 ber->ber_ptr = ber->ber_end;
492 }
493
494 ber->ber_rwptr = NULL;
495 }
496
497
498 #ifdef LDAP_DEBUG
499
500 void
ber_dump(BerElement * ber,int inout)501 ber_dump( BerElement *ber, int inout )
502 {
503 (void) fprintf( stderr, catgets(slapdcat, 1, 77, "ber_dump: buf 0x%1$lx, ptr 0x%2$lx, end 0x%3$lx\n"),
504 ber->ber_buf, ber->ber_ptr, ber->ber_end );
505 if ( inout == 1 ) {
506 (void) fprintf( stderr, catgets(slapdcat, 1, 78, " current len %ld, contents:\n"),
507 ber->ber_end - ber->ber_ptr );
508 lber_bprint( ber->ber_ptr, ber->ber_end - ber->ber_ptr );
509 } else {
510 (void) fprintf( stderr, catgets(slapdcat, 1, 78, " current len %ld, contents:\n"),
511 ber->ber_ptr - ber->ber_buf );
512 lber_bprint( ber->ber_buf, ber->ber_ptr - ber->ber_buf );
513 }
514 }
515
516 void
ber_sos_dump(Seqorset * sos)517 ber_sos_dump( Seqorset *sos )
518 {
519 (void) fprintf( stderr, catgets(slapdcat, 1, 79, "*** sos dump ***\n") );
520 while ( sos != NULLSEQORSET ) {
521 (void) fprintf( stderr, catgets(slapdcat, 1, 80, "ber_sos_dump: clen %1$ld first 0x%2$lx ptr 0x%3$lx\n"),
522 sos->sos_clen, sos->sos_first, sos->sos_ptr );
523 (void) fprintf( stderr, catgets(slapdcat, 1, 81, " current len %ld contents:\n"),
524 sos->sos_ptr - sos->sos_first );
525 lber_bprint( sos->sos_first, sos->sos_ptr - sos->sos_first );
526
527 sos = sos->sos_next;
528 }
529 (void) fprintf( stderr, catgets(slapdcat, 1, 82, "*** end dump ***\n") );
530 }
531
532 #endif
533
534 /* return the tag - LBER_DEFAULT returned means trouble */
535 static unsigned int
get_tag(Sockbuf * sb)536 get_tag( Sockbuf *sb )
537 {
538 unsigned char xbyte;
539 unsigned int tag;
540 char *tagp;
541 int i;
542
543 if ( BerRead( sb, (char *) &xbyte, 1 ) != 1 )
544 return( LBER_DEFAULT );
545
546 if ( (xbyte & LBER_BIG_TAG_MASK) != LBER_BIG_TAG_MASK )
547 return( (unsigned int) xbyte );
548
549 tagp = (char *) &tag;
550 tagp[0] = xbyte;
551 for ( i = 1; i < sizeof(int); i++ ) {
552 if ( BerRead( sb, (char *) &xbyte, 1 ) != 1 )
553 return( LBER_DEFAULT );
554
555 tagp[i] = xbyte;
556
557 if ( ! (xbyte & LBER_MORE_TAG_MASK) )
558 break;
559 }
560
561 /* tag too big! */
562 if ( i == sizeof(int) )
563 return( LBER_DEFAULT );
564
565 /* want leading, not trailing 0's */
566 return( tag >> (sizeof(int) - i - 1) );
567 }
568
569 unsigned int
ber_get_next(Sockbuf * sb,unsigned int * len,BerElement * ber)570 ber_get_next( Sockbuf *sb, unsigned int *len, BerElement *ber )
571 {
572 unsigned int tag, netlen, toread;
573 unsigned char lc;
574 int rc;
575 int noctets, diff;
576
577 #ifdef LDAP_DEBUG
578 if ( lber_debug )
579 (void) fprintf( stderr, catgets(slapdcat, 1, 83, "ber_get_next\n") );
580 #endif
581
582 /*
583 * Any ber element looks like this: tag length contents.
584 * Assuming everything's ok, we return the tag byte (we
585 * can assume a single byte), return the length in len,
586 * and the rest of the undecoded element in buf.
587 *
588 * Assumptions:
589 * 1) small tags (less than 128)
590 * 2) definite lengths
591 * 3) primitive encodings used whenever possible
592 */
593
594 /*
595 * first time through - malloc the buffer, set up ptrs, and
596 * read the tag and the length and as much of the rest as we can
597 */
598
599 if ( ber->ber_rwptr == NULL ) {
600 /*
601 * First, we read the tag.
602 */
603
604 if ( (tag = get_tag( sb )) == LBER_DEFAULT ) {
605 return( LBER_DEFAULT );
606 }
607 ber->ber_tag = tag;
608
609 /*
610 * Next, read the length. The first byte contains the length
611 * of the length. If bit 8 is set, the length is the int
612 * form, otherwise it's the short form. We don't allow a
613 * length that's greater than what we can hold in an unsigned
614 * int.
615 */
616
617 *len = netlen = 0;
618 if ( BerRead( sb, (char *) &lc, 1 ) != 1 ) {
619 return( LBER_DEFAULT );
620 }
621 if ( lc & 0x80 ) {
622 noctets = (lc & 0x7f);
623 if ( noctets > sizeof(unsigned int) )
624 return( LBER_DEFAULT );
625 diff = sizeof(unsigned int) - noctets;
626 if ( BerRead( sb, (char *) &netlen + diff, noctets ) !=
627 noctets ) {
628 return( LBER_DEFAULT );
629 }
630 *len = LBER_NTOHL( netlen );
631 } else {
632 *len = lc;
633 }
634 ber->ber_len = *len;
635
636 /*
637 * Finally, malloc a buffer for the contents and read it in.
638 * It's this buffer that's passed to all the other ber decoding
639 * routines.
640 */
641
642 #if defined( DOS ) && !defined( _WIN32 )
643 if ( *len > 65535 ) { /* DOS can't allocate > 64K */
644 return( LBER_DEFAULT );
645 }
646 #endif /* DOS && !_WIN32 */
647
648 if ( ( sb->sb_options & LBER_MAX_INCOMING_SIZE ) &&
649 *len > sb->sb_max_incoming ) {
650 return( LBER_DEFAULT );
651 }
652
653 if ( (ber->ber_buf = (char *) malloc( (size_t)*len )) == NULL ) {
654 return( LBER_DEFAULT );
655 }
656 ber->ber_ptr = ber->ber_buf;
657 ber->ber_end = ber->ber_buf + *len;
658 ber->ber_rwptr = ber->ber_buf;
659 }
660
661 toread = (uintptr_t)ber->ber_end - (uintptr_t)ber->ber_rwptr;
662 do {
663 if ( (rc = BerRead( sb, ber->ber_rwptr, (int)toread )) <= 0 ) {
664 return( LBER_DEFAULT );
665 }
666
667 toread -= rc;
668 ber->ber_rwptr += rc;
669 } while ( toread != 0 ); /* DF SUN for LINT */
670
671 #ifdef LDAP_DEBUG
672 if ( lber_debug ) {
673 (void) fprintf( stderr, catgets(slapdcat, 1, 84, "ber_get_next: tag 0x%1$lx len %2$ld contents:\n"),
674 tag, ber->ber_len );
675 if ( lber_debug > 1 )
676 ber_dump( ber, 1 );
677 }
678 #endif
679
680 *len = ber->ber_len;
681 ber->ber_rwptr = NULL;
682 return( ber->ber_tag );
683 }
684