diff options
Diffstat (limited to 'ldap/c-sdk/libldap/os-ip.c')
-rw-r--r-- | ldap/c-sdk/libldap/os-ip.c | 1862 |
1 files changed, 1862 insertions, 0 deletions
diff --git a/ldap/c-sdk/libldap/os-ip.c b/ldap/c-sdk/libldap/os-ip.c new file mode 100644 index 000000000..2e70139be --- /dev/null +++ b/ldap/c-sdk/libldap/os-ip.c @@ -0,0 +1,1862 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla 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/MPL/ + * + * 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 the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* + * Copyright (c) 1995 Regents of the University of Michigan. + * All rights reserved. + */ +/* + * os-ip.c -- platform-specific TCP & UDP related code + */ + +#if 0 +#ifndef lint +static char copyright[] = "@(#) Copyright (c) 1995 Regents of the University of Michigan.\nAll rights reserved.\n"; +#endif +#endif + +/* + * On platforms where poll() does not exist, we use select(). + * Therefore, we should increase the number of file descriptors + * we can use by #defining FD_SETSIZE to a large number before + * we include <sys/select.h> or its equivalent. We do not need + * to do this for Windows, because on that platform there is no + * relationship between FD_SETSIZE and the highest numbered file + * descriptor we can use. See the document fdsetsize.txt in + * this directory for a description of the setting of FD_SETSIZE + * for the OS'es we care about. + */ +#ifndef NSLDAPI_HAVE_POLL +/* XXX value for BSDI? */ +/* XXX value for macintosh (if applicable)? */ +#endif + +#include "ldap-int.h" +#ifdef NSLDAPI_CONNECT_MUST_NOT_BE_INTERRUPTED +#include <signal.h> +#endif + +#ifdef NSLDAPI_HAVE_POLL +#include <poll.h> +#endif + + +#ifdef _WINDOWS +#define NSLDAPI_INVALID_OS_SOCKET( s ) ((s) == INVALID_SOCKET) +#else +#define NSLDAPI_INVALID_OS_SOCKET( s ) ((s) < 0 ) +#endif + + +#define NSLDAPI_POLL_ARRAY_GROWTH 5 /* grow arrays 5 elements at a time */ + + +/* + * Structures and union for tracking status of network sockets + */ +#ifdef NSLDAPI_HAVE_POLL +struct nsldapi_os_statusinfo { /* used with native OS poll() */ + struct pollfd *ossi_pollfds; + int ossi_pollfds_size; +}; +#else /* NSLDAPI_HAVE_POLL */ +struct nsldapi_os_statusinfo { /* used with native OS select() */ + fd_set ossi_readfds; + fd_set ossi_writefds; + fd_set ossi_use_readfds; + fd_set ossi_use_writefds; +}; +#endif /* else NSLDAPI_HAVE_POLL */ + +struct nsldapi_cb_statusinfo { /* used with ext. I/O poll() callback */ + LDAP_X_PollFD *cbsi_pollfds; + int cbsi_pollfds_size; +}; + +/* + * NSLDAPI_CB_POLL_MATCH() evaluates to non-zero (true) if the Sockbuf *sdp + * matches the LDAP_X_PollFD pollfd. + */ +#ifdef _WINDOWS +#define NSLDAPI_CB_POLL_SD_CAST (unsigned int) +#else +#define NSLDAPI_CB_POLL_SD_CAST +#endif +#define NSLDAPI_CB_POLL_MATCH( sbp, pollfd ) \ + ((sbp)->sb_sd == NSLDAPI_CB_POLL_SD_CAST ((pollfd).lpoll_fd) && \ + (sbp)->sb_ext_io_fns.lbextiofn_socket_arg == (pollfd).lpoll_socketarg) + + +struct nsldapi_iostatus_info { + int ios_type; +#define NSLDAPI_IOSTATUS_TYPE_OSNATIVE 1 /* poll() or select() */ +#define NSLDAPI_IOSTATUS_TYPE_CALLBACK 2 /* poll()-like */ + int ios_read_count; + int ios_write_count; + union { + struct nsldapi_os_statusinfo ios_osinfo; + struct nsldapi_cb_statusinfo ios_cbinfo; + } ios_status; +}; + +#ifndef NSLDAPI_AVOID_OS_SOCKETS +#ifdef NSLDAPI_HAVE_POLL +static int nsldapi_add_to_os_pollfds( int fd, + struct nsldapi_os_statusinfo *pip, short events ); +static int nsldapi_clear_from_os_pollfds( int fd, + struct nsldapi_os_statusinfo *pip, short events ); +static int nsldapi_find_in_os_pollfds( int fd, + struct nsldapi_os_statusinfo *pip, short revents ); +#endif /* NSLDAPI_HAVE_POLL */ +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + +static int nsldapi_iostatus_init_nolock( LDAP *ld ); +static int nsldapi_add_to_cb_pollfds( Sockbuf *sb, + struct nsldapi_cb_statusinfo *pip, short events ); +static int nsldapi_clear_from_cb_pollfds( Sockbuf *sb, + struct nsldapi_cb_statusinfo *pip, short events ); +static int nsldapi_find_in_cb_pollfds( Sockbuf *sb, + struct nsldapi_cb_statusinfo *pip, short revents ); + + +#ifdef irix +#ifndef _PR_THREADS +/* + * XXXmcs: on IRIX NSPR's poll() and select() wrappers will crash if NSPR + * has not been initialized. We work around the problem by bypassing + * the NSPR wrapper functions and going directly to the OS' functions. + */ +#define NSLDAPI_POLL _poll +#define NSLDAPI_SELECT _select +extern int _poll(struct pollfd *fds, unsigned long nfds, int timeout); +extern int _select(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, struct timeval *timeout); +#else /* _PR_THREADS */ +#define NSLDAPI_POLL poll +#define NSLDAPI_SELECT select +#endif /* else _PR_THREADS */ +#else /* irix */ +#define NSLDAPI_POLL poll +#define NSLDAPI_SELECT select +#endif /* else irix */ + + +static LBER_SOCKET nsldapi_os_socket( LDAP *ld, int secure, int domain, + int type, int protocol ); +static int nsldapi_os_ioctl( LBER_SOCKET s, int option, int *statusp ); +static int nsldapi_os_connect_with_to( LBER_SOCKET s, struct sockaddr *name, + int namelen, int msec_timeout ); +#if defined(KERBEROS) +char * nsldapi_host_connected_to( LDAP *ld, Sockbuf *sb ); +#endif + +/* + * Function typedefs used by nsldapi_try_each_host() + */ +typedef LBER_SOCKET (NSLDAPI_SOCKET_FN)( LDAP *ld, int secure, int domain, + int type, int protocol ); +typedef int (NSLDAPI_IOCTL_FN)( LBER_SOCKET s, int option, int *statusp ); +typedef int (NSLDAPI_CONNECT_WITH_TO_FN )( LBER_SOCKET s, struct sockaddr *name, + int namelen, int msec_timeout ); +typedef int (NSLDAPI_CONNECT_FN )( LBER_SOCKET s, struct sockaddr *name, + int namelen ); +typedef int (NSLDAPI_CLOSE_FN )( LBER_SOCKET s ); + +static int nsldapi_try_each_host( LDAP *ld, const char *hostlist, int defport, + int secure, NSLDAPI_SOCKET_FN *socketfn, NSLDAPI_IOCTL_FN *ioctlfn, + NSLDAPI_CONNECT_WITH_TO_FN *connectwithtofn, + NSLDAPI_CONNECT_FN *connectfn, NSLDAPI_CLOSE_FN *closefn ); + + +static int +nsldapi_os_closesocket( LBER_SOCKET s ) +{ + int rc; + +#ifdef NSLDAPI_AVOID_OS_SOCKETS + rc = -1; +#else /* NSLDAPI_AVOID_OS_SOCKETS */ +#ifdef _WINDOWS + rc = closesocket( s ); +#else /* _WINDOWS */ + rc = close( s ); +#endif /* _WINDOWS */ +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + return( rc ); +} + + +static LBER_SOCKET +nsldapi_os_socket( LDAP *ld, int secure, int domain, int type, int protocol ) +{ +#ifdef NSLDAPI_AVOID_OS_SOCKETS + return -1; +#else /* NSLDAPI_AVOID_OS_SOCKETS */ + int s, invalid_socket; + char *errmsg = NULL; + + if ( secure ) { + LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, + nsldapi_strdup( "secure mode not supported" )); + return( -1 ); + } + + s = socket( domain, type, protocol ); + + /* + * if the socket() call failed or it returned a socket larger + * than we can deal with, return a "local error." + */ + if ( NSLDAPI_INVALID_OS_SOCKET( s )) { + errmsg = "unable to create a socket"; + invalid_socket = 1; + } else { /* valid socket -- check for overflow */ + invalid_socket = 0; +#if !defined(NSLDAPI_HAVE_POLL) && !defined(_WINDOWS) + /* not on Windows and do not have poll() */ + if ( s >= FD_SETSIZE ) { + errmsg = "can't use socket >= FD_SETSIZE"; + } +#endif + } + + if ( errmsg != NULL ) { /* local socket error */ + if ( !invalid_socket ) { + nsldapi_os_closesocket( s ); + } + errmsg = nsldapi_strdup( errmsg ); + LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, errmsg ); + return( -1 ); + } + + return( s ); +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ +} + + + +/* + * Non-blocking connect call function + */ +static int +nsldapi_os_connect_with_to(LBER_SOCKET sockfd, struct sockaddr *saptr, + int salen, int msec) +{ +#ifdef NSLDAPI_AVOID_OS_SOCKETS + return -1; +#else /* NSLDAPI_AVOID_OS_SOCKETS */ + int n, error; + int len; +#if defined(_WINDOWS) || defined(XP_OS2) + int nonblock = 1; + int block = 0; +#else + int flags; +#endif /* _WINDOWS */ +#ifdef NSLDAPI_HAVE_POLL + struct pollfd pfd; +#else + struct timeval tval; + fd_set rset, wset; +#ifdef _WINDOWS + fd_set eset; +#endif +#endif /* NSLDAPI_HAVE_POLL */ + + + LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_connect_nonblock timeout: %d (msec)\n", + msec, 0, 0); + +#ifdef _WINDOWS + ioctlsocket(sockfd, FIONBIO, &nonblock); +#elif defined(XP_OS2) + ioctl( sockfd, FIONBIO, &nonblock, sizeof(nonblock) ); +#else + flags = fcntl(sockfd, F_GETFL, 0); + fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); +#endif /* _WINDOWS */ + + error = 0; + if ((n = connect(sockfd, saptr, salen)) < 0) +#ifdef _WINDOWS + if ((n != SOCKET_ERROR) && (WSAGetLastError() != WSAEWOULDBLOCK)) { +#else + if (errno != EINPROGRESS) { +#endif /* _WINDOWS */ +#ifdef LDAP_DEBUG + if ( ldap_debug & LDAP_DEBUG_TRACE ) { + perror("connect"); + } +#endif + return (-1); + } + + /* success */ + if (n == 0) + goto done; + +#ifdef NSLDAPI_HAVE_POLL + pfd.fd = sockfd; + pfd.events = POLLOUT; +#else + FD_ZERO(&rset); + FD_SET(sockfd, &rset); + wset = rset; + +#ifdef _WINDOWS + eset = rset; +#endif /* _WINDOWS */ +#endif /* NSLDAPI_HAVE_POLL */ + + if (msec < 0 && msec != LDAP_X_IO_TIMEOUT_NO_TIMEOUT) { + LDAPDebug( LDAP_DEBUG_TRACE, "Invalid timeout value detected.." + "resetting connect timeout to default value " + "(LDAP_X_IO_TIMEOUT_NO_TIMEOUT\n", 0, 0, 0); + msec = LDAP_X_IO_TIMEOUT_NO_TIMEOUT; +#ifndef NSLDAPI_HAVE_POLL + } else { + if (msec != 0) { + tval.tv_sec = msec / 1000; + tval.tv_usec = 1000 * ( msec % 1000 ); + } else { + tval.tv_sec = 0; + tval.tv_usec = 0; + } +#endif /* NSLDAPI_HAVE_POLL */ + } + +#ifdef NSLDAPI_HAVE_POLL + if ((n = poll(&pfd, 1, + (msec != LDAP_X_IO_TIMEOUT_NO_TIMEOUT) ? msec : -1)) == 0) { + errno = ETIMEDOUT; + return (-1); + } + if (pfd.revents & (POLLOUT|POLLERR|POLLHUP|POLLNVAL)) { + len = sizeof(error); + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *)&error, &len) + < 0) + return (-1); +#ifdef LDAP_DEBUG + } else if ( ldap_debug & LDAP_DEBUG_TRACE ) { + perror("poll error: sockfd not set"); +#endif + } + +#else /* NSLDAPI_HAVE_POLL */ + /* if timeval structure == NULL, select will block indefinitely */ + /* != NULL, and value == 0, select will */ + /* not block */ + /* Windows is a bit quirky on how it behaves w.r.t nonblocking */ + /* connects. If the connect fails, the exception fd, eset, is */ + /* set to show the failure. The first argument in select is */ + /* ignored */ + +#ifdef _WINDOWS + if ((n = select(sockfd +1, &rset, &wset, &eset, + (msec != LDAP_X_IO_TIMEOUT_NO_TIMEOUT) ? &tval : NULL)) == 0) { + errno = WSAETIMEDOUT; + return (-1); + } + /* if wset is set, the connect worked */ + if (FD_ISSET(sockfd, &wset) || FD_ISSET(sockfd, &rset)) { + len = sizeof(error); + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *)&error, &len) + < 0) + return (-1); + goto done; + } + + /* if eset is set, the connect failed */ + if (FD_ISSET(sockfd, &eset)) { + return (-1); + } + + /* failure on select call */ + if (n == SOCKET_ERROR) { + perror("select error: SOCKET_ERROR returned"); + return (-1); + } +#else + if ((n = select(sockfd +1, &rset, &wset, NULL, + (msec != LDAP_X_IO_TIMEOUT_NO_TIMEOUT) ? &tval : NULL)) == 0) { + errno = ETIMEDOUT; + return (-1); + } + if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) { + len = sizeof(error); + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *)&error, &len) + < 0) + return (-1); +#ifdef LDAP_DEBUG + } else if ( ldap_debug & LDAP_DEBUG_TRACE ) { + perror("select error: sockfd not set"); +#endif + } +#endif /* _WINDOWS */ +#endif /* NSLDAPI_HAVE_POLL */ + +done: +#ifdef _WINDOWS + ioctlsocket(sockfd, FIONBIO, &block); +#elif defined(XP_OS2) + ioctl( sockfd, FIONBIO, &nonblock, sizeof(block) ); +#else + fcntl(sockfd, F_SETFL, flags); +#endif /* _WINDOWS */ + + if (error) { + errno = error; + return (-1); + } + + return (0); +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ +} + + +static int +nsldapi_os_ioctl( LBER_SOCKET s, int option, int *statusp ) +{ +#ifdef NSLDAPI_AVOID_OS_SOCKETS + return -1; +#else /* NSLDAPI_AVOID_OS_SOCKETS */ + int err; +#if defined(_WINDOWS) || defined(XP_OS2) + u_long iostatus; +#endif + + if ( FIONBIO != option ) { + return( -1 ); + } + +#ifdef _WINDOWS + iostatus = *(u_long *)statusp; + err = ioctlsocket( s, FIONBIO, &iostatus ); +#else +#ifdef XP_OS2 + err = ioctl( s, FIONBIO, (caddr_t)&iostatus, sizeof(iostatus) ); +#else + err = ioctl( s, FIONBIO, (caddr_t)statusp ); +#endif +#endif + + return( err ); +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ +} + + +int +nsldapi_connect_to_host( LDAP *ld, Sockbuf *sb, const char *hostlist, + int defport, int secure, char **krbinstancep ) +/* + * "defport" must be in host byte order + * zero is returned upon success, -1 if fatal error, -2 EINPROGRESS + * if -1 is returned, ld_errno is set + */ +{ + int s; + + LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_connect_to_host: %s, port: %d\n", + NULL == hostlist ? "NULL" : hostlist, defport, 0 ); + + /* + * If an extended I/O connect callback has been defined, just use it. + */ + if ( NULL != ld->ld_extconnect_fn ) { + unsigned long connect_opts = 0; + + if ( ld->ld_options & LDAP_BITOPT_ASYNC) { + connect_opts |= LDAP_X_EXTIOF_OPT_NONBLOCKING; + } + if ( secure ) { + connect_opts |= LDAP_X_EXTIOF_OPT_SECURE; + } + s = ld->ld_extconnect_fn( hostlist, defport, + ld->ld_connect_timeout, connect_opts, + ld->ld_ext_session_arg, + &sb->sb_ext_io_fns.lbextiofn_socket_arg ); + + } else { +#ifdef NSLDAPI_AVOID_OS_SOCKETS + return( -1 ); +#else /* NSLDAPI_AVOID_OS_SOCKETS */ + s = nsldapi_try_each_host( ld, hostlist, + defport, secure, nsldapi_os_socket, + nsldapi_os_ioctl, nsldapi_os_connect_with_to, + NULL, nsldapi_os_closesocket ); +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + } + + if ( s < 0 ) { + LDAP_SET_LDERRNO( ld, LDAP_CONNECT_ERROR, NULL, NULL ); + return( -1 ); + } + + sb->sb_sd = s; + + /* + * Set krbinstancep (canonical name of host for use by Kerberos). + */ +#ifdef KERBEROS + char *p; + + if (( *krbinstancep = nsldapi_host_connected_to( sb )) != NULL + && ( p = strchr( *krbinstancep, '.' )) != NULL ) { + *p = '\0'; + } +#else /* KERBEROS */ + *krbinstancep = NULL; +#endif /* KERBEROS */ + + return( 0 ); +} + + +/* + * Returns a socket number if successful and -1 if an error occurs. + */ +static int +nsldapi_try_each_host( LDAP *ld, const char *hostlist, + int defport, int secure, NSLDAPI_SOCKET_FN *socketfn, + NSLDAPI_IOCTL_FN *ioctlfn, NSLDAPI_CONNECT_WITH_TO_FN *connectwithtofn, + NSLDAPI_CONNECT_FN *connectfn, NSLDAPI_CLOSE_FN *closefn ) +{ +#ifdef NSLDAPI_AVOID_OS_SOCKETS + return -1; +#else /* NSLDAPI_AVOID_OS_SOCKETS */ + int rc = -1; + int s = 0; + int i, err, connected, use_hp; + int parse_err, port; + struct sockaddr_in sin; + nsldapi_in_addr_t address; + char **addrlist, *ldhpbuf, *ldhpbuf_allocd = NULL; + char *host; + LDAPHostEnt ldhent, *ldhp; + struct hostent *hp; + struct ldap_x_hostlist_status *status; +#ifdef GETHOSTBYNAME_BUF_T + GETHOSTBYNAME_BUF_T hbuf; + struct hostent hent; +#endif /* GETHOSTBYNAME_BUF_T */ + + connected = 0; + parse_err = ldap_x_hostlist_first( hostlist, defport, &host, &port, + &status ); + while ( !connected && LDAP_SUCCESS == parse_err && host != NULL ) { + ldhpbuf_allocd = NULL; + ldhp = NULL; + hp = NULL; + s = 0; + use_hp = 0; + addrlist = NULL; + + + if (( address = inet_addr( host )) == -1 ) { + if ( ld->ld_dns_gethostbyname_fn == NULL ) { +#ifdef GETHOSTBYNAME_R_RETURNS_INT + (void)GETHOSTBYNAME( host, &hent, hbuf, + sizeof(hbuf), &hp, &err ); +#else + hp = GETHOSTBYNAME( host, &hent, hbuf, + sizeof(hbuf), &err ); +#endif + if ( hp != NULL ) { + addrlist = hp->h_addr_list; + } + } else { + /* + * DNS callback installed... use it. + */ +#ifdef GETHOSTBYNAME_buf_t + /* avoid allocation by using hbuf if large enough */ + if ( sizeof( hbuf ) < ld->ld_dns_bufsize ) { + ldhpbuf = ldhpbuf_allocd + = NSLDAPI_MALLOC( ld->ld_dns_bufsize ); + } else { + ldhpbuf = (char *)hbuf; + } +#else /* GETHOSTBYNAME_buf_t */ + ldhpbuf = ldhpbuf_allocd = NSLDAPI_MALLOC( + ld->ld_dns_bufsize ); +#endif /* else GETHOSTBYNAME_buf_t */ + + if ( ldhpbuf == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, + NULL, NULL ); + ldap_memfree( host ); + ldap_x_hostlist_statusfree( status ); + return( -1 ); + } + + if (( ldhp = ld->ld_dns_gethostbyname_fn( host, + &ldhent, ldhpbuf, ld->ld_dns_bufsize, &err, + ld->ld_dns_extradata )) != NULL ) { + addrlist = ldhp->ldaphe_addr_list; + } + } + + if ( addrlist == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_CONNECT_ERROR, NULL, NULL ); + LDAP_SET_ERRNO( ld, EHOSTUNREACH ); /* close enough */ + if ( ldhpbuf_allocd != NULL ) { + NSLDAPI_FREE( ldhpbuf_allocd ); + } + ldap_memfree( host ); + ldap_x_hostlist_statusfree( status ); + return( -1 ); + } + use_hp = 1; + } + + rc = -1; + for ( i = 0; !use_hp || ( addrlist[ i ] != 0 ); i++ ) { + if ( -1 == ( s = (*socketfn)( ld, secure, AF_INET, + SOCK_STREAM, 0 ))) { + if ( ldhpbuf_allocd != NULL ) { + NSLDAPI_FREE( ldhpbuf_allocd ); + } + ldap_memfree( host ); + ldap_x_hostlist_statusfree( status ); + return( -1 ); + } + + if ( ld->ld_options & LDAP_BITOPT_ASYNC ) { + int iostatus = 1; + + err = (*ioctlfn)( s, FIONBIO, &iostatus ); + if ( err == -1 ) { + LDAPDebug( LDAP_DEBUG_ANY, + "FIONBIO ioctl failed on %d\n", + s, 0, 0 ); + } + } + + (void)memset( (char *)&sin, 0, sizeof( struct sockaddr_in )); + sin.sin_family = AF_INET; + sin.sin_port = htons( (unsigned short)port ); + + SAFEMEMCPY( (char *) &sin.sin_addr.s_addr, + ( use_hp ? (char *) addrlist[ i ] : + (char *) &address ), sizeof( sin.sin_addr.s_addr) ); + + { +#ifdef NSLDAPI_CONNECT_MUST_NOT_BE_INTERRUPTED +/* + * Block all of the signals that might interrupt connect() since + * there is an OS bug that causes connect() to fail if it is restarted. + * Look in ../../include/portable.h for the definition of + * NSLDAPI_CONNECT_MUST_NOT_BE_INTERRUPTED. + */ + sigset_t ints_off, oldset; + + sigemptyset( &ints_off ); + sigaddset( &ints_off, SIGALRM ); + sigaddset( &ints_off, SIGIO ); + sigaddset( &ints_off, SIGCLD ); + + NSLDAPI_MT_SAFE_SIGPROCMASK( SIG_BLOCK, &ints_off, &oldset ); +#endif /* NSLDAPI_CONNECT_MUST_NOT_BE_INTERRUPTED */ + + if ( NULL != connectwithtofn ) { + err = (*connectwithtofn)(s, + (struct sockaddr *)&sin, + sizeof(struct sockaddr_in), + ld->ld_connect_timeout); + } else { + err = (*connectfn)(s, + (struct sockaddr *)&sin, + sizeof(struct sockaddr_in)); + } +#ifdef NSLDAPI_CONNECT_MUST_NOT_BE_INTERRUPTED +/* + * restore original signal mask + */ + NSLDAPI_MT_SAFE_SIGPROCMASK( SIG_SETMASK, &oldset, 0 ); +#endif /* NSLDAPI_CONNECT_MUST_NOT_BE_INTERRUPTED */ + + } + if ( err >= 0 ) { + connected = 1; + rc = 0; + break; + } else { + if ( ld->ld_options & LDAP_BITOPT_ASYNC) { +#ifdef _WINDOWS + if (err == -1 && WSAGetLastError() == WSAEWOULDBLOCK) + LDAP_SET_ERRNO( ld, EWOULDBLOCK ); +#endif /* _WINDOWS */ + err = LDAP_GET_ERRNO( ld ); + if ( NSLDAPI_ERRNO_IO_INPROGRESS( err )) { + LDAPDebug( LDAP_DEBUG_TRACE, "connect would block...\n", + 0, 0, 0 ); + rc = -2; + break; + } + } + +#ifdef LDAP_DEBUG + if ( ldap_debug & LDAP_DEBUG_TRACE ) { + perror( (char *)inet_ntoa( sin.sin_addr )); + } +#endif + (*closefn)( s ); + if ( !use_hp ) { + break; + } + } + } + + ldap_memfree( host ); + parse_err = ldap_x_hostlist_next( &host, &port, status ); + } + + if ( ldhpbuf_allocd != NULL ) { + NSLDAPI_FREE( ldhpbuf_allocd ); + } + ldap_memfree( host ); + ldap_x_hostlist_statusfree( status ); + + if ( connected ) { + LDAPDebug( LDAP_DEBUG_TRACE, "sd %d connected to: %s\n", + s, inet_ntoa( sin.sin_addr ), 0 ); + } + + return( rc == 0 ? s : -1 ); +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ +} + +void +nsldapi_close_connection( LDAP *ld, Sockbuf *sb ) +{ + if ( ld->ld_extclose_fn == NULL ) { +#ifndef NSLDAPI_AVOID_OS_SOCKETS + nsldapi_os_closesocket( sb->sb_sd ); +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + } else { + ld->ld_extclose_fn( sb->sb_sd, + sb->sb_ext_io_fns.lbextiofn_socket_arg ); + } +} + + +#ifdef KERBEROS +char * +nsldapi_host_connected_to( LDAP *ld, Sockbuf *sb ) +{ + struct hostent *hp; + char *p; + int len; + struct sockaddr_in sin; + + (void)memset( (char *)&sin, 0, sizeof( struct sockaddr_in )); + len = sizeof( sin ); + if ( getpeername( sb->sb_sd, (struct sockaddr *)&sin, &len ) == -1 ) { + return( NULL ); + } + + /* + * do a reverse lookup on the addr to get the official hostname. + * this is necessary for kerberos to work right, since the official + * hostname is used as the kerberos instance. + */ +#error XXXmcs: need to use DNS callbacks here + if (( hp = (struct hostent *)gethostbyaddr( (char *) &sin.sin_addr, + sizeof( sin.sin_addr ), AF_INET )) != NULL ) { + if ( hp->h_name != NULL ) { + return( nsldapi_strdup( hp->h_name )); + } + } + + return( NULL ); +} +#endif /* KERBEROS */ + + +/* + * Returns 0 if all goes well and -1 if an error occurs (error code set in ld) + * Also allocates initializes ld->ld_iostatus if needed.. + */ +int +nsldapi_iostatus_interest_write( LDAP *ld, Sockbuf *sb ) +{ + int rc = 0; + NSLDAPIIOStatus *iosp; + + LDAP_MUTEX_LOCK( ld, LDAP_IOSTATUS_LOCK ); + + if ( ld->ld_iostatus == NULL + && nsldapi_iostatus_init_nolock( ld ) < 0 ) { + rc = -1; + goto unlock_and_return; + } + + iosp = ld->ld_iostatus; + + if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_OSNATIVE ) { +#ifdef NSLDAPI_AVOID_OS_SOCKETS + rc = -1; + goto unlock_and_return; +#else /* NSLDAPI_AVOID_OS_SOCKETS */ +#ifdef NSLDAPI_HAVE_POLL + if ( nsldapi_add_to_os_pollfds( sb->sb_sd, + &iosp->ios_status.ios_osinfo, POLLOUT )) { + ++iosp->ios_write_count; + } +#else /* NSLDAPI_HAVE_POLL */ + if ( !FD_ISSET( sb->sb_sd, + &iosp->ios_status.ios_osinfo.ossi_writefds )) { + FD_SET( sb->sb_sd, + &iosp->ios_status.ios_osinfo.ossi_writefds ); + ++iosp->ios_write_count; + } +#endif /* else NSLDAPI_HAVE_POLL */ +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + + } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_CALLBACK ) { + if ( nsldapi_add_to_cb_pollfds( sb, + &iosp->ios_status.ios_cbinfo, LDAP_X_POLLOUT )) { + ++iosp->ios_write_count; + } + + } else { + LDAPDebug( LDAP_DEBUG_ANY, + "nsldapi_iostatus_interest_write: unknown I/O type %d\n", + iosp->ios_type, 0, 0 ); + } + +unlock_and_return: + LDAP_MUTEX_UNLOCK( ld, LDAP_IOSTATUS_LOCK ); + return( rc ); +} + + +/* + * Returns 0 if all goes well and -1 if an error occurs (error code set in ld) + * Also allocates initializes ld->ld_iostatus if needed.. + */ +int +nsldapi_iostatus_interest_read( LDAP *ld, Sockbuf *sb ) +{ + int rc = 0; + NSLDAPIIOStatus *iosp; + + LDAP_MUTEX_LOCK( ld, LDAP_IOSTATUS_LOCK ); + + if ( ld->ld_iostatus == NULL + && nsldapi_iostatus_init_nolock( ld ) < 0 ) { + rc = -1; + goto unlock_and_return; + } + + iosp = ld->ld_iostatus; + + if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_OSNATIVE ) { +#ifdef NSLDAPI_AVOID_OS_SOCKETS + rc = -1; + goto unlock_and_return; +#else /* NSLDAPI_AVOID_OS_SOCKETS */ +#ifdef NSLDAPI_HAVE_POLL + if ( nsldapi_add_to_os_pollfds( sb->sb_sd, + &iosp->ios_status.ios_osinfo, POLLIN )) { + ++iosp->ios_read_count; + } +#else /* NSLDAPI_HAVE_POLL */ + if ( !FD_ISSET( sb->sb_sd, + &iosp->ios_status.ios_osinfo.ossi_readfds )) { + FD_SET( sb->sb_sd, + &iosp->ios_status.ios_osinfo.ossi_readfds ); + ++iosp->ios_read_count; + } +#endif /* else NSLDAPI_HAVE_POLL */ +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + + } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_CALLBACK ) { + if ( nsldapi_add_to_cb_pollfds( sb, + &iosp->ios_status.ios_cbinfo, LDAP_X_POLLIN )) { + ++iosp->ios_read_count; + } + } else { + LDAPDebug( LDAP_DEBUG_ANY, + "nsldapi_iostatus_interest_read: unknown I/O type %d\n", + iosp->ios_type, 0, 0 ); + } + +unlock_and_return: + LDAP_MUTEX_UNLOCK( ld, LDAP_IOSTATUS_LOCK ); + return( rc ); +} + + +/* + * Returns 0 if all goes well and -1 if an error occurs (error code set in ld) + * Also allocates initializes ld->ld_iostatus if needed.. + */ +int +nsldapi_iostatus_interest_clear( LDAP *ld, Sockbuf *sb ) +{ + int rc = 0; + NSLDAPIIOStatus *iosp; + + LDAP_MUTEX_LOCK( ld, LDAP_IOSTATUS_LOCK ); + + if ( ld->ld_iostatus == NULL + && nsldapi_iostatus_init_nolock( ld ) < 0 ) { + rc = -1; + goto unlock_and_return; + } + + iosp = ld->ld_iostatus; + + if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_OSNATIVE ) { +#ifdef NSLDAPI_AVOID_OS_SOCKETS + rc = -1; + goto unlock_and_return; +#else /* NSLDAPI_AVOID_OS_SOCKETS */ +#ifdef NSLDAPI_HAVE_POLL + if ( nsldapi_clear_from_os_pollfds( sb->sb_sd, + &iosp->ios_status.ios_osinfo, POLLOUT )) { + --iosp->ios_write_count; + } + if ( nsldapi_clear_from_os_pollfds( sb->sb_sd, + &iosp->ios_status.ios_osinfo, POLLIN )) { + --iosp->ios_read_count; + } +#else /* NSLDAPI_HAVE_POLL */ + if ( FD_ISSET( sb->sb_sd, + &iosp->ios_status.ios_osinfo.ossi_writefds )) { + FD_CLR( sb->sb_sd, + &iosp->ios_status.ios_osinfo.ossi_writefds ); + --iosp->ios_write_count; + } + if ( FD_ISSET( sb->sb_sd, + &iosp->ios_status.ios_osinfo.ossi_readfds )) { + FD_CLR( sb->sb_sd, + &iosp->ios_status.ios_osinfo.ossi_readfds ); + --iosp->ios_read_count; + } +#endif /* else NSLDAPI_HAVE_POLL */ +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + + } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_CALLBACK ) { + if ( nsldapi_clear_from_cb_pollfds( sb, + &iosp->ios_status.ios_cbinfo, LDAP_X_POLLOUT )) { + --iosp->ios_write_count; + } + if ( nsldapi_clear_from_cb_pollfds( sb, + &iosp->ios_status.ios_cbinfo, LDAP_X_POLLIN )) { + --iosp->ios_read_count; + } + } else { + LDAPDebug( LDAP_DEBUG_ANY, + "nsldapi_iostatus_interest_clear: unknown I/O type %d\n", + iosp->ios_type, 0, 0 ); + } + +unlock_and_return: + LDAP_MUTEX_UNLOCK( ld, LDAP_IOSTATUS_LOCK ); + return( rc ); +} + + +/* + * Return a non-zero value if sb is ready for write. + */ +int +nsldapi_iostatus_is_write_ready( LDAP *ld, Sockbuf *sb ) +{ + int rc = 0; + NSLDAPIIOStatus *iosp; + + LDAP_MUTEX_LOCK( ld, LDAP_IOSTATUS_LOCK ); + iosp = ld->ld_iostatus; + if ( iosp == NULL ) { + goto unlock_and_return; + } + + if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_OSNATIVE ) { +#ifdef NSLDAPI_AVOID_OS_SOCKETS + goto unlock_and_return; +#else /* NSLDAPI_AVOID_OS_SOCKETS */ +#ifdef NSLDAPI_HAVE_POLL + /* + * if we are using poll() we do something a little tricky: if + * any bits in the socket's returned events field other than + * POLLIN (ready for read) are set, we return true. This + * is done so we notice when a server closes a connection + * or when another error occurs. The actual error will be + * noticed later when we call write() or send(). + */ + rc = nsldapi_find_in_os_pollfds( sb->sb_sd, + &iosp->ios_status.ios_osinfo, ~POLLIN ); + +#else /* NSLDAPI_HAVE_POLL */ + rc = FD_ISSET( sb->sb_sd, + &iosp->ios_status.ios_osinfo.ossi_use_writefds ); +#endif /* else NSLDAPI_HAVE_POLL */ +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + + } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_CALLBACK ) { + rc = nsldapi_find_in_cb_pollfds( sb, + &iosp->ios_status.ios_cbinfo, ~LDAP_X_POLLIN ); + + } else { + LDAPDebug( LDAP_DEBUG_ANY, + "nsldapi_iostatus_is_write_ready: unknown I/O type %d\n", + iosp->ios_type, 0, 0 ); + rc = 0; + } + +unlock_and_return: + LDAP_MUTEX_UNLOCK( ld, LDAP_IOSTATUS_LOCK ); + return( rc ); +} + + +/* + * Return a non-zero value if sb is ready for read. + */ +int +nsldapi_iostatus_is_read_ready( LDAP *ld, Sockbuf *sb ) +{ + int rc = 0; + NSLDAPIIOStatus *iosp; + + LDAP_MUTEX_LOCK( ld, LDAP_IOSTATUS_LOCK ); + iosp = ld->ld_iostatus; + if ( iosp == NULL ) { + goto unlock_and_return; + } + + if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_OSNATIVE ) { +#ifdef NSLDAPI_AVOID_OS_SOCKETS + goto unlock_and_return; +#else /* NSLDAPI_AVOID_OS_SOCKETS */ +#ifdef NSLDAPI_HAVE_POLL + /* + * if we are using poll() we do something a little tricky: if + * any bits in the socket's returned events field other than + * POLLOUT (ready for write) are set, we return true. This + * is done so we notice when a server closes a connection + * or when another error occurs. The actual error will be + * noticed later when we call read() or recv(). + */ + rc = nsldapi_find_in_os_pollfds( sb->sb_sd, + &iosp->ios_status.ios_osinfo, ~POLLOUT ); + +#else /* NSLDAPI_HAVE_POLL */ + rc = FD_ISSET( sb->sb_sd, + &iosp->ios_status.ios_osinfo.ossi_use_readfds ); +#endif /* else NSLDAPI_HAVE_POLL */ +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + + } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_CALLBACK ) { + rc = nsldapi_find_in_cb_pollfds( sb, + &iosp->ios_status.ios_cbinfo, ~LDAP_X_POLLOUT ); + + } else { + LDAPDebug( LDAP_DEBUG_ANY, + "nsldapi_iostatus_is_read_ready: unknown I/O type %d\n", + iosp->ios_type, 0, 0 ); + rc = 0; + } + +unlock_and_return: + LDAP_MUTEX_UNLOCK( ld, LDAP_IOSTATUS_LOCK ); + return( rc ); +} + + +/* + * Allocate and initialize ld->ld_iostatus if not already done. + * Should be called with LDAP_IOSTATUS_LOCK locked. + * Returns 0 if all goes well and -1 if not (sets error in ld) + */ +static int +nsldapi_iostatus_init_nolock( LDAP *ld ) +{ + NSLDAPIIOStatus *iosp; + + if ( ld->ld_iostatus != NULL ) { + return( 0 ); + } + + if (( iosp = (NSLDAPIIOStatus *)NSLDAPI_CALLOC( 1, + sizeof( NSLDAPIIOStatus ))) == NULL ) { + LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); + return( -1 ); + } + + if ( ld->ld_extpoll_fn == NULL ) { + iosp->ios_type = NSLDAPI_IOSTATUS_TYPE_OSNATIVE; +#ifdef NSLDAPI_AVOID_OS_SOCKETS + return( -1 ); +#else /* NSLDAPI_AVOID_OS_SOCKETS */ +#ifndef NSLDAPI_HAVE_POLL + FD_ZERO( &iosp->ios_status.ios_osinfo.ossi_readfds ); + FD_ZERO( &iosp->ios_status.ios_osinfo.ossi_writefds ); +#endif /* !NSLDAPI_HAVE_POLL */ +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + + } else { + iosp->ios_type = NSLDAPI_IOSTATUS_TYPE_CALLBACK; + } + + ld->ld_iostatus = iosp; + return( 0 ); +} + + +void +nsldapi_iostatus_free( LDAP *ld ) +{ + if ( ld == NULL ) { + return; + } + + + /* clean up classic I/O compatibility glue */ + if ( ld->ld_io_fns_ptr != NULL ) { + if ( ld->ld_ext_session_arg != NULL ) { + NSLDAPI_FREE( ld->ld_ext_session_arg ); + } + NSLDAPI_FREE( ld->ld_io_fns_ptr ); + } + + /* clean up I/O status tracking info. */ + if ( ld->ld_iostatus != NULL ) { + NSLDAPIIOStatus *iosp = ld->ld_iostatus; + + if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_OSNATIVE ) { +#ifdef NSLDAPI_HAVE_POLL + if ( iosp->ios_status.ios_osinfo.ossi_pollfds + != NULL ) { + NSLDAPI_FREE( + iosp->ios_status.ios_osinfo.ossi_pollfds ); + } +#endif /* NSLDAPI_HAVE_POLL */ + + } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_CALLBACK ) { + if ( iosp->ios_status.ios_cbinfo.cbsi_pollfds + != NULL ) { + NSLDAPI_FREE( + iosp->ios_status.ios_cbinfo.cbsi_pollfds ); + } + } else { + LDAPDebug( LDAP_DEBUG_ANY, + "nsldapi_iostatus_free: unknown I/O type %d\n", + iosp->ios_type, 0, 0 ); + } + + NSLDAPI_FREE( iosp ); + } +} + + + +#if !defined(NSLDAPI_HAVE_POLL) && !defined(NSLDAPI_AVOID_OS_SOCKETS) +static int +nsldapi_get_select_table_size( void ) +{ + static int tblsize = 0; /* static */ + + if ( tblsize == 0 ) { +#if defined(_WINDOWS) || defined(XP_OS2) + tblsize = FOPEN_MAX; /* ANSI spec. */ +#else +#ifdef USE_SYSCONF + tblsize = sysconf( _SC_OPEN_MAX ); +#else /* USE_SYSCONF */ + tblsize = getdtablesize(); +#endif /* else USE_SYSCONF */ +#endif /* else _WINDOWS */ + + if ( tblsize >= FD_SETSIZE ) { + /* + * clamp value so we don't overrun the fd_set structure + */ + tblsize = FD_SETSIZE - 1; + } + } + + return( tblsize ); +} +#endif /* !defined(NSLDAPI_HAVE_POLL) && !defined(NSLDAPI_AVOID_OS_SOCKETS) */ + + +static int +nsldapi_tv2ms( struct timeval *tv ) +{ + if ( tv == NULL ) { + return( -1 ); /* infinite timout for poll() */ + } + + return( tv->tv_sec * 1000 + tv->tv_usec / 1000 ); +} + + +int +nsldapi_iostatus_poll( LDAP *ld, struct timeval *timeout ) +{ + int rc; + NSLDAPIIOStatus *iosp; + + LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_iostatus_poll\n", 0, 0, 0 ); + + LDAP_MUTEX_LOCK( ld, LDAP_IOSTATUS_LOCK ); + iosp = ld->ld_iostatus; + + if ( iosp == NULL || + ( iosp->ios_read_count <= 0 && iosp->ios_write_count <= 0 )) { + rc = 0; /* simulate a timeout */ + + } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_OSNATIVE ) { +#ifndef NSLDAPI_AVOID_OS_SOCKETS +#ifdef NSLDAPI_HAVE_POLL + + rc = NSLDAPI_POLL( iosp->ios_status.ios_osinfo.ossi_pollfds, + iosp->ios_status.ios_osinfo.ossi_pollfds_size, + nsldapi_tv2ms( timeout )); + +#else /* NSLDAPI_HAVE_POLL */ + + /* two (potentially large) struct copies */ + iosp->ios_status.ios_osinfo.ossi_use_readfds + = iosp->ios_status.ios_osinfo.ossi_readfds; + iosp->ios_status.ios_osinfo.ossi_use_writefds + = iosp->ios_status.ios_osinfo.ossi_writefds; + +#ifdef HPUX9 + rc = NSLDAPI_SELECT( nsldapi_get_select_table_size(), + (int *)&iosp->ios_status.ios_osinfo.ossi_use_readfds + (int *)&iosp->ios_status.ios_osinfo.ossi_use_writefds, + NULL, timeout ); +#else + rc = NSLDAPI_SELECT( nsldapi_get_select_table_size(), + &iosp->ios_status.ios_osinfo.ossi_use_readfds, + &iosp->ios_status.ios_osinfo.ossi_use_writefds, + NULL, timeout ); +#endif /* else HPUX9 */ +#endif /* else NSLDAPI_HAVE_POLL */ +#endif /* NSLDAPI_AVOID_OS_SOCKETS */ + + } else if ( iosp->ios_type == NSLDAPI_IOSTATUS_TYPE_CALLBACK ) { + /* + * We always pass the session extended I/O argument to + * the extended poll() callback. + */ + rc = ld->ld_extpoll_fn( + iosp->ios_status.ios_cbinfo.cbsi_pollfds, + iosp->ios_status.ios_cbinfo.cbsi_pollfds_size, + nsldapi_tv2ms( timeout ), ld->ld_ext_session_arg ); + + } else { + LDAPDebug( LDAP_DEBUG_ANY, + "nsldapi_iostatus_poll: unknown I/O type %d\n", + iosp->ios_type, 0, 0 ); + rc = 0; /* simulate a timeout (what else to do?) */ + } + + LDAP_MUTEX_UNLOCK( ld, LDAP_IOSTATUS_LOCK ); + return( rc ); +} + +#if defined(NSLDAPI_HAVE_POLL) && !defined(NSLDAPI_AVOID_OS_SOCKETS) +/* + * returns 1 if "fd" was added to pollfds. + * returns 1 if some of the bits in "events" were added to pollfds. + * returns 0 if no changes were made. + */ +static int +nsldapi_add_to_os_pollfds( int fd, struct nsldapi_os_statusinfo *pip, + short events ) +{ + int i, openslot; + + /* first we check to see if "fd" is already in our pollfds */ + openslot = -1; + for ( i = 0; i < pip->ossi_pollfds_size; ++i ) { + if ( pip->ossi_pollfds[ i ].fd == fd ) { + if (( pip->ossi_pollfds[ i ].events & events ) + != events ) { + pip->ossi_pollfds[ i ].events |= events; + return( 1 ); + } else { + return( 0 ); + } + } + if ( pip->ossi_pollfds[ i ].fd == -1 && openslot == -1 ) { + openslot = i; /* remember for later */ + } + } + + /* + * "fd" is not currently being poll'd on -- add to array. + * if we need to expand the pollfds array, we do it in increments of + * NSLDAPI_POLL_ARRAY_GROWTH (#define near the top of this file). + */ + if ( openslot == -1 ) { + struct pollfd *newpollfds; + + if ( pip->ossi_pollfds_size == 0 ) { + newpollfds = (struct pollfd *)NSLDAPI_MALLOC( + NSLDAPI_POLL_ARRAY_GROWTH + * sizeof( struct pollfd )); + } else { + newpollfds = (struct pollfd *)NSLDAPI_REALLOC( + pip->ossi_pollfds, (NSLDAPI_POLL_ARRAY_GROWTH + + pip->ossi_pollfds_size) + * sizeof( struct pollfd )); + } + if ( newpollfds == NULL ) { /* XXXmcs: no way to return err! */ + return( 0 ); + } + pip->ossi_pollfds = newpollfds; + openslot = pip->ossi_pollfds_size; + pip->ossi_pollfds_size += NSLDAPI_POLL_ARRAY_GROWTH; + for ( i = openslot + 1; i < pip->ossi_pollfds_size; ++i ) { + pip->ossi_pollfds[ i ].fd = -1; + pip->ossi_pollfds[ i ].events = + pip->ossi_pollfds[ i ].revents = 0; + } + } + pip->ossi_pollfds[ openslot ].fd = fd; + pip->ossi_pollfds[ openslot ].events = events; + pip->ossi_pollfds[ openslot ].revents = 0; + return( 1 ); +} + + +/* + * returns 1 if any "events" from "fd" were removed from pollfds + * returns 0 of "fd" wasn't in pollfds or if events did not overlap. + */ +static int +nsldapi_clear_from_os_pollfds( int fd, struct nsldapi_os_statusinfo *pip, + short events ) +{ + int i; + + for ( i = 0; i < pip->ossi_pollfds_size; ++i ) { + if ( pip->ossi_pollfds[i].fd == fd ) { + if (( pip->ossi_pollfds[ i ].events & events ) != 0 ) { + pip->ossi_pollfds[ i ].events &= ~events; + if ( pip->ossi_pollfds[ i ].events == 0 ) { + pip->ossi_pollfds[i].fd = -1; + } + return( 1 ); /* events overlap */ + } else { + return( 0 ); /* events do not overlap */ + } + } + } + + return( 0 ); /* "fd" was not found */ +} + + +/* + * returns 1 if any "revents" from "fd" were set in pollfds revents field. + * returns 0 if not. + */ +static int +nsldapi_find_in_os_pollfds( int fd, struct nsldapi_os_statusinfo *pip, + short revents ) +{ + int i; + + for ( i = 0; i < pip->ossi_pollfds_size; ++i ) { + if ( pip->ossi_pollfds[i].fd == fd ) { + if (( pip->ossi_pollfds[ i ].revents & revents ) != 0 ) { + return( 1 ); /* revents overlap */ + } else { + return( 0 ); /* revents do not overlap */ + } + } + } + + return( 0 ); /* "fd" was not found */ +} +#endif /* !defined(NSLDAPI_HAVE_POLL) && !defined(NSLDAPI_AVOID_OS_SOCKETS) */ + +/* + * returns 1 if "sb" was added to pollfds. + * returns 1 if some of the bits in "events" were added to pollfds. + * returns 0 if no changes were made. + */ +static int +nsldapi_add_to_cb_pollfds( Sockbuf *sb, struct nsldapi_cb_statusinfo *pip, + short events ) +{ + int i, openslot; + + /* first we check to see if "sb" is already in our pollfds */ + openslot = -1; + for ( i = 0; i < pip->cbsi_pollfds_size; ++i ) { + if ( NSLDAPI_CB_POLL_MATCH( sb, pip->cbsi_pollfds[ i ] )) { + if (( pip->cbsi_pollfds[ i ].lpoll_events & events ) + != events ) { + pip->cbsi_pollfds[ i ].lpoll_events |= events; + return( 1 ); + } else { + return( 0 ); + } + } + if ( pip->cbsi_pollfds[ i ].lpoll_fd == -1 && openslot == -1 ) { + openslot = i; /* remember for later */ + } + } + + /* + * "sb" is not currently being poll'd on -- add to array. + * if we need to expand the pollfds array, we do it in increments of + * NSLDAPI_POLL_ARRAY_GROWTH (#define near the top of this file). + */ + if ( openslot == -1 ) { + LDAP_X_PollFD *newpollfds; + + if ( pip->cbsi_pollfds_size == 0 ) { + newpollfds = (LDAP_X_PollFD *)NSLDAPI_MALLOC( + NSLDAPI_POLL_ARRAY_GROWTH + * sizeof( LDAP_X_PollFD )); + } else { + newpollfds = (LDAP_X_PollFD *)NSLDAPI_REALLOC( + pip->cbsi_pollfds, (NSLDAPI_POLL_ARRAY_GROWTH + + pip->cbsi_pollfds_size) + * sizeof( LDAP_X_PollFD )); + } + if ( newpollfds == NULL ) { /* XXXmcs: no way to return err! */ + return( 0 ); + } + pip->cbsi_pollfds = newpollfds; + openslot = pip->cbsi_pollfds_size; + pip->cbsi_pollfds_size += NSLDAPI_POLL_ARRAY_GROWTH; + for ( i = openslot + 1; i < pip->cbsi_pollfds_size; ++i ) { + pip->cbsi_pollfds[ i ].lpoll_fd = -1; + pip->cbsi_pollfds[ i ].lpoll_socketarg = NULL; + pip->cbsi_pollfds[ i ].lpoll_events = + pip->cbsi_pollfds[ i ].lpoll_revents = 0; + } + } + pip->cbsi_pollfds[ openslot ].lpoll_fd = sb->sb_sd; + pip->cbsi_pollfds[ openslot ].lpoll_socketarg = + sb->sb_ext_io_fns.lbextiofn_socket_arg; + pip->cbsi_pollfds[ openslot ].lpoll_events = events; + pip->cbsi_pollfds[ openslot ].lpoll_revents = 0; + return( 1 ); +} + + +/* + * returns 1 if any "events" from "sb" were removed from pollfds + * returns 0 of "sb" wasn't in pollfds or if events did not overlap. + */ +static int +nsldapi_clear_from_cb_pollfds( Sockbuf *sb, + struct nsldapi_cb_statusinfo *pip, short events ) +{ + int i; + + for ( i = 0; i < pip->cbsi_pollfds_size; ++i ) { + if ( NSLDAPI_CB_POLL_MATCH( sb, pip->cbsi_pollfds[ i ] )) { + if (( pip->cbsi_pollfds[ i ].lpoll_events + & events ) != 0 ) { + pip->cbsi_pollfds[ i ].lpoll_events &= ~events; + if ( pip->cbsi_pollfds[ i ].lpoll_events + == 0 ) { + pip->cbsi_pollfds[i].lpoll_fd = -1; + } + return( 1 ); /* events overlap */ + } else { + return( 0 ); /* events do not overlap */ + } + } + } + + return( 0 ); /* "sb" was not found */ +} + + +/* + * returns 1 if any "revents" from "sb" were set in pollfds revents field. + * returns 0 if not. + */ +static int +nsldapi_find_in_cb_pollfds( Sockbuf *sb, struct nsldapi_cb_statusinfo *pip, + short revents ) +{ + int i; + + for ( i = 0; i < pip->cbsi_pollfds_size; ++i ) { + if ( NSLDAPI_CB_POLL_MATCH( sb, pip->cbsi_pollfds[ i ] )) { + if (( pip->cbsi_pollfds[ i ].lpoll_revents + & revents ) != 0 ) { + return( 1 ); /* revents overlap */ + } else { + return( 0 ); /* revents do not overlap */ + } + } + } + + return( 0 ); /* "sb" was not found */ +} + + +/* + * Install read and write functions into lber layer / sb + */ +int +nsldapi_install_lber_extiofns( LDAP *ld, Sockbuf *sb ) +{ + struct lber_x_ext_io_fns lberiofns; + + if ( NULL != sb ) { + lberiofns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE; + lberiofns.lbextiofn_read = ld->ld_extread_fn; + lberiofns.lbextiofn_write = ld->ld_extwrite_fn; + lberiofns.lbextiofn_writev = ld->ld_extwritev_fn; + lberiofns.lbextiofn_socket_arg = ld->ld_ext_session_arg; + + if ( ber_sockbuf_set_option( sb, LBER_SOCKBUF_OPT_EXT_IO_FNS, + &lberiofns ) != 0 ) { + return( LDAP_LOCAL_ERROR ); + } + } + + return( LDAP_SUCCESS ); +} + + +/* + ****************************************************************************** + * One struct and several functions to bridge the gap between new extended + * I/O functions that are installed using ldap_set_option( ..., + * LDAP_OPT_EXTIO_FN_PTRS, ... ) and the original "classic" I/O functions + * (installed using LDAP_OPT_IO_FN_PTRS) follow. + * + * Our basic strategy is to use the new extended arg to hold a pointer to a + * structure that contains a pointer to the LDAP * (which contains pointers + * to the old functions so we can call them) as well as a pointer to an + * LBER_SOCKET to hold the socket used by the classic functions (the new + * functions use a simple int for the socket). + */ +typedef struct nsldapi_compat_socket_info { + LBER_SOCKET csi_socket; + LDAP *csi_ld; +} NSLDAPICompatSocketInfo; + +static int LDAP_CALLBACK +nsldapi_ext_compat_read( int s, void *buf, int len, + struct lextiof_socket_private *arg ) +{ + NSLDAPICompatSocketInfo *csip = (NSLDAPICompatSocketInfo *)arg; + struct ldap_io_fns *iofns = csip->csi_ld->ld_io_fns_ptr; + + return( iofns->liof_read( csip->csi_socket, buf, len )); +} + + +static int LDAP_CALLBACK +nsldapi_ext_compat_write( int s, const void *buf, int len, + struct lextiof_socket_private *arg ) +{ + NSLDAPICompatSocketInfo *csip = (NSLDAPICompatSocketInfo *)arg; + struct ldap_io_fns *iofns = csip->csi_ld->ld_io_fns_ptr; + + return( iofns->liof_write( csip->csi_socket, buf, len )); +} + + +static int LDAP_CALLBACK +nsldapi_ext_compat_poll( LDAP_X_PollFD fds[], int nfds, int timeout, + struct lextiof_session_private *arg ) +{ + NSLDAPICompatSocketInfo *csip = (NSLDAPICompatSocketInfo *)arg; + struct ldap_io_fns *iofns = csip->csi_ld->ld_io_fns_ptr; + fd_set readfds, writefds; + int i, rc, maxfd = 0; + struct timeval tv, *tvp; + + /* + * Prepare fd_sets for select() + */ + FD_ZERO( &readfds ); + FD_ZERO( &writefds ); + for ( i = 0; i < nfds; ++i ) { + if ( fds[ i ].lpoll_fd < 0 ) { + continue; + } + + if ( fds[ i ].lpoll_fd >= FD_SETSIZE ) { + LDAP_SET_ERRNO( csip->csi_ld, EINVAL ); + return( -1 ); + } + + if ( 0 != ( fds[i].lpoll_events & LDAP_X_POLLIN )) { + FD_SET( fds[i].lpoll_fd, &readfds ); + } + + if ( 0 != ( fds[i].lpoll_events & LDAP_X_POLLOUT )) { + FD_SET( fds[i].lpoll_fd, &writefds ); + } + + fds[i].lpoll_revents = 0; /* clear revents */ + + if ( fds[i].lpoll_fd >= maxfd ) { + maxfd = fds[i].lpoll_fd; + } + } + + /* + * select() using callback. + */ + ++maxfd; + if ( timeout == -1 ) { + tvp = NULL; + } else { + tv.tv_sec = timeout / 1000; + tv.tv_usec = 1000 * ( timeout - tv.tv_sec * 1000 ); + tvp = &tv; + } + rc = iofns->liof_select( maxfd, &readfds, &writefds, NULL, tvp ); + if ( rc <= 0 ) { /* timeout or fatal error */ + return( rc ); + } + + /* + * Use info. in fd_sets to populate poll() revents. + */ + for ( i = 0; i < nfds; ++i ) { + if ( fds[ i ].lpoll_fd < 0 ) { + continue; + } + + if ( 0 != ( fds[i].lpoll_events & LDAP_X_POLLIN ) + && FD_ISSET( fds[i].lpoll_fd, &readfds )) { + fds[i].lpoll_revents |= LDAP_X_POLLIN; + } + + if ( 0 != ( fds[i].lpoll_events & LDAP_X_POLLOUT ) + && FD_ISSET( fds[i].lpoll_fd, &writefds )) { + fds[i].lpoll_revents |= LDAP_X_POLLOUT; + } + + /* XXXmcs: any other cases to deal with? LDAP_X_POLLERR? */ + } + + return( rc ); +} + + +static LBER_SOCKET +nsldapi_compat_socket( LDAP *ld, int secure, int domain, int type, + int protocol ) +{ + int s; + + s = ld->ld_io_fns_ptr->liof_socket( domain, type, protocol ); + + if ( s >= 0 ) { + char *errmsg = NULL; + +#ifdef NSLDAPI_HAVE_POLL + if ( ld->ld_io_fns_ptr->liof_select != NULL + && s >= FD_SETSIZE ) { + errmsg = "can't use socket >= FD_SETSIZE"; + } +#elif !defined(_WINDOWS) /* not on Windows and do not have poll() */ + if ( s >= FD_SETSIZE ) { + errmsg = "can't use socket >= FD_SETSIZE"; + } +#endif + + if ( NULL == errmsg && secure && + ld->ld_io_fns_ptr->liof_ssl_enable( s ) < 0 ) { + errmsg = "failed to enable secure mode"; + } + + if ( NULL != errmsg ) { + if ( NULL == ld->ld_io_fns_ptr->liof_close ) { + nsldapi_os_closesocket( s ); + } else { + ld->ld_io_fns_ptr->liof_close( s ); + } + LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, + nsldapi_strdup( errmsg )); + return( -1 ); + } + } + + return( s ); +} + + +/* + * Note: timeout is ignored because we have no way to pass it via + * the old I/O callback interface. + */ +static int LDAP_CALLBACK +nsldapi_ext_compat_connect( const char *hostlist, int defport, int timeout, + unsigned long options, struct lextiof_session_private *sessionarg, + struct lextiof_socket_private **socketargp ) +{ + NSLDAPICompatSocketInfo *defcsip; + struct ldap_io_fns *iofns; + int s, secure; + NSLDAPI_SOCKET_FN *socketfn; + NSLDAPI_IOCTL_FN *ioctlfn; + NSLDAPI_CONNECT_WITH_TO_FN *connectwithtofn; + NSLDAPI_CONNECT_FN *connectfn; + NSLDAPI_CLOSE_FN *closefn; + + defcsip = (NSLDAPICompatSocketInfo *)sessionarg; + iofns = defcsip->csi_ld->ld_io_fns_ptr; + + if ( 0 != ( options & LDAP_X_EXTIOF_OPT_SECURE )) { + if ( NULL == iofns->liof_ssl_enable ) { + LDAP_SET_ERRNO( defcsip->csi_ld, EINVAL ); + return( -1 ); + } + secure = 1; + } else { + secure = 0; + } + + socketfn = ( iofns->liof_socket == NULL ) ? + nsldapi_os_socket : nsldapi_compat_socket; + ioctlfn = ( iofns->liof_ioctl == NULL ) ? + nsldapi_os_ioctl : (NSLDAPI_IOCTL_FN *)(iofns->liof_ioctl); + if ( NULL == iofns->liof_connect ) { + connectwithtofn = nsldapi_os_connect_with_to; + connectfn = NULL; + } else { + connectwithtofn = NULL; + connectfn = iofns->liof_connect; + } + closefn = ( iofns->liof_close == NULL ) ? + nsldapi_os_closesocket : iofns->liof_close; + + s = nsldapi_try_each_host( defcsip->csi_ld, hostlist, defport, + secure, socketfn, ioctlfn, connectwithtofn, + connectfn, closefn ); + + if ( s >= 0 ) { + NSLDAPICompatSocketInfo *csip; + + if (( csip = (NSLDAPICompatSocketInfo *)NSLDAPI_CALLOC( 1, + sizeof( NSLDAPICompatSocketInfo ))) == NULL ) { + (*closefn)( s ); + LDAP_SET_LDERRNO( defcsip->csi_ld, LDAP_NO_MEMORY, + NULL, NULL ); + return( -1 ); + } + + csip->csi_socket = s; + csip->csi_ld = defcsip->csi_ld; + *socketargp = (void *)csip; + + /* + * We always return 1, which is a valid but not unique socket + * (file descriptor) number. The extended I/O functions only + * require that the combination of the void *arg and the int + * socket be unique. Since we allocate the + * NSLDAPICompatSocketInfo that we assign to arg, we meet + * that requirement. + */ + s = 1; + } + + return( s ); +} + + +static int LDAP_CALLBACK +nsldapi_ext_compat_close( int s, struct lextiof_socket_private *arg ) +{ + NSLDAPICompatSocketInfo *csip = (NSLDAPICompatSocketInfo *)arg; + struct ldap_io_fns *iofns = csip->csi_ld->ld_io_fns_ptr; + int rc; + + rc = iofns->liof_close( csip->csi_socket ); + + NSLDAPI_FREE( csip ); + + return( rc ); +} + +/* + * Install the I/O functions. + * Return an LDAP error code (LDAP_SUCCESS if all goes well). + */ +int +nsldapi_install_compat_io_fns( LDAP *ld, struct ldap_io_fns *iofns ) +{ + NSLDAPICompatSocketInfo *defcsip; + + if (( defcsip = (NSLDAPICompatSocketInfo *)NSLDAPI_CALLOC( 1, + sizeof( NSLDAPICompatSocketInfo ))) == NULL ) { + return( LDAP_NO_MEMORY ); + } + + defcsip->csi_socket = -1; + defcsip->csi_ld = ld; + + if ( ld->ld_io_fns_ptr != NULL ) { + (void)memset( (char *)ld->ld_io_fns_ptr, 0, + sizeof( struct ldap_io_fns )); + } else if (( ld->ld_io_fns_ptr = (struct ldap_io_fns *)NSLDAPI_CALLOC( + 1, sizeof( struct ldap_io_fns ))) == NULL ) { + NSLDAPI_FREE( defcsip ); + return( LDAP_NO_MEMORY ); + } + + /* struct copy */ + *(ld->ld_io_fns_ptr) = *iofns; + + ld->ld_extio_size = LBER_X_EXTIO_FNS_SIZE; + ld->ld_ext_session_arg = defcsip; + ld->ld_extread_fn = nsldapi_ext_compat_read; + ld->ld_extwrite_fn = nsldapi_ext_compat_write; + ld->ld_extpoll_fn = nsldapi_ext_compat_poll; + ld->ld_extconnect_fn = nsldapi_ext_compat_connect; + ld->ld_extclose_fn = nsldapi_ext_compat_close; + + return( nsldapi_install_lber_extiofns( ld, ld->ld_sbp )); +} +/* + * end of compat I/O functions + ****************************************************************************** + */ |