1 /*
2  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 
7 #pragma ident	"%Z%%M%	%I%	%E% SMI"
8 
9 
10 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
11  *
12  * The contents of this file are subject to the Netscape Public License
13  * Version 1.0 (the "NPL"); you may not use this file except in
14  * compliance with the NPL.  You may obtain a copy of the NPL at
15  * http://www.mozilla.org/NPL/
16  *
17  * Software distributed under the NPL is distributed on an "AS IS" basis,
18  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
19  * for the specific language governing rights and limitations under the
20  * NPL.
21  *
22  * The Initial Developer of this code under the NPL is Netscape
23  * Communications Corporation.  Portions created by Netscape are
24  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
25  * Reserved.
26  */
27 
28 /*
29  * Copyright (c) 1990 Regents of the University of Michigan.
30  * All rights reserved.
31  *
32  * Redistribution and use in source and binary forms are permitted
33  * provided that this notice is preserved and that due credit is given
34  * to the University of Michigan at Ann Arbor. The name of the University
35  * may not be used to endorse or promote products derived from this
36  * software without specific prior written permission. This software
37  * is provided ``as is'' without express or implied warranty.
38  */
39 /* io.c - ber general i/o routines */
40 
41 #include "lber-int.h"
42 
43 #define bergetc( sb, len )    ( sb->sb_ber.ber_end > sb->sb_ber.ber_ptr ? \
44 			  (unsigned char)*sb->sb_ber.ber_ptr++ : \
45 			  ber_filbuf( sb, len ))
46 
47 # ifdef macintosh
48 /*
49  * MacTCP/OpenTransport
50  */
51 #  define read( s, b, l ) tcpread( s, 0, (unsigned char *)b, l, NULL )
52 #  define MAX_WRITE	65535
53 #  define BerWrite( sb, b, l )   tcpwrite( sb->sb_sd, (unsigned char *)(b), (l<MAX_WRITE)? l : MAX_WRITE )
54 # else /* macintosh */
55 #  if defined(_WIN32) || defined(_WINDOWS) || defined(XP_OS2)
56 /*
57  * 32-bit Windows Socket API (under Windows NT or Windows 95)
58  */
59 #   define read( s, b, l )		recv( s, b, l, 0 )
60 #   define BerWrite( s, b, l )	send( s->sb_sd, b, l, 0 )
61 #  else /* _WIN32 */
62 /*
63  * everything else (Unix/BSD 4.3 socket API)
64  */
65 #   define BerWrite( sb, b, l )	write( sb->sb_sd, b, l )
66 #   define udp_read( sb, b, l, al ) recvfrom(sb->sb_sd, (char *)b, l, 0, \
67 		(struct sockaddr *)sb->sb_fromaddr, \
68 		(al = sizeof(struct sockaddr), &al))
69 #   define udp_write( sb, b, l ) sendto(sb->sb_sd, (char *)(b), l, 0, \
70 		(struct sockaddr *)sb->sb_useaddr, sizeof(struct sockaddr))
71 #  endif /* _WIN32 */
72 # endif /* macintosh */
73 
74 #ifndef udp_read
75 #define udp_read( sb, b, l, al )	CLDAP NOT SUPPORTED
76 #define udp_write( sb, b, l )		CLDAP NOT SUPPORTED
77 #endif /* udp_read */
78 
79 #define EXBUFSIZ	1024
80 
81 #ifdef LDAP_DEBUG
82 int	lber_debug;
83 #endif
84 
85 /*
86  * function prototypes
87  */
88 static void nslberi_install_compat_io_fns( Sockbuf *sb );
89 static int nslberi_extread_compat( int s, void *buf, int len,
90 		struct lextiof_socket_private *arg );
91 static int nslberi_extwrite_compat( int s, const void *buf, int len,
92 		struct lextiof_socket_private *arg );
93 
94 
95 /*
96  * internal global structure for memory allocation callback functions
97  */
98 static struct lber_memalloc_fns nslberi_memalloc_fns;
99 
100 
101 /*
102  * buffered read from "sb".
103  * returns value of first character read on success and -1 on error.
104  */
105 static int
106 ber_filbuf( Sockbuf *sb, ber_slen_t len )
107 {
108 	ssize_t	rc;
109 #ifdef CLDAP
110 	int	addrlen;
111 #endif /* CLDAP */
112 
113 	if ( sb->sb_ber.ber_buf == NULL ) {
114 		if ( (sb->sb_ber.ber_buf = (char *)NSLBERI_MALLOC(
115 		    READBUFSIZ )) == NULL ) {
116 			return( -1 );
117 		}
118 		sb->sb_ber.ber_flags &= ~LBER_FLAG_NO_FREE_BUFFER;
119 		sb->sb_ber.ber_ptr = sb->sb_ber.ber_buf;
120 		sb->sb_ber.ber_end = sb->sb_ber.ber_buf;
121 	}
122 
123 	if ( sb->sb_naddr > 0 ) {
124 #ifdef CLDAP
125 		rc = udp_read(sb, sb->sb_ber.ber_buf, READBUFSIZ, addrlen );
126 #ifdef LDAP_DEBUG
127 		if ( lber_debug ) {
128 			char msg[80];
129 			sprintf( msg, "ber_filbuf udp_read %d bytes\n",
130 				rc );
131 			ber_err_print( msg );
132 			if ( lber_debug > 1 && rc > 0 )
133 				lber_bprint( sb->sb_ber.ber_buf, rc );
134 		}
135 #endif /* LDAP_DEBUG */
136 #else /* CLDAP */
137 		rc = -1;
138 #endif /* CLDAP */
139 	} else {
140 		if ( sb->sb_ext_io_fns.lbextiofn_read != NULL ) {
141 			rc = sb->sb_ext_io_fns.lbextiofn_read(
142 			    sb->sb_sd, sb->sb_ber.ber_buf,
143 			    ((sb->sb_options & LBER_SOCKBUF_OPT_NO_READ_AHEAD)
144 			    && (len < READBUFSIZ)) ? len : READBUFSIZ,
145 			    sb->sb_ext_io_fns.lbextiofn_socket_arg );
146 		} else {
147 			rc = read( sb->sb_sd, sb->sb_ber.ber_buf,
148 			    ((sb->sb_options & LBER_SOCKBUF_OPT_NO_READ_AHEAD)
149 			    && (len < READBUFSIZ)) ? len : READBUFSIZ );
150 		}
151 	}
152 
153 	if ( rc > 0 ) {
154 		sb->sb_ber.ber_ptr = sb->sb_ber.ber_buf + 1;
155 		sb->sb_ber.ber_end = sb->sb_ber.ber_buf + rc;
156 		return( (unsigned char)*sb->sb_ber.ber_buf );
157 	}
158 
159 	return( -1 );
160 }
161 
162 
163 static ber_int_t
164 BerRead( Sockbuf *sb, char *buf, ber_slen_t len )
165 {
166 	int		c;
167 	ber_int_t	nread = 0;
168 
169 	while ( len > 0 ) {
170 		if ( (c = bergetc( sb, len )) < 0 ) {
171 			if ( nread > 0 )
172 				break;
173 			return( c );
174 		}
175 		*buf++ = c;
176 		nread++;
177 		len--;
178 	}
179 
180 	return( nread );
181 }
182 
183 
184 /*
185  * Note: ber_read() only uses the ber_end and ber_ptr elements of ber.
186  * Functions like ber_get_tag(), ber_skip_tag, and ber_peek_tag() rely on
187  * that fact, so if this code is changed to use any additional elements of
188  * the ber structure, those functions will need to be changed as well.
189  */
190 ber_int_t
191 LDAP_CALL
192 ber_read( BerElement *ber, char *buf, ber_len_t len )
193 {
194 	ber_len_t	actuallen;
195 	ber_uint_t	nleft;
196 
197 	nleft = ber->ber_end - ber->ber_ptr;
198 	actuallen = nleft < len ? nleft : len;
199 
200 	SAFEMEMCPY( buf, ber->ber_ptr, (size_t)actuallen );
201 
202 	ber->ber_ptr += actuallen;
203 
204 	return( (ber_int_t)actuallen );
205 }
206 
207 /*
208  * enlarge the ber buffer.
209  * return 0 on success, -1 on error.
210  */
211 int
212 nslberi_ber_realloc( BerElement *ber, ber_len_t len )
213 {
214 	ber_uint_t	need, have, total;
215 	size_t		have_bytes;
216 	Seqorset	*s;
217 	ber_int_t	off;
218 	char		*oldbuf;
219 
220 	have_bytes = ber->ber_end - ber->ber_buf;
221 	have = have_bytes / EXBUFSIZ;
222 	need = (len < EXBUFSIZ ? 1 : (len + (EXBUFSIZ - 1)) / EXBUFSIZ);
223 	total = have * EXBUFSIZ + need * EXBUFSIZ;
224 
225 	oldbuf = ber->ber_buf;
226 
227 	if (ber->ber_buf == NULL) {
228 		if ( (ber->ber_buf = (char *)NSLBERI_MALLOC( (size_t)total ))
229 		    == NULL ) {
230 			return( -1 );
231 		}
232 		ber->ber_flags &= ~LBER_FLAG_NO_FREE_BUFFER;
233 	} else {
234 		if ( ber->ber_flags & LBER_FLAG_NO_FREE_BUFFER ) {
235 			/* transition to malloc'd buffer */
236 			if ( (ber->ber_buf = (char *)NSLBERI_MALLOC(
237 			    (size_t)total )) == NULL ) {
238 				return( -1 );
239 			}
240 			ber->ber_flags &= ~LBER_FLAG_NO_FREE_BUFFER;
241 			/* copy existing data into new malloc'd buffer */
242 			SAFEMEMCPY( ber->ber_buf, oldbuf, have_bytes );
243 		} else {
244 			if ( (ber->ber_buf = (char *)NSLBERI_REALLOC(
245 			    ber->ber_buf,(size_t)total )) == NULL ) {
246 				return( -1 );
247 			}
248 		}
249 	}
250 
251 	ber->ber_end = ber->ber_buf + total;
252 
253 	/*
254 	 * If the stinking thing was moved, we need to go through and
255 	 * reset all the sos and ber pointers.  Offsets would've been
256 	 * a better idea... oh well.
257 	 */
258 
259 	if ( ber->ber_buf != oldbuf ) {
260 		ber->ber_ptr = ber->ber_buf + (ber->ber_ptr - oldbuf);
261 
262 		for ( s = ber->ber_sos; s != NULLSEQORSET; s = s->sos_next ) {
263 			off = s->sos_first - oldbuf;
264 			s->sos_first = ber->ber_buf + off;
265 
266 			off = s->sos_ptr - oldbuf;
267 			s->sos_ptr = ber->ber_buf + off;
268 		}
269 	}
270 
271 	return( 0 );
272 }
273 
274 /*
275  * returns "len" on success and -1 on failure.
276  */
277 ber_int_t
278 LDAP_CALL
279 ber_write( BerElement *ber, char *buf, ber_len_t len, int nosos )
280 {
281 	if ( nosos || ber->ber_sos == NULL ) {
282 		if ( ber->ber_ptr + len > ber->ber_end ) {
283 			if ( nslberi_ber_realloc( ber, len ) != 0 )
284 				return( -1 );
285 		}
286 		SAFEMEMCPY( ber->ber_ptr, buf, (size_t)len );
287 		ber->ber_ptr += len;
288 		return( len );
289 	} else {
290 		if ( ber->ber_sos->sos_ptr + len > ber->ber_end ) {
291 			if ( nslberi_ber_realloc( ber, len ) != 0 )
292 				return( -1 );
293 		}
294 		SAFEMEMCPY( ber->ber_sos->sos_ptr, buf, (size_t)len );
295 		ber->ber_sos->sos_ptr += len;
296 		ber->ber_sos->sos_clen += len;
297 		return( len );
298 	}
299 }
300 
301 void
302 LDAP_CALL
303 ber_free( BerElement *ber, int freebuf )
304 {
305 	if ( ber != NULL ) {
306 		    if ( freebuf &&
307 			!(ber->ber_flags & LBER_FLAG_NO_FREE_BUFFER)) {
308 			    NSLBERI_FREE(ber->ber_buf);
309 		    }
310 		    NSLBERI_FREE( (char *) ber );
311 	}
312 }
313 
314 /*
315  * return >= 0 on success, -1 on failure.
316  */
317 int
318 LDAP_CALL
319 ber_flush( Sockbuf *sb, BerElement *ber, int freeit )
320 {
321 	ssize_t	nwritten, towrite, rc;
322 
323 	if ( ber->ber_rwptr == NULL ) {
324 		ber->ber_rwptr = ber->ber_buf;
325 	} else if (ber->ber_rwptr >= ber->ber_end) {
326           /* we will use the ber_rwptr to continue an exited flush,
327              so if rwptr is not within the buffer we return an error. */
328           return( -1 );
329         }
330 	towrite = ber->ber_ptr - ber->ber_rwptr;
331 
332 #ifdef LDAP_DEBUG
333 	if ( lber_debug ) {
334 		char msg[80];
335 		sprintf( msg, "ber_flush: %ld bytes to sd %ld%s\n", towrite,
336 		    sb->sb_sd, ber->ber_rwptr != ber->ber_buf ? " (re-flush)"
337 		    : "" );
338 		ber_err_print( msg );
339 		if ( lber_debug > 1 )
340 			lber_bprint( ber->ber_rwptr, towrite );
341 	}
342 #endif
343 #if !defined(macintosh) && !defined(DOS)
344 	if ( sb->sb_options & (LBER_SOCKBUF_OPT_TO_FILE | LBER_SOCKBUF_OPT_TO_FILE_ONLY) ) {
345 		rc = write( sb->sb_copyfd, ber->ber_buf, towrite );
346 		if ( sb->sb_options & LBER_SOCKBUF_OPT_TO_FILE_ONLY ) {
347 			return( (int)rc );
348 		}
349 	}
350 #endif
351 
352 	nwritten = 0;
353 	do {
354 		if (sb->sb_naddr > 0) {
355 #ifdef CLDAP
356 			rc = udp_write( sb, ber->ber_buf + nwritten,
357 			    (size_t)towrite );
358 #else /* CLDAP */
359 			rc = -1;
360 #endif /* CLDAP */
361 			if ( rc <= 0 )
362 				return( -1 );
363 			/* fake error if write was not atomic */
364 			if (rc < towrite) {
365 #if !defined( macintosh ) && !defined( DOS )
366 			    errno = EMSGSIZE;  /* For Win32, see portable.h */
367 #endif
368 			    return( -1 );
369 			}
370 		} else {
371 			if ( sb->sb_ext_io_fns.lbextiofn_write != NULL ) {
372 				if ( (rc = sb->sb_ext_io_fns.lbextiofn_write(
373 				    sb->sb_sd, ber->ber_rwptr, (size_t)towrite,
374 				    sb->sb_ext_io_fns.lbextiofn_socket_arg ))
375 				    <= 0 ) {
376 					return( -1 );
377 				}
378 			} else {
379 				if ( (rc = BerWrite( sb, ber->ber_rwptr,
380 				    (size_t) towrite )) <= 0 ) {
381 					return( -1 );
382 				}
383 			}
384 		}
385 		towrite -= rc;
386 		nwritten += rc;
387 		ber->ber_rwptr += rc;
388 	} while ( towrite > 0 );
389 
390 	if ( freeit )
391 		ber_free( ber, 1 );
392 
393 	return( 0 );
394 }
395 
396 
397 /* we pre-allocate a buffer to save the extra malloc later */
398 BerElement *
399 LDAP_CALL
400 ber_alloc_t( int options )
401 {
402 	BerElement	*ber;
403 
404 	if ( (ber = (BerElement*)NSLBERI_CALLOC( 1,
405 	    sizeof(struct berelement) + EXBUFSIZ )) == NULL ) {
406 		return( NULL );
407 	}
408 
409 	/*
410 	 * for compatibility with the C LDAP API standard, we recognize
411 	 * LBER_USE_DER as LBER_OPT_USE_DER.  See lber.h for a bit more info.
412 	 */
413 	if ( options & LBER_USE_DER ) {
414 		options &= ~LBER_USE_DER;
415 		options |= LBER_OPT_USE_DER;
416 	}
417 
418 	ber->ber_tag = LBER_DEFAULT;
419 	ber->ber_options = options;
420 	ber->ber_buf = (char*)ber + sizeof(struct berelement);
421 	ber->ber_ptr = ber->ber_buf;
422 	ber->ber_end = ber->ber_buf + EXBUFSIZ;
423 	ber->ber_flags = LBER_FLAG_NO_FREE_BUFFER;
424 
425 	return( ber );
426 }
427 
428 
429 BerElement *
430 LDAP_CALL
431 ber_alloc()
432 {
433 	return( ber_alloc_t( 0 ) );
434 }
435 
436 BerElement *
437 LDAP_CALL
438 der_alloc()
439 {
440 	return( ber_alloc_t( LBER_OPT_USE_DER ) );
441 }
442 
443 BerElement *
444 LDAP_CALL
445 ber_dup( BerElement *ber )
446 {
447 	BerElement	*new;
448 
449 	if ( (new = ber_alloc()) == NULL )
450 		return( NULL );
451 
452 	*new = *ber;
453 
454 	return( new );
455 }
456 
457 
458 void
459 LDAP_CALL
460 ber_init_w_nullchar( BerElement *ber, int options )
461 {
462 	(void) memset( (char *)ber, '\0', sizeof(struct berelement) );
463 	ber->ber_tag = LBER_DEFAULT;
464 
465 	/*
466 	 * For compatibility with the C LDAP API standard, we recognize
467 	 * LBER_USE_DER as LBER_OPT_USE_DER.  See lber.h for a bit more info.
468 	 */
469 	if ( options & LBER_USE_DER ) {
470 		options &= ~LBER_USE_DER;
471 		options |= LBER_OPT_USE_DER;
472 	}
473 
474 	ber->ber_options = options;
475 }
476 
477 
478 void
479 LDAP_CALL
480 ber_reset( BerElement *ber, int was_writing )
481 {
482 	if ( was_writing ) {
483 		ber->ber_end = ber->ber_ptr;
484 		ber->ber_ptr = ber->ber_buf;
485 	} else {
486 		ber->ber_ptr = ber->ber_end;
487 	}
488 
489 	ber->ber_rwptr = NULL;
490 }
491 
492 
493 #ifdef LDAP_DEBUG
494 
495 void
496 ber_dump( BerElement *ber, int inout )
497 {
498 	char msg[128];
499 	sprintf( msg, "ber_dump: buf 0x%lx, ptr 0x%lx, rwptr 0x%lx, end 0x%lx\n",
500 	    ber->ber_buf, ber->ber_ptr, ber->ber_rwptr, ber->ber_end );
501 	ber_err_print( msg );
502 	if ( inout == 1 ) {
503 		sprintf( msg, "          current len %ld, contents:\n",
504 		    ber->ber_end - ber->ber_ptr );
505 		ber_err_print( msg );
506 		lber_bprint( ber->ber_ptr, ber->ber_end - ber->ber_ptr );
507 	} else {
508 		sprintf( msg, "          current len %ld, contents:\n",
509 		    ber->ber_ptr - ber->ber_buf );
510 		ber_err_print( msg );
511 		lber_bprint( ber->ber_buf, ber->ber_ptr - ber->ber_buf );
512 	}
513 }
514 
515 void
516 ber_sos_dump( Seqorset *sos )
517 {
518 	char msg[80];
519 	ber_err_print ( "*** sos dump ***\n" );
520 	while ( sos != NULLSEQORSET ) {
521 		sprintf( msg, "ber_sos_dump: clen %ld first 0x%lx ptr 0x%lx\n",
522 		    sos->sos_clen, sos->sos_first, sos->sos_ptr );
523 		ber_err_print( msg );
524 		sprintf( msg, "              current len %ld contents:\n",
525 		    sos->sos_ptr - sos->sos_first );
526 		ber_err_print( msg );
527 		lber_bprint( sos->sos_first, sos->sos_ptr - sos->sos_first );
528 
529 		sos = sos->sos_next;
530 	}
531 	ber_err_print( "*** end dump ***\n" );
532 }
533 
534 #endif
535 
536 /* return the tag - LBER_DEFAULT returned means trouble */
537 static ber_tag_t
538 get_tag( Sockbuf *sb )
539 {
540 	unsigned char	xbyte;
541 	ber_tag_t	tag;
542 	char		*tagp;
543 	int		i;
544 
545 	if ( (i = BerRead( sb, (char *) &xbyte, 1 )) != 1 ) {
546 		return( LBER_DEFAULT );
547 	}
548 
549 	if ( (xbyte & LBER_BIG_TAG_MASK) != LBER_BIG_TAG_MASK ) {
550 		return( (ber_uint_t) xbyte );
551 	}
552 
553 	tagp = (char *) &tag;
554 	tagp[0] = xbyte;
555 	for ( i = 1; i < sizeof(ber_int_t); i++ ) {
556 		if ( BerRead( sb, (char *) &xbyte, 1 ) != 1 )
557 			return( LBER_DEFAULT );
558 
559 		tagp[i] = xbyte;
560 
561 		if ( ! (xbyte & LBER_MORE_TAG_MASK) )
562 			break;
563 	}
564 
565 	/* tag too big! */
566 	if ( i == sizeof(ber_int_t) )
567 		return( LBER_DEFAULT );
568 
569 	/* want leading, not trailing 0's */
570 	return( tag >> (sizeof(ber_int_t) - i - 1) );
571 }
572 
573 ber_tag_t
574 LDAP_CALL
575 ber_get_next( Sockbuf *sb, ber_len_t *len, BerElement *ber )
576 {
577 	ber_tag_t	tag = 0;
578 	ber_len_t	netlen;
579 	ber_uint_t	 toread;
580 	unsigned char	lc;
581 	ber_int_t	rc;
582 	int		noctets, diff;
583 
584 #ifdef LDAP_DEBUG
585 	if ( lber_debug )
586 		ber_err_print( "ber_get_next\n" );
587 #endif
588 
589 	/*
590 	 * Any ber element looks like this: tag length contents.
591 	 * Assuming everything's ok, we return the tag byte (we
592 	 * can assume a single byte), return the length in len,
593 	 * and the rest of the undecoded element in buf.
594 	 *
595 	 * Assumptions:
596 	 *	1) small tags (less than 128)
597 	 *	2) definite lengths
598 	 *	3) primitive encodings used whenever possible
599 	 */
600 
601 	/*
602 	 * first time through - malloc the buffer, set up ptrs, and
603 	 * read the tag and the length and as much of the rest as we can
604 	 */
605 
606 	if ( ber->ber_rwptr == NULL ) {
607 		/*
608 		 * First, we read the tag.
609 		 */
610 
611 		if ( (tag = get_tag( sb )) == LBER_DEFAULT ) {
612 			return( LBER_DEFAULT );
613 		}
614 		ber->ber_tag = tag;
615 
616 		/*
617 		 * Next, read the length.  The first byte contains the length
618 		 * of the length.  If bit 8 is set, the length is the long
619 		 * form, otherwise it's the short form.  We don't allow a
620 		 * length that's greater than what we can hold in an unsigned
621 		 * long.
622 		 */
623 
624 		*len = netlen = 0;
625 		if ( BerRead( sb, (char *) &lc, 1 ) != 1 ) {
626 			return( LBER_DEFAULT );
627 		}
628 		if ( lc & 0x80 ) {
629 			noctets = (lc & 0x7f);
630 			if ( noctets > sizeof(ber_uint_t) )
631 				return( LBER_DEFAULT );
632 			diff = sizeof(ber_uint_t) - noctets;
633 			if ( BerRead( sb, (char *) &netlen + diff, noctets ) !=
634 			    noctets ) {
635 				return( LBER_DEFAULT );
636 			}
637 			*len = LBER_NTOHL( netlen );
638 		} else {
639 			*len = lc;
640 		}
641 		ber->ber_len = *len;
642 
643 		/*
644 		 * Finally, malloc a buffer for the contents and read it in.
645 		 * It's this buffer that's passed to all the other ber decoding
646 		 * routines.
647 		 */
648 
649 #if defined( DOS ) && !( defined( _WIN32 ) || defined(XP_OS2) )
650 		if ( *len > 65535 ) {	/* DOS can't allocate > 64K */
651 		    return( LBER_DEFAULT );
652 		}
653 #endif /* DOS && !_WIN32 */
654 
655 		if ( ( sb->sb_options & LBER_SOCKBUF_OPT_MAX_INCOMING_SIZE )
656 		    && *len > sb->sb_max_incoming ) {
657 			return( LBER_DEFAULT );
658 		}
659 
660 		if ( (ber->ber_buf = (char *)NSLBERI_CALLOC( 1,(size_t)*len ))
661 		    == NULL ) {
662 			return( LBER_DEFAULT );
663 		}
664 		ber->ber_flags &= ~LBER_FLAG_NO_FREE_BUFFER;
665 		ber->ber_ptr = ber->ber_buf;
666 		ber->ber_end = ber->ber_buf + *len;
667 		ber->ber_rwptr = ber->ber_buf;
668 	}
669 
670 	toread = (uintptr_t)ber->ber_end - (uintptr_t)ber->ber_rwptr;
671 	do {
672 		if ( (rc = BerRead( sb, ber->ber_rwptr, (ber_int_t)toread )) <= 0 ) {
673 			return( LBER_DEFAULT );
674 		}
675 
676 		toread -= rc;
677 		ber->ber_rwptr += rc;
678 	} while ( toread > 0 );
679 
680 #ifdef LDAP_DEBUG
681 	if ( lber_debug ) {
682 		char msg[80];
683 		sprintf( msg, "ber_get_next: tag 0x%lx len %ld contents:\n",
684 		    tag, ber->ber_len );
685 		ber_err_print( msg );
686 		if ( lber_debug > 1 )
687 			ber_dump( ber, 1 );
688 	}
689 #endif
690 
691 	*len = ber->ber_len;
692 	ber->ber_rwptr = NULL;
693 	return( ber->ber_tag );
694 }
695 
696 Sockbuf *
697 LDAP_CALL
698 ber_sockbuf_alloc()
699 {
700 	return( (Sockbuf *)NSLBERI_CALLOC( 1, sizeof(struct sockbuf) ) );
701 }
702 
703 void
704 LDAP_CALL
705 ber_sockbuf_free(Sockbuf *p)
706 {
707 	if ( p != NULL ) {
708 		if ( p->sb_ber.ber_buf != NULL &&
709 		    !(p->sb_ber.ber_flags & LBER_FLAG_NO_FREE_BUFFER) ) {
710 			NSLBERI_FREE( p->sb_ber.ber_buf );
711 		}
712 		NSLBERI_FREE(p);
713 	}
714 }
715 
716 /*
717  * return 0 on success and -1 on error
718  */
719 int
720 LDAP_CALL
721 ber_set_option( struct berelement *ber, int option, void *value )
722 {
723 
724   /*
725    * memory allocation callbacks are global, so it is OK to pass
726    * NULL for ber.  Handle this as a special case.
727    */
728   if ( option == LBER_OPT_MEMALLOC_FN_PTRS ) {
729     /* struct copy */
730     nslberi_memalloc_fns = *((struct lber_memalloc_fns *)value);
731     return( 0 );
732   }
733 
734   /*
735    * lber_debug is global, so it is OK to pass
736    * NULL for ber.  Handle this as a special case.
737    */
738   if ( option == LBER_OPT_DEBUG_LEVEL ) {
739 #ifdef LDAP_DEBUG
740     lber_debug = *(int *)value;
741 #endif
742     return( 0 );
743   }
744 
745   /*
746    * all the rest require a non-NULL ber
747    */
748   if ( !NSLBERI_VALID_BERELEMENT_POINTER( ber )) {
749     return( -1 );
750   }
751 
752   switch ( option ) {
753 	case LBER_OPT_USE_DER:
754 	case LBER_OPT_TRANSLATE_STRINGS:
755 		if ( value != NULL ) {
756 			ber->ber_options |= option;
757 		} else {
758 			ber->ber_options &= ~option;
759 		}
760 		break;
761 	case LBER_OPT_REMAINING_BYTES:
762 		ber->ber_end = ber->ber_ptr + *((ber_uint_t *)value);
763 		break;
764 	case LBER_OPT_TOTAL_BYTES:
765 		ber->ber_end = ber->ber_buf + *((ber_uint_t *)value);
766 		break;
767 	case LBER_OPT_BYTES_TO_WRITE:
768 		ber->ber_ptr = ber->ber_buf + *((ber_uint_t *)value);
769 		break;
770 	default:
771 		return( -1 );
772   }
773 
774   return( 0 );
775 }
776 
777 /*
778  * return 0 on success and -1 on error
779  */
780 int
781 LDAP_CALL
782 ber_get_option( struct berelement *ber, int option, void *value )
783 {
784 	/*
785 	 * memory callocation callbacks are global, so it is OK to pass
786 	 * NULL for ber.  Handle this as a special case
787 	 */
788 	if ( option == LBER_OPT_MEMALLOC_FN_PTRS ) {
789 		/* struct copy */
790 		*((struct lber_memalloc_fns *)value) = nslberi_memalloc_fns;
791 		return( 0 );
792 	}
793 
794 	/*
795 	 * lber_debug is global, so it is OK to pass
796 	 * NULL for ber.  Handle this as a special case.
797 	 */
798 	if ( option == LBER_OPT_DEBUG_LEVEL ) {
799 #ifdef LDAP_DEBUG
800 	 *(int *)value =  lber_debug;
801 #endif
802 	  return( 0 );
803 	}
804 	/*
805 	 * all the rest require a non-NULL ber
806 	 */
807 	if ( !NSLBERI_VALID_BERELEMENT_POINTER( ber )) {
808 		return( -1 );
809 	}
810 
811 	switch ( option ) {
812 	case LBER_OPT_USE_DER:
813 	case LBER_OPT_TRANSLATE_STRINGS:
814 		*((int *) value) = (ber->ber_options & option);
815 		break;
816 	case LBER_OPT_REMAINING_BYTES:
817 		*((ber_uint_t *) value) = ber->ber_end - ber->ber_ptr;
818 		break;
819 	case LBER_OPT_TOTAL_BYTES:
820 		*((ber_uint_t *) value) = ber->ber_end - ber->ber_buf;
821 		break;
822 	case LBER_OPT_BYTES_TO_WRITE:
823 		*((ber_uint_t *) value) = ber->ber_ptr - ber->ber_buf;
824 		break;
825 	default:
826 		return( -1 );
827 	}
828 
829 	return( 0 );
830 }
831 
832 /*
833  * return 0 on success and -1 on error
834  */
835 int
836 LDAP_CALL
837 ber_sockbuf_set_option( Sockbuf *sb, int option, void *value )
838 {
839 	struct lber_x_ext_io_fns	*extiofns;
840 
841 	if ( !NSLBERI_VALID_SOCKBUF_POINTER( sb )) {
842 		return( -1 );
843 	}
844 
845 	switch ( option ) {
846 	case LBER_SOCKBUF_OPT_MAX_INCOMING_SIZE:
847 		sb->sb_max_incoming = *((ber_uint_t *) value);
848 		/* FALL */
849 	case LBER_SOCKBUF_OPT_TO_FILE:
850 	case LBER_SOCKBUF_OPT_TO_FILE_ONLY:
851 	case LBER_SOCKBUF_OPT_NO_READ_AHEAD:
852 		if ( value != NULL ) {
853 			sb->sb_options |= option;
854 		} else {
855 			sb->sb_options &= ~option;
856 		}
857 		break;
858 	case LBER_SOCKBUF_OPT_DESC:
859 		sb->sb_sd = *((LBER_SOCKET *) value);
860 		break;
861 	case LBER_SOCKBUF_OPT_COPYDESC:
862 		sb->sb_copyfd = *((LBER_SOCKET *) value);
863 		break;
864 	case LBER_SOCKBUF_OPT_READ_FN:
865 		sb->sb_io_fns.lbiof_read = (LDAP_IOF_READ_CALLBACK *) value;
866 		nslberi_install_compat_io_fns( sb );
867 		break;
868 	case LBER_SOCKBUF_OPT_WRITE_FN:
869 		sb->sb_io_fns.lbiof_write = (LDAP_IOF_WRITE_CALLBACK *) value;
870 		nslberi_install_compat_io_fns( sb );
871 		break;
872 	case LBER_SOCKBUF_OPT_EXT_IO_FNS:
873 		extiofns = (struct lber_x_ext_io_fns *) value;
874 		if ( extiofns == NULL ) {	/* remove */
875 			(void)memset( (char *)&sb->sb_ext_io_fns, '\0',
876 				sizeof(sb->sb_ext_io_fns ));
877 		} else if ( extiofns->lbextiofn_size
878 		    == LBER_X_EXTIO_FNS_SIZE ) {
879 			/* struct copy */
880 			sb->sb_ext_io_fns = *extiofns;
881 		} else {
882 			return( -1 );
883 		}
884 		break;
885 	default:
886 		return( -1 );
887 	}
888 
889 	return( 0 );
890 }
891 
892 /*
893  * return 0 on success and -1 on error
894  */
895 int
896 LDAP_CALL
897 ber_sockbuf_get_option( Sockbuf *sb, int option, void *value )
898 {
899 	struct lber_x_ext_io_fns	*extiofns;
900 
901 	if ( !NSLBERI_VALID_SOCKBUF_POINTER( sb )) {
902 		return( -1 );
903 	}
904 
905 	switch ( option ) {
906 	case LBER_SOCKBUF_OPT_MAX_INCOMING_SIZE:
907 		*((ber_uint_t *) value) = sb->sb_max_incoming;
908 		break;
909 	case LBER_SOCKBUF_OPT_TO_FILE:
910 	case LBER_SOCKBUF_OPT_TO_FILE_ONLY:
911 	case LBER_SOCKBUF_OPT_NO_READ_AHEAD:
912 		*((int *) value) = (sb->sb_options & option);
913 		break;
914 	case LBER_SOCKBUF_OPT_DESC:
915 		*((LBER_SOCKET *) value) = sb->sb_sd;
916 		break;
917 	case LBER_SOCKBUF_OPT_COPYDESC:
918 		*((LBER_SOCKET *) value) = sb->sb_copyfd;
919 		break;
920 	case LBER_SOCKBUF_OPT_READ_FN:
921 		*((LDAP_IOF_READ_CALLBACK **) value)
922 		    = sb->sb_io_fns.lbiof_read;
923 		break;
924 	case LBER_SOCKBUF_OPT_WRITE_FN:
925 		*((LDAP_IOF_WRITE_CALLBACK **) value)
926 		    = sb->sb_io_fns.lbiof_write;
927 		break;
928 	case LBER_SOCKBUF_OPT_EXT_IO_FNS:
929 		extiofns = (struct lber_x_ext_io_fns *) value;
930 		if ( extiofns == NULL || extiofns->lbextiofn_size
931 		    != LBER_X_EXTIO_FNS_SIZE ) {
932 			return( -1 );
933 		}
934 		/* struct copy */
935 		*extiofns = sb->sb_ext_io_fns;
936 		break;
937 	default:
938 		return( -1 );
939 	}
940 
941 	return( 0 );
942 }
943 
944 
945 /* new dboreham code below: */
946 
947 struct byte_buffer  {
948 	unsigned char *p;
949 	int offset;
950 	int length;
951 };
952 typedef struct byte_buffer byte_buffer;
953 
954 
955 /* This call allocates us a BerElement structure plus some extra memory.
956  * It returns a pointer to the BerElement, plus a pointer to the extra memory.
957  * This routine also allocates a ber data buffer within the same block, thus
958  * saving a call to calloc later when we read data.
959  */
960 void*
961 LDAP_CALL
962 ber_special_alloc(size_t size, BerElement **ppBer)
963 {
964 	char *mem = NULL;
965 
966 	/* Make sure mem size requested is aligned */
967 	if (0 != ( size & 0x03 )) {
968 		size += (sizeof(ber_int_t) - (size & 0x03));
969 	}
970 
971 	mem = NSLBERI_MALLOC(sizeof(struct berelement) + EXBUFSIZ + size );
972 	if (NULL == mem) {
973 		return NULL;
974 	}
975 	*ppBer = (BerElement*) (mem + size);
976 	memset(*ppBer,0,sizeof(struct berelement));
977 	(*ppBer)->ber_tag = LBER_DEFAULT;
978 	(*ppBer)->ber_buf = mem + size + sizeof(struct berelement);
979 	(*ppBer)->ber_ptr = (*ppBer)->ber_buf;
980 	(*ppBer)->ber_end = (*ppBer)->ber_buf + EXBUFSIZ;
981 	(*ppBer)->ber_flags = LBER_FLAG_NO_FREE_BUFFER;
982 	return (void*)mem;
983 }
984 
985 void
986 LDAP_CALL
987 ber_special_free(void* buf, BerElement *ber)
988 {
989 	if (!(ber->ber_flags & LBER_FLAG_NO_FREE_BUFFER)) {
990 		NSLBERI_FREE(ber->ber_buf);
991 	}
992 	NSLBERI_FREE( buf );
993 }
994 
995 static int
996 read_bytes(byte_buffer *b, unsigned char *return_buffer, int bytes_to_read)
997 {
998 	/* copy up to bytes_to_read bytes into the caller's buffer, return the number of bytes copied */
999 	int bytes_to_copy = 0;
1000 
1001 	if (bytes_to_read <= (b->length - b->offset) ) {
1002 		bytes_to_copy = bytes_to_read;
1003 	} else {
1004 		bytes_to_copy = (b->length - b->offset);
1005 	}
1006 	if (1 == bytes_to_copy) {
1007 		*return_buffer = *(b->p+b->offset++);
1008 	} else
1009 	if (0 == bytes_to_copy) {
1010 		;
1011 	} else
1012 	{
1013 		memcpy(return_buffer,b->p+b->offset,bytes_to_copy);
1014 		b->offset += bytes_to_copy;
1015 	}
1016 	return bytes_to_copy;
1017 }
1018 
1019 /* return the tag - LBER_DEFAULT returned means trouble */
1020 static ber_tag_t
1021 get_buffer_tag(byte_buffer *sb )
1022 {
1023 	unsigned char	xbyte;
1024 	ber_tag_t	tag;
1025 	char		*tagp;
1026 	int		i;
1027 
1028 	if ( (i = read_bytes( sb, &xbyte, 1 )) != 1 ) {
1029 		return( LBER_DEFAULT );
1030 	}
1031 
1032 	if ( (xbyte & LBER_BIG_TAG_MASK) != LBER_BIG_TAG_MASK ) {
1033 		return( (ber_uint_t) xbyte );
1034 	}
1035 
1036 	tagp = (char *) &tag;
1037 	tagp[0] = xbyte;
1038 	for ( i = 1; i < sizeof(ber_int_t); i++ ) {
1039 		if ( read_bytes( sb, &xbyte, 1 ) != 1 )
1040 			return( LBER_DEFAULT );
1041 
1042 		tagp[i] = xbyte;
1043 
1044 		if ( ! (xbyte & LBER_MORE_TAG_MASK) )
1045 			break;
1046 	}
1047 
1048 	/* tag too big! */
1049 	if ( i == sizeof(ber_int_t) )
1050 		return( LBER_DEFAULT );
1051 
1052 	/* want leading, not trailing 0's */
1053 	return( tag >> (sizeof(ber_int_t) - i - 1) );
1054 }
1055 
1056 /* Like ber_get_next, but from a byte buffer the caller already has. */
1057 /* Bytes_Scanned returns the number of bytes we actually looked at in the buffer. */
1058 /* ber_get_next_buffer is now implemented in terms of ber_get_next_buffer_ext */
1059 /* and is here for backward compatibility.  This new function allows us to pass */
1060 /* the Sockbuf structure along */
1061 
1062 ber_uint_t
1063 LDAP_CALL
1064 ber_get_next_buffer( void *buffer, size_t buffer_size, ber_len_t *len,
1065     BerElement *ber, ber_uint_t *Bytes_Scanned )
1066 {
1067 	return (ber_get_next_buffer_ext( buffer, buffer_size, len, ber,
1068 		Bytes_Scanned, NULL));
1069 }
1070 
1071 ber_uint_t
1072 LDAP_CALL
1073 ber_get_next_buffer_ext( void *buffer, size_t buffer_size, ber_len_t *len,
1074     BerElement *ber, ber_uint_t *Bytes_Scanned, Sockbuf *sock )
1075 {
1076 	ber_tag_t	tag = 0;
1077 	ber_len_t	netlen;
1078 	ber_uint_t	toread;
1079 	unsigned char	lc;
1080 	ssize_t		rc;
1081 	int		noctets, diff;
1082 	byte_buffer sb = {0};
1083 
1084 
1085 	/*
1086 	 * Any ber element looks like this: tag length contents.
1087 	 * Assuming everything's ok, we return the tag byte (we
1088 	 * can assume a single byte), return the length in len,
1089 	 * and the rest of the undecoded element in buf.
1090 	 *
1091 	 * Assumptions:
1092 	 *	1) small tags (less than 128)
1093 	 *	2) definite lengths
1094 	 *	3) primitive encodings used whenever possible
1095 	 */
1096 
1097 	/*
1098 	 * first time through - malloc the buffer, set up ptrs, and
1099 	 * read the tag and the length and as much of the rest as we can
1100 	 */
1101 
1102 	sb.p = buffer;
1103 	sb.length = buffer_size;
1104 
1105 	if ( ber->ber_rwptr == NULL ) {
1106 		/*
1107 		 * First, we read the tag.
1108 		 */
1109 
1110 		if ( (tag = get_buffer_tag( &sb )) == LBER_DEFAULT ) {
1111 			goto premature_exit;
1112 		}
1113 		ber->ber_tag = tag;
1114 
1115 		/*
1116 		 * Next, read the length.  The first byte contains the length
1117 		 * of the length.  If bit 8 is set, the length is the long
1118 		 * form, otherwise it's the short form.  We don't allow a
1119 		 * length that's greater than what we can hold in an unsigned
1120 		 * long.
1121 		 */
1122 
1123 		*len = netlen = 0;
1124 		if ( read_bytes( &sb, &lc, 1 ) != 1 ) {
1125 			goto premature_exit;
1126 		}
1127 		if ( lc & 0x80 ) {
1128 			noctets = (lc & 0x7f);
1129 			if ( noctets > sizeof(ber_uint_t) )
1130 				goto premature_exit;
1131 			diff = sizeof(ber_uint_t) - noctets;
1132 			if ( read_bytes( &sb, (unsigned char *)&netlen + diff,
1133 			    noctets ) != noctets ) {
1134 				goto premature_exit;
1135 			}
1136 			*len = LBER_NTOHL( netlen );
1137 		} else {
1138 			*len = lc;
1139 		}
1140 		ber->ber_len = *len;
1141 
1142 		/*
1143 		 * Finally, malloc a buffer for the contents and read it in.
1144 		 * It's this buffer that's passed to all the other ber decoding
1145 		 * routines.
1146 		 */
1147 
1148 #if defined( DOS ) && !defined( _WIN32 )
1149 		if ( *len > 65535 ) {	/* DOS can't allocate > 64K */
1150 			goto premature_exit;
1151 		}
1152 #endif /* DOS && !_WIN32 */
1153 
1154                 if ( (sock != NULL)  &&
1155 		    ( sock->sb_options & LBER_SOCKBUF_OPT_MAX_INCOMING_SIZE )
1156                     && (*len > sock->sb_max_incoming) ) {
1157                         return( LBER_DEFAULT );
1158                 }
1159 
1160 		if ( ber->ber_buf + *len > ber->ber_end ) {
1161 			if ( nslberi_ber_realloc( ber, *len ) != 0 )
1162 				goto premature_exit;
1163 		}
1164 		ber->ber_ptr = ber->ber_buf;
1165 		ber->ber_end = ber->ber_buf + *len;
1166 		ber->ber_rwptr = ber->ber_buf;
1167 	}
1168 
1169 	toread = (uintptr_t)ber->ber_end - (uintptr_t)ber->ber_rwptr;
1170 	do {
1171 		if ( (rc = read_bytes( &sb, (unsigned char *)ber->ber_rwptr,
1172 		    (ber_int_t)toread )) <= 0 ) {
1173 			goto premature_exit;
1174 		}
1175 
1176 		toread -= rc;
1177 		ber->ber_rwptr += rc;
1178 	} while ( toread > 0 );
1179 
1180 	*len = ber->ber_len;
1181 	*Bytes_Scanned = sb.offset;
1182 	return( ber->ber_tag );
1183 
1184 premature_exit:
1185 	/*
1186 	 * we're here because we hit the end of the buffer before seeing
1187 	 * all of the PDU
1188 	 */
1189 	*Bytes_Scanned = sb.offset;
1190 	return(LBER_DEFAULT);
1191 }
1192 
1193 
1194 /* The ber_flatten routine allocates a struct berval whose contents
1195  * are a BER encoding taken from the ber argument. The bvPtr pointer
1196  * points to the returned berval, which must be freed using
1197  * ber_bvfree().  This routine returns 0 on success and -1 on error.
1198  * The use of ber_flatten on a BerElement in which all '{' and '}'
1199  * format modifiers have not been properly matched can result in a
1200  * berval whose contents are not a valid BER encoding.
1201  * Note that the ber_ptr is not modified.
1202  */
1203 int
1204 LDAP_CALL
1205 ber_flatten( BerElement *ber, struct berval **bvPtr )
1206 {
1207 	struct berval *new;
1208 	ber_len_t len;
1209 
1210 	/* allocate a struct berval */
1211 	if ( (new = (struct berval *)NSLBERI_MALLOC( sizeof(struct berval) ))
1212 	    == NULL ) {
1213 		return( -1 );
1214 	}
1215 
1216 	/*
1217 	 * Copy everything from the BerElement's ber_buf to ber_ptr
1218 	 * into the berval structure.
1219 	 */
1220 	if ( ber == NULL ) {
1221 	    new->bv_val = NULL;
1222 	    new->bv_len = 0;
1223 	} else {
1224  	    len = ber->ber_ptr - ber->ber_buf;
1225 	    if ( ( new->bv_val = (char *)NSLBERI_MALLOC( len + 1 )) == NULL ) {
1226 		    ber_bvfree( new );
1227 		    return( -1 );
1228 	    }
1229  	    SAFEMEMCPY( new->bv_val, ber->ber_buf, (size_t)len );
1230 	    new->bv_val[len] = '\0';
1231 	    new->bv_len = len;
1232 	}
1233 
1234 	/* set bvPtr pointer to point to the returned berval */
1235   	*bvPtr = new;
1236 
1237         return( 0 );
1238 }
1239 
1240 
1241 /*
1242  * The ber_init function constructs and returns a new BerElement
1243  * containing a copy of the data in the bv argument.  ber_init
1244  * returns the null pointer on error.
1245  */
1246 BerElement *
1247 LDAP_CALL
1248 ber_init( const struct berval *bv )
1249 {
1250 	BerElement *ber;
1251 
1252 	/* construct BerElement */
1253 	if (( ber = ber_alloc_t( 0 )) != NULLBER ) {
1254 		/* copy data from the bv argument into BerElement */
1255 		/* XXXmcs: had to cast unsigned long bv_len to long */
1256 		if ( (ber_write ( ber, bv->bv_val, bv->bv_len, 0 ))
1257 		    != (ber_slen_t)bv->bv_len ) {
1258 			ber_free( ber, 1 );
1259 			return( NULL );
1260 		}
1261 	}
1262 
1263 	/*
1264 	 * reset ber_ptr back to the beginning of buffer so that this new
1265 	 * and initialized ber element can be READ
1266 	 */
1267 	ber_reset( ber, 1);
1268 
1269 	/*
1270 	 * return a ptr to a new BerElement containing a copy of the data
1271 	 * in the bv argument or a null pointer on error
1272 	 */
1273 	return( ber );
1274 }
1275 
1276 
1277 /*
1278  * memory allocation functions.
1279  */
1280 void *
1281 nslberi_malloc( size_t size )
1282 {
1283 	return( nslberi_memalloc_fns.lbermem_malloc == NULL ?
1284 	    malloc( size ) :
1285 	    nslberi_memalloc_fns.lbermem_malloc( size ));
1286 }
1287 
1288 
1289 void *
1290 nslberi_calloc( size_t nelem, size_t elsize )
1291 {
1292 	return( nslberi_memalloc_fns.lbermem_calloc == NULL ?
1293 	    calloc(  nelem, elsize ) :
1294 	    nslberi_memalloc_fns.lbermem_calloc( nelem, elsize ));
1295 }
1296 
1297 
1298 void *
1299 nslberi_realloc( void *ptr, size_t size )
1300 {
1301 	return( nslberi_memalloc_fns.lbermem_realloc == NULL ?
1302 	    realloc( ptr, size ) :
1303 	    nslberi_memalloc_fns.lbermem_realloc( ptr, size ));
1304 }
1305 
1306 
1307 void
1308 nslberi_free( void *ptr )
1309 {
1310 	if ( nslberi_memalloc_fns.lbermem_free == NULL ) {
1311 		free( ptr );
1312 	} else {
1313 		nslberi_memalloc_fns.lbermem_free( ptr );
1314 	}
1315 }
1316 
1317 
1318 /*
1319  ******************************************************************************
1320  * functions to bridge the gap between new extended I/O functions that are
1321  *    installed using ber_sockbuf_set_option( ..., LBER_SOCKBUF_OPT_EXT_IO_FNS,
1322  *    ... ).
1323  *
1324  * the basic strategy is to use the new extended arg to hold a pointer to the
1325  *    Sockbuf itself so we can find the old functions and call them.
1326  * note that the integer socket s passed in is not used.  we use the sb_sd
1327  *    from the Sockbuf itself because it is the correct type.
1328  */
1329 static int
1330 nslberi_extread_compat( int s, void *buf, int len,
1331 	struct lextiof_socket_private *arg )
1332 {
1333 	Sockbuf	*sb = (Sockbuf *)arg;
1334 
1335 	return( sb->sb_io_fns.lbiof_read( sb->sb_sd, buf, len ));
1336 }
1337 
1338 
1339 static int
1340 nslberi_extwrite_compat( int s, const void *buf, int len,
1341 	struct lextiof_socket_private *arg )
1342 {
1343 	Sockbuf	*sb = (Sockbuf *)arg;
1344 
1345 	return( sb->sb_io_fns.lbiof_write( sb->sb_sd, buf, len ));
1346 }
1347 
1348 
1349 /*
1350  * Install I/O compatiblity functions.  This can't fail.
1351  */
1352 static void
1353 nslberi_install_compat_io_fns( Sockbuf *sb )
1354 {
1355 	sb->sb_ext_io_fns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE;
1356 	sb->sb_ext_io_fns.lbextiofn_read = nslberi_extread_compat;
1357 	sb->sb_ext_io_fns.lbextiofn_write = nslberi_extwrite_compat;
1358 	sb->sb_ext_io_fns.lbextiofn_socket_arg = (void *)sb;
1359 }
1360 /*
1361  * end of compat I/O functions
1362  ******************************************************************************
1363  */
1364