Skip to content
Snippets Groups Projects
Commit 1b41dfbc authored by Kurt Zeilenga's avatar Kurt Zeilenga
Browse files

LDAP_OPT_NETWORK_TIMEOUT feature (ITS#239) from Lars Uffmann.

Needs a bit more work.
	- global net/api timeouts are not inherited on session creation.
	- need configure check for inet_aton() (coming soon)
	- ioctl/fcntl portability issues (should share implementation
		with lber routines)
parent 4f92977f
No related branches found
No related tags found
No related merge requests found
......@@ -112,6 +112,7 @@ LDAP_BEGIN_DECL
#define LDAP_OPT_TIMEOUT 0x5002 /* default timeout */
#define LDAP_OPT_REFHOPLIMIT 0x5003 /* ref hop limit */
#define LDAP_OPT_MATCHED_DN 0x5004 /* should have been in draft */
#define LDAP_OPT_NETWORK_TIMEOUT 0x5005 /* socket level timeout */
/* TLS options */
#define LDAP_OPT_X_TLS_CACERTFILE 0x6001
......
......@@ -315,12 +315,15 @@ void ldap_int_initialize( void )
if ( ldap_int_tblsize == 0 )
ldap_int_ip_init();
gopts.ldo_debug = 0;
gopts.ldo_version = LDAP_VERSION2;
gopts.ldo_deref = LDAP_DEREF_NEVER;
gopts.ldo_timelimit = LDAP_NO_LIMIT;
gopts.ldo_sizelimit = LDAP_NO_LIMIT;
gopts.ldo_debug = 0;
gopts.ldo_tm_api = (struct timeval *)NULL;
gopts.ldo_tm_net = (struct timeval *)NULL;
gopts.ldo_defhost = LDAP_STRDUP("localhost");
gopts.ldo_defport = LDAP_PORT;
......
......@@ -90,6 +90,9 @@ struct ldapoptions {
#define LDAP_VALID_SESSION 0x2
int ldo_debug;
/* per API call timeout */
struct timeval *ldo_tm_api;
struct timeval *ldo_tm_net;
ber_int_t ldo_version; /* version to connect at */
ber_int_t ldo_deref;
......@@ -119,6 +122,7 @@ struct ldapoptions {
LDAP_BOOLEANS ldo_booleans; /* boolean options */
};
/*
* structure for tracking LDAP server host, ports, DNs, etc.
*/
......@@ -353,9 +357,10 @@ int open_ldap_connection( LDAP *ld, Sockbuf *sb, const char *host, int defport,
* in os-ip.c
*/
extern int ldap_int_tblsize;
int ldap_int_timeval_dup( struct timeval **dest, const struct timeval *tm );
int ldap_connect_to_host( LDAP *ld, Sockbuf *sb, const char *host,
unsigned long address, int port, int async );
int ldap_connect_to_host( Sockbuf *sb, const char *host, unsigned long address, int port,
int async );
void ldap_close_connection( Sockbuf *sb );
#ifdef HAVE_KERBEROS
......
......@@ -236,13 +236,13 @@ open_ldap_connection( LDAP *ld, Sockbuf *sb, const char *host, int defport,
port = defport;
}
if (( rc = ldap_connect_to_host( sb, curhost, 0L,
if (( rc = ldap_connect_to_host( ld, sb, curhost, 0L,
port, async )) != -1 ) {
break;
}
}
} else {
rc = ldap_connect_to_host( sb, NULL, htonl( INADDR_LOOPBACK ),
rc = ldap_connect_to_host( ld, sb, 0, htonl( INADDR_LOOPBACK ),
defport, async );
}
......
......@@ -11,6 +11,7 @@
#include <ac/socket.h>
#include <ac/string.h>
#include <ac/time.h>
#include "ldap-int.h"
......@@ -155,6 +156,22 @@ ldap_get_option(
* (ber_socket_t *) outvalue = ber_pvt_sb_get_desc( &(ld->ld_sb) );
return LDAP_OPT_SUCCESS;
case LDAP_OPT_TIMEOUT:
/* the caller has to free outvalue ! */
if ( ldap_int_timeval_dup( outvalue, lo->ldo_tm_api) != 0 )
{
return LDAP_OPT_ERROR;
}
return LDAP_OPT_SUCCESS;
case LDAP_OPT_NETWORK_TIMEOUT:
/* the caller has to free outvalue ! */
if ( ldap_int_timeval_dup( outvalue, lo->ldo_tm_net ) != 0 )
{
return LDAP_OPT_ERROR;
}
return LDAP_OPT_SUCCESS;
case LDAP_OPT_DEREF:
* (int *) outvalue = lo->ldo_deref;
return LDAP_OPT_SUCCESS;
......@@ -322,38 +339,8 @@ ldap_set_option(
return LDAP_OPT_SUCCESS;
}
if(invalue == NULL) {
/* no place to set from */
return LDAP_OPT_ERROR;
}
switch(option) {
case LDAP_OPT_API_INFO:
case LDAP_OPT_DESC:
/* READ ONLY */
break;
case LDAP_OPT_DEREF:
lo->ldo_deref = * (const int *) invalue;
return LDAP_OPT_SUCCESS;
case LDAP_OPT_SIZELIMIT:
lo->ldo_sizelimit = * (const int *) invalue;
return LDAP_OPT_SUCCESS;
case LDAP_OPT_TIMELIMIT:
lo->ldo_timelimit = * (const int *) invalue;
return LDAP_OPT_SUCCESS;
case LDAP_OPT_PROTOCOL_VERSION: {
int vers = * (const int *) invalue;
if (vers < LDAP_VERSION_MIN || vers > LDAP_VERSION_MAX) {
/* not supported */
break;
}
ld->ld_version = vers;
} return LDAP_OPT_SUCCESS;
/* options which can withstand invalue == NULL */
switch ( option ) {
case LDAP_OPT_SERVER_CONTROLS: {
LDAPControl *const *controls =
(LDAPControl *const *) invalue;
......@@ -392,6 +379,70 @@ ldap_set_option(
}
} return LDAP_OPT_SUCCESS;
case LDAP_OPT_TIMEOUT: {
const struct timeval *tv =
(struct timeval *) invalue;
if ( lo->ldo_tm_api != NULL ) {
LDAP_FREE( lo->ldo_tm_api );
lo->ldo_tm_api = NULL;
}
if ( ldap_int_timeval_dup( &lo->ldo_tm_api, tv ) != 0 ) {
return LDAP_OPT_ERROR;
}
} return LDAP_OPT_SUCCESS;
case LDAP_OPT_NETWORK_TIMEOUT: {
const struct timeval *tv =
(struct timeval *) invalue;
if ( lo->ldo_tm_net != NULL ) {
LDAP_FREE( lo->ldo_tm_net );
lo->ldo_tm_net = NULL;
}
if ( ldap_int_timeval_dup( &lo->ldo_tm_net, tv ) != 0 ) {
return LDAP_OPT_ERROR;
}
} return LDAP_OPT_SUCCESS;
}
if(invalue == NULL) {
/* no place to set from */
return LDAP_OPT_ERROR;
}
/* options which cannot withstand invalue == NULL */
switch(option) {
case LDAP_OPT_API_INFO:
case LDAP_OPT_DESC:
/* READ ONLY */
break;
case LDAP_OPT_DEREF:
lo->ldo_deref = * (const int *) invalue;
return LDAP_OPT_SUCCESS;
case LDAP_OPT_SIZELIMIT:
lo->ldo_sizelimit = * (const int *) invalue;
return LDAP_OPT_SUCCESS;
case LDAP_OPT_TIMELIMIT:
lo->ldo_timelimit = * (const int *) invalue;
return LDAP_OPT_SUCCESS;
case LDAP_OPT_PROTOCOL_VERSION: {
int vers = * (const int *) invalue;
if (vers < LDAP_VERSION_MIN || vers > LDAP_VERSION_MAX) {
/* not supported */
break;
}
ld->ld_version = vers;
} return LDAP_OPT_SUCCESS;
case LDAP_OPT_HOST_NAME: {
const char *host = (const char *) invalue;
......
......@@ -25,6 +25,13 @@
#include <io.h>
#endif /* HAVE_IO_H */
#if defined( HAVE_FCNTL_H )
#include <fcntl.h>
#ifndef O_NONBLOCK
#define O_NONBLOCK O_NDELAY
#endif
#endif /* HAVE_FCNTL_H */
#if defined( HAVE_SYS_FILIO_H )
#include <sys/filio.h>
#elif defined( HAVE_SYS_IOCTL_H )
......@@ -35,143 +42,315 @@
int ldap_int_tblsize = 0;
int
ldap_connect_to_host( Sockbuf *sb, const char *host, unsigned long address,
int port, int async )
/*
* 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
* async is only used ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS (non-0 means don't wait for connect)
* XXX async is not used yet!
* nonblock connect code
* written by Lars Uffmann, <lars.uffmann@mediaway.net>.
*
* Copyright 1999, Lars Uffmann, All rights reserved.
* This software is not subject to any license of my employer
* mediaWays GmbH.
*
* OpenLDAP COPYING RESTRICTIONS APPLY, see COPYRIGHT file
*
* Read about the rationale in ldap_connect_timeout:
* ftp://koobera.math.uic.edu/www/docs/connect.html.
*/
#define osip_debug(ld,fmt,arg1,arg2,arg3) \
do { \
ldap_log_printf(ld, LDAP_DEBUG_TRACE, fmt, arg1, arg2, arg3); \
} while(0)
static void
ldap_pvt_set_errno(int err)
{
int rc, i;
ber_socket_t s = AC_SOCKET_INVALID;
int connected, use_hp;
struct sockaddr_in sin;
struct hostent *hp = NULL;
errno = err;
}
int
ldap_int_timeval_dup( struct timeval **dest, const struct timeval *src )
{
struct timeval *new;
assert( dest != NULL );
if (src == NULL) {
*dest = NULL;
return 0;
}
new = (struct timeval *) malloc(sizeof(struct timeval));
if( new == NULL ) {
*dest = NULL;
return 1;
}
SAFEMEMCPY( (char *) new, (char *) src, sizeof(struct timeval));
*dest = new;
return 0;
}
static int
ldap_pvt_ndelay_on(LDAP *ld, int fd)
{
osip_debug(ld, "ldap_ndelay_on: %d\n",fd,0,0);
#ifdef notyet
ioctl_t status; /* for ioctl call */
#endif /* notyet */
/* buffers for ldap_pvt_gethostbyname_a */
struct hostent he_buf;
int local_h_errno;
char *ha_buf=NULL;
#define DO_RETURN(x) if (ha_buf) LDAP_FREE(ha_buf); return (x);
/* #if defined( HAVE_FCNTL_H ) */
return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NONBLOCK);
#else
{
ioctl_t status = 1;
return ioctl( fd, FIONBIO, (caddr_t)&status );
}
#endif
return 0;
}
Debug( LDAP_DEBUG_TRACE, "ldap_connect_to_host: %s:%d\n",
( host == NULL ) ? "(by address)" : host, (int) ntohs( (short) port ), 0 );
connected = use_hp = 0;
if ( host != NULL ) {
address = inet_addr( host );
/* This was just a test for -1 until OSF1 let inet_addr return
unsigned int, which is narrower than 'unsigned long address' */
if ( address == 0xffffffff || address == (unsigned long) -1 ) {
if ( ( ldap_pvt_gethostbyname_a( host, &he_buf, &ha_buf,
&hp, &local_h_errno) < 0) || (hp==NULL))
{
#ifdef HAVE_WINSOCK
errno = WSAGetLastError();
static int
ldap_pvt_ndelay_off(LDAP *ld, int fd)
{
osip_debug(ld, "ldap_ndelay_off: %d\n",fd,0,0);
#ifdef notyet
/* #if defined( HAVE_FCNTL_H ) */
return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK);
#else
errno = EHOSTUNREACH; /* not exactly right, but... */
{
ioctl_t status = 0;
return ioctl( fd, FIONBIO, (caddr_t)&status );
}
#endif
DO_RETURN( -1 );
}
use_hp = 1;
}
}
}
static ber_socket_t
ldap_pvt_socket(LDAP *ld)
{
ber_socket_t s = socket(AF_INET, SOCK_STREAM, 0);
osip_debug(ld, "ldap_new_socket: %d\n",s,0,0);
return ( s );
}
static int
ldap_pvt_close_socket(LDAP *ld, int s)
{
osip_debug(ld, "ldap_close_socket: %d\n",s,0,0);
return tcp_close(s);
}
static int
ldap_pvt_prepare_socket(LDAP *ld, int fd)
{
osip_debug(ld, "ldap_prepare_socket: %d\n",fd,0,0);
rc = -1;
for ( i = 0; !use_hp || ( hp->h_addr_list[ i ] != 0 ); i++ ) {
if (( s = socket( AF_INET, SOCK_STREAM, 0 )) < 0 ) {
DO_RETURN( -1 );
}
#ifdef TCP_NODELAY
{
int tmp = 1;
if( setsockopt( s, IPPROTO_TCP, TCP_NODELAY,
(char *) &tmp, sizeof(tmp) ) == -1 )
{
Debug( LDAP_DEBUG_ANY,
"setsockopt(TCP_NODELAY failed on %d\n",
s, 0, 0 );
}
}
{
int dummy = 1;
if ( setsockopt(fd,IPPROTO_TCP,TCP_NODELAY,&dummy,sizeof(dummy)) == -1 )
return -1;
}
#endif
return 0;
}
/*
* check the socket for errors after select returned.
*/
static int
ldap_pvt_is_socket_ready(LDAP *ld, int s)
{
osip_debug(ld, "ldap_is_sock_ready: %d\n",s,0,0);
#define TRACE \
{ \
osip_debug(ld, \
"ldap_is_socket_ready: errror on socket %d: errno: %d (%s)\n", \
s, \
errno, \
strerror(errno) ); \
}
#ifdef notyet
status = 1;
if ( async && ioctl( s, FIONBIO, (caddr_t)&status ) == -1 ) {
Debug( LDAP_DEBUG_ANY, "FIONBIO ioctl failed on %d\n",
s, 0, 0 );
/* #ifdef SO_ERROR */
{
int so_errno;
int dummy = sizeof(so_errno);
if ( getsockopt(s,SOL_SOCKET,SO_ERROR,&so_errno,&dummy) == -1 )
return -1;
if ( so_errno ) {
ldap_pvt_set_errno(so_errno);
TRACE;
return -1;
}
return 0;
}
#else
{
/* error slippery */
struct sockaddr_in sin;
char ch;
int dummy = sizeof(sin);
if ( getpeername(s, (struct sockaddr *) &sin, &dummy) == -1 ) {
read(s, &ch, 1);
#ifdef HAVE_WINSOCK
ldap_pvt_set_errno( WSAGetLastError() );
#endif
TRACE;
return -1;
}
#endif /* notyet */
(void)memset( (char *)&sin, 0, sizeof( struct sockaddr_in ));
sin.sin_family = AF_INET;
sin.sin_port = port;
SAFEMEMCPY( (char *) &sin.sin_addr.s_addr,
( use_hp ? (char *) hp->h_addr_list[ i ] :
(char *) &address ), sizeof( sin.sin_addr.s_addr) );
if ( connect( s, (struct sockaddr *)&sin,
sizeof( struct sockaddr_in )) >= 0 ) {
connected = 1;
rc = 0;
break;
} else {
return 0;
}
#endif
return -1;
#undef TRACE
}
static int
ldap_pvt_connect(LDAP *ld, int s, struct sockaddr_in *sin, int async)
{
struct timeval tv, *opt_tv=NULL;
fd_set wfds, *z=NULL;
if ( (opt_tv = ld->ld_options.ldo_tm_net) != NULL ) {
tv.tv_usec = opt_tv->tv_usec;
tv.tv_sec = opt_tv->tv_sec;
}
osip_debug(ld, "ldap_connect_timeout: fd: %d tm: %d async: %d\n",
s, opt_tv ? tv.tv_sec : -1, async);
if ( ldap_pvt_ndelay_on(ld, s) == -1 )
return ( -1 );
if ( connect(s, (struct sockaddr *) sin, sizeof(struct sockaddr_in)) == 0 )
{
if ( ldap_pvt_ndelay_off(ld, s) == -1 )
return ( -1 );
return ( 0 );
}
#ifdef HAVE_WINSOCK
errno = WSAGetLastError();
ldap_pvt_set_errno( WSAGetLastError() );
#endif
if ( (errno != EINPROGRESS) && (errno != EWOULDBLOCK) )
return ( -1 );
#ifdef notyet
#ifdef EAGAIN
if ( errno == EINPROGRESS || errno == EAGAIN ) {
#else /* EAGAIN */
if ( errno == EINPROGRESS ) {
#endif /* EAGAIN */
Debug( LDAP_DEBUG_TRACE,
"connect would block...\n", 0, 0, 0 );
rc = -2;
break;
}
#endif /* notyet */
if ( async ) return ( -2 );
#endif
#ifdef LDAP_DEBUG
if ( ldap_debug & LDAP_DEBUG_TRACE ) {
perror( (char *)inet_ntoa( sin.sin_addr ));
}
FD_ZERO(&wfds); FD_SET(s, &wfds );
if ( select(s + 1, z, &wfds, z, opt_tv ? &tv : NULL) == -1)
return ( -1 );
if ( FD_ISSET(s, &wfds) ) {
if ( ldap_pvt_is_socket_ready(ld, s) == -1 )
return ( -1 );
if ( ldap_pvt_ndelay_off(ld, s) == -1 )
return ( -1 );
return ( 0 );
}
osip_debug(ld, "ldap_connect_timeout: timed out\n",0,0,0);
ldap_pvt_set_errno( ETIMEDOUT );
return ( -1 );
}
static int
ldap_pvt_inet_aton( LDAP *ld, const char *host, struct in_addr *in)
{
#ifdef notyet
/* #ifdef HAVE_INET_ATON */
return inet_aton( host, in );
#else
{
unsigned long u = inet_addr( host );
if ( u != 0xffffffff || u != (unsigned long) -1 ) {
in->s_addr = u;
return 1;
}
}
#endif
return 0;
}
int
ldap_connect_to_host(LDAP *ld, Sockbuf *sb, const char *host,
unsigned long address, int port, int async)
{
struct sockaddr_in sin;
struct in_addr in;
ber_socket_t s = AC_SOCKET_INVALID;
int rc, i, use_hp = 0;
struct hostent *hp, he_buf;
int local_h_errno;
char *ha_buf=NULL, *p, *q;
osip_debug(ld, "ldap_connect_to_host\n",0,0,0);
if (host != NULL) {
if (! ldap_pvt_inet_aton( ld, host, &in) ) {
rc = ldap_pvt_gethostbyname_a(host, &he_buf, &ha_buf,
&hp, &local_h_errno);
if ( rc < 0 )
; /*XXX NO MEMORY? */
if ( (rc < 0) || (hp == NULL) ) {
#ifdef HAVE_WINSOCK
ldap_pvt_set_errno( WSAGetLastError() );
#else
/* not exactly right, but... */
ldap_pvt_set_errno( EHOSTUNREACH );
#endif
tcp_close( s );
if ( !use_hp ) {
break;
if (ha_buf) LDAP_FREE(ha_buf);
return -1;
}
use_hp = 1;
}
address = in.s_addr;
}
ber_pvt_sb_set_desc( sb, s );
rc = s = -1;
for ( i = 0; !use_hp || (hp->h_addr_list[i] != 0); ++i, rc = -1 ) {
if ( connected ) {
if ( (s = ldap_pvt_socket( ld )) == -1 )
/* use_hp ? continue : break; */
break;
#ifdef notyet
status = 0;
if ( !async && ioctl( s, FIONBIO, (caddr_t)&on ) == -1 ) {
Debug( LDAP_DEBUG_ANY, "FIONBIO ioctl failed on %d\n",
s, 0, 0 );
if ( ldap_pvt_prepare_socket(ld, s) == -1 ) {
ldap_pvt_close_socket(ld, s);
/* use_hp ? continue : break; */
break;
}
#endif /* notyet */
Debug( LDAP_DEBUG_TRACE, "sd %d connected to: %s\n",
s, (char *) inet_ntoa( sin.sin_addr ), 0 );
}
(void)memset((char *)&sin, 0, sizeof(struct sockaddr_in));
sin.sin_family = AF_INET;
sin.sin_port = port;
p = (char *)&sin.sin_addr.s_addr;
q = use_hp ? (char *)hp->h_addr_list[i] : (char *)&address;
SAFEMEMCPY(p, q, sizeof(p) );
DO_RETURN( rc );
}
osip_debug(ld, "ldap_connect_to_host: Trying %s:%d\n",
inet_ntoa(sin.sin_addr),ntohs(sin.sin_port),0);
rc = ldap_pvt_connect(ld, s, &sin, async);
#undef DO_RETURN
if ( (rc == 0) || (rc == -2) ) {
ber_pvt_sb_set_desc( sb, s );
break;
}
ldap_pvt_close_socket(ld, s);
if (!use_hp)
break;
}
if (ha_buf) LDAP_FREE(ha_buf);
return rc;
}
void
ldap_close_connection( Sockbuf *sb )
......
......@@ -133,6 +133,16 @@ ldap_ld_free(
ld->ld_options.ldo_defhost = NULL;
}
if ( ld->ld_options.ldo_tm_api != NULL ) {
LDAP_FREE( ld->ld_options.ldo_tm_api );
ld->ld_options.ldo_tm_api = NULL;
}
if ( ld->ld_options.ldo_tm_net != NULL ) {
LDAP_FREE( ld->ld_options.ldo_tm_net );
ld->ld_options.ldo_tm_net = NULL;
}
ber_pvt_sb_destroy( &(ld->ld_sb) );
LDAP_FREE( (char *) ld );
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment