diff options
Diffstat (limited to 'usr/src/lib/libldap5/sources/ldap/prldap')
-rw-r--r-- | usr/src/lib/libldap5/sources/ldap/prldap/ldappr-dns.c | 366 | ||||
-rw-r--r-- | usr/src/lib/libldap5/sources/ldap/prldap/ldappr-error.c | 229 | ||||
-rw-r--r-- | usr/src/lib/libldap5/sources/ldap/prldap/ldappr-int.h | 113 | ||||
-rw-r--r-- | usr/src/lib/libldap5/sources/ldap/prldap/ldappr-io.c | 721 | ||||
-rw-r--r-- | usr/src/lib/libldap5/sources/ldap/prldap/ldappr-public.c | 382 | ||||
-rw-r--r-- | usr/src/lib/libldap5/sources/ldap/prldap/ldappr-threads.c | 754 |
6 files changed, 2565 insertions, 0 deletions
diff --git a/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-dns.c b/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-dns.c new file mode 100644 index 0000000000..7c49225109 --- /dev/null +++ b/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-dns.c @@ -0,0 +1,366 @@ +/* + * Copyright 2002-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + + +/* + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998-1999 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +/* + * DNS callback functions for libldap that use the NSPR (Netscape + * Portable Runtime) thread API. + * + */ + +#ifdef _SOLARIS_SDK +#include "solaris-int.h" +#include <libintl.h> +#include <syslog.h> +#include <nsswitch.h> +#include <synch.h> +#include <nss_dbdefs.h> +#include <netinet/in.h> +static char *host_service = NULL; +static DEFINE_NSS_DB_ROOT(db_root_hosts); +#endif + +#include "ldappr-int.h" + +static LDAPHostEnt *prldap_gethostbyname( const char *name, + LDAPHostEnt *result, char *buffer, int buflen, int *statusp, + void *extradata ); +static LDAPHostEnt *prldap_gethostbyaddr( const char *addr, int length, + int type, LDAPHostEnt *result, char *buffer, int buflen, + int *statusp, void *extradata ); +static int prldap_getpeername( LDAP *ld, struct sockaddr *addr, + char *buffer, int buflen ); +static LDAPHostEnt *prldap_convert_hostent( LDAPHostEnt *ldhp, + PRHostEnt *prhp ); + +#ifdef _SOLARIS_SDK +static LDAPHostEnt * +prldap_gethostbyname1(const char *name, LDAPHostEnt *result, + char *buffer, int buflen, int *statusp, void *extradata); +extern int +str2hostent(const char *instr, int lenstr, void *ent, char *buffer, + int buflen); +#endif /* _SOLARIS_SDK */ + + +/* + * Install NSPR DNS functions into ld (if ld is NULL, they are installed + * as the default functions for new LDAP * handles). + * + * Returns 0 if all goes well and -1 if not. + */ +int +prldap_install_dns_functions( LDAP *ld ) +{ + struct ldap_dns_fns dnsfns; + + memset( &dnsfns, '\0', sizeof(struct ldap_dns_fns) ); + dnsfns.lddnsfn_bufsize = PR_NETDB_BUF_SIZE; + dnsfns.lddnsfn_gethostbyname = prldap_gethostbyname; + dnsfns.lddnsfn_gethostbyaddr = prldap_gethostbyaddr; + dnsfns.lddnsfn_getpeername = prldap_getpeername; + if ( ldap_set_option( ld, LDAP_OPT_DNS_FN_PTRS, (void *)&dnsfns ) != 0 ) { + return( -1 ); + } + + return( 0 ); +} + + +static LDAPHostEnt * +prldap_gethostbyname( const char *name, LDAPHostEnt *result, + char *buffer, int buflen, int *statusp, void *extradata ) +{ + PRHostEnt prhent; + + if( !statusp || ( *statusp = (int)PR_GetIPNodeByName( name, + PRLDAP_DEFAULT_ADDRESS_FAMILY, PR_AI_DEFAULT, + buffer, buflen, &prhent )) == PR_FAILURE ) { + return( NULL ); + } + + return( prldap_convert_hostent( result, &prhent )); +} + + +static LDAPHostEnt * +prldap_gethostbyaddr( const char *addr, int length, int type, + LDAPHostEnt *result, char *buffer, int buflen, int *statusp, + void *extradata ) +{ + PRHostEnt prhent; + PRNetAddr iaddr; + + if ( PR_SetNetAddr(PR_IpAddrNull, PRLDAP_DEFAULT_ADDRESS_FAMILY, + 0, &iaddr) == PR_FAILURE + || PR_StringToNetAddr( addr, &iaddr ) == PR_FAILURE ) { + return( NULL ); + } + + if( !statusp || (*statusp = PR_GetHostByAddr(&iaddr, buffer, + buflen, &prhent )) == PR_FAILURE ) { + return( NULL ); + } + return( prldap_convert_hostent( result, &prhent )); +} + +static int +prldap_getpeername( LDAP *ld, struct sockaddr *addr, char *buffer, int buflen) +{ + PRLDAPIOSocketArg *sa; + PRFileDesc *fd; + PRNetAddr iaddr; + int ret; + + if (NULL != ld) { + ret = prldap_socket_arg_from_ld( ld, &sa ); + if (ret != LDAP_SUCCESS) { + return (-1); + } + ret = PR_GetPeerName(sa->prsock_prfd, &iaddr); + if( ret == PR_FAILURE ) { + return( -1 ); + } + *addr = *((struct sockaddr *)&iaddr.raw); + ret = PR_NetAddrToString(&iaddr, buffer, buflen); + if( ret == PR_FAILURE ) { + return( -1 ); + } + return (0); + } + return (-1); +} + + +/* + * Function: prldap_convert_hostent() + * Description: copy the fields of a PRHostEnt struct to an LDAPHostEnt + * Returns: the LDAPHostEnt pointer passed in. + */ +static LDAPHostEnt * +prldap_convert_hostent( LDAPHostEnt *ldhp, PRHostEnt *prhp ) +{ + ldhp->ldaphe_name = prhp->h_name; + ldhp->ldaphe_aliases = prhp->h_aliases; + ldhp->ldaphe_addrtype = prhp->h_addrtype; + ldhp->ldaphe_length = prhp->h_length; + ldhp->ldaphe_addr_list = prhp->h_addr_list; + return( ldhp ); +} + +#ifdef _SOLARIS_SDK +/* + * prldap_x_install_dns_skipdb attempts to prevent recursion in resolving + * the hostname to an IP address when a host name is given to LDAP user. + * + * For example, libsldap cannot use LDAP to resolve the host name to an + * address because of recursion. The caller is instructing libldap to skip + * the specified name service when resolving addresses for the specified + * ldap connection. + * + * Note: + * This only supports ipv4 addresses currently. + * + * Since host_service applies to all connections, calling + * prldap_x_install_dns_skipdb with name services other than + * ldap or what uses ldap (for example nis+ might use ldap) to + * skip will lead to unpredictable results. + * + * Returns: + * 0 if success and data base found + * -1 if failure + */ + +int +prldap_x_install_dns_skipdb(LDAP *ld, const char *skip) +{ + enum __nsw_parse_err pserr; + struct __nsw_switchconfig *conf; + struct __nsw_lookup *lkp; + struct ldap_dns_fns dns_fns; + char *name_list = NULL; + char *tmp; + const char *name; + int len; + boolean_t got_skip = B_FALSE; + + /* + * db_root_hosts.lock mutex is used to ensure that the name list + * is not in use by the name service switch while we are updating + * the host_service + */ + + (void) mutex_lock(&db_root_hosts.lock); + conf = __nsw_getconfig("hosts", &pserr); + if (conf == NULL) { + (void) mutex_unlock(&db_root_hosts.lock); + return (0); + } + + /* check for skip and count other backends */ + for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) { + name = lkp->service_name; + if (strcmp(name, skip) == 0) { + got_skip = B_TRUE; + continue; + } + if (name_list == NULL) + name_list = strdup(name); + else { + len = strlen(name_list); + tmp = realloc(name_list, len + strlen(name) + 2); + if (tmp == NULL) { + free(name_list); + name_list = NULL; + } else { + name_list = tmp; + name_list[len++] = ' '; + (void) strcpy(name_list+len, name); + } + } + if (name_list == NULL) { /* alloc error */ + (void) mutex_unlock(&db_root_hosts.lock); + __nsw_freeconfig(conf); + return (-1); + } + } + __nsw_freeconfig(conf); + if (!got_skip) { + /* + * Since skip name service not used for hosts, we do not need + * to install our private address resolution function + */ + (void) mutex_unlock(&db_root_hosts.lock); + if (name_list != NULL) + free(name_list); + return (0); + } + if (host_service != NULL) + free(host_service); + host_service = name_list; + (void) mutex_unlock(&db_root_hosts.lock); + + if (ldap_get_option(ld, LDAP_OPT_DNS_FN_PTRS, &dns_fns) != 0) + return (-1); + dns_fns.lddnsfn_bufsize = PR_NETDB_BUF_SIZE; + dns_fns.lddnsfn_gethostbyname = prldap_gethostbyname1; + if (ldap_set_option(ld, LDAP_OPT_DNS_FN_PTRS, &dns_fns) != 0) + return (-1); + return (0); +} + +/* + * prldap_initf_hosts is passed to and called by nss_search() as a + * service routine. + * + * Returns: + * None + */ + +static void +prldap_initf_hosts(nss_db_params_t *p) +{ + static char *no_service = ""; + + p->name = NSS_DBNAM_HOSTS; + p->flags |= NSS_USE_DEFAULT_CONFIG; + p->default_config = host_service == NULL ? no_service : host_service; +} + +/* + * called by prldap_gethostbyname1() + */ +/* + * prldap_switch_gethostbyname_r is called by prldap_gethostbyname1 as a + * substitute for gethostbyname_r(). A method which prevents recursion. see + * prldap_gethostbyname1() and prldap_x_install_dns_skipdb(). + * + * Returns: + * Valid pointer to hostent if success + * PR_FAILURE if failure + */ + +static struct hostent * +prldap_switch_gethostbyname_r(const char *name, + struct hostent *result, char *buffer, int buflen, + int *h_errnop) +{ + nss_XbyY_args_t arg; + nss_status_t res; + + /* + * Log the information indicating that we are trying to + * resolve the LDAP server name. + */ + syslog(LOG_INFO, "libldap: Resolving server name \"%s\"", name); + + NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent); + + arg.key.name = name; + arg.stayopen = 0; + + res = nss_search(&db_root_hosts, prldap_initf_hosts, + NSS_DBOP_HOSTS_BYNAME, &arg); + arg.status = res; + *h_errnop = arg.h_errno; + return (struct hostent *)NSS_XbyY_FINI(&arg); +} + +/* + * prldap_gethostbyname1 is used to be a substitute gethostbyname_r for + * libldap when it is unsafe to use the normal nameservice functions. + * + * Returns: + * pointer to LDAPHostEnt: if success contains the address + * NULL pointer: if failure + */ + +static LDAPHostEnt * +prldap_gethostbyname1(const char *name, LDAPHostEnt *result, + char *buffer, int buflen, int *statusp, void *extradata) +{ + int h_errno; + LDAPHostEnt prhent; + + if (!statusp || ( *statusp = prldap_switch_gethostbyname_r(name, + &prhent, buffer, buflen, &h_errno )) == PR_FAILURE) { + /* + * If we got here, it means that we are not able to + * resolve the LDAP server name and so warn the system + * adminstrator accordingly. + */ + syslog(LOG_WARNING, "libldap: server name \"%s\" could not " + "be resolved", name); + return (NULL); + } + + return (prldap_convert_hostent(result, &prhent)); +} + +#endif /* _SOLARIS_SDK */ diff --git a/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-error.c b/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-error.c new file mode 100644 index 0000000000..83f41bb54c --- /dev/null +++ b/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-error.c @@ -0,0 +1,229 @@ +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998-1999 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +/* + * Utilities for manageing the relationship between NSPR errors and + * OS (errno-style) errors. + * + * The overall strategy used is to map NSPR errors into OS errors. + */ + +#include "ldappr-int.h" + + +void +prldap_set_system_errno( int oserrno ) +{ + PR_SetError( PR_UNKNOWN_ERROR, oserrno ); +} + + +int +prldap_get_system_errno( void ) +{ + return( PR_GetOSError()); +} + +/* + * Retrieve the NSPR error number, convert to a system error code, and return + * the result. + */ +struct prldap_errormap_entry { + PRInt32 erm_nspr; /* NSPR error code */ + int erm_system; /* corresponding system error code */ +}; + +/* XXX: not sure if this extra mapping for Windows is good or correct */ +#ifdef _WINDOWS +#ifndef ENOTSUP +#define ENOTSUP -1 +#endif +#ifndef ETIMEDOUT +#define ETIMEDOUT WSAETIMEDOUT +#endif +#ifndef EADDRNOTAVAIL +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#endif +#ifndef EAFNOSUPPORT +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#endif +#ifndef EISCONN +#define EISCONN WSAEISCONN +#endif +#ifndef EADDRINUSE +#define EADDRINUSE WSAEADDRINUSE +#endif +#ifndef ECONNREFUSED +#define ECONNREFUSED WSAECONNREFUSED +#endif +#ifndef EHOSTUNREACH +#define EHOSTUNREACH WSAEHOSTUNREACH +#endif +#ifndef ENOTCONN +#define ENOTCONN WSAENOTCONN +#endif +#ifndef ENOTSOCK +#define ENOTSOCK WSAENOTSOCK +#endif +#ifndef EPROTOTYPE +#define EPROTOTYPE WSAEPROTOTYPE +#endif +#ifndef EOPNOTSUPP +#define EOPNOTSUPP WSAEOPNOTSUPP +#endif +#ifndef EPROTONOSUPPORT +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#endif +#ifndef EOVERFLOW +#define EOVERFLOW -1 +#endif +#ifndef ECONNRESET +#define ECONNRESET WSAECONNRESET +#endif +#ifndef ELOOP +#define ELOOP WSAELOOP +#endif +#ifndef ENOTBLK +#define ENOTBLK -1 +#endif +#ifndef ETXTBSY +#define ETXTBSY -1 +#endif +#ifndef ENETDOWN +#define ENETDOWN WSAENETDOWN +#endif +#ifndef ESHUTDOWN +#define ESHUTDOWN WSAESHUTDOWN +#endif +#ifndef ECONNABORTED +#define ECONNABORTED WSAECONNABORTED +#endif +#endif /* _WINDOWS */ + +/* XXX: need to verify that the -1 entries are correct (no mapping) */ +static struct prldap_errormap_entry prldap_errormap[] = { + { PR_OUT_OF_MEMORY_ERROR, ENOMEM }, + { PR_BAD_DESCRIPTOR_ERROR, EBADF }, + { PR_WOULD_BLOCK_ERROR, EAGAIN }, + { PR_ACCESS_FAULT_ERROR, EFAULT }, + { PR_INVALID_METHOD_ERROR, EINVAL }, /* XXX: correct mapping ? */ + { PR_ILLEGAL_ACCESS_ERROR, EACCES }, /* XXX: correct mapping ? */ + { PR_UNKNOWN_ERROR, -1 }, + { PR_PENDING_INTERRUPT_ERROR, -1 }, + { PR_NOT_IMPLEMENTED_ERROR, ENOTSUP }, + { PR_IO_ERROR, EIO }, + { PR_IO_TIMEOUT_ERROR, ETIMEDOUT }, /* XXX: correct mapping ? */ + { PR_IO_PENDING_ERROR, -1 }, + { PR_DIRECTORY_OPEN_ERROR, ENOTDIR }, + { PR_INVALID_ARGUMENT_ERROR, EINVAL }, + { PR_ADDRESS_NOT_AVAILABLE_ERROR, EADDRNOTAVAIL }, + { PR_ADDRESS_NOT_SUPPORTED_ERROR, EAFNOSUPPORT }, + { PR_IS_CONNECTED_ERROR, EISCONN }, + { PR_BAD_ADDRESS_ERROR, EFAULT }, /* XXX: correct mapping ? */ + { PR_ADDRESS_IN_USE_ERROR, EADDRINUSE }, + { PR_CONNECT_REFUSED_ERROR, ECONNREFUSED }, + { PR_NETWORK_UNREACHABLE_ERROR, EHOSTUNREACH }, + { PR_CONNECT_TIMEOUT_ERROR, ETIMEDOUT }, + { PR_NOT_CONNECTED_ERROR, ENOTCONN }, + { PR_LOAD_LIBRARY_ERROR, -1 }, + { PR_UNLOAD_LIBRARY_ERROR, -1 }, + { PR_FIND_SYMBOL_ERROR, -1 }, + { PR_INSUFFICIENT_RESOURCES_ERROR, -1 }, + { PR_DIRECTORY_LOOKUP_ERROR, -1 }, + { PR_TPD_RANGE_ERROR, -1 }, + { PR_PROC_DESC_TABLE_FULL_ERROR, -1 }, + { PR_SYS_DESC_TABLE_FULL_ERROR, -1 }, + { PR_NOT_SOCKET_ERROR, ENOTSOCK }, + { PR_NOT_TCP_SOCKET_ERROR, EPROTOTYPE }, + { PR_SOCKET_ADDRESS_IS_BOUND_ERROR, -1 }, + { PR_NO_ACCESS_RIGHTS_ERROR, EACCES }, /* XXX: correct mapping ? */ + { PR_OPERATION_NOT_SUPPORTED_ERROR, EOPNOTSUPP }, + { PR_PROTOCOL_NOT_SUPPORTED_ERROR, EPROTONOSUPPORT }, + { PR_REMOTE_FILE_ERROR, -1 }, +#if defined(OSF1) + { PR_BUFFER_OVERFLOW_ERROR, -1 }, +#else + { PR_BUFFER_OVERFLOW_ERROR, EOVERFLOW }, +#endif /* OSF1 */ + { PR_CONNECT_RESET_ERROR, ECONNRESET }, + { PR_RANGE_ERROR, ERANGE }, + { PR_DEADLOCK_ERROR, EDEADLK }, +#if defined(HPUX11) || defined(AIX4_3) || defined(OSF1) + { PR_FILE_IS_LOCKED_ERROR, -1 }, /* XXX: correct mapping ? */ +#else + { PR_FILE_IS_LOCKED_ERROR, EDEADLOCK }, /* XXX: correct mapping ? */ +#endif /* HPUX11 */ + { PR_FILE_TOO_BIG_ERROR, EFBIG }, + { PR_NO_DEVICE_SPACE_ERROR, ENOSPC }, + { PR_PIPE_ERROR, EPIPE }, + { PR_NO_SEEK_DEVICE_ERROR, ESPIPE }, + { PR_IS_DIRECTORY_ERROR, EISDIR }, + { PR_LOOP_ERROR, ELOOP }, + { PR_NAME_TOO_LONG_ERROR, ENAMETOOLONG }, + { PR_FILE_NOT_FOUND_ERROR, ENOENT }, + { PR_NOT_DIRECTORY_ERROR, ENOTDIR }, + { PR_READ_ONLY_FILESYSTEM_ERROR, EROFS }, + { PR_DIRECTORY_NOT_EMPTY_ERROR, ENOTEMPTY }, + { PR_FILESYSTEM_MOUNTED_ERROR, EBUSY }, + { PR_NOT_SAME_DEVICE_ERROR, EXDEV }, + { PR_DIRECTORY_CORRUPTED_ERROR, -1 }, + { PR_FILE_EXISTS_ERROR, EEXIST }, + { PR_MAX_DIRECTORY_ENTRIES_ERROR, -1 }, + { PR_INVALID_DEVICE_STATE_ERROR, ENOTBLK }, /* XXX: correct mapping ? */ + { PR_DEVICE_IS_LOCKED_ERROR, -2 }, + { PR_NO_MORE_FILES_ERROR, ENFILE }, + { PR_END_OF_FILE_ERROR, -1 }, + { PR_FILE_SEEK_ERROR, ESPIPE }, /* XXX: correct mapping ? */ + { PR_FILE_IS_BUSY_ERROR, ETXTBSY }, + { PR_OPERATION_ABORTED_ERROR, -1 }, + { PR_IN_PROGRESS_ERROR, -1 }, + { PR_ALREADY_INITIATED_ERROR, -1 }, + { PR_GROUP_EMPTY_ERROR, -1 }, + { PR_INVALID_STATE_ERROR, -1 }, + { PR_NETWORK_DOWN_ERROR, ENETDOWN }, + { PR_SOCKET_SHUTDOWN_ERROR, ESHUTDOWN }, + { PR_CONNECT_ABORTED_ERROR, ECONNABORTED }, + { PR_HOST_UNREACHABLE_ERROR, EHOSTUNREACH }, + { PR_MAX_ERROR, -1 }, +}; + + +int +prldap_prerr2errno( void ) +{ + int oserr, i; + PRInt32 nsprerr; + + nsprerr = PR_GetError(); + + oserr = -1; /* unknown */ + for ( i = 0; prldap_errormap[i].erm_nspr != PR_MAX_ERROR; ++i ) { + if ( prldap_errormap[i].erm_nspr == nsprerr ) { + oserr = prldap_errormap[i].erm_system; + break; + } + } + + return( oserr ); +} diff --git a/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-int.h b/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-int.h new file mode 100644 index 0000000000..79fbeff849 --- /dev/null +++ b/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-int.h @@ -0,0 +1,113 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + + +/* + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998-1999 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +/* + * Internal header for libprldap -- glue NSPR (Netscape Portable Runtime) + * to libldap. + * + */ + +#include <ldap.h> +#include <nspr.h> +#include <ldappr.h> + +#include <errno.h> +#include <string.h> + +#ifdef _SOLARIS_SDK +#include "solaris-int.h" +#endif + +/* + * All of the sockets we use are IPv6 capable. + * Change the following #define to PR_AF_INET to support IPv4 only. + */ +#define PRLDAP_DEFAULT_ADDRESS_FAMILY PR_AF_INET6 + +/* + * Data structures: + */ + +/* data structure that populates the I/O callback session arg. */ +typedef struct lextiof_session_private { + PRPollDesc *prsess_pollds; /* for poll callback */ + int prsess_pollds_count; /* # of elements in pollds */ + int prsess_io_max_timeout; /* in milliseconds */ + void *prsess_appdata; /* application specific data */ +} PRLDAPIOSessionArg; + +/* data structure that populates the I/O callback socket-specific arg. */ +typedef struct lextiof_socket_private { + PRFileDesc *prsock_prfd; /* associated NSPR file desc. */ + int prsock_io_max_timeout; /* in milliseconds */ + void *prsock_appdata; /* application specific data */ +} PRLDAPIOSocketArg; + + +/* + * Function prototypes: + */ + +/* + * From ldapprio.c: + */ +int prldap_install_io_functions( LDAP *ld, int shared ); +int prldap_session_arg_from_ld( LDAP *ld, PRLDAPIOSessionArg **sessargpp ); +int prldap_set_io_max_timeout( PRLDAPIOSessionArg *prsessp, + int io_max_timeout ); +int prldap_get_io_max_timeout( PRLDAPIOSessionArg *prsessp, + int *io_max_timeoutp ); +int prldap_socket_arg_from_ld( LDAP *ld, PRLDAPIOSocketArg **sockargpp ); + + +/* + * From ldapprthreads.c: + */ +int prldap_install_thread_functions( LDAP *ld, int shared ); +int prldap_thread_new_handle( LDAP *ld, void *sessionarg ); +void prldap_thread_dispose_handle( LDAP *ld, void *sessionarg ); + + +/* + * From ldapprdns.c: + */ +int prldap_install_dns_functions( LDAP *ld ); +#ifdef _SOLARIS_SDK +int prldap_x_install_dns_skipdb( LDAP *ld, const char *skip ); +#endif + + + +/* + * From ldapprerror.c: + */ +void prldap_set_system_errno( int e ); +int prldap_get_system_errno( void ); +int prldap_prerr2errno( void ); diff --git a/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-io.c b/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-io.c new file mode 100644 index 0000000000..824a8ca212 --- /dev/null +++ b/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-io.c @@ -0,0 +1,721 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + + +/* + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998-1999 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +/* + * Extended I/O callback functions for libldap that use + * NSPR (Netscape Portable Runtime) I/O. + * + * High level strategy: we use the socket-specific arg to hold our own data + * structure that includes the NSPR file handle (PRFileDesc *), among other + * useful information. We use the default argument to hold an LDAP session + * handle specific data structure. + */ + +#include "ldappr-int.h" +#include <string.h> + +#define PRLDAP_POLL_ARRAY_GROWTH 5 /* grow arrays 5 elements at a time */ + +/* + * Local function prototypes: + */ +static PRIntervalTime prldap_timeout2it( int ms_timeout, int ms_maxtimeout ); +static int LDAP_CALLBACK prldap_read( int s, void *buf, int bufsize, + struct lextiof_socket_private *socketarg ); +static int LDAP_CALLBACK prldap_write( int s, const void *buf, int len, + struct lextiof_socket_private *socketarg ); +static int LDAP_CALLBACK prldap_poll( LDAP_X_PollFD fds[], int nfds, + int timeout, struct lextiof_session_private *sessionarg ); +static int LDAP_CALLBACK prldap_connect( const char *hostlist, int defport, + int timeout, unsigned long options, + struct lextiof_session_private *sessionarg, + struct lextiof_socket_private **socketargp +#ifdef _SOLARIS_SDK + , void **dhost ); +#else + ); +#endif /* _SOLARIS_SDK */ +static int LDAP_CALLBACK prldap_close( int s, + struct lextiof_socket_private *socketarg ); +static int LDAP_CALLBACK prldap_newhandle( LDAP *ld, + struct lextiof_session_private *sessionarg ); +static void LDAP_CALLBACK prldap_disposehandle( LDAP *ld, + struct lextiof_session_private *sessionarg ); +static int LDAP_CALLBACK prldap_shared_newhandle( LDAP *ld, + struct lextiof_session_private *sessionarg ); +static void LDAP_CALLBACK prldap_shared_disposehandle( LDAP *ld, + struct lextiof_session_private *sessionarg ); +static PRLDAPIOSessionArg *prldap_session_arg_alloc( void ); +static void prldap_session_arg_free( PRLDAPIOSessionArg **prsesspp ); +static PRLDAPIOSocketArg *prldap_socket_arg_alloc( PRLDAPIOSessionArg *sessionarg ); +static void prldap_socket_arg_free( PRLDAPIOSocketArg **prsockpp ); +static void *prldap_safe_realloc( void *ptr, PRUint32 size ); + + + +/* + * Local macros: + */ +/* given a socket-specific arg, return the corresponding PRFileDesc * */ +#define PRLDAP_GET_PRFD( socketarg ) \ + (((PRLDAPIOSocketArg *)(socketarg))->prsock_prfd) + +/* + * Static variables. + */ +static int prldap_default_io_max_timeout = LDAP_X_IO_TIMEOUT_NO_TIMEOUT; + +/* + * Install NSPR I/O functions into ld (if ld is NULL, they are installed + * as the default functions for new LDAP * handles). + * + * Returns 0 if all goes well and -1 if not. + */ +int +prldap_install_io_functions( LDAP *ld, int shared ) +{ + struct ldap_x_ext_io_fns iofns; + + memset( &iofns, 0, sizeof(iofns)); + iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE; + iofns.lextiof_read = prldap_read; + iofns.lextiof_write = prldap_write; + iofns.lextiof_poll = prldap_poll; + iofns.lextiof_connect = prldap_connect; + iofns.lextiof_close = prldap_close; + if ( shared ) { + iofns.lextiof_newhandle = prldap_shared_newhandle; + iofns.lextiof_disposehandle = prldap_shared_disposehandle; + } else { + iofns.lextiof_newhandle = prldap_newhandle; + iofns.lextiof_disposehandle = prldap_disposehandle; + } + if ( NULL != ld ) { + /* + * If we are dealing with a real ld, we allocate the session specific + * data structure now. If not allocated here, it will be allocated + * inside prldap_newhandle() or prldap_shared_newhandle(). + */ + if ( NULL == + ( iofns.lextiof_session_arg = prldap_session_arg_alloc())) { + ldap_set_lderrno( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( -1 ); + } + } else { + iofns.lextiof_session_arg = NULL; + } + + if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, &iofns ) != 0 ) { + prldap_session_arg_free( + (PRLDAPIOSessionArg **) &iofns.lextiof_session_arg ); + return( -1 ); + } + + return( 0 ); +} + + +static PRIntervalTime +prldap_timeout2it( int ms_timeout, int ms_maxtimeout ) +{ + PRIntervalTime prit; + + if ( LDAP_X_IO_TIMEOUT_NO_WAIT == ms_timeout ) { + prit = PR_INTERVAL_NO_WAIT; + } else if ( LDAP_X_IO_TIMEOUT_NO_TIMEOUT == ms_timeout ) { + prit = PR_INTERVAL_NO_TIMEOUT; + } else { + prit = PR_MillisecondsToInterval( ms_timeout ); + } + + /* cap at maximum I/O timeout */ + if ( LDAP_X_IO_TIMEOUT_NO_WAIT == ms_maxtimeout ) { + prit = LDAP_X_IO_TIMEOUT_NO_WAIT; + } else if ( LDAP_X_IO_TIMEOUT_NO_TIMEOUT != ms_maxtimeout ) { + if ( LDAP_X_IO_TIMEOUT_NO_TIMEOUT == ms_timeout || + ms_timeout > ms_maxtimeout ) { + prit = PR_MillisecondsToInterval( ms_maxtimeout ); + } + } + +#ifdef PRLDAP_DEBUG + if ( PR_INTERVAL_NO_WAIT == prit ) { + fprintf( stderr, "prldap_timeout2it: NO_WAIT\n" ); + } else if ( PR_INTERVAL_NO_TIMEOUT == prit ) { + fprintf( stderr, "prldap_timeout2it: NO_TIMEOUT\n" ); + } else { + fprintf( stderr, "prldap_timeout2it: %dms\n", + PR_IntervalToMilliseconds(prit)); + } +#endif /* PRLDAP_DEBUG */ + + return( prit ); +} + + +static int LDAP_CALLBACK +prldap_read( int s, void *buf, int bufsize, + struct lextiof_socket_private *socketarg ) +{ + PRIntervalTime prit; + + prit = prldap_timeout2it( LDAP_X_IO_TIMEOUT_NO_TIMEOUT, + socketarg->prsock_io_max_timeout ); + return( PR_Recv( PRLDAP_GET_PRFD(socketarg), buf, bufsize, 0, prit )); +} + + +static int LDAP_CALLBACK +prldap_write( int s, const void *buf, int len, + struct lextiof_socket_private *socketarg ) +{ + PRIntervalTime prit; + + prit = prldap_timeout2it( LDAP_X_IO_TIMEOUT_NO_TIMEOUT, + socketarg->prsock_io_max_timeout ); + + /* + * Note the 4th parameter (flags) to PR_Send() has been obsoleted and + * must always be 0 + */ + return( PR_Send( PRLDAP_GET_PRFD(socketarg), buf, len, 0, prit )); +} + + +struct prldap_eventmap_entry { + PRInt16 evm_nspr; /* corresponding NSPR PR_Poll() event */ + int evm_ldap; /* LDAP poll event */ +}; + +static struct prldap_eventmap_entry prldap_eventmap[] = { + { PR_POLL_READ, LDAP_X_POLLIN }, + { PR_POLL_EXCEPT, LDAP_X_POLLPRI }, + { PR_POLL_WRITE, LDAP_X_POLLOUT }, + { PR_POLL_ERR, LDAP_X_POLLERR }, + { PR_POLL_HUP, LDAP_X_POLLHUP }, + { PR_POLL_NVAL, LDAP_X_POLLNVAL }, +}; + +#define PRLDAP_EVENTMAP_ENTRIES \ + sizeof(prldap_eventmap)/sizeof(struct prldap_eventmap_entry ) + +static int LDAP_CALLBACK +prldap_poll( LDAP_X_PollFD fds[], int nfds, int timeout, + struct lextiof_session_private *sessionarg ) +{ + PRLDAPIOSessionArg *prsessp = sessionarg; + PRPollDesc *pds; + int i, j, rc; + + if ( NULL == prsessp ) { + prldap_set_system_errno( EINVAL ); + return( -1 ); + } + + /* allocate or resize NSPR poll descriptor array */ + if ( prsessp->prsess_pollds_count < nfds ) { + pds = prldap_safe_realloc( prsessp->prsess_pollds, + ( nfds + PRLDAP_POLL_ARRAY_GROWTH ) + * sizeof( PRPollDesc )); + if ( NULL == pds ) { + prldap_set_system_errno( prldap_prerr2errno()); + return( -1 ); + } + prsessp->prsess_pollds = pds; + prsessp->prsess_pollds_count = nfds + PRLDAP_POLL_ARRAY_GROWTH; + } else { + pds = prsessp->prsess_pollds; + } + + /* populate NSPR poll info. based on LDAP info. */ + for ( i = 0; i < nfds; ++i ) { + if ( NULL == fds[i].lpoll_socketarg ) { + pds[i].fd = NULL; + } else { + pds[i].fd = PRLDAP_GET_PRFD( fds[i].lpoll_socketarg ); + } + pds[i].in_flags = pds[i].out_flags = 0; + if ( fds[i].lpoll_fd >= 0 ) { + for ( j = 0; j < PRLDAP_EVENTMAP_ENTRIES; ++j ) { + if (( fds[i].lpoll_events & prldap_eventmap[j].evm_ldap ) + != 0 ) { + pds[i].in_flags |= prldap_eventmap[j].evm_nspr; + } + } + } + fds[i].lpoll_revents = 0; /* clear revents */ + } + + /* call PR_Poll() to do the real work */ + rc = PR_Poll( pds, nfds, + prldap_timeout2it( timeout, prsessp->prsess_io_max_timeout )); + + /* populate LDAP info. based on NSPR results */ + for ( i = 0; i < nfds; ++i ) { + if ( pds[i].fd != NULL ) { + for ( j = 0; j < PRLDAP_EVENTMAP_ENTRIES; ++j ) { + if (( pds[i].out_flags & prldap_eventmap[j].evm_nspr ) + != 0 ) { + fds[i].lpoll_revents |= prldap_eventmap[j].evm_ldap; + } + } + } + } + + return( rc ); +} + + +/* + * Utility function to try one TCP connect() + * Returns 1 if successful and -1 if not. Sets the NSPR fd inside prsockp. + */ +static int +prldap_try_one_address( struct lextiof_socket_private *prsockp, + PRNetAddr *addrp, int port, int timeout, unsigned long options ) +{ + /* + * Set up address and open a TCP socket: + */ + if ( PR_SUCCESS != PR_SetNetAddr( PR_IpAddrNull, /* don't touch IP addr. */ + PRLDAP_DEFAULT_ADDRESS_FAMILY, (PRUint16)port, addrp )) { + return( -1 ); + } + + if (( prsockp->prsock_prfd = PR_OpenTCPSocket( + PRLDAP_DEFAULT_ADDRESS_FAMILY )) == NULL ) { + return( -1 ); + } + + /* + * Set nonblocking option if requested: + */ + if ( 0 != ( options & LDAP_X_EXTIOF_OPT_NONBLOCKING )) { + PRSocketOptionData optdata; + + optdata.option = PR_SockOpt_Nonblocking; + optdata.value.non_blocking = PR_TRUE; + if ( PR_SetSocketOption( prsockp->prsock_prfd, &optdata ) + != PR_SUCCESS ) { + prldap_set_system_errno( prldap_prerr2errno()); + PR_Close( prsockp->prsock_prfd ); + return( -1 ); + } + } + +#ifdef PRLDAP_DEBUG + { + char buf[ 256 ], *p, *fmtstr; + + if ( PR_SUCCESS != PR_NetAddrToString( addrp, buf, sizeof(buf ))) { + strcpy( buf, "conversion failed!" ); + } + if ( strncmp( buf, "::ffff:", 7 ) == 0 ) { + /* IPv4 address mapped into IPv6 address space */ + p = buf + 7; + fmtstr = "prldap_try_one_address(): Trying %s:%d...\n"; + } else { + p = buf; + fmtstr = "prldap_try_one_address(): Trying [%s]:%d...\n"; + } + fprintf( stderr, fmtstr, p, PR_ntohs( addrp->ipv6.port )); + } +#endif /* PRLDAP_DEBUG */ + + /* + * Try to open the TCP connection itself: + */ + if ( PR_SUCCESS != PR_Connect( prsockp->prsock_prfd, addrp, + prldap_timeout2it( timeout, prsockp->prsock_io_max_timeout ))) { + PR_Close( prsockp->prsock_prfd ); + prsockp->prsock_prfd = NULL; + return( -1 ); + } + +#ifdef PRLDAP_DEBUG + fputs( "prldap_try_one_address(): Connected.\n", stderr ); +#endif /* PRLDAP_DEBUG */ + + /* + * Success. Return a valid file descriptor (1 is always valid) + */ + return( 1 ); +} + + +/* + * XXXmcs: At present, this code ignores the timeout when doing DNS lookups. + */ +static int LDAP_CALLBACK +prldap_connect( const char *hostlist, int defport, int timeout, + unsigned long options, struct lextiof_session_private *sessionarg, + struct lextiof_socket_private **socketargp +#ifdef _SOLARIS_SDK + , void **dhost ) +#else + ) +#endif /* _SOLARIS_SDK */ +{ + int rc, parse_err, port; + char *host, hbuf[ PR_NETDB_BUF_SIZE ]; + struct ldap_x_hostlist_status *status; + struct lextiof_socket_private *prsockp; + PRNetAddr addr; + PRHostEnt hent; +#ifdef _SOLARIS_SDK + char *hostname = NULL; + char *nsldapi_strdup(char *); +#endif /* _SOLARIS_SDK */ + + if ( 0 != ( options & LDAP_X_EXTIOF_OPT_SECURE )) { + prldap_set_system_errno( EINVAL ); + return( -1 ); + } + + if ( NULL == ( prsockp = prldap_socket_arg_alloc( sessionarg ))) { + prldap_set_system_errno( prldap_prerr2errno()); + return( -1 ); + } + + rc = -1; /* pessimistic */ + for ( parse_err = ldap_x_hostlist_first( hostlist, defport, &host, &port, + &status ); + rc < 0 && LDAP_SUCCESS == parse_err && NULL != host; + parse_err = ldap_x_hostlist_next( &host, &port, status )) { + + if ( PR_SUCCESS == PR_StringToNetAddr( host, &addr )) { + + if ( PRLDAP_DEFAULT_ADDRESS_FAMILY == PR_AF_INET6 && + PR_AF_INET == PR_NetAddrFamily( &addr )) { + PRUint32 ipv4ip = addr.inet.ip; + memset( &addr, 0, sizeof(addr)); + PR_ConvertIPv4AddrToIPv6( ipv4ip, &addr.ipv6.ip ); + addr.ipv6.family = PR_AF_INET6; + + } + rc = prldap_try_one_address( prsockp, &addr, port, + timeout, options ); + } else { + if ( PR_SUCCESS == PR_GetIPNodeByName( host, + PRLDAP_DEFAULT_ADDRESS_FAMILY, PR_AI_DEFAULT | PR_AI_ALL, hbuf, + sizeof( hbuf ), &hent )) { + PRIntn enumIndex = 0; + + while ( rc < 0 && ( enumIndex = PR_EnumerateHostEnt( + enumIndex, &hent, (PRUint16)port, &addr )) > 0 ) { + rc = prldap_try_one_address( prsockp, &addr, port, + timeout, options ); + } + } + } + +#ifdef _SOLARIS_SDK + if ( NULL != hostname ) ldap_memfree(hostname); + hostname = nsldapi_strdup(host); +#endif /* _SOLARIS_SDK */ + ldap_memfree( host ); + } + + ldap_x_hostlist_statusfree( status ); + + if ( rc < 0 ) { + prldap_set_system_errno( prldap_prerr2errno()); + prldap_socket_arg_free( &prsockp ); + } else { + *socketargp = prsockp; + } + +#ifdef _SOLARIS_SDK + if ( NULL != hostname && NULL != dhost ) *dhost = hostname; + else if ( NULL != hostname ) ldap_memfree(hostname); +#endif /* _SOLARIS_SDK */ + + return( rc ); +} + + +static int LDAP_CALLBACK +prldap_close( int s, struct lextiof_socket_private *socketarg ) +{ + int rc; + + rc = 0; + if ( PR_Close( PRLDAP_GET_PRFD(socketarg)) != PR_SUCCESS ) { + rc = -1; + prldap_set_system_errno( prldap_prerr2errno()); + } + prldap_socket_arg_free( &socketarg ); + + return( rc ); +} + + +/* + * LDAP session handle creation callback. + * + * Allocate a session argument if not already done, and then call the + * thread's new handle function. + */ +static int LDAP_CALLBACK +prldap_newhandle( LDAP *ld, struct lextiof_session_private *sessionarg ) +{ + + if ( NULL == sessionarg ) { + struct ldap_x_ext_io_fns iofns; + + memset( &iofns, 0, sizeof(iofns)); + iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE; + if ( ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, + (void *)&iofns ) < 0 ) { + return( ldap_get_lderrno( ld, NULL, NULL )); + } + if ( NULL == + ( iofns.lextiof_session_arg = prldap_session_arg_alloc())) { + return( LDAP_NO_MEMORY ); + } + if ( ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, + (void *)&iofns ) < 0 ) { + return( ldap_get_lderrno( ld, NULL, NULL )); + } + } + + return( LDAP_SUCCESS ); +} + + +/* only called/installed if shared is non-zero. */ +static int LDAP_CALLBACK +prldap_shared_newhandle( LDAP *ld, struct lextiof_session_private *sessionarg ) +{ + int rc; + + if (( rc = prldap_newhandle( ld, sessionarg )) == LDAP_SUCCESS ) { + rc = prldap_thread_new_handle( ld, sessionarg ); + } + + return( rc ); +} + + +static void LDAP_CALLBACK +prldap_disposehandle( LDAP *ld, struct lextiof_session_private *sessionarg ) +{ + prldap_session_arg_free( &sessionarg ); +} + + +/* only called/installed if shared is non-zero */ +static void LDAP_CALLBACK +prldap_shared_disposehandle( LDAP *ld, + struct lextiof_session_private *sessionarg ) +{ + prldap_thread_dispose_handle( ld, sessionarg ); + prldap_disposehandle( ld, sessionarg ); +} + + +/* + * Allocate a session argument. + */ +static PRLDAPIOSessionArg * +prldap_session_arg_alloc( void ) +{ + PRLDAPIOSessionArg *prsessp; + + prsessp = PR_Calloc( 1, sizeof( PRLDAPIOSessionArg )); + + if ( NULL != prsessp ) { + /* copy global defaults to the new session handle */ + prsessp->prsess_io_max_timeout = prldap_default_io_max_timeout; + } + + return( prsessp ); +} + + +static void +prldap_session_arg_free( PRLDAPIOSessionArg **prsesspp ) +{ + if ( NULL != prsesspp && NULL != *prsesspp ) { + if ( NULL != (*prsesspp)->prsess_pollds ) { + PR_Free( (*prsesspp)->prsess_pollds ); + (*prsesspp)->prsess_pollds = NULL; + } + PR_Free( *prsesspp ); + *prsesspp = NULL; + } +} + + +/* + * Given an LDAP session handle, retrieve a session argument. + * Returns an LDAP error code. + */ +int +prldap_session_arg_from_ld( LDAP *ld, PRLDAPIOSessionArg **sessargpp ) +{ + struct ldap_x_ext_io_fns iofns; + + if ( NULL == ld || NULL == sessargpp ) { + /* XXXmcs: NULL ld's are not supported */ + ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + memset( &iofns, 0, sizeof(iofns)); + iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE; + if ( ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, (void *)&iofns ) < 0 ) { + return( ldap_get_lderrno( ld, NULL, NULL )); + } + + if ( NULL == iofns.lextiof_session_arg ) { + ldap_set_lderrno( ld, LDAP_LOCAL_ERROR, NULL, NULL ); + return( LDAP_LOCAL_ERROR ); + } + + *sessargpp = iofns.lextiof_session_arg; + return( LDAP_SUCCESS ); +} + + +/* + * Given an LDAP session handle, retrieve a socket argument. + * Returns an LDAP error code. + */ +int +prldap_socket_arg_from_ld( LDAP *ld, PRLDAPIOSocketArg **sockargpp ) +{ + Sockbuf *sbp; + struct lber_x_ext_io_fns extiofns; + + if ( NULL == ld || NULL == sockargpp ) { + /* XXXmcs: NULL ld's are not supported */ + ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + if ( ldap_get_option( ld, LDAP_X_OPT_SOCKBUF, (void *)&sbp ) < 0 ) { + return( ldap_get_lderrno( ld, NULL, NULL )); + } + + memset( &extiofns, 0, sizeof(extiofns)); + extiofns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE; + if ( ber_sockbuf_get_option( sbp, LBER_SOCKBUF_OPT_EXT_IO_FNS, + (void *)&extiofns ) < 0 ) { + return( ldap_get_lderrno( ld, NULL, NULL )); + } + + if ( NULL == extiofns.lbextiofn_socket_arg ) { + ldap_set_lderrno( ld, LDAP_LOCAL_ERROR, NULL, NULL ); + return( LDAP_LOCAL_ERROR ); + } + + *sockargpp = extiofns.lbextiofn_socket_arg; + return( LDAP_SUCCESS ); +} + + +/* + * Allocate a socket argument. + */ +static PRLDAPIOSocketArg * +prldap_socket_arg_alloc( PRLDAPIOSessionArg *sessionarg ) +{ + PRLDAPIOSocketArg *prsockp; + + prsockp = PR_Calloc( 1, sizeof( PRLDAPIOSocketArg )); + + if ( NULL != prsockp && NULL != sessionarg ) { + /* copy socket defaults from the session */ + prsockp->prsock_io_max_timeout = sessionarg->prsess_io_max_timeout; + } + + return( prsockp ); +} + + +static void +prldap_socket_arg_free( PRLDAPIOSocketArg **prsockpp ) +{ + if ( NULL != prsockpp && NULL != *prsockpp ) { + PR_Free( *prsockpp ); + *prsockpp = NULL; + } +} + + +static void * +prldap_safe_realloc( void *ptr, PRUint32 size ) +{ + void *p; + + if ( NULL == ptr ) { + p = PR_Malloc( size ); + } else { + p = PR_Realloc( ptr, size ); + } + + return( p ); +} + + + +/* returns an LDAP result code */ +int +prldap_set_io_max_timeout( PRLDAPIOSessionArg *prsessp, int io_max_timeout ) +{ + int rc = LDAP_SUCCESS; /* optimistic */ + + if ( NULL == prsessp ) { + prldap_default_io_max_timeout = io_max_timeout; + } else { + prsessp->prsess_io_max_timeout = io_max_timeout; + } + + return( rc ); +} + + +/* returns an LDAP result code */ +int +prldap_get_io_max_timeout( PRLDAPIOSessionArg *prsessp, int *io_max_timeoutp ) +{ + int rc = LDAP_SUCCESS; /* optimistic */ + + if ( NULL == io_max_timeoutp ) { + rc = LDAP_PARAM_ERROR; + } else if ( NULL == prsessp ) { + *io_max_timeoutp = prldap_default_io_max_timeout; + } else { + *io_max_timeoutp = prsessp->prsess_io_max_timeout; + } + + return( rc ); +} diff --git a/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-public.c b/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-public.c new file mode 100644 index 0000000000..ad36e53e04 --- /dev/null +++ b/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-public.c @@ -0,0 +1,382 @@ +/* + * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + + +/* + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998-1999 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +/* + * Public interface for libprldap -- use NSPR (Netscape Portable Runtime) + * I/O, threads, etc. with libldap. + * + */ + +#include "ldappr-int.h" + + +/* + * Function: prldap_init(). + * + * Create a new LDAP session handle, but with NSPR I/O, threading, and DNS + * functions installed. + * + * Pass a non-zero value for the 'shared' parameter if you plan to use + * this LDAP * handle from more than one thread. + * + * prldap_init() returns an LDAP session handle (or NULL if an error occurs). + */ +LDAP * LDAP_CALL +prldap_init( const char *defhost, int defport, int shared ) +{ + LDAP *ld; + + if (( ld = ldap_init( defhost, defport )) != NULL ) { + if ( prldap_install_routines( ld, shared ) != LDAP_SUCCESS ) { + prldap_set_system_errno( EINVAL ); /* XXXmcs: just a guess! */ + ldap_unbind( ld ); + ld = NULL; + } + } + + return( ld ); +} + + +/* + * Function: prldap_install_routines(). + * + * Install NSPR I/O, threading, and DNS functions so they will be used by + * 'ld'. + * + * If 'ld' is NULL, the functions are installed as the default functions + * for all new LDAP * handles). + * + * Pass a non-zero value for the 'shared' parameter if you plan to use + * this LDAP * handle from more than one thread. + * + * prldap_install_routines() returns an LDAP API error code (LDAP_SUCCESS + * if all goes well). + */ +int LDAP_CALL +prldap_install_routines( LDAP *ld, int shared ) +{ + + if ( prldap_install_io_functions( ld, shared ) != 0 + || prldap_install_thread_functions( ld, shared ) != 0 + || prldap_install_dns_functions( ld ) != 0 ) { + return( ldap_get_lderrno( ld, NULL, NULL )); + } + + return( LDAP_SUCCESS ); +} + + +#ifndef _SOLARIS_SDK /* Not used, left in to stay in sync with iplanet */ +/* + * Function: prldap_set_session_option(). + * + * Given an LDAP session handle or a session argument such is passed to + * SOCKET, POLL, NEWHANDLE, or DISPOSEHANDLE extended I/O callbacks, set + * an option that affects the prldap layer. + * + * If 'ld' and 'session" are both NULL, the option is set as the default + * for all new prldap sessions. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well). + */ +int LDAP_CALL +prldap_set_session_option( LDAP *ld, void *sessionarg, int option, ... ) +{ + int rc = LDAP_SUCCESS; /* optimistic */ + PRLDAPIOSessionArg *prsessp = NULL; + va_list ap; + + if ( NULL != ld ) { + if ( LDAP_SUCCESS != + ( rc = prldap_session_arg_from_ld( ld, &prsessp ))) { + return( rc ); + } + } else if ( NULL != sessionarg ) { + prsessp = (PRLDAPIOSessionArg *)sessionarg; + } + + va_start( ap, option ); + switch ( option ) { + case PRLDAP_OPT_IO_MAX_TIMEOUT: + rc = prldap_set_io_max_timeout( prsessp, va_arg( ap, int )); + break; + default: + rc = LDAP_PARAM_ERROR; + } + va_end( ap ); + + return( rc ); +} + + +/* + * Function: prldap_get_session_option(). + * + * Given an LDAP session handle or a session argument such is passed to + * SOCKET, POLL, NEWHANDLE, or DISPOSEHANDLE extended I/O callbacks, retrieve + * the setting for an option that affects the prldap layer. + * + * If 'ld' and 'session" are both NULL, the default option value for all new + * new prldap sessions is retrieved. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well). + */ +int LDAP_CALL prldap_get_session_option( LDAP *ld, void *sessionarg, + int option, ... ) +{ + int rc = LDAP_SUCCESS; /* optimistic */ + PRLDAPIOSessionArg *prsessp = NULL; + va_list ap; + + if ( NULL != ld ) { + if ( LDAP_SUCCESS != + ( rc = prldap_session_arg_from_ld( ld, &prsessp ))) { + return( rc ); + } + } else if ( NULL != sessionarg ) { + prsessp = (PRLDAPIOSessionArg *)sessionarg; + } + + va_start( ap, option ); + switch ( option ) { + case PRLDAP_OPT_IO_MAX_TIMEOUT: + rc = prldap_get_io_max_timeout( prsessp, va_arg( ap, int * )); + break; + default: + rc = LDAP_PARAM_ERROR; + } + va_end( ap ); + + return( rc ); +} +#endif /* !_SOLARIS_SDK */ + + +/* + * Function: prldap_set_session_info(). + * + * Given an LDAP session handle, set some application-specific data. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well). + */ +int LDAP_CALL +prldap_set_session_info( LDAP *ld, void *sessionarg, PRLDAPSessionInfo *seip ) +{ + int rc; + PRLDAPIOSessionArg *prsessp; + + if ( seip == NULL || PRLDAP_SESSIONINFO_SIZE != seip->seinfo_size ) { + ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + if ( NULL != ld ) { + if ( LDAP_SUCCESS != + ( rc = prldap_session_arg_from_ld( ld, &prsessp ))) { + return( rc ); + } + } else if ( NULL != sessionarg ) { + prsessp = (PRLDAPIOSessionArg *)sessionarg; + } else { + ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + prsessp->prsess_appdata = seip->seinfo_appdata; + return( LDAP_SUCCESS ); +} + + +/* + * Function: prldap_get_session_info(). + * + * Given an LDAP session handle, retrieve some application-specific data. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well, in + * which case the fields in the structure that seip points to are filled in). + */ +int LDAP_CALL +prldap_get_session_info( LDAP *ld, void *sessionarg, PRLDAPSessionInfo *seip ) +{ + int rc; + PRLDAPIOSessionArg *prsessp; + + if ( seip == NULL || PRLDAP_SESSIONINFO_SIZE != seip->seinfo_size ) { + ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + if ( NULL != ld ) { + if ( LDAP_SUCCESS != + ( rc = prldap_session_arg_from_ld( ld, &prsessp ))) { + return( rc ); + } + } else if ( NULL != sessionarg ) { + prsessp = (PRLDAPIOSessionArg *)sessionarg; + } else { + ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + seip->seinfo_appdata = prsessp->prsess_appdata; + return( LDAP_SUCCESS ); +} + + +/* + * Function: prldap_set_socket_info(). + * + * Given an integer fd and a void * argument such as those passed to the + * extended I/O callback functions, set socket specific information. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well). + * + * Note: it is only safe to change soinfo_prfd from within the SOCKET + * extended I/O callback function. + */ +int LDAP_CALL +prldap_set_socket_info( int fd, void *socketarg, PRLDAPSocketInfo *soip ) +{ + PRLDAPIOSocketArg *prsockp; + + if ( NULL == socketarg || NULL == soip || + PRLDAP_SOCKETINFO_SIZE != soip->soinfo_size ) { + return( LDAP_PARAM_ERROR ); + } + + prsockp = (PRLDAPIOSocketArg *)socketarg; + prsockp->prsock_prfd = soip->soinfo_prfd; + prsockp->prsock_appdata = soip->soinfo_appdata; + + return( LDAP_SUCCESS ); +} + + +/* + * Function: prldap_get_socket_info(). + * + * Given an integer fd and a void * argument such as those passed to the + * extended I/O callback functions, retrieve socket specific information. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well, in + * which case the fields in the structure that soip points to are filled in). + */ +int LDAP_CALL +prldap_get_socket_info( int fd, void *socketarg, PRLDAPSocketInfo *soip ) +{ + PRLDAPIOSocketArg *prsockp; + + if ( NULL == socketarg || NULL == soip || + PRLDAP_SOCKETINFO_SIZE != soip->soinfo_size ) { + return( LDAP_PARAM_ERROR ); + } + + prsockp = (PRLDAPIOSocketArg *)socketarg; + soip->soinfo_prfd = prsockp->prsock_prfd; + soip->soinfo_appdata = prsockp->prsock_appdata; + + return( LDAP_SUCCESS ); +} + +/* + * Function: prldap_get_default_socket_info(). + * + * Given an LDAP session handle, retrieve socket specific information. + * If ld is NULL, LDAP_PARAM_ERROR is returned. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well, in + * which case the fields in the structure that soip points to are filled in). + */ +int LDAP_CALL +prldap_get_default_socket_info( LDAP *ld, PRLDAPSocketInfo *soip ) +{ + int rc; + PRLDAPIOSocketArg *prsockp; + + + if ( NULL == soip || PRLDAP_SOCKETINFO_SIZE != soip->soinfo_size ) { + ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + if ( NULL != ld ) { + if ( LDAP_SUCCESS != + ( rc = prldap_socket_arg_from_ld( ld, &prsockp ))) { + return( rc ); + } + } else { + ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + soip->soinfo_prfd = prsockp->prsock_prfd; + soip->soinfo_appdata = prsockp->prsock_appdata; + + return( LDAP_SUCCESS ); +} + + +/* + * Function: prldap_set_default_socket_info(). + * + * Given an LDAP session handle, set socket specific information. + * If ld is NULL, LDAP_PARAM_ERROR is returned. + * + * Returns an LDAP API error code (LDAP_SUCCESS if all goes well, in + * which case the fields in the structure that soip points to are filled in). + */ +int LDAP_CALL +prldap_set_default_socket_info( LDAP *ld, PRLDAPSocketInfo *soip ) +{ + int rc; + PRLDAPIOSocketArg *prsockp; + + + if ( NULL == soip || PRLDAP_SOCKETINFO_SIZE != soip->soinfo_size ) { + ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + if ( NULL != ld ) { + if ( LDAP_SUCCESS != + ( rc = prldap_socket_arg_from_ld( ld, &prsockp ))) { + return( rc ); + } + } else { + ldap_set_lderrno( ld, LDAP_PARAM_ERROR, NULL, NULL ); + return( LDAP_PARAM_ERROR ); + } + + prsockp->prsock_prfd = soip->soinfo_prfd; + prsockp->prsock_appdata = soip->soinfo_appdata; + + return( LDAP_SUCCESS ); +} diff --git a/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-threads.c b/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-threads.c new file mode 100644 index 0000000000..b6b8a0e27a --- /dev/null +++ b/usr/src/lib/libldap5/sources/ldap/prldap/ldappr-threads.c @@ -0,0 +1,754 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + + +/* + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998-1999 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +/* + * Thread callback functions for libldap that use the NSPR (Netscape + * Portable Runtime) thread API. + * + */ + +#ifdef _SOLARIS_SDK +#include <thread.h> +#include <synch.h> +#include <prinit.h> +#include <prthread.h> +#include <syslog.h> +#include <string.h> +#include <sys/types.h> +#include <signal.h> +#include <errno.h> +extern int errno; +#endif /* _SOLARIS_SDK */ + +#include "ldappr-int.h" + +#ifndef _SOLARIS_SDK +/* + * Macros: + */ +/* + * Grow thread private data arrays 10 elements at a time. + */ +#define PRLDAP_TPD_ARRAY_INCREMENT 10 + +/* + * Structures and types: + */ +/* + * Structure used by libldap thread callbacks to maintain error information. + */ +typedef struct prldap_errorinfo { + int plei_lderrno; + char *plei_matched; + char *plei_errmsg; +} PRLDAP_ErrorInfo; + +/* + * Structure used to maintain thread-private data. At the present time, + * only error info. is thread-private. One of these structures is allocated + * for each thread. + */ +typedef struct prldap_tpd_header { + int ptpdh_tpd_count; /* # of data items allocated */ + void **ptpdh_dataitems; /* array of data items */ +} PRLDAP_TPDHeader; + +/* + * Structure used by associate a PRLDAP thread-private data index with an + * LDAP session handle. One of these exists for each active LDAP session + * handle. + */ +typedef struct prldap_tpd_map { + LDAP *prtm_ld; /* non-NULL if in use */ + PRUintn prtm_index; /* index into TPD array */ + struct prldap_tpd_map *prtm_next; +} PRLDAP_TPDMap; + +#ifdef _SOLARIS_SDK +extern mutex_t inited_mutex; +#endif /* _SOLARIS_SDK */ + +/* + * Static Variables: + */ +/* + * prldap_map_list points to all of the PRLDAP_TPDMap structures + * we have ever allocated. We recycle them as we open and close LDAP + * sessions. + */ +static PRLDAP_TPDMap *prldap_map_list = NULL; + + +/* + * The prldap_map_mutex is used to protect access to the prldap_map_list. + */ +static PRLock *prldap_map_mutex = NULL; + +/* + * The prldap_tpd_maxindex value is used to track the largest TPD array + * index we have used. + */ +static PRInt32 prldap_tpd_maxindex = -1; + +/* + * prldap_tpdindex is an NSPR thread private data index we use to + * maintain our own thread-private data. It is initialized inside + * prldap_init_tpd(). + */ +static PRUintn prldap_tpdindex = 0; + +/* + * The prldap_callonce_init_tpd structure is used by NSPR to ensure + * that prldap_init_tpd() is called at most once. + */ +static PRCallOnceType prldap_callonce_init_tpd = { 0, 0, 0 }; + + +/* + * Private function prototypes: + */ +static void prldap_set_ld_error( int err, char *matched, char *errmsg, + void *errorarg ); +static int prldap_get_ld_error( char **matchedp, char **errmsgp, + void *errorarg ); +#endif +static void *prldap_mutex_alloc( void ); +static void prldap_mutex_free( void *mutex ); +static int prldap_mutex_lock( void *mutex ); +static int prldap_mutex_unlock( void *mutex ); +static void *prldap_get_thread_id( void ); +#ifndef _SOLARIS_SDK +static PRStatus prldap_init_tpd( void ); +static PRLDAP_TPDMap *prldap_allocate_map( LDAP *ld ); +static void prldap_return_map( PRLDAP_TPDMap *map ); +static PRUintn prldap_new_tpdindex( void ); +static int prldap_set_thread_private( PRInt32 tpdindex, void *priv ); +static void *prldap_get_thread_private( PRInt32 tpdindex ); +static PRLDAP_TPDHeader *prldap_tsd_realloc( PRLDAP_TPDHeader *tsdhdr, + int maxindex ); +static void prldap_tsd_destroy( void *priv ); +#endif + + +/* + * Install NSPR thread functions into ld (if ld is NULL, they are installed + * as the default functions for new LDAP * handles). + * + * Returns 0 if all goes well and -1 if not. + */ +int +prldap_install_thread_functions( LDAP *ld, int shared ) +{ + struct ldap_thread_fns tfns; + struct ldap_extra_thread_fns xtfns; + +#ifndef _SOLARIS_SDK + if ( PR_CallOnce( &prldap_callonce_init_tpd, prldap_init_tpd ) + != PR_SUCCESS ) { + ldap_set_lderrno( ld, LDAP_LOCAL_ERROR, NULL, NULL ); + return( -1 ); + } +#endif /* _SOLARIS_SDK */ + + /* set thread function pointers */ + memset( &tfns, '\0', sizeof(struct ldap_thread_fns) ); + tfns.ltf_get_errno = prldap_get_system_errno; + tfns.ltf_set_errno = prldap_set_system_errno; + if ( shared ) { + tfns.ltf_mutex_alloc = prldap_mutex_alloc; + tfns.ltf_mutex_free = prldap_mutex_free; + tfns.ltf_mutex_lock = prldap_mutex_lock; + tfns.ltf_mutex_unlock = prldap_mutex_unlock; +#ifdef _SOLARIS_SDK + tfns.ltf_get_lderrno = NULL; + tfns.ltf_set_lderrno = NULL; +#else + tfns.ltf_get_lderrno = prldap_get_ld_error; + tfns.ltf_set_lderrno = prldap_set_ld_error; + if ( ld != NULL ) { + /* + * If this is a real ld (i.e., we are not setting the global + * defaults) allocate thread private data for error information. + * If ld is NULL we do not do this here but it is done in + * prldap_thread_new_handle(). + */ + if (( tfns.ltf_lderrno_arg = (void *)prldap_allocate_map( ld )) + == NULL ) { + return( -1 ); + } + } +#endif + } + + if ( ldap_set_option( ld, LDAP_OPT_THREAD_FN_PTRS, + (void *)&tfns ) != 0 ) { +#ifndef _SOLARIS_SDK + prldap_return_map( (PRLDAP_TPDMap *)tfns.ltf_lderrno_arg ); +#endif + return( -1 ); + } + + /* set extended thread function pointers */ + memset( &xtfns, '\0', sizeof(struct ldap_extra_thread_fns) ); + xtfns.ltf_threadid_fn = prldap_get_thread_id; + if ( ldap_set_option( ld, LDAP_OPT_EXTRA_THREAD_FN_PTRS, + (void *)&xtfns ) != 0 ) { + return( -1 ); + } + + return( 0 ); +} + + +static void * +prldap_mutex_alloc( void ) +{ + return( (void *)PR_NewLock()); +} + + +static void +prldap_mutex_free( void *mutex ) +{ + PR_DestroyLock( (PRLock *)mutex ); +} + + +static int +prldap_mutex_lock( void *mutex ) +{ + PR_Lock( (PRLock *)mutex ); + return( 0 ); +} + + +static int +prldap_mutex_unlock( void *mutex ) +{ + if ( PR_Unlock( (PRLock *)mutex ) == PR_FAILURE ) { + return( -1 ); + } + + return( 0 ); +} + + +static void * +prldap_get_thread_id( void ) +{ +#ifdef _SOLARIS_SDK + return ((void *)thr_self()); +#else + return( (void *)PR_GetCurrentThread()); +#endif +} + +#ifndef _SOLARIS_SDK +static int +prldap_get_ld_error( char **matchedp, char **errmsgp, void *errorarg ) +{ + PRLDAP_TPDMap *map; + PRLDAP_ErrorInfo *eip; + + if (( map = (PRLDAP_TPDMap *)errorarg ) != NULL && ( eip = + (PRLDAP_ErrorInfo *)prldap_get_thread_private( + map->prtm_index )) != NULL ) { + if ( matchedp != NULL ) { + *matchedp = eip->plei_matched; + } + if ( errmsgp != NULL ) { + *errmsgp = eip->plei_errmsg; + } + return( eip->plei_lderrno ); + } else { + if ( matchedp != NULL ) { + *matchedp = NULL; + } + if ( errmsgp != NULL ) { + *errmsgp = NULL; + } + return( LDAP_LOCAL_ERROR ); /* punt */ + } +} + + +static void +prldap_set_ld_error( int err, char *matched, char *errmsg, void *errorarg ) +{ + PRLDAP_TPDMap *map; + PRLDAP_ErrorInfo *eip; + + if (( map = (PRLDAP_TPDMap *)errorarg ) != NULL ) { + if (( eip = (PRLDAP_ErrorInfo *)prldap_get_thread_private( + map->prtm_index )) == NULL ) { + /* + * Error info. has not yet been allocated for this thread. + * Do so now. Note that we free this memory only for the + * thread that calls prldap_thread_dispose_handle(), which + * should be the one that called ldap_unbind() -- see + * prldap_return_map(). Not freeing the memory used by + * other threads is deemed acceptable since it will be + * recycled and used by other LDAP sessions. All of the + * thread-private memory is freed when a thread exits + * (inside the prldap_tsd_destroy() function). + */ + eip = (PRLDAP_ErrorInfo *)PR_Calloc( 1, + sizeof( PRLDAP_ErrorInfo )); + if ( eip == NULL ) { + return; /* punt */ + } + (void)prldap_set_thread_private( map->prtm_index, eip ); + } + + eip->plei_lderrno = err; + if ( eip->plei_matched != NULL ) { + ldap_memfree( eip->plei_matched ); + } + eip->plei_matched = matched; + if ( eip->plei_errmsg != NULL ) { + ldap_memfree( eip->plei_errmsg ); + } + eip->plei_errmsg = errmsg; + } +} +#endif + + +/* + * Called when a new LDAP * session handle is allocated. + * Allocate thread-private data for error information, but only if + * it has not already been allocated and the get_ld_error callback has + * been installed. If ld is not NULL when prldap_install_thread_functions() + * is called, we will have already allocated the thread-private data there. + */ +int +prldap_thread_new_handle( LDAP *ld, void *sessionarg ) +{ + struct ldap_thread_fns tfns; + +#ifndef _SOLARIS_SDK + if ( ldap_get_option( ld, LDAP_OPT_THREAD_FN_PTRS, (void *)&tfns ) != 0 ) { + return( LDAP_LOCAL_ERROR ); + } + + if ( tfns.ltf_lderrno_arg == NULL && tfns.ltf_get_lderrno != NULL ) { + if (( tfns.ltf_lderrno_arg = (void *)prldap_allocate_map( ld )) == NULL + || ldap_set_option( ld, LDAP_OPT_THREAD_FN_PTRS, + (void *)&tfns ) != 0 ) { + return( LDAP_LOCAL_ERROR ); + } + } +#endif + + return( LDAP_SUCCESS ); +} + + +/* + * Called when an LDAP * session handle is being destroyed. + * Clean up our thread private data map. + */ +void +prldap_thread_dispose_handle( LDAP *ld, void *sessionarg ) +{ +#ifndef _SOLARIS_SDK + struct ldap_thread_fns tfns; + + if ( ldap_get_option( ld, LDAP_OPT_THREAD_FN_PTRS, + (void *)&tfns ) == 0 && + tfns.ltf_lderrno_arg != NULL ) { + prldap_return_map( (PRLDAP_TPDMap *)tfns.ltf_lderrno_arg ); + } +#endif +} + + +#ifndef _SOLARIS_SDK +static PRStatus +prldap_init_tpd( void ) +{ + if (( prldap_map_mutex = PR_NewLock()) == NULL || PR_NewThreadPrivateIndex( + &prldap_tpdindex, prldap_tsd_destroy ) != PR_SUCCESS ) { + return( PR_FAILURE ); + } + + prldap_map_list = NULL; + + return( PR_SUCCESS ); +} + + +/* + * Function: prldap_allocate_map() + * Description: allocate a thread-private data map to use for a new + * LDAP session handle. + * Returns: a pointer to the TPD map or NULL if none available. + */ +static PRLDAP_TPDMap * +prldap_allocate_map( LDAP *ld ) +{ + PRLDAP_TPDMap *map, *prevmap; + + PR_Lock( prldap_map_mutex ); + + /* + * first look for a map that is already allocated but free to be re-used + */ + prevmap = NULL; + for ( map = prldap_map_list; map != NULL; map = map->prtm_next ) { + if ( map->prtm_ld == NULL ) { + break; + } + prevmap = map; + } + + /* + * if none we found (map == NULL), try to allocate a new one and add it + * to the end of our global list. + */ + if ( map == NULL ) { + PRUintn tpdindex; + + tpdindex = prldap_new_tpdindex(); + map = (PRLDAP_TPDMap *)PR_Malloc( sizeof( PRLDAP_TPDMap )); + if ( map != NULL ) { + map->prtm_index = tpdindex; + map->prtm_next = NULL; + if ( prevmap == NULL ) { + prldap_map_list = map; + } else { + prevmap->prtm_next = map; + } + } + } + + if ( map != NULL ) { + map->prtm_ld = ld; /* now marked as "in use" */ + /* since we are reusing...reset */ + /* to initial state */ + (void)prldap_set_thread_private( map->prtm_index, NULL ); + } + + PR_Unlock( prldap_map_mutex ); + + return( map ); +} + + +/* + * Function: prldap_return_map() + * Description: return a thread-private data map to the pool of ones + * available for re-use. + */ +static void +prldap_return_map( PRLDAP_TPDMap *map ) +{ + PRLDAP_ErrorInfo *eip; + + PR_Lock( prldap_map_mutex ); + + /* + * Dispose of thread-private LDAP error information. Note that this + * only disposes of the memory consumed on THIS thread, but that is + * okay. See the comment in prldap_set_ld_error() for the reason why. + */ + if (( eip = (PRLDAP_ErrorInfo *)prldap_get_thread_private( + map->prtm_index )) != NULL && + prldap_set_thread_private( map->prtm_index, NULL ) == 0 ) { + if ( eip->plei_matched != NULL ) { + ldap_memfree( eip->plei_matched ); + } + if ( eip->plei_errmsg != NULL ) { + ldap_memfree( eip->plei_errmsg ); + } + + PR_Free( eip ); + } + + /* mark map as available for re-use */ + map->prtm_ld = NULL; + + PR_Unlock( prldap_map_mutex ); +} + + +/* + * Function: prldap_new_tpdindex() + * Description: allocate a thread-private data index. + * Returns: the new index. + */ +static PRUintn +prldap_new_tpdindex( void ) +{ + PRUintn tpdindex; + + tpdindex = (PRUintn)PR_AtomicIncrement( &prldap_tpd_maxindex ); + return( tpdindex ); +} + + +/* + * Function: prldap_set_thread_private() + * Description: store a piece of thread-private data. + * Returns: 0 if successful and -1 if not. + */ +static int +prldap_set_thread_private( PRInt32 tpdindex, void *priv ) +{ + PRLDAP_TPDHeader *tsdhdr; + + if ( tpdindex > prldap_tpd_maxindex ) { + return( -1 ); /* bad index */ + } + + tsdhdr = (PRLDAP_TPDHeader *)PR_GetThreadPrivate( prldap_tpdindex ); + if ( tsdhdr == NULL || tpdindex >= tsdhdr->ptpdh_tpd_count ) { + tsdhdr = prldap_tsd_realloc( tsdhdr, tpdindex ); + if ( tsdhdr == NULL ) { + return( -1 ); /* realloc failed */ + } + } + + tsdhdr->ptpdh_dataitems[ tpdindex ] = priv; + return( 0 ); +} + + +/* + * Function: prldap_get_thread_private() + * Description: retrieve a piece of thread-private data. If not set, + * NULL is returned. + * Returns: 0 if successful and -1 if not. + */ +static void * +prldap_get_thread_private( PRInt32 tpdindex ) +{ + PRLDAP_TPDHeader *tsdhdr; + + tsdhdr = (PRLDAP_TPDHeader *)PR_GetThreadPrivate( prldap_tpdindex ); + if ( tsdhdr == NULL ) { + return( NULL ); /* no thread private data */ + } + + if ( tpdindex >= tsdhdr->ptpdh_tpd_count + || tsdhdr->ptpdh_dataitems == NULL ) { + return( NULL ); /* fewer data items than requested index */ + } + + return( tsdhdr->ptpdh_dataitems[ tpdindex ] ); +} + + +/* + * Function: prldap_tsd_realloc() + * Description: enlarge the thread-private data array. + * Returns: the new PRLDAP_TPDHeader value (non-NULL if successful). + * Note: tsdhdr can be NULL (allocates a new PRLDAP_TPDHeader). + */ +static PRLDAP_TPDHeader * +prldap_tsd_realloc( PRLDAP_TPDHeader *tsdhdr, int maxindex ) +{ + void *newdataitems = NULL; + int count; + + if ( tsdhdr == NULL ) { + /* allocate a new thread private data header */ + if (( tsdhdr = PR_Calloc( 1, sizeof( PRLDAP_TPDHeader ))) == NULL ) { + return( NULL ); + } + (void)PR_SetThreadPrivate( prldap_tpdindex, tsdhdr ); + } + + /* + * Make the size of the new array the next highest multiple of + * the array increment value that is greater than maxindex. + */ + count = PRLDAP_TPD_ARRAY_INCREMENT * + ( 1 + ( maxindex / PRLDAP_TPD_ARRAY_INCREMENT )); + + /* increase the size of the data item array if necessary */ + if ( count > tsdhdr->ptpdh_tpd_count ) { + newdataitems = (PRLDAP_ErrorInfo *)PR_Calloc( count, sizeof( void * )); + if ( newdataitems == NULL ) { + return( NULL ); + } + if ( tsdhdr->ptpdh_dataitems != NULL ) { /* preserve old data */ + memcpy( newdataitems, tsdhdr->ptpdh_dataitems, + tsdhdr->ptpdh_tpd_count * sizeof( void * )); + PR_Free( tsdhdr->ptpdh_dataitems ); + } + + tsdhdr->ptpdh_tpd_count = count; + tsdhdr->ptpdh_dataitems = newdataitems; + } + + return( tsdhdr ); +} + + +/* + * Function: prldap_tsd_destroy() + * Description: Free a thread-private data array. Installed as an NSPR TPD + * destructor function + * Returns: nothing. + * Note: this function assumes that each TPD item installed at the PRLDAP + * level can be freed with a call to PR_Free(). + */ +static void +prldap_tsd_destroy( void *priv ) +{ + PRLDAP_TPDHeader *tsdhdr; + int i; + + tsdhdr = (PRLDAP_TPDHeader *)priv; + if ( tsdhdr != NULL ) { + if ( tsdhdr->ptpdh_dataitems != NULL ) { + for ( i = 0; i < tsdhdr->ptpdh_tpd_count; ++i ) { + if ( tsdhdr->ptpdh_dataitems[ i ] != NULL ) { + PR_Free( tsdhdr->ptpdh_dataitems[ i ] ); + tsdhdr->ptpdh_dataitems[ i ] = NULL; + } + } + PR_Free( tsdhdr->ptpdh_dataitems ); + tsdhdr->ptpdh_dataitems = NULL; + } + PR_Free( tsdhdr ); + } +} +#endif + +#ifdef _SOLARIS_SDK +#pragma init(prldap_nspr_init) +static mutex_t nspr_init_lock = DEFAULTMUTEX; +static mutex_t nspr_idle_lock = DEFAULTMUTEX; +static cond_t nspr_idle_cond = DEFAULTCV; +static int nspr_pr_init_is_done = 0; +static int nspr_initialized = 0; + +void * +prldap_nspr_idle_primordial_thread(void *arg) { + /* + * Make sure PR_Init finishes before any other thread can continue + */ + (void) mutex_lock(&nspr_idle_lock); + if (PR_Initialized() == PR_FALSE) + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + nspr_pr_init_is_done = 1; + (void) cond_signal(&nspr_idle_cond); + (void) mutex_unlock(&nspr_idle_lock); + + /* Debug only */ + syslog(LOG_DEBUG, "NSPR is initialized by the" + "idle primordial thread tid %ld created by thread " + "tid %ld", thr_self(), (long)arg); + pause(); + +} + +/* + * Initialize NSPR once + * + * Ideally this should be done in .init of NSPR. + * This is a workaround so only main thread can initialize + * NSPR but main() does not need to call PR_Init(). + * The future direction is NSPR free so we don't want programs + * to call PR_Init(). + * + * For most of cases, programs link libldap (-lldap) + * and .init is executed before the control is transfered to + * main(). + * But for programs linking libnsl (-lnsl), libldap is loaded + * via dlopen("nss_ldap.so.1", RTLD_LAZY) so the thread loads + * libldap is not necessary a main or a primordial + * thread. In the latter case, an idle primordial thread is created + * to initialize NSPR so NSPR won't be initialized by non-primordial + * threads. + * libldap is built with "-z nodelete" so libldap and libnspr4.so + * are persistent in the address space. + */ +void +prldap_nspr_init(void) { + struct sigaction action; + + /* + * For performance reason, test it here first + */ + if (nspr_initialized != 0) + return; + + (void) mutex_lock(&nspr_init_lock); + /* Make sure PR_Init() is executed only once */ + if (nspr_initialized == 0) { + /* + * PR_Init changes the signal handler of SIGPIPE to SIG_IGN. + * Save the original and restore it after PR_Init. + */ + (void) sigaction(SIGPIPE, NULL, &action); + + if (thr_self() == 1) { + /* main thread */ + if (PR_Initialized() == PR_FALSE) + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + nspr_initialized = 1; + } else { + if (thr_create(NULL, NULL, + prldap_nspr_idle_primordial_thread, + (void *)thr_self(), THR_DETACHED, NULL) != 0) { + syslog(LOG_ERR, + "libldap:.init: Can't create thread. " + "%s", strerror(errno)); + } else { + /* + * Make sure PR_Init finishes before any other thread + * can continue. + * It's unlikely, but not impossible that this thread + * finishes dlopen and starts to call + * LDAP API when the idle thread still has not + * finished PR_Init() yet. + */ + (void) mutex_lock(&nspr_idle_lock); + while (nspr_pr_init_is_done == 0) { + (void) cond_wait(&nspr_idle_cond, + &nspr_idle_lock); + + } + (void) mutex_unlock(&nspr_idle_lock); + nspr_initialized = 1; + } + } + /* + * Restore signal handling attributes of SIGPIPE + */ + (void) sigaction(SIGPIPE, &action, NULL); + + } + (void) mutex_unlock(&nspr_init_lock); +} +#endif |