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