os-ip.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright 2005 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):
*/
/*
* 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"
#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
/*
* 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() */
};
#endif /* else NSLDAPI_HAVE_POLL */
struct nsldapi_cb_statusinfo { /* used with ext. I/O poll() callback */
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
#if defined(LDAP_SASLIO_HOOKS)
#else
#endif
struct nsldapi_iostatus_info {
int ios_type;
int ios_read_count;
int ios_write_count;
union {
struct nsldapi_os_statusinfo ios_osinfo;
struct nsldapi_cb_statusinfo ios_cbinfo;
} ios_status;
};
#ifdef NSLDAPI_HAVE_POLL
static int nsldapi_add_to_os_pollfds( int fd,
static int nsldapi_clear_from_os_pollfds( int fd,
static int nsldapi_find_in_os_pollfds( int fd,
#endif /* NSLDAPI_HAVE_POLL */
#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
#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 */
/*
* Function typedefs used by nsldapi_try_each_host()
*/
int namelen );
typedef int (NSLDAPI_CLOSE_FN )( LBER_SOCKET s );
static int
{
int rc;
#ifdef _WINDOWS
rc = closesocket( s );
#else
#endif
return( rc );
}
static LBER_SOCKET
{
int s, invalid_socket;
if ( secure ) {
"secure mode not supported") ));
return( -1 );
}
/*
* 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 )) {
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 ( !invalid_socket ) {
nsldapi_os_closesocket( s );
}
return( -1 );
}
return( s );
}
/*
* Non-blocking connect call function
*/
static int
{
#ifndef _WINDOWS
int flags;
#endif /* _WINDOWS */
int n, error;
int len;
#ifdef _WINDOWS
int nonblock = 1;
int block = 0;
#endif /* _WINDOWS */
int continue_on_intr = 0;
#ifdef _SOLARIS_SDK
#else
#endif
msec, 0, 0);
#ifdef _WINDOWS
#else
#endif /* _WINDOWS */
error = 0;
#ifdef _WINDOWS
#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 _WINDOWS
#endif /* _WINDOWS */
"resetting connect timeout to default value "
"(LDAP_X_IO_TIMEOUT_NO_TIMEOUT\n", 0, 0, 0);
} else {
if (msec != 0) {
#ifdef _SOLARIS_SDK
start_time = gethrtime();
#else
#endif
} else {
}
}
/* 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
return (-1);
}
/* if wset is set, the connect worked */
< 0)
return (-1);
goto done;
}
/* if eset is set, the connect failed */
return (-1);
}
/* failure on select call */
if (n == SOCKET_ERROR) {
perror("select error: SOCKET_ERROR returned");
return (-1);
}
#else
/*
* if LDAP_BITOPT_RESTART and select() is interrupted
* try again.
*/
do {
continue_on_intr = 0;
(msec != LDAP_X_IO_TIMEOUT_NO_TIMEOUT) ? \
return (-1);
}
if (n < 0) {
continue_on_intr = 1;
errno = 0;
/* honour the timeout */
if ((msec != LDAP_X_IO_TIMEOUT_NO_TIMEOUT) &&
(msec != 0)) {
#ifdef _SOLARIS_SDK
if ((tv_time -=
(tmp_time - start_time)) <= 0) {
#else
(tmp_time - start_time)) <= 0) {
#endif
/* timeout */
return (-1);
}
#ifdef _SOLARIS_SDK
#endif
}
} else {
#ifdef LDAP_DEBUG
perror("select error: ");
#endif
return (-1);
}
}
} while (continue_on_intr == 1);
< 0)
return (-1);
#ifdef LDAP_DEBUG
} else if ( ldap_debug & LDAP_DEBUG_TRACE ) {
perror("select error: sockfd not set");
#endif
}
#endif /* _WINDOWS */
done:
#ifdef _WINDOWS
#else
#endif /* _WINDOWS */
if (error) {
return (-1);
}
return (0);
}
static int
{
int err;
#ifdef _WINDOWS
#endif
return( -1 );
}
#ifdef _WINDOWS
#else
#endif
return( err );
}
int
/*
* "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;
/*
* If an extended I/O connect callback has been defined, just use it.
*/
unsigned long connect_opts = 0;
}
if ( secure ) {
}
#ifdef _SOLARIS_SDK
, NULL );
#else
);
#endif /* _SOLARIS_SDK */
} else {
}
if ( s < 0 ) {
return( -1 );
}
/*
* Set krbinstancep (canonical name of host for use by Kerberos).
*/
#ifdef KERBEROS
char *p;
*p = '\0';
}
#else /* KERBEROS */
*krbinstancep = NULL;
#endif /* KERBEROS */
return( 0 );
}
/*
* Returns a socket number if successful and -1 if an error occurs.
*/
static int
{
struct sockaddr_in sin;
char *host;
struct ldap_x_hostlist_status *status;
#ifdef GETHOSTBYNAME_BUF_T
#endif /* GETHOSTBYNAME_BUF_T */
connected = 0;
&status );
s = 0;
use_hp = 0;
}
} else {
/*
* DNS callback installed... use it.
*/
#ifdef GETHOSTBYNAME_buf_t
/* avoid allocation by using hbuf if large enough */
} else {
}
#else /* GETHOSTBYNAME_buf_t */
ld->ld_dns_bufsize );
#endif /* else GETHOSTBYNAME_buf_t */
ldap_memfree( host );
return( -1 );
}
}
}
if ( ldhpbuf_allocd != NULL ) {
}
ldap_memfree( host );
return( -1 );
}
use_hp = 1;
}
rc = -1;
SOCK_STREAM, 0 ))) {
if ( ldhpbuf_allocd != NULL ) {
}
ldap_memfree( host );
return( -1 );
}
int iostatus = 1;
if ( err == -1 ) {
"FIONBIO ioctl failed on %d\n",
s, 0, 0 );
}
}
{
/*
* 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
*/
sigemptyset( &ints_off );
#endif /* LDAP_CONNECT_MUST_NOT_BE_INTERRUPTED */
if ( NULL != connectwithtofn ) {
err = (*connectwithtofn)(s,
sizeof(struct sockaddr_in),
ld);
} else {
sizeof(struct sockaddr_in));
}
/*
* restore original signal mask
*/
#endif /* LDAP_CONNECT_MUST_NOT_BE_INTERRUPTED */
}
if ( err >= 0 ) {
connected = 1;
rc = 0;
break;
} else {
#ifdef _WINDOWS
#endif /* _WINDOWS */
if ( NSLDAPI_ERRNO_IO_INPROGRESS( err )) {
0, 0, 0 );
rc = -2;
break;
}
}
#ifdef LDAP_DEBUG
if ( ldap_debug & LDAP_DEBUG_TRACE ) {
}
#endif
(*closefn)( s );
if ( !use_hp ) {
break;
}
}
}
ldap_memfree( host );
}
if ( ldhpbuf_allocd != NULL ) {
}
ldap_memfree( host );
if ( connected ) {
}
return( rc == 0 ? s : -1 );
}
void
{
} else {
}
}
#ifdef KERBEROS
char *
{
char *p;
int len;
struct sockaddr_in sin;
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.
*/
}
}
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_init_nolock( ld ) < 0 ) {
return( -1 );
}
#ifdef NSLDAPI_HAVE_POLL
++iosp->ios_write_count;
}
#else /* NSLDAPI_HAVE_POLL */
++iosp->ios_write_count;
}
#endif /* else NSLDAPI_HAVE_POLL */
if ( nsldapi_add_to_cb_pollfds( sb,
++iosp->ios_write_count;
}
} else {
"nsldapi_iostatus_interest_write: unknown I/O type %d\n",
}
return( 0 );
}
/*
* 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_init_nolock( ld ) < 0 ) {
return( -1 );
}
#ifdef NSLDAPI_HAVE_POLL
++iosp->ios_read_count;
}
#else /* NSLDAPI_HAVE_POLL */
++iosp->ios_read_count;
}
#endif /* else NSLDAPI_HAVE_POLL */
if ( nsldapi_add_to_cb_pollfds( sb,
++iosp->ios_read_count;
}
} else {
"nsldapi_iostatus_interest_read: unknown I/O type %d\n",
}
return( 0 );
}
/*
* 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_init_nolock( ld ) < 0 ) {
return( -1 );
}
#ifdef NSLDAPI_HAVE_POLL
--iosp->ios_write_count;
}
--iosp->ios_read_count;
}
#else /* NSLDAPI_HAVE_POLL */
--iosp->ios_write_count;
}
--iosp->ios_read_count;
}
#endif /* else NSLDAPI_HAVE_POLL */
if ( nsldapi_clear_from_cb_pollfds( sb,
--iosp->ios_write_count;
}
if ( nsldapi_clear_from_cb_pollfds( sb,
--iosp->ios_read_count;
}
} else {
"nsldapi_iostatus_interest_clear: unknown I/O type %d\n",
}
return( 0 );
}
/*
* Return a non-zero value if sb is ready for write.
*/
int
{
int rc;
#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().
*/
#else /* NSLDAPI_HAVE_POLL */
#endif /* else NSLDAPI_HAVE_POLL */
} else {
"nsldapi_iostatus_is_write_ready: unknown I/O type %d\n",
rc = 0;
}
return( rc );
}
/*
* Return a non-zero value if sb is ready for read.
*/
int
{
int rc;
#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().
*/
#else /* NSLDAPI_HAVE_POLL */
#endif /* else NSLDAPI_HAVE_POLL */
} else {
"nsldapi_iostatus_is_read_ready: unknown I/O type %d\n",
rc = 0;
}
return( rc );
}
/*
* Allocated 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
{
return( 0 );
}
sizeof( NSLDAPIIOStatus ))) == NULL ) {
return( -1 );
}
#ifndef NSLDAPI_HAVE_POLL
#endif /* !NSLDAPI_HAVE_POLL */
} else {
}
return( 0 );
}
void
{
return;
}
/* clean up classic I/O compatibility glue */
}
}
/* clean up I/O status tracking info. */
#ifdef NSLDAPI_HAVE_POLL
!= NULL ) {
}
#endif /* NSLDAPI_HAVE_POLL */
!= NULL ) {
}
} else {
"nsldapi_iostatus_free: unknown I/O type %d\n",
}
NSLDAPI_FREE( iosp );
}
}
static int
nsldapi_get_select_table_size( void )
{
static int tblsize = 0; /* static */
if ( tblsize == 0 ) {
#else
#ifdef USE_SYSCONF
#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
*/
}
}
return( tblsize );
}
static int
{
return( -1 ); /* infinite timout for poll() */
}
}
int
{
int rc;
rc = 0; /* simulate a timeout */
#ifdef NSLDAPI_HAVE_POLL
nsldapi_tv2ms( timeout ));
#else /* NSLDAPI_HAVE_POLL */
/* two (potentially large) struct copies */
#ifdef HPUX9
#else
#endif /* else HPUX9 */
#endif /* else NSLDAPI_HAVE_POLL */
/*
* We always pass the session extended I/O argument to
* the extended poll() callback.
*/
} else {
"nsldapi_iostatus_poll: unknown I/O type %d\n",
rc = 0; /* simulate a timeout (what else to do?) */
}
return( rc );
}
#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
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 ) {
!= events ) {
return( 1 );
} else {
return( 0 );
}
}
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 ) {
* sizeof( struct pollfd ));
} else {
+ pip->ossi_pollfds_size)
* sizeof( struct pollfd ));
}
return( 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
short events )
{
int i;
for ( i = 0; i < pip->ossi_pollfds_size; ++i ) {
}
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
short revents )
{
int i;
for ( i = 0; i < pip->ossi_pollfds_size; ++i ) {
return( 1 ); /* revents overlap */
} else {
return( 0 ); /* revents do not overlap */
}
}
}
return( 0 ); /* "fd" was not found */
}
#endif /* NSLDAPI_HAVE_POLL */
/*
* 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
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 ) {
!= events ) {
return( 1 );
} else {
return( 0 );
}
}
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 ) {
if ( pip->cbsi_pollfds_size == 0 ) {
* sizeof( LDAP_X_PollFD ));
} else {
+ pip->cbsi_pollfds_size)
* sizeof( LDAP_X_PollFD ));
}
return( 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
{
int i;
for ( i = 0; i < pip->cbsi_pollfds_size; ++i ) {
& events ) != 0 ) {
== 0 ) {
}
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
short revents )
{
int i;
for ( i = 0; i < pip->cbsi_pollfds_size; ++i ) {
& 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
{
struct lber_x_ext_io_fns lberiofns;
&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 {
static int LDAP_CALLBACK
struct lextiof_socket_private *arg )
{
}
static int LDAP_CALLBACK
struct lextiof_socket_private *arg )
{
}
static int LDAP_CALLBACK
struct lextiof_session_private *arg )
{
/*
* Prepare fd_sets for select()
*/
for ( i = 0; i < nfds; ++i ) {
continue;
}
return( -1 );
}
}
}
}
}
/*
* select() using callback.
*/
++maxfd;
if ( timeout == -1 ) {
} else {
}
if ( rc <= 0 ) { /* timeout or fatal error */
return( rc );
}
/*
* Use info. in fd_sets to populate poll() revents.
*/
for ( i = 0; i < nfds; ++i ) {
continue;
}
}
}
/* XXXmcs: any other cases to deal with? LDAP_X_POLLERR? */
}
return( rc );
}
static LBER_SOCKET
int protocol )
{
int s;
if ( s >= 0 ) {
#ifdef NSLDAPI_HAVE_POLL
&& s >= FD_SETSIZE ) {
"can't use socket >= FD_SETSIZE");
}
if ( s >= FD_SETSIZE ) {
errmsg = "can't use socket >= FD_SETSIZE";
}
#endif
"failed to enable secure mode");
}
nsldapi_os_closesocket( s );
} else {
}
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
struct lextiof_socket_private **socketargp
#ifdef _SOLARIS_SDK
, void **not_used )
#else
)
#endif /* _SOLARIS_SDK */
{
struct ldap_io_fns *iofns;
int s, secure;
if ( 0 != ( options & LDAP_X_EXTIOF_OPT_SECURE )) {
return( -1 );
}
secure = 1;
} else {
secure = 0;
}
} else {
}
if ( s >= 0 ) {
sizeof( NSLDAPICompatSocketInfo ))) == NULL ) {
(*closefn)( s );
return( -1 );
}
csip->csi_socket = s;
*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
{
int rc;
NSLDAPI_FREE( csip );
return( rc );
}
/*
* Install the I/O functions.
* Return an LDAP error code (LDAP_SUCCESS if all goes well).
*/
int
{
sizeof( NSLDAPICompatSocketInfo ))) == NULL ) {
return( LDAP_NO_MEMORY );
}
sizeof( struct ldap_io_fns ));
NSLDAPI_FREE( defcsip );
return( LDAP_NO_MEMORY );
}
/* struct copy */
}
/*
* end of compat I/O functions
******************************************************************************
*/
#ifdef _SOLARIS_SDK
/*
* _ns_gethostbyaddr is a helper function for the ssl layer so that
* it can use the ldap layer's gethostbyaddr resolver.
*/
void *extradata)
{
return (NULL);
}
#endif /* _SOLARIS_SDK */