1 /*
2  * Copyright 2001-2002 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 
9 /*
10  * The contents of this file are subject to the Netscape Public
11  * License Version 1.1 (the "License"); you may not use this file
12  * except in compliance with the License. You may obtain a copy of
13  * the License at http://www.mozilla.org/NPL/
14  *
15  * Software distributed under the License is distributed on an "AS
16  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
17  * implied. See the License for the specific language governing
18  * rights and limitations under the License.
19  *
20  * The Original Code is Mozilla Communicator client code, released
21  * March 31, 1998.
22  *
23  * The Initial Developer of the Original Code is Netscape
24  * Communications Corporation. Portions created by Netscape are
25  * Copyright (C) 1998-1999 Netscape Communications Corporation. All
26  * Rights Reserved.
27  *
28  * Contributor(s):
29  */
30 
31 /*
32  * Extended I/O callback functions for libldap that use
33  * NSPR (Netscape Portable Runtime) I/O.
34  *
35  * High level strategy: we use the socket-specific arg to hold our own data
36  * structure that includes the NSPR file handle (PRFileDesc *), among other
37  * useful information.  We use the default argument to hold an LDAP session
38  * handle specific data structure.
39  */
40 
41 #include "ldappr-int.h"
42 #include <string.h>
43 
44 #define PRLDAP_POLL_ARRAY_GROWTH  5  /* grow arrays 5 elements at a time */
45 
46 /*
47  * Local function prototypes:
48  */
49 static PRIntervalTime prldap_timeout2it( int ms_timeout, int ms_maxtimeout );
50 static int LDAP_CALLBACK prldap_read( int s, void *buf, int bufsize,
51 	struct lextiof_socket_private *socketarg );
52 static int LDAP_CALLBACK prldap_write( int s, const void *buf, int len,
53 	struct lextiof_socket_private *socketarg );
54 static int LDAP_CALLBACK prldap_poll( LDAP_X_PollFD fds[], int nfds,
55 	int timeout, struct lextiof_session_private *sessionarg );
56 static int LDAP_CALLBACK prldap_connect( const char *hostlist, int defport,
57 	int timeout, unsigned long options,
58 	struct lextiof_session_private *sessionarg,
59 	struct lextiof_socket_private **socketargp
60 #ifdef _SOLARIS_SDK
61 	, void **dhost );
62 #else
63 	);
64 #endif	/* _SOLARIS_SDK */
65 static int LDAP_CALLBACK prldap_close( int s,
66 	struct lextiof_socket_private *socketarg );
67 static int LDAP_CALLBACK prldap_newhandle( LDAP *ld,
68 	struct lextiof_session_private *sessionarg );
69 static void LDAP_CALLBACK prldap_disposehandle( LDAP *ld,
70 	struct lextiof_session_private *sessionarg );
71 static int LDAP_CALLBACK prldap_shared_newhandle( LDAP *ld,
72 	struct lextiof_session_private *sessionarg );
73 static void LDAP_CALLBACK prldap_shared_disposehandle( LDAP *ld,
74 	struct lextiof_session_private *sessionarg );
75 static PRLDAPIOSessionArg *prldap_session_arg_alloc( void );
76 static void prldap_session_arg_free( PRLDAPIOSessionArg **prsesspp );
77 static PRLDAPIOSocketArg *prldap_socket_arg_alloc( PRLDAPIOSessionArg *sessionarg );
78 static void prldap_socket_arg_free( PRLDAPIOSocketArg **prsockpp );
79 static void *prldap_safe_realloc( void *ptr, PRUint32 size );
80 
81 
82 
83 /*
84  * Local macros:
85  */
86 /* given a socket-specific arg, return the corresponding PRFileDesc * */
87 #define PRLDAP_GET_PRFD( socketarg )	\
88 		(((PRLDAPIOSocketArg *)(socketarg))->prsock_prfd)
89 
90 /*
91  * Static variables.
92  */
93 static int prldap_default_io_max_timeout = LDAP_X_IO_TIMEOUT_NO_TIMEOUT;
94 
95 /*
96  * Install NSPR I/O functions into ld (if ld is NULL, they are installed
97  * as the default functions for new LDAP * handles).
98  *
99  * Returns 0 if all goes well and -1 if not.
100  */
101 int
102 prldap_install_io_functions( LDAP *ld, int shared )
103 {
104     struct ldap_x_ext_io_fns	iofns;
105 
106     memset( &iofns, 0, sizeof(iofns));
107     iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
108     iofns.lextiof_read = prldap_read;
109     iofns.lextiof_write = prldap_write;
110     iofns.lextiof_poll = prldap_poll;
111     iofns.lextiof_connect = prldap_connect;
112     iofns.lextiof_close = prldap_close;
113     if ( shared ) {
114 	iofns.lextiof_newhandle = prldap_shared_newhandle;
115 	iofns.lextiof_disposehandle = prldap_shared_disposehandle;
116     } else {
117 	iofns.lextiof_newhandle = prldap_newhandle;
118 	iofns.lextiof_disposehandle = prldap_disposehandle;
119     }
120     if ( NULL != ld ) {
121 	/*
122 	 * If we are dealing with a real ld, we allocate the session specific
123 	 * data structure now.  If not allocated here, it will be allocated
124 	 * inside prldap_newhandle() or prldap_shared_newhandle().
125 	 */
126 	if ( NULL ==
127 		( iofns.lextiof_session_arg = prldap_session_arg_alloc())) {
128 	    ldap_set_lderrno( ld, LDAP_NO_MEMORY, NULL, NULL );
129 	    return( -1 );
130 	}
131     } else {
132 	iofns.lextiof_session_arg = NULL;
133     }
134 
135     if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, &iofns ) != 0 ) {
136 	prldap_session_arg_free(
137 		(PRLDAPIOSessionArg **) &iofns.lextiof_session_arg );
138 	return( -1 );
139     }
140 
141     return( 0 );
142 }
143 
144 
145 static PRIntervalTime
146 prldap_timeout2it( int ms_timeout, int ms_maxtimeout )
147 {
148     PRIntervalTime	prit;
149 
150     if ( LDAP_X_IO_TIMEOUT_NO_WAIT == ms_timeout ) {
151 	prit = PR_INTERVAL_NO_WAIT;
152     } else if ( LDAP_X_IO_TIMEOUT_NO_TIMEOUT == ms_timeout ) {
153 	prit = PR_INTERVAL_NO_TIMEOUT;
154     } else {
155 	prit = PR_MillisecondsToInterval( ms_timeout );
156     }
157 
158     /* cap at maximum I/O timeout */
159     if ( LDAP_X_IO_TIMEOUT_NO_WAIT == ms_maxtimeout ) {
160 	prit = LDAP_X_IO_TIMEOUT_NO_WAIT;
161     } else if ( LDAP_X_IO_TIMEOUT_NO_TIMEOUT != ms_maxtimeout ) {
162 	if ( LDAP_X_IO_TIMEOUT_NO_TIMEOUT == ms_timeout ||
163 		    ms_timeout > ms_maxtimeout ) {
164 	    prit = PR_MillisecondsToInterval( ms_maxtimeout );
165 	}
166     }
167 
168 #ifdef PRLDAP_DEBUG
169     if ( PR_INTERVAL_NO_WAIT == prit ) {
170 	fprintf( stderr, "prldap_timeout2it: NO_WAIT\n" );
171     } else if ( PR_INTERVAL_NO_TIMEOUT == prit ) {
172 	fprintf( stderr, "prldap_timeout2it: NO_TIMEOUT\n" );
173     } else {
174 	fprintf( stderr, "prldap_timeout2it: %dms\n",
175 		PR_IntervalToMilliseconds(prit));
176     }
177 #endif /* PRLDAP_DEBUG */
178 
179     return( prit );
180 }
181 
182 
183 static int LDAP_CALLBACK
184 prldap_read( int s, void *buf, int bufsize,
185 	struct lextiof_socket_private *socketarg )
186 {
187     PRIntervalTime	prit;
188 
189     prit = prldap_timeout2it( LDAP_X_IO_TIMEOUT_NO_TIMEOUT,
190 			socketarg->prsock_io_max_timeout );
191     return( PR_Recv( PRLDAP_GET_PRFD(socketarg), buf, bufsize, 0, prit ));
192 }
193 
194 
195 static int LDAP_CALLBACK
196 prldap_write( int s, const void *buf, int len,
197 	struct lextiof_socket_private *socketarg )
198 {
199     PRIntervalTime	prit;
200 
201     prit = prldap_timeout2it( LDAP_X_IO_TIMEOUT_NO_TIMEOUT,
202 			socketarg->prsock_io_max_timeout );
203 
204     /*
205      * Note the 4th parameter (flags) to PR_Send() has been obsoleted and
206      * must always be 0
207      */
208     return( PR_Send( PRLDAP_GET_PRFD(socketarg), buf, len, 0, prit ));
209 }
210 
211 
212 struct prldap_eventmap_entry {
213     PRInt16	evm_nspr;	/* corresponding NSPR PR_Poll() event */
214     int		evm_ldap;	/* LDAP poll event */
215 };
216 
217 static struct prldap_eventmap_entry prldap_eventmap[] = {
218     { PR_POLL_READ,	LDAP_X_POLLIN },
219     { PR_POLL_EXCEPT,	LDAP_X_POLLPRI },
220     { PR_POLL_WRITE,	LDAP_X_POLLOUT },
221     { PR_POLL_ERR,	LDAP_X_POLLERR },
222     { PR_POLL_HUP,	LDAP_X_POLLHUP },
223     { PR_POLL_NVAL,	LDAP_X_POLLNVAL },
224 };
225 
226 #define PRLDAP_EVENTMAP_ENTRIES	\
227 	sizeof(prldap_eventmap)/sizeof(struct prldap_eventmap_entry )
228 
229 static int LDAP_CALLBACK
230 prldap_poll( LDAP_X_PollFD fds[], int nfds, int timeout,
231 	struct lextiof_session_private *sessionarg )
232 {
233     PRLDAPIOSessionArg	*prsessp = sessionarg;
234     PRPollDesc		*pds;
235     int			i, j, rc;
236 
237     if ( NULL == prsessp ) {
238 	prldap_set_system_errno( EINVAL );
239 	return( -1 );
240     }
241 
242     /* allocate or resize NSPR poll descriptor array */
243     if ( prsessp->prsess_pollds_count < nfds ) {
244 	pds = prldap_safe_realloc( prsessp->prsess_pollds,
245 		( nfds + PRLDAP_POLL_ARRAY_GROWTH )
246 		* sizeof( PRPollDesc ));
247 	if ( NULL == pds ) {
248 	    prldap_set_system_errno( prldap_prerr2errno());
249 	    return( -1 );
250 	}
251 	prsessp->prsess_pollds = pds;
252 	prsessp->prsess_pollds_count = nfds + PRLDAP_POLL_ARRAY_GROWTH;
253     } else {
254 	pds = prsessp->prsess_pollds;
255     }
256 
257     /* populate NSPR poll info. based on LDAP info. */
258     for ( i = 0; i < nfds; ++i ) {
259 	if ( NULL == fds[i].lpoll_socketarg ) {
260 	    pds[i].fd = NULL;
261 	} else {
262 	    pds[i].fd = PRLDAP_GET_PRFD( fds[i].lpoll_socketarg );
263 	}
264 	pds[i].in_flags = pds[i].out_flags = 0;
265 	if ( fds[i].lpoll_fd >= 0 ) {
266 	    for ( j = 0; j < PRLDAP_EVENTMAP_ENTRIES; ++j ) {
267 		if (( fds[i].lpoll_events & prldap_eventmap[j].evm_ldap )
268 		    != 0 ) {
269 			pds[i].in_flags |= prldap_eventmap[j].evm_nspr;
270 		}
271 	    }
272 	}
273 	fds[i].lpoll_revents = 0;	/* clear revents */
274     }
275 
276     /* call PR_Poll() to do the real work */
277     rc = PR_Poll( pds, nfds,
278 	    prldap_timeout2it( timeout, prsessp->prsess_io_max_timeout ));
279 
280     /* populate LDAP info. based on NSPR results */
281     for ( i = 0; i < nfds; ++i ) {
282 	if ( pds[i].fd != NULL ) {
283 	    for ( j = 0; j < PRLDAP_EVENTMAP_ENTRIES; ++j ) {
284 		if (( pds[i].out_flags & prldap_eventmap[j].evm_nspr )
285 			!= 0 ) {
286 		    fds[i].lpoll_revents |= prldap_eventmap[j].evm_ldap;
287 		}
288 	    }
289 	}
290     }
291 
292     return( rc );
293 }
294 
295 
296 /*
297  * Utility function to try one TCP connect()
298  * Returns 1 if successful and -1 if not.  Sets the NSPR fd inside prsockp.
299  */
300 static int
301 prldap_try_one_address( struct lextiof_socket_private *prsockp,
302     PRNetAddr *addrp, int port, int timeout, unsigned long options )
303 {
304     /*
305      * Set up address and open a TCP socket:
306      */
307     if ( PR_SUCCESS != PR_SetNetAddr( PR_IpAddrNull, /* don't touch IP addr. */
308 		PRLDAP_DEFAULT_ADDRESS_FAMILY, (PRUint16)port, addrp )) {
309 	return( -1 );
310     }
311 
312     if (( prsockp->prsock_prfd = PR_OpenTCPSocket(
313 		PRLDAP_DEFAULT_ADDRESS_FAMILY )) == NULL ) {
314 	return( -1 );
315     }
316 
317     /*
318      * Set nonblocking option if requested:
319      */
320     if ( 0 != ( options & LDAP_X_EXTIOF_OPT_NONBLOCKING )) {
321 	PRSocketOptionData	optdata;
322 
323 	optdata.option = PR_SockOpt_Nonblocking;
324 	optdata.value.non_blocking = PR_TRUE;
325 	if ( PR_SetSocketOption( prsockp->prsock_prfd, &optdata )
326 		    != PR_SUCCESS ) {
327 	    prldap_set_system_errno( prldap_prerr2errno());
328 	    PR_Close( prsockp->prsock_prfd );
329 	    return( -1 );
330 	}
331     }
332 
333 #ifdef PRLDAP_DEBUG
334     {
335 	char	buf[ 256 ], *p, *fmtstr;
336 
337 	if ( PR_SUCCESS != PR_NetAddrToString( addrp, buf, sizeof(buf ))) {
338 		strcpy( buf, "conversion failed!" );
339 	}
340 	if ( strncmp( buf, "::ffff:", 7 ) == 0 ) {
341 		/* IPv4 address mapped into IPv6 address space */
342 		p = buf + 7;
343 		fmtstr = "prldap_try_one_address(): Trying %s:%d...\n";
344 	} else {
345 		p = buf;
346 		fmtstr = "prldap_try_one_address(): Trying [%s]:%d...\n";
347 	}
348 	fprintf( stderr, fmtstr, p, PR_ntohs( addrp->ipv6.port ));
349     }
350 #endif /* PRLDAP_DEBUG */
351 
352     /*
353      * Try to open the TCP connection itself:
354      */
355     if ( PR_SUCCESS != PR_Connect( prsockp->prsock_prfd, addrp,
356 		prldap_timeout2it( timeout, prsockp->prsock_io_max_timeout ))) {
357 	PR_Close( prsockp->prsock_prfd );
358 	prsockp->prsock_prfd = NULL;
359 	return( -1 );
360     }
361 
362 #ifdef PRLDAP_DEBUG
363     fputs( "prldap_try_one_address(): Connected.\n", stderr );
364 #endif /* PRLDAP_DEBUG */
365 
366     /*
367      * Success.  Return a valid file descriptor (1 is always valid)
368      */
369     return( 1 );
370 }
371 
372 
373 /*
374  * XXXmcs: At present, this code ignores the timeout when doing DNS lookups.
375  */
376 static int LDAP_CALLBACK
377 prldap_connect( const char *hostlist, int defport, int timeout,
378 	unsigned long options, struct lextiof_session_private *sessionarg,
379 	struct lextiof_socket_private **socketargp
380 #ifdef _SOLARIS_SDK
381 	, void **dhost )
382 #else
383 	)
384 #endif	/* _SOLARIS_SDK */
385 {
386     int					rc, parse_err, port;
387     char				*host, hbuf[ PR_NETDB_BUF_SIZE ];
388     struct ldap_x_hostlist_status	*status;
389     struct lextiof_socket_private	*prsockp;
390     PRNetAddr				addr;
391     PRHostEnt				hent;
392 #ifdef _SOLARIS_SDK
393     char				*hostname = NULL;
394     char				*nsldapi_strdup(char *);
395 #endif	/* _SOLARIS_SDK */
396 
397     if ( 0 != ( options & LDAP_X_EXTIOF_OPT_SECURE )) {
398 	prldap_set_system_errno( EINVAL );
399 	return( -1 );
400     }
401 
402     if ( NULL == ( prsockp = prldap_socket_arg_alloc( sessionarg ))) {
403 	prldap_set_system_errno( prldap_prerr2errno());
404 	return( -1 );
405     }
406 
407     rc = -1;	/* pessimistic */
408     for ( parse_err = ldap_x_hostlist_first( hostlist, defport, &host, &port,
409 		&status );
410 		rc < 0 && LDAP_SUCCESS == parse_err && NULL != host;
411 		parse_err = ldap_x_hostlist_next( &host, &port, status )) {
412 
413 	if ( PR_SUCCESS == PR_StringToNetAddr( host, &addr )) {
414 
415 		if ( PRLDAP_DEFAULT_ADDRESS_FAMILY == PR_AF_INET6 &&
416 				PR_AF_INET == PR_NetAddrFamily( &addr )) {
417 			PRUint32	ipv4ip = addr.inet.ip;
418 			memset( &addr, 0, sizeof(addr));
419 			PR_ConvertIPv4AddrToIPv6( ipv4ip, &addr.ipv6.ip );
420 			addr.ipv6.family = PR_AF_INET6;
421 
422 		}
423 	    rc = prldap_try_one_address( prsockp, &addr, port,
424 			timeout, options );
425 	} else {
426 	    if ( PR_SUCCESS == PR_GetIPNodeByName( host,
427 			PRLDAP_DEFAULT_ADDRESS_FAMILY, PR_AI_DEFAULT | PR_AI_ALL, hbuf,
428 			sizeof( hbuf ), &hent )) {
429 		PRIntn enumIndex = 0;
430 
431 		while ( rc < 0 && ( enumIndex = PR_EnumerateHostEnt(
432 			    enumIndex, &hent, (PRUint16)port, &addr )) > 0 ) {
433 		    rc = prldap_try_one_address( prsockp, &addr, port,
434 				timeout, options );
435 		}
436 	    }
437 	}
438 
439 #ifdef _SOLARIS_SDK
440 	if ( NULL != hostname ) ldap_memfree(hostname);
441 	hostname = nsldapi_strdup(host);
442 #endif	/* _SOLARIS_SDK */
443 	ldap_memfree( host );
444     }
445 
446     ldap_x_hostlist_statusfree( status );
447 
448     if ( rc < 0 ) {
449 	prldap_set_system_errno( prldap_prerr2errno());
450 	prldap_socket_arg_free( &prsockp );
451     } else {
452 	*socketargp = prsockp;
453     }
454 
455 #ifdef _SOLARIS_SDK
456     if ( NULL != hostname && NULL != dhost ) *dhost = hostname;
457     else if ( NULL != hostname ) ldap_memfree(hostname);
458 #endif	/* _SOLARIS_SDK */
459 
460     return( rc );
461 }
462 
463 
464 static int LDAP_CALLBACK
465 prldap_close( int s, struct lextiof_socket_private *socketarg )
466 {
467     int		rc;
468 
469     rc = 0;
470     if ( PR_Close( PRLDAP_GET_PRFD(socketarg)) != PR_SUCCESS ) {
471 	rc = -1;
472 	prldap_set_system_errno( prldap_prerr2errno());
473     }
474     prldap_socket_arg_free( &socketarg );
475 
476     return( rc );
477 }
478 
479 
480 /*
481  * LDAP session handle creation callback.
482  *
483  * Allocate a session argument if not already done, and then call the
484  * thread's new handle function.
485  */
486 static int LDAP_CALLBACK
487 prldap_newhandle( LDAP *ld, struct lextiof_session_private *sessionarg )
488 {
489 
490     if ( NULL == sessionarg ) {
491 	struct ldap_x_ext_io_fns	iofns;
492 
493 	memset( &iofns, 0, sizeof(iofns));
494 	iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
495 	if ( ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS,
496 		(void *)&iofns ) < 0 ) {
497 	    return( ldap_get_lderrno( ld, NULL, NULL ));
498 	}
499 	if ( NULL ==
500 		( iofns.lextiof_session_arg = prldap_session_arg_alloc())) {
501 	    return( LDAP_NO_MEMORY );
502 	}
503 	if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS,
504 		    (void *)&iofns ) < 0 ) {
505 	    return( ldap_get_lderrno( ld, NULL, NULL ));
506 	}
507     }
508 
509     return( LDAP_SUCCESS );
510 }
511 
512 
513 /* only called/installed if shared is non-zero. */
514 static int LDAP_CALLBACK
515 prldap_shared_newhandle( LDAP *ld, struct lextiof_session_private *sessionarg )
516 {
517     int		rc;
518 
519     if (( rc = prldap_newhandle( ld, sessionarg )) == LDAP_SUCCESS ) {
520 	rc = prldap_thread_new_handle( ld, sessionarg );
521     }
522 
523     return( rc );
524 }
525 
526 
527 static void LDAP_CALLBACK
528 prldap_disposehandle( LDAP *ld, struct lextiof_session_private *sessionarg )
529 {
530     prldap_session_arg_free( &sessionarg );
531 }
532 
533 
534 /* only called/installed if shared is non-zero */
535 static void LDAP_CALLBACK
536 prldap_shared_disposehandle( LDAP *ld,
537 	struct lextiof_session_private *sessionarg )
538 {
539     prldap_thread_dispose_handle( ld, sessionarg );
540     prldap_disposehandle( ld, sessionarg );
541 }
542 
543 
544 /*
545  * Allocate a session argument.
546  */
547 static PRLDAPIOSessionArg *
548 prldap_session_arg_alloc( void )
549 {
550     PRLDAPIOSessionArg		*prsessp;
551 
552     prsessp = PR_Calloc( 1, sizeof( PRLDAPIOSessionArg ));
553 
554     if ( NULL != prsessp ) {
555 	/* copy global defaults to the new session handle */
556 	prsessp->prsess_io_max_timeout = prldap_default_io_max_timeout;
557     }
558 
559     return( prsessp );
560 }
561 
562 
563 static void
564 prldap_session_arg_free( PRLDAPIOSessionArg **prsesspp )
565 {
566     if ( NULL != prsesspp && NULL != *prsesspp ) {
567 	if ( NULL != (*prsesspp)->prsess_pollds ) {
568 	    PR_Free( (*prsesspp)->prsess_pollds );
569 	    (*prsesspp)->prsess_pollds = NULL;
570 	}
571 	PR_Free( *prsesspp );
572 	*prsesspp = NULL;
573     }
574 }
575 
576 
577 /*
578  * Given an LDAP session handle, retrieve a session argument.
579  * Returns an LDAP error code.
580  */
581 int
582 prldap_session_arg_from_ld( LDAP *ld, PRLDAPIOSessionArg **sessargpp )
583 {
584     struct ldap_x_ext_io_fns	iofns;
585 
586     if ( NULL == ld || NULL == sessargpp ) {
587 	/* XXXmcs: NULL ld's are not supported */
588 	ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL );
589 	return( LDAP_PARAM_ERROR );
590     }
591 
592     memset( &iofns, 0, sizeof(iofns));
593     iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
594     if ( ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns ) < 0 ) {
595 	return( ldap_get_lderrno( ld, NULL, NULL ));
596     }
597 
598     if ( NULL == iofns.lextiof_session_arg ) {
599 	ldap_set_lderrno( ld, LDAP_LOCAL_ERROR, NULL, NULL );
600 	return( LDAP_LOCAL_ERROR );
601     }
602 
603     *sessargpp = iofns.lextiof_session_arg;
604     return( LDAP_SUCCESS );
605 }
606 
607 
608 /*
609  * Given an LDAP session handle, retrieve a socket argument.
610  * Returns an LDAP error code.
611  */
612 int
613 prldap_socket_arg_from_ld( LDAP *ld, PRLDAPIOSocketArg **sockargpp )
614 {
615     Sockbuf *sbp;
616     struct lber_x_ext_io_fns    extiofns;
617 
618     if ( NULL == ld || NULL == sockargpp ) {
619         /* XXXmcs: NULL ld's are not supported */
620         ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL );
621         return( LDAP_PARAM_ERROR );
622     }
623 
624     if ( ldap_get_option( ld, LDAP_X_OPT_SOCKBUF, (void *)&sbp ) < 0 ) {
625         return( ldap_get_lderrno( ld, NULL, NULL ));
626     }
627 
628     memset( &extiofns, 0, sizeof(extiofns));
629     extiofns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE;
630     if ( ber_sockbuf_get_option( sbp, LBER_SOCKBUF_OPT_EXT_IO_FNS,
631         (void *)&extiofns ) < 0 ) {
632         return( ldap_get_lderrno( ld, NULL, NULL ));
633     }
634 
635     if ( NULL == extiofns.lbextiofn_socket_arg ) {
636         ldap_set_lderrno( ld, LDAP_LOCAL_ERROR, NULL, NULL );
637         return( LDAP_LOCAL_ERROR );
638     }
639 
640     *sockargpp = extiofns.lbextiofn_socket_arg;
641     return( LDAP_SUCCESS );
642 }
643 
644 
645 /*
646  * Allocate a socket argument.
647  */
648 static PRLDAPIOSocketArg *
649 prldap_socket_arg_alloc( PRLDAPIOSessionArg *sessionarg )
650 {
651     PRLDAPIOSocketArg		*prsockp;
652 
653     prsockp = PR_Calloc( 1, sizeof( PRLDAPIOSocketArg ));
654 
655     if ( NULL != prsockp && NULL != sessionarg ) {
656 	/* copy socket defaults from the session */
657 	prsockp->prsock_io_max_timeout = sessionarg->prsess_io_max_timeout;
658     }
659 
660     return( prsockp );
661 }
662 
663 
664 static void
665 prldap_socket_arg_free( PRLDAPIOSocketArg **prsockpp )
666 {
667     if ( NULL != prsockpp && NULL != *prsockpp ) {
668 	PR_Free( *prsockpp );
669 	*prsockpp = NULL;
670     }
671 }
672 
673 
674 static void *
675 prldap_safe_realloc( void *ptr, PRUint32 size )
676 {
677     void	*p;
678 
679     if ( NULL == ptr ) {
680 	p = PR_Malloc( size );
681     } else {
682 	p = PR_Realloc( ptr, size );
683     }
684 
685     return( p );
686 }
687 
688 
689 
690 /* returns an LDAP result code */
691 int
692 prldap_set_io_max_timeout( PRLDAPIOSessionArg *prsessp, int io_max_timeout )
693 {
694     int	rc = LDAP_SUCCESS;	/* optimistic */
695 
696     if ( NULL == prsessp ) {
697 	prldap_default_io_max_timeout = io_max_timeout;
698     } else {
699 	prsessp->prsess_io_max_timeout = io_max_timeout;
700     }
701 
702     return( rc );
703 }
704 
705 
706 /* returns an LDAP result code */
707 int
708 prldap_get_io_max_timeout( PRLDAPIOSessionArg *prsessp, int *io_max_timeoutp )
709 {
710     int	rc = LDAP_SUCCESS;	/* optimistic */
711 
712     if ( NULL == io_max_timeoutp ) {
713 	rc = LDAP_PARAM_ERROR;
714     } else if ( NULL == prsessp ) {
715 	*io_max_timeoutp = prldap_default_io_max_timeout;
716     } else {
717 	*io_max_timeoutp = prsessp->prsess_io_max_timeout;
718     }
719 
720     return( rc );
721 }
722