From 3f04d776155a14bc03d7a0b9bcaabcd385f9dba3 Mon Sep 17 00:00:00 2001 From: Quanah Gibson-Mount Date: Wed, 3 Sep 2008 23:11:06 +0000 Subject: [PATCH] Add LDAP_OPT_CONNECT_CB connection callbacks Pass LDAPURLDescs to connect functions instead of host/port --- include/ldap.h | 26 ++++++++++- libraries/libldap/ldap-int.h | 15 +++++- libraries/libldap/open.c | 34 ++------------ libraries/libldap/options.c | 28 +++++++++++- libraries/libldap/os-ip.c | 88 ++++++++++++++++++++++++++++++++---- libraries/libldap/os-local.c | 11 +++-- libraries/libldap/request.c | 22 +++++++++ libraries/libldap/unbind.c | 12 +++++ 8 files changed, 189 insertions(+), 47 deletions(-) diff --git a/include/ldap.h b/include/ldap.h index fe2934366b..7f87330dae 100644 --- a/include/ldap.h +++ b/include/ldap.h @@ -123,10 +123,11 @@ LDAP_BEGIN_DECL #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 @@ -135,7 +136,7 @@ LDAP_BEGIN_DECL /* #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 @@ -881,6 +882,27 @@ struct ldap_sync_t { * 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. diff --git a/libraries/libldap/ldap-int.h b/libraries/libldap/ldap-int.h index be8e1c015c..bbd6b6deed 100644 --- a/libraries/libldap/ldap-int.h +++ b/libraries/libldap/ldap-int.h @@ -160,6 +160,11 @@ struct ldaptls { }; #endif +typedef struct ldaplist { + struct ldaplist *ll_next; + void *ll_data; +} ldaplist; + /* * structure representing get/set'able options * which have global defaults. @@ -236,6 +241,9 @@ struct ldapoptions { LDAP_URLLIST_PROC *ldo_urllist_proc; void *ldo_urllist_params; + /* LDAP connection callback stack */ + ldaplist *ldo_conn_cbs; + LDAP_BOOLEANS ldo_booleans; /* boolean options */ }; @@ -503,7 +511,7 @@ LDAP_F (void) ldap_int_ip_init( void ); 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 ); @@ -522,12 +530,15 @@ LDAP_F (void) ldap_mark_select_clear( LDAP *ld, Sockbuf *sb ); 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 */ /* diff --git a/libraries/libldap/open.c b/libraries/libldap/open.c index 6dec230650..cda6e86423 100644 --- a/libraries/libldap/open.c +++ b/libraries/libldap/open.c @@ -123,6 +123,7 @@ ldap_create( LDAP **ldp ) 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 @@ -343,27 +344,10 @@ ldap_int_open_connection( 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_" ); @@ -375,19 +359,9 @@ ldap_int_open_connection( #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 @@ -406,7 +380,7 @@ ldap_int_open_connection( #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, diff --git a/libraries/libldap/options.c b/libraries/libldap/options.c index b4224bc1f1..9f1e9f1bc7 100644 --- a/libraries/libldap/options.c +++ b/libraries/libldap/options.c @@ -249,7 +249,22 @@ ldap_get_option( 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 */ @@ -734,6 +749,17 @@ ldap_set_option( 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; } diff --git a/libraries/libldap/os-ip.c b/libraries/libldap/os-ip.c index b70d60dda2..b5beacf252 100644 --- a/libraries/libldap/os-ip.c +++ b/libraries/libldap/os-ip.c @@ -424,16 +424,66 @@ ldap_pvt_inet_aton( const char *host, struct in_addr *in) } #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]; @@ -448,8 +498,22 @@ ldap_connect_to_host(LDAP *ld, Sockbuf *sb, 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, @@ -537,8 +601,11 @@ ldap_connect_to_host(LDAP *ld, Sockbuf *sb, 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); } @@ -609,8 +676,11 @@ ldap_connect_to_host(LDAP *ld, Sockbuf *sb, 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); diff --git a/libraries/libldap/os-local.c b/libraries/libldap/os-local.c index 0f3c1ee34d..047151e626 100644 --- a/libraries/libldap/os-local.c +++ b/libraries/libldap/os-local.c @@ -319,11 +319,12 @@ sendcred: } 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); @@ -350,8 +351,12 @@ ldap_connect_to_path(LDAP *ld, Sockbuf *sb, const char *path, int async) 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; diff --git a/libraries/libldap/request.c b/libraries/libldap/request.c index 31d72ca0e2..8d92aa743d 100644 --- a/libraries/libldap/request.c +++ b/libraries/libldap/request.c @@ -642,6 +642,28 @@ ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind ) 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 ) { diff --git a/libraries/libldap/unbind.c b/libraries/libldap/unbind.c index 42277bb6f1..ff975856f6 100644 --- a/libraries/libldap/unbind.c +++ b/libraries/libldap/unbind.c @@ -112,6 +112,18 @@ ldap_ld_free( 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; -- 2.39.5