cboatwri bcc6f383bb Added Directory (ldap) SDK source files for public release
git-svn-id: svn://10.0.0.236/trunk@2476 18797224-902f-48f8-a5cc-f745e15eee43
1998-05-28 04:23:42 +00:00

754 lines
18 KiB
C

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/*
* 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
#include "ldap-int.h"
#ifdef LDAP_CONNECT_MUST_NOT_BE_INTERRUPTED
#include <signal.h>
#endif
#ifdef NSLDAPI_HAVE_POLL
#include <poll.h>
#endif
#ifdef _WINDOWS
#define CLOSESOCKET(_s) closesocket((_s))
#else
#define CLOSESOCKET(_s) close((_s))
#endif
struct selectinfo {
fd_set si_readfds;
fd_set si_writefds;
fd_set si_use_readfds;
fd_set si_use_writefds;
#ifdef NSLDAPI_HAVE_POLL
struct pollfd *si_pollfds;
int si_pollfds_size;
#endif
};
#ifdef NSLDAPI_HAVE_POLL
static int add_to_pollfds( int fd, struct selectinfo *sip, short events );
static int clear_from_pollfds( int fd, struct selectinfo *sip,
short events );
static int find_in_pollfds( int fd, struct selectinfo *sip, short revents );
#endif
#ifdef irix
/*
* 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
#define NSLDAPI_POLL poll
#define NSLDAPI_SELECT select
#endif
int
nsldapi_connect_to_host( LDAP *ld, Sockbuf *sb, char *host,
unsigned long address, int port, int async, int secure )
/*
* if host == NULL, connect using address
* "address" and "port" must be in network byte order
* zero is returned upon success, -1 if fatal error, -2 EINPROGRESS
* if -1 is returned, ld_errno is set
* non-zero async means don't wait for connect
*/
{
int rc, i, s, connected, use_hp;
struct sockaddr_in sin;
char **addrlist, *ldhpbuf, *ldhpbuf_allocd;
LDAPHostEnt ldhent, *ldhp;
struct hostent *hp;
#ifdef GETHOSTBYNAME_BUF_T
GETHOSTBYNAME_BUF_T hbuf;
struct hostent hent;
#endif
int err;
#ifdef LDAP_ASYNC_IO
int status; /* for ioctl call */
#endif
LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_connect_to_host: %s:%d\n",
( host == NULL ) ? "(by address)" : host,
ntohs( (unsigned short)port ), 0 );
if ( secure && ld->ld_ssl_enable_fn == NULL ) {
LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL );
return( -1 );
}
ldhpbuf_allocd = NULL;
ldhp = NULL;
hp = NULL;
s = 0;
connected = use_hp = 0;
addrlist = NULL;
if ( host != NULL && ( address = inet_addr( host )) == -1 ) {
if ( ld->ld_dns_gethostbyname_fn == NULL ) {
if (( hp = GETHOSTBYNAME( host, &hent, hbuf,
sizeof(hbuf), &err )) != 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 /* GETHOSTBYNAME_buf_t */
if ( ldhpbuf == NULL ) {
LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL,
NULL );
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 );
}
return( -1 );
}
use_hp = 1;
}
rc = -1;
for ( i = 0; !use_hp || ( addrlist[ i ] != 0 ); i++ ) {
if ( ld->ld_socket_fn == NULL ) {
s = socket( AF_INET, SOCK_STREAM, 0 );
} else {
s = ld->ld_socket_fn( AF_INET, SOCK_STREAM, 0 );
}
/*
* if the socket() call failed or it returned a socket larger
* than we can deal with, return a "local error."
*/
#ifdef _WINDOWS
if ( s < 0 ) {
#elif NSLDAPI_HAVE_POLL
if ( s < 0 || ( ld->ld_select_fn != NULL && s >= FD_SETSIZE )) {
#else /* not on Windows and do not have poll() */
if ( s < 0 || s >= FD_SETSIZE ) {
#endif
char *errmsg;
if ( s < 0 ) {
errmsg = "unable to create a socket";
} else {
errmsg = "can't use socket >= FD_SETSIZE";
if ( ld->ld_close_fn == NULL ) {
CLOSESOCKET( s );
} else {
ld->ld_close_fn( s );
}
}
errmsg = nsldapi_strdup( errmsg );
LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, errmsg );
if ( ldhpbuf_allocd != NULL ) {
NSLDAPI_FREE( ldhpbuf_allocd );
}
return( -1 );
}
#ifdef LDAP_ASYNC_IO
status = 1;
if ( async ) {
if ( ld->ld_ioctl_fn == NULL ) {
err = ioctl( s, FIONBIO, (caddr_t)&status );
} else {
err = ld->ld_ioctl_fn( s, FIONBIO,
(caddr_t)&status );
}
if ( err == -1 ) {
LDAPDebug( LDAP_DEBUG_ANY,
"FIONBIO ioctl failed on %d\n", s, 0, 0 );
}
}
#endif /* LDAP_ASYNC_IO */
(void)memset( (char *)&sin, 0, sizeof( struct sockaddr_in ));
sin.sin_family = AF_INET;
sin.sin_port = port;
if ( secure && ld->ld_ssl_enable_fn( s ) < 0 ) {
if ( ld->ld_close_fn == NULL ) {
CLOSESOCKET( s );
} else {
ld->ld_close_fn( s );
}
LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL );
if ( ldhpbuf_allocd != NULL ) {
NSLDAPI_FREE( ldhpbuf_allocd );
}
return( -1 );
}
SAFEMEMCPY( (char *) &sin.sin_addr.s_addr,
( use_hp ? (char *) addrlist[ i ] :
(char *) &address ), sizeof( sin.sin_addr.s_addr) );
if ( ld->ld_connect_fn == NULL ) {
#ifdef LDAP_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
* ns/netsite/ldap/include/portable.h for the definition of
* LDAP_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 );
sigprocmask( SIG_BLOCK, &ints_off, &oldset );
#endif
err = connect( s, (struct sockaddr *)&sin,
sizeof( struct sockaddr_in ));
#ifdef LDAP_CONNECT_MUST_NOT_BE_INTERRUPTED
/*
* restore original signal mask
*/
sigprocmask( SIG_SETMASK, &oldset, 0 );
#endif
} else {
if (ld->ld_options & LDAP_BITOPT_ASYNC)
{
err = 0;
}
else {
err = ld->ld_connect_fn( s,
(struct sockaddr *)&sin,
sizeof( struct sockaddr_in ));
}
}
if ( err >= 0 ) {
connected = 1;
rc = 0;
break;
} else {
#ifdef LDAP_ASYNC_IO
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;
}
#endif /* LDAP_ASYNC_IO */
#ifdef LDAP_DEBUG
if ( ldap_debug & LDAP_DEBUG_TRACE ) {
perror( (char *)inet_ntoa( sin.sin_addr ));
}
#endif
if ( ld->ld_close_fn == NULL ) {
CLOSESOCKET( s );
} else {
ld->ld_close_fn( s );
}
if ( !use_hp ) {
break;
}
}
}
if ( ldhpbuf_allocd != NULL ) {
NSLDAPI_FREE( ldhpbuf_allocd );
}
sb->sb_sd = s;
if ( connected ) {
LDAPDebug( LDAP_DEBUG_TRACE, "sd %d connected to: %s\n",
s, inet_ntoa( sin.sin_addr ), 0 );
}
if ( rc == -1 ) {
LDAP_SET_LDERRNO( ld, LDAP_CONNECT_ERROR, NULL, NULL );
}
return( rc );
}
void
nsldapi_close_connection( LDAP *ld, Sockbuf *sb )
{
if ( ld->ld_close_fn == NULL ) {
CLOSESOCKET( sb->sb_sd );
} else {
ld->ld_close_fn( sb->sb_sd );
}
}
#ifdef KERBEROS
char *
nsldapi_host_connected_to( 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.
*/
/* XXXmcs: need to use DNS callbacks here XXX */
XXX
if (( hp = 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 */
void
nsldapi_mark_select_write( LDAP *ld, Sockbuf *sb )
{
struct selectinfo *sip;
LDAP_MUTEX_LOCK( ld, LDAP_SELECT_LOCK );
sip = (struct selectinfo *)ld->ld_selectinfo;
#ifdef NSLDAPI_HAVE_POLL
if ( ld->ld_select_fn == NULL ) {
if ( add_to_pollfds( sb->sb_sd, sip, POLLOUT )) {
++ld->ld_selectwritecnt;
}
LDAP_MUTEX_UNLOCK( ld, LDAP_SELECT_LOCK );
return;
}
#endif
if ( !FD_ISSET( sb->sb_sd, &sip->si_writefds )) {
FD_SET( sb->sb_sd, &sip->si_writefds );
++ld->ld_selectwritecnt;
}
LDAP_MUTEX_UNLOCK( ld, LDAP_SELECT_LOCK );
}
void
nsldapi_mark_select_read( LDAP *ld, Sockbuf *sb )
{
struct selectinfo *sip;
LDAP_MUTEX_LOCK( ld, LDAP_SELECT_LOCK );
sip = (struct selectinfo *)ld->ld_selectinfo;
#ifdef NSLDAPI_HAVE_POLL
if ( ld->ld_select_fn == NULL ) {
if ( add_to_pollfds( sb->sb_sd, sip, POLLIN )) {
++ld->ld_selectreadcnt;
}
LDAP_MUTEX_UNLOCK( ld, LDAP_SELECT_LOCK );
return;
}
#endif
if ( !FD_ISSET( sb->sb_sd, &sip->si_readfds )) {
FD_SET( sb->sb_sd, &sip->si_readfds );
++ld->ld_selectreadcnt;
}
LDAP_MUTEX_UNLOCK( ld, LDAP_SELECT_LOCK );
}
void
nsldapi_mark_select_clear( LDAP *ld, Sockbuf *sb )
{
struct selectinfo *sip;
LDAP_MUTEX_LOCK( ld, LDAP_SELECT_LOCK );
sip = (struct selectinfo *)ld->ld_selectinfo;
#ifdef NSLDAPI_HAVE_POLL
if ( ld->ld_select_fn == NULL ) {
if ( clear_from_pollfds( sb->sb_sd, sip, POLLIN )) {
--ld->ld_selectreadcnt;
}
if ( clear_from_pollfds( sb->sb_sd, sip, POLLOUT )) {
--ld->ld_selectwritecnt;
}
LDAP_MUTEX_UNLOCK( ld, LDAP_SELECT_LOCK );
return;
}
#endif
if ( FD_ISSET( sb->sb_sd, &sip->si_writefds )) {
FD_CLR( sb->sb_sd, &sip->si_writefds );
--ld->ld_selectwritecnt;
}
if ( FD_ISSET( sb->sb_sd, &sip->si_readfds )) {
FD_CLR( sb->sb_sd, &sip->si_readfds );
--ld->ld_selectreadcnt;
}
LDAP_MUTEX_UNLOCK( ld, LDAP_SELECT_LOCK );
}
int
nsldapi_is_write_ready( LDAP *ld, Sockbuf *sb )
{
struct selectinfo *sip;
LDAP_MUTEX_LOCK( ld, LDAP_SELECT_LOCK );
sip = (struct selectinfo *)ld->ld_selectinfo;
#ifdef NSLDAPI_HAVE_POLL
if ( ld->ld_select_fn == NULL ) {
LDAP_MUTEX_UNLOCK( ld, LDAP_SELECT_LOCK );
/*
* 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().
*/
return( find_in_pollfds( sb->sb_sd, sip, ~POLLIN ));
}
#endif
LDAP_MUTEX_UNLOCK( ld, LDAP_SELECT_LOCK );
return( FD_ISSET( sb->sb_sd, &sip->si_use_writefds ));
}
int
nsldapi_is_read_ready( LDAP *ld, Sockbuf *sb )
{
struct selectinfo *sip;
LDAP_MUTEX_LOCK( ld, LDAP_SELECT_LOCK );
sip = (struct selectinfo *)ld->ld_selectinfo;
#ifdef NSLDAPI_HAVE_POLL
if ( ld->ld_select_fn == NULL ) {
LDAP_MUTEX_UNLOCK( ld, LDAP_SELECT_LOCK );
/*
* 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().
*/
return( find_in_pollfds( sb->sb_sd, sip, ~POLLOUT ));
}
#endif
LDAP_MUTEX_UNLOCK( ld, LDAP_SELECT_LOCK );
return( FD_ISSET( sb->sb_sd, &sip->si_use_readfds ));
}
void *
nsldapi_new_select_info()
{
struct selectinfo *sip;
if (( sip = (struct selectinfo *)NSLDAPI_CALLOC( 1,
sizeof( struct selectinfo ))) != NULL ) {
FD_ZERO( &sip->si_readfds );
FD_ZERO( &sip->si_writefds );
}
return( (void *)sip );
}
void
nsldapi_free_select_info( void *vsip )
{
struct selectinfo *sip = (struct selectinfo *)vsip;
#ifdef NSLDAPI_HAVE_POLL
if ( sip->si_pollfds != NULL ) {
NSLDAPI_FREE( sip->si_pollfds );
}
#endif
NSLDAPI_FREE( sip );
}
int
nsldapi_do_ldap_select( LDAP *ld, struct timeval *timeout )
{
struct selectinfo *sip;
static int tblsize = 0;
LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_do_ldap_select\n", 0, 0, 0 );
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 /* USE_SYSCONF */
#endif /* _WINDOWS */
if ( tblsize >= FD_SETSIZE ) {
/*
* clamp value so we don't overrun the fd_set structure
*/
tblsize = FD_SETSIZE - 1;
}
}
if ( ld->ld_selectreadcnt <= 0 && ld->ld_selectwritecnt <= 0 ) {
return( 0 ); /* simulate a timeout */
}
sip = (struct selectinfo *)ld->ld_selectinfo;
#ifdef NSLDAPI_HAVE_POLL
if ( ld->ld_select_fn == NULL ) {
int to;
if ( timeout == NULL ) {
to = -1;
} else {
to = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
}
return( NSLDAPI_POLL( sip->si_pollfds, sip->si_pollfds_size,
to ));
}
#endif
sip->si_use_readfds = sip->si_readfds;
sip->si_use_writefds = sip->si_writefds;
if ( ld->ld_select_fn != NULL ) {
return( ld->ld_select_fn( tblsize, &sip->si_use_readfds,
&sip->si_use_writefds, NULL, timeout ));
} else {
#ifdef HPUX9
return( NSLDAPI_SELECT( tblsize, (int *)&sip->si_use_readfds,
(int *)&sip->si_use_writefds, NULL, timeout ));
#else
return( NSLDAPI_SELECT( tblsize, &sip->si_use_readfds,
&sip->si_use_writefds, NULL, timeout ));
#endif
}
}
#ifdef NSLDAPI_HAVE_POLL
/*
* 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
add_to_pollfds( int fd, struct selectinfo *sip, short events )
{
int i, openslot;
/* first we check to see if "fd" is already in our pollfds */
openslot = -1;
for ( i = 0; i < sip->si_pollfds_size; ++i ) {
if ( sip->si_pollfds[ i ].fd == fd ) {
if (( sip->si_pollfds[ i ].events & events )
!= events ) {
sip->si_pollfds[ i ].events |= events;
return( 1 );
} else {
return( 0 );
}
}
if ( sip->si_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 5.
*/
if ( openslot == -1 ) {
struct pollfd *newpollfds;
if ( sip->si_pollfds_size == 0 ) {
newpollfds = (struct pollfd *)NSLDAPI_MALLOC(
5 * sizeof( struct pollfd ));
} else {
newpollfds = (struct pollfd *)NSLDAPI_REALLOC(
sip->si_pollfds, (5 + sip->si_pollfds_size) *
sizeof( struct pollfd ));
}
if ( newpollfds == NULL ) { /* XXXmcs: no way to return err! */
return( 0 );
}
sip->si_pollfds = newpollfds;
openslot = sip->si_pollfds_size;
sip->si_pollfds_size += 5;
for ( i = openslot + 1; i < sip->si_pollfds_size; ++i ) {
sip->si_pollfds[ i ].fd = -1;
sip->si_pollfds[ i ].events =
sip->si_pollfds[ i ].revents = 0;
}
}
sip->si_pollfds[ openslot ].fd = fd;
sip->si_pollfds[ openslot ].events = events;
sip->si_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
clear_from_pollfds( int fd, struct selectinfo *sip,
short events )
{
int i;
for ( i = 0; i < sip->si_pollfds_size; ++i ) {
if ( sip->si_pollfds[i].fd == fd ) {
if (( sip->si_pollfds[ i ].events & events ) != 0 ) {
sip->si_pollfds[ i ].events &= ~events;
if ( sip->si_pollfds[ i ].events == 0 ) {
sip->si_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
find_in_pollfds( int fd, struct selectinfo *sip, short revents )
{
int i;
for ( i = 0; i < sip->si_pollfds_size; ++i ) {
if ( sip->si_pollfds[i].fd == fd ) {
if (( sip->si_pollfds[ i ].revents & revents ) != 0 ) {
return( 1 ); /* revents overlap */
} else {
return( 0 ); /* revents do not overlap */
}
}
}
return( 0 ); /* "fd" was not found */
}
#endif /* NSLDAPI_HAVE_POLL */
/******************************************************
* ntstubs.c - Stubs needed on NT when linking in
* the SSL code. If these stubs were not here, the
* named functions below would not be located at link
* time, because there is no implementation of the
* functions for Win32 in cross-platform libraries.
*
******************************************************/
#if defined( _WIN32 ) && defined ( NET_SSL )
/*#include <xp_file.h>*/
typedef enum {xpAddrBook} XP_FileType ;
typedef FILE * XP_File;
typedef char * XP_FilePerm;
XP_File XP_FileOpen(const char* name, XP_FileType type,
const XP_FilePerm permissions)
{
return NULL;
}
char *
WH_FileName (const char *name, XP_FileType type)
{
return NULL;
}
#endif /* WIN32 && NET_SSL */