#define LDAP_OPT_SOCKBUF 0x5008 /* sockbuf */
#define LDAP_OPT_DEFBASE 0x5009 /* searchbase */
#define LDAP_OPT_CONNECT_ASYNC 0x5010 /* create connections asynchronously */
+#define LDAP_OPT_CONNECT_CB 0x5011 /* connection callbacks */
/* OpenLDAP TLS options */
#define LDAP_OPT_X_TLS 0x6000
-#define LDAP_OPT_X_TLS_CTX 0x6001 /* OpenSSL CTX */
+#define LDAP_OPT_X_TLS_CTX 0x6001 /* OpenSSL CTX* */
#define LDAP_OPT_X_TLS_CACERTFILE 0x6002
#define LDAP_OPT_X_TLS_CACERTDIR 0x6003
#define LDAP_OPT_X_TLS_CERTFILE 0x6004
/* #define LDAP_OPT_X_TLS_PROTOCOL 0x6007 */
#define LDAP_OPT_X_TLS_CIPHER_SUITE 0x6008
#define LDAP_OPT_X_TLS_RANDOM_FILE 0x6009
-#define LDAP_OPT_X_TLS_SSL_CTX 0x600a
+#define LDAP_OPT_X_TLS_SSL_CTX 0x600a /* OpenSSL SSL* */
#define LDAP_OPT_X_TLS_CRLCHECK 0x600b
#define LDAP_OPT_X_TLS_CONNECT_CB 0x600c
#define LDAP_OPT_X_TLS_CONNECT_ARG 0x600d
* End of LDAP sync (RFC4533) API
*/
+/*
+ * Connection callbacks...
+ */
+struct ldap_conncb;
+struct sockaddr;
+
+/* Called after a connection is established */
+typedef int (ldap_conn_add_f) LDAP_P(( LDAP *ld, Sockbuf *sb, LDAPURLDesc *srv, struct sockaddr *addr,
+ struct ldap_conncb *ctx ));
+/* Called before a connection is closed */
+typedef void (ldap_conn_del_f) LDAP_P(( LDAP *ld, Sockbuf *sb, struct ldap_conncb *ctx ));
+
+/* Callbacks are pushed on a stack. Last one pushed is first one executed. The
+ * delete callback is called with a NULL Sockbuf just before freeing the LDAP handle.
+ */
+typedef struct ldap_conncb {
+ ldap_conn_add_f *lc_add;
+ ldap_conn_del_f *lc_del;
+ void *lc_arg;
+} ldap_conncb;
+
/*
* The API draft spec says we should declare (or cause to be declared)
* 'struct timeval'. We don't. See IETF LDAPext discussions.
};
#endif
+typedef struct ldaplist {
+ struct ldaplist *ll_next;
+ void *ll_data;
+} ldaplist;
+
/*
* structure representing get/set'able options
* which have global defaults.
LDAP_URLLIST_PROC *ldo_urllist_proc;
void *ldo_urllist_params;
+ /* LDAP connection callback stack */
+ ldaplist *ldo_conn_cbs;
+
LDAP_BOOLEANS ldo_booleans; /* boolean options */
};
LDAP_F (int) ldap_int_timeval_dup( struct timeval **dest,
const struct timeval *tm );
LDAP_F (int) ldap_connect_to_host( LDAP *ld, Sockbuf *sb,
- int proto, const char *host, int port, int async );
+ int proto, LDAPURLDesc *srv, int async );
LDAP_F (int) ldap_int_poll( LDAP *ld, ber_socket_t s,
struct timeval *tvp );
LDAP_F (int) ldap_is_read_ready( LDAP *ld, Sockbuf *sb );
LDAP_F (int) ldap_is_write_ready( LDAP *ld, Sockbuf *sb );
+LDAP_F (int) ldap_int_connect_cbs( LDAP *ld, Sockbuf *sb,
+ ber_socket_t *s, LDAPURLDesc *srv, struct sockaddr *addr );
+
/*
* in os-local.c
*/
#ifdef LDAP_PF_LOCAL
LDAP_F (int) ldap_connect_to_path( LDAP *ld, Sockbuf *sb,
- const char *path, int async );
+ LDAPURLDesc *srv, int async );
#endif /* LDAP_PF_LOCAL */
/*
ld->ld_options.ldo_sctrls = NULL;
ld->ld_options.ldo_cctrls = NULL;
ld->ld_options.ldo_defludp = NULL;
+ ld->ld_options.ldo_conn_cbs = NULL;
#ifdef HAVE_CYRUS_SASL
ld->ld_options.ldo_def_sasl_mech = gopts->ldo_def_sasl_mech
switch ( proto = ldap_pvt_url_scheme2proto( srv->lud_scheme ) ) {
case LDAP_PROTO_TCP:
- port = srv->lud_port;
-
- if ( srv->lud_host == NULL || *srv->lud_host == 0 ) {
- host = NULL;
- } else {
- host = srv->lud_host;
- }
-
- if( !port ) {
- if( strcmp(srv->lud_scheme, "ldaps") == 0 ) {
- port = LDAPS_PORT;
- } else {
- port = LDAP_PORT;
- }
- }
-
rc = ldap_connect_to_host( ld, conn->lconn_sb,
- proto, host, port, async );
+ proto, srv, async );
if ( rc == -1 ) return rc;
-
#ifdef LDAP_DEBUG
ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
LBER_SBIOD_LEVEL_PROVIDER, (void *)"tcp_" );
#ifdef LDAP_CONNECTIONLESS
case LDAP_PROTO_UDP:
- port = srv->lud_port;
-
- if ( srv->lud_host == NULL || *srv->lud_host == 0 ) {
- host = NULL;
- } else {
- host = srv->lud_host;
- }
-
- if( !port ) port = LDAP_PORT;
-
LDAP_IS_UDP(ld) = 1;
rc = ldap_connect_to_host( ld, conn->lconn_sb,
- proto, host, port, async );
+ proto, srv, async );
if ( rc == -1 ) return rc;
#ifdef LDAP_DEBUG
#ifdef LDAP_PF_LOCAL
/* only IPC mechanism supported is PF_LOCAL (PF_UNIX) */
rc = ldap_connect_to_path( ld, conn->lconn_sb,
- srv->lud_host, async );
+ srv, async );
if ( rc == -1 ) return rc;
#ifdef LDAP_DEBUG
ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
case LDAP_OPT_CONNECT_ASYNC:
* (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_CONNECT_ASYNC);
return LDAP_OPT_SUCCESS;
-
+
+ case LDAP_OPT_CONNECT_CB:
+ {
+ /* Getting deletes the specified callback */
+ ldaplist **ll = &lo->ldo_conn_cbs;
+ for (;*ll;ll = &(*ll)->ll_next) {
+ if ((*ll)->ll_data == outvalue) {
+ ldaplist *lc = *ll;
+ *ll = lc->ll_next;
+ LDAP_FREE(lc);
+ break;
+ }
+ }
+ }
+ return LDAP_OPT_SUCCESS;
+
case LDAP_OPT_RESULT_CODE:
if(ld == NULL) {
/* bad param */
case LDAP_OPT_DEBUG_LEVEL:
lo->ldo_debug = * (const int *) invalue;
return LDAP_OPT_SUCCESS;
+
+ case LDAP_OPT_CONNECT_CB:
+ {
+ /* setting pushes the callback */
+ ldaplist *ll;
+ ll = LDAP_MALLOC( sizeof( *ll ));
+ ll->ll_data = (void *)invalue;
+ ll->ll_next = lo->ldo_conn_cbs;
+ lo->ldo_conn_cbs = ll;
+ }
+ return LDAP_OPT_SUCCESS;
}
return LDAP_OPT_ERROR;
}
}
#endif
+int
+ldap_int_connect_cbs(LDAP *ld, Sockbuf *sb, ber_socket_t *s, LDAPURLDesc *srv, struct sockaddr *addr)
+{
+ struct ldapoptions *lo;
+ ldaplist *ll;
+ ldap_conncb *cb;
+ int rc;
+
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_FD, s );
+
+ /* Invoke all handle-specific callbacks first */
+ lo = &ld->ld_options;
+ for (ll = lo->ldo_conn_cbs; ll; ll = ll->ll_next) {
+ cb = ll->ll_data;
+ rc = cb->lc_add( ld, sb, srv, addr, cb );
+ /* on any failure, call the teardown functions for anything
+ * that previously succeeded
+ */
+ if ( rc ) {
+ ldaplist *l2;
+ for (l2 = lo->ldo_conn_cbs; l2 != ll; l2 = l2->ll_next) {
+ cb = l2->ll_data;
+ cb->lc_del( ld, sb, cb );
+ }
+ /* a failure might have implicitly closed the fd */
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, s );
+ return rc;
+ }
+ }
+ lo = LDAP_INT_GLOBAL_OPT();
+ for (ll = lo->ldo_conn_cbs; ll; ll = ll->ll_next) {
+ cb = ll->ll_data;
+ rc = cb->lc_add( ld, sb, srv, addr, cb );
+ if ( rc ) {
+ ldaplist *l2;
+ for (l2 = lo->ldo_conn_cbs; l2 != ll; l2 = l2->ll_next) {
+ cb = l2->ll_data;
+ cb->lc_del( ld, sb, cb );
+ }
+ lo = &ld->ld_options;
+ for (l2 = lo->ldo_conn_cbs; l2; l2 = l2->ll_next) {
+ cb = l2->ll_data;
+ cb->lc_del( ld, sb, cb );
+ }
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, s );
+ return rc;
+ }
+ }
+ return 0;
+}
int
ldap_connect_to_host(LDAP *ld, Sockbuf *sb,
- int proto,
- const char *host, int port,
+ int proto, LDAPURLDesc *srv,
int async )
{
int rc;
- int socktype;
+ int socktype, port;
ber_socket_t s = AC_SOCKET_INVALID;
+ char *host;
#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
char serv[7];
char *ha_buf=NULL;
#endif
- if( host == NULL ) host = "localhost";
-
+ if ( srv->lud_host == NULL || *srv->lud_host == 0 ) {
+ host = "localhost";
+ } else {
+ host = srv->lud_host;
+ }
+
+ port = srv->lud_port;
+
+ if( !port ) {
+ if( strcmp(srv->lud_scheme, "ldaps") == 0 ) {
+ port = LDAPS_PORT;
+ } else {
+ port = LDAP_PORT;
+ }
+ }
+
switch(proto) {
case LDAP_PROTO_TCP: socktype = SOCK_STREAM;
osip_debug( ld,
rc = ldap_pvt_connect( ld, s,
sai->ai_addr, sai->ai_addrlen, async );
if ( rc == 0 || rc == -2 ) {
- ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_FD, &s );
- break;
+ err = ldap_int_connect_cbs( ld, sb, &s, srv, sai->ai_addr );
+ if ( err )
+ rc = err;
+ else
+ break;
}
ldap_pvt_close_socket(ld, s);
}
async);
if ( (rc == 0) || (rc == -2) ) {
- ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_FD, &s );
- break;
+ i = ldap_int_connect_cbs( ld, sb, &s, srv, (struct sockaddr *)&sin );
+ if ( i )
+ rc = i;
+ else
+ break;
}
ldap_pvt_close_socket(ld, s);
}
int
-ldap_connect_to_path(LDAP *ld, Sockbuf *sb, const char *path, int async)
+ldap_connect_to_path(LDAP *ld, Sockbuf *sb, LDAPURLDesc *srv, int async)
{
struct sockaddr_un server;
ber_socket_t s;
int rc;
+ const char *path = srv->lud_host;
oslocal_debug(ld, "ldap_connect_to_path\n",0,0,0);
rc = ldap_pvt_connect(ld, s, &server, async);
if (rc == 0) {
- ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_FD, (void *)&s );
- } else {
+ int err;
+ err = ldap_int_connect_cbs( ld, sb, &s, srv, (struct sockaddr *)&server );
+ if ( err )
+ rc = err;
+ }
+ if ( rc ) {
ldap_pvt_close_socket(ld, s);
}
return rc;
ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
#endif
+ /* process connection callbacks */
+ {
+ struct ldapoptions *lo;
+ ldaplist *ll;
+ ldap_conncb *cb;
+
+ lo = &ld->ld_options;
+ if ( lo->ldo_conn_cbs ) {
+ for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
+ cb = ll->ll_data;
+ cb->lc_del( ld, lc->lconn_sb, cb );
+ }
+ }
+ lo = LDAP_INT_GLOBAL_OPT();
+ if ( lo->ldo_conn_cbs ) {
+ for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
+ cb = ll->ll_data;
+ cb->lc_del( ld, lc->lconn_sb, cb );
+ }
+ }
+ }
+
if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) {
ldap_mark_select_clear( ld, lc->lconn_sb );
if ( unbind ) {
ldap_pvt_thread_mutex_unlock( &ld->ld_res_mutex );
#endif
+ /* final close callbacks */
+ {
+ ldaplist *ll, *next;
+
+ for ( ll = ld->ld_options.ldo_conn_cbs; ll; ll = next ) {
+ ldap_conncb *cb = ll->ll_data;
+ next = ll->ll_next;
+ cb->lc_del( ld, NULL, cb );
+ LDAP_FREE( ll );
+ }
+ }
+
if ( ld->ld_error != NULL ) {
LDAP_FREE( ld->ld_error );
ld->ld_error = NULL;