void nsldapi_handle_reconnect( LDAP *ld ) { LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_handle_reconnect\n", 0, 0, 0 ); /* * if the default connection has been lost and is now marked dead, * dispose of the default connection so it will get re-established. * * if not, clear the bind DN and status to ensure that we don't * report the wrong bind DN to a different thread while waiting * for our bind result to return from the server. */ LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK ); if ( NULL != ld->ld_defconn ) { if ( LDAP_CONNST_DEAD == ld->ld_defconn->lconn_status ) { nsldapi_free_connection( ld, ld->ld_defconn, NULL, NULL, 1, 0 ); ld->ld_defconn = NULL; } else if ( ld->ld_defconn->lconn_binddn != NULL ) { NSLDAPI_FREE( ld->ld_defconn->lconn_binddn ); ld->ld_defconn->lconn_binddn = NULL; ld->ld_defconn->lconn_bound = 0; } } LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); }
void nsldapi_free_request( LDAP *ld, LDAPRequest *lr, int free_conn ) { LDAPRequest *tmplr, *nextlr; LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_free_request 0x%x (origid %d, msgid %d)\n", lr, lr->lr_origid, lr->lr_msgid ); if ( lr->lr_parent != NULL ) { --lr->lr_parent->lr_outrefcnt; } /* free all of our spawned referrals (child requests) */ for ( tmplr = lr->lr_child; tmplr != NULL; tmplr = nextlr ) { nextlr = tmplr->lr_sibling; nsldapi_free_request( ld, tmplr, free_conn ); } if ( free_conn ) { nsldapi_free_connection( ld, lr->lr_conn, NULL, NULL, 0, 1 ); } if ( lr->lr_prev == NULL ) { ld->ld_requests = lr->lr_next; } else { lr->lr_prev->lr_next = lr->lr_next; } if ( lr->lr_next != NULL ) { lr->lr_next->lr_prev = lr->lr_prev; } if ( lr->lr_ber != NULL ) { ber_free( lr->lr_ber, 1 ); } if ( lr->lr_res_error != NULL ) { NSLDAPI_FREE( lr->lr_res_error ); } if ( lr->lr_res_matched != NULL ) { NSLDAPI_FREE( lr->lr_res_matched ); } if ( lr->lr_binddn != NULL ) { NSLDAPI_FREE( lr->lr_binddn ); } NSLDAPI_FREE( lr ); }
LDAPConn * nsldapi_new_connection( LDAP *ld, LDAPServer **srvlistp, int use_ldsb, int connect, int bind ) { int rc; LDAPConn *lc; LDAPServer *prevsrv, *srv; Sockbuf *sb = NULL; /* * make a new LDAP server connection */ if (( lc = (LDAPConn *)NSLDAPI_CALLOC( 1, sizeof( LDAPConn ))) == NULL || ( !use_ldsb && ( sb = ber_sockbuf_alloc()) == NULL )) { if ( lc != NULL ) { NSLDAPI_FREE( (char *)lc ); } LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); return( NULL ); } LDAP_MUTEX_LOCK( ld, LDAP_OPTION_LOCK ); if ( !use_ldsb ) { /* * we have allocated a new sockbuf * set I/O routines to match those in default LDAP sockbuf */ IFP sb_fn; struct lber_x_ext_io_fns extiofns; extiofns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE; if ( ber_sockbuf_get_option( ld->ld_sbp, LBER_SOCKBUF_OPT_EXT_IO_FNS, &extiofns ) == 0 ) { ber_sockbuf_set_option( sb, LBER_SOCKBUF_OPT_EXT_IO_FNS, &extiofns ); } if ( ber_sockbuf_get_option( ld->ld_sbp, LBER_SOCKBUF_OPT_READ_FN, (void *)&sb_fn ) == 0 && sb_fn != NULL ) { ber_sockbuf_set_option( sb, LBER_SOCKBUF_OPT_READ_FN, (void *)sb_fn ); } if ( ber_sockbuf_get_option( ld->ld_sbp, LBER_SOCKBUF_OPT_WRITE_FN, (void *)&sb_fn ) == 0 && sb_fn != NULL ) { ber_sockbuf_set_option( sb, LBER_SOCKBUF_OPT_WRITE_FN, (void *)sb_fn ); } } lc->lconn_sb = ( use_ldsb ) ? ld->ld_sbp : sb; lc->lconn_version = ld->ld_version; /* inherited */ LDAP_MUTEX_UNLOCK( ld, LDAP_OPTION_LOCK ); if ( connect ) { prevsrv = NULL; /* * save the return code for later */ for ( srv = *srvlistp; srv != NULL; srv = srv->lsrv_next ) { rc = nsldapi_connect_to_host( ld, lc->lconn_sb, srv->lsrv_host, srv->lsrv_port, ( srv->lsrv_options & LDAP_SRV_OPT_SECURE ) != 0, &lc->lconn_krbinstance ); if (rc != -1) { break; } prevsrv = srv; } if ( srv == NULL ) { if ( !use_ldsb ) { NSLDAPI_FREE( (char *)lc->lconn_sb ); } NSLDAPI_FREE( (char *)lc ); /* nsldapi_open_ldap_connection has already set ld_errno */ return( NULL ); } if ( prevsrv == NULL ) { *srvlistp = srv->lsrv_next; } else { prevsrv->lsrv_next = srv->lsrv_next; } lc->lconn_server = srv; } if (ld->ld_options & LDAP_BITOPT_ASYNC && rc == -2) { lc->lconn_status = LDAP_CONNST_CONNECTING; } else { lc->lconn_status = LDAP_CONNST_CONNECTED; } lc->lconn_next = ld->ld_conns; ld->ld_conns = lc; /* * XXX for now, we always do a synchronous bind. This will have * to change in the long run... */ if ( bind ) { int err, lderr, freepasswd, authmethod; char *binddn, *passwd; LDAPConn *savedefconn; freepasswd = err = 0; if ( ld->ld_rebind_fn == NULL ) { binddn = passwd = ""; authmethod = LDAP_AUTH_SIMPLE; } else { if (( lderr = (*ld->ld_rebind_fn)( ld, &binddn, &passwd, &authmethod, 0, ld->ld_rebind_arg )) == LDAP_SUCCESS ) { freepasswd = 1; } else { LDAP_SET_LDERRNO( ld, lderr, NULL, NULL ); err = -1; } } if ( err == 0 ) { savedefconn = ld->ld_defconn; ld->ld_defconn = lc; ++lc->lconn_refcnt; /* avoid premature free */ /* * when binding, we will back down as low as LDAPv2 * if we get back "protocol error" from bind attempts */ for ( ;; ) { /* LDAP_MUTEX_UNLOCK(ld, LDAP_CONN_LOCK); */ if (( lderr = ldap_bind_s( ld, binddn, passwd, authmethod )) == LDAP_SUCCESS ) { /* LDAP_MUTEX_LOCK(ld, LDAP_CONN_LOCK); */ break; } /* LDAP_MUTEX_LOCK(ld, LDAP_CONN_LOCK); */ if ( lc->lconn_version <= LDAP_VERSION2 || lderr != LDAP_PROTOCOL_ERROR ) { err = -1; break; } --lc->lconn_version; /* try lower version */ } --lc->lconn_refcnt; ld->ld_defconn = savedefconn; } if ( freepasswd ) { (*ld->ld_rebind_fn)( ld, &binddn, &passwd, &authmethod, 1, ld->ld_rebind_arg ); } if ( err != 0 ) { nsldapi_free_connection( ld, lc, NULL, NULL, 1, 0 ); lc = NULL; } } return( lc ); }
/* returns the message id of the request or -1 if an error occurs */ int nsldapi_send_server_request( LDAP *ld, /* session handle */ BerElement *ber, /* message to send */ int msgid, /* ID of message to send */ LDAPRequest *parentreq, /* non-NULL for referred requests */ LDAPServer *srvlist, /* servers to connect to (NULL for default) */ LDAPConn *lc, /* connection to use (NULL for default) */ char *bindreqdn, /* non-NULL for bind requests */ int bind /* perform a bind after opening new conn.? */ ) { LDAPRequest *lr; int err; int incparent; /* did we bump parent's ref count? */ LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_send_server_request\n", 0, 0, 0 ); incparent = 0; LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK ); if ( lc == NULL ) { if ( srvlist == NULL ) { if ( ld->ld_defconn == NULL ) { LDAP_MUTEX_LOCK( ld, LDAP_OPTION_LOCK ); if ( bindreqdn == NULL && ( ld->ld_options & LDAP_BITOPT_RECONNECT ) != 0 ) { LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, NULL, NULL ); ber_free( ber, 1 ); LDAP_MUTEX_UNLOCK( ld, LDAP_OPTION_LOCK ); LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); return( -1 ); } LDAP_MUTEX_UNLOCK( ld, LDAP_OPTION_LOCK ); if ( nsldapi_open_ldap_defconn( ld ) < 0 ) { ber_free( ber, 1 ); LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); return( -1 ); } } lc = ld->ld_defconn; } else { if (( lc = find_connection( ld, srvlist, 1 )) == NULL ) { if ( bind && (parentreq != NULL) ) { /* Remember the bind in the parent */ incparent = 1; ++parentreq->lr_outrefcnt; } lc = nsldapi_new_connection( ld, &srvlist, 0, 1, bind ); } free_servers( srvlist ); } } /* * the logic here is: * if * 1. no connections exists, * or * 2. if the connection is either not in the connected * or connecting state in an async io model * or * 3. the connection is notin a connected state with normal (non async io) */ if ( lc == NULL || ( (ld->ld_options & LDAP_BITOPT_ASYNC && lc->lconn_status != LDAP_CONNST_CONNECTING && lc->lconn_status != LDAP_CONNST_CONNECTED) || (!(ld->ld_options & LDAP_BITOPT_ASYNC ) && lc->lconn_status != LDAP_CONNST_CONNECTED) ) ) { ber_free( ber, 1 ); if ( lc != NULL ) { LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, NULL, NULL ); } if ( incparent ) { /* Forget about the bind */ --parentreq->lr_outrefcnt; } LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); return( -1 ); } use_connection( ld, lc ); if (( lr = (LDAPRequest *)NSLDAPI_CALLOC( 1, sizeof( LDAPRequest ))) == NULL || ( bindreqdn != NULL && ( bindreqdn = nsldapi_strdup( bindreqdn )) == NULL )) { if ( lr != NULL ) { NSLDAPI_FREE( lr ); } LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL ); nsldapi_free_connection( ld, lc, NULL, NULL, 0, 0 ); ber_free( ber, 1 ); if ( incparent ) { /* Forget about the bind */ --parentreq->lr_outrefcnt; } LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); return( -1 ); } lr->lr_binddn = bindreqdn; lr->lr_msgid = msgid; lr->lr_status = LDAP_REQST_INPROGRESS; lr->lr_res_errno = LDAP_SUCCESS; /* optimistic */ lr->lr_ber = ber; lr->lr_conn = lc; if ( parentreq != NULL ) { /* sub-request */ if ( !incparent ) { /* Increment if we didn't do it before the bind */ ++parentreq->lr_outrefcnt; } lr->lr_origid = parentreq->lr_origid; lr->lr_parentcnt = parentreq->lr_parentcnt + 1; lr->lr_parent = parentreq; if ( parentreq->lr_child != NULL ) { lr->lr_sibling = parentreq->lr_child; } parentreq->lr_child = lr; } else { /* original request */ lr->lr_origid = lr->lr_msgid; } LDAP_MUTEX_LOCK( ld, LDAP_REQ_LOCK ); if (( lr->lr_next = ld->ld_requests ) != NULL ) { lr->lr_next->lr_prev = lr; } ld->ld_requests = lr; lr->lr_prev = NULL; if (( err = nsldapi_ber_flush( ld, lc->lconn_sb, ber, 0, 1 )) != 0 ) { /* need to continue write later */ if (ld->ld_options & LDAP_BITOPT_ASYNC && err == -2 ) { lr->lr_status = LDAP_REQST_WRITING; nsldapi_iostatus_interest_write( ld, lc->lconn_sb ); } else { LDAP_SET_LDERRNO( ld, LDAP_SERVER_DOWN, NULL, NULL ); nsldapi_free_request( ld, lr, 0 ); nsldapi_free_connection( ld, lc, NULL, NULL, 0, 0 ); LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK ); LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); return( -1 ); } } else { if ( parentreq == NULL ) { ber->ber_end = ber->ber_ptr; ber->ber_ptr = ber->ber_buf; } /* sent -- waiting for a response */ if (ld->ld_options & LDAP_BITOPT_ASYNC) { lc->lconn_status = LDAP_CONNST_CONNECTED; } nsldapi_iostatus_interest_read( ld, lc->lconn_sb ); } LDAP_MUTEX_UNLOCK( ld, LDAP_REQ_LOCK ); LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); LDAP_SET_LDERRNO( ld, LDAP_SUCCESS, NULL, NULL ); return( msgid ); }
/* * Abandon all outstanding requests for msgid (included child requests * spawned when chasing referrals). This function calls itself recursively. * No locking is done is this function so it must be done by the caller. * Returns an LDAP error code and sets it in LDAP *ld as well */ static int do_abandon(LDAP *ld, int origid, int msgid, LDAPControl **serverctrls, LDAPControl **clientctrls) { BerElement *ber; int i, bererr, lderr, sendabandon; LDAPRequest *lr = NULL; /* * An abandon request looks like this: * AbandonRequest ::= MessageID */ LDAPDebug(LDAP_DEBUG_TRACE, "do_abandon origid %d, msgid %d\n", origid, msgid, 0); /* optimistic */ lderr = LDAP_SUCCESS; /* * Find the request that we are abandoning. Don't send an * abandon message unless there is something to abandon. */ sendabandon = 0; for (lr = ld->ld_requests; lr != NULL; lr = lr->lr_next) { if (lr->lr_msgid == msgid) { /* this message */ if (origid == msgid && lr->lr_parent != NULL) { /* don't let caller abandon child requests! */ lderr = LDAP_PARAM_ERROR; goto set_errorcode_and_return; } if (lr->lr_status == LDAP_REQST_INPROGRESS) { /* * We only need to send an abandon message if * the request is in progress. */ sendabandon = 1; } break; } if (lr->lr_origid == msgid) { /* child: abandon it */ (void)do_abandon(ld, msgid, lr->lr_msgid, serverctrls, clientctrls); /* we ignore errors from child abandons... */ } } if (ldap_msgdelete(ld, msgid) == 0) { /* we had all the results and deleted them */ goto set_errorcode_and_return; } if (lr != NULL && sendabandon) { /* create a message to send */ if ((lderr = nsldapi_alloc_ber_with_options(ld, &ber)) == LDAP_SUCCESS) { int abandon_msgid; LDAP_MUTEX_LOCK(ld, LDAP_MSGID_LOCK); abandon_msgid = ++ld->ld_msgid; LDAP_MUTEX_UNLOCK(ld, LDAP_MSGID_LOCK); #ifdef CLDAP if (ld->ld_dbp->sb_naddr > 0) { bererr = ber_printf(ber, "{isti", abandon_msgid, ld->ld_cldapdn, LDAP_REQ_ABANDON, msgid); } else { #endif /* CLDAP */ bererr = ber_printf(ber, "{iti", abandon_msgid, LDAP_REQ_ABANDON, msgid); #ifdef CLDAP } #endif /* CLDAP */ if (bererr == -1 || (lderr = nsldapi_put_controls(ld, serverctrls, 1, ber)) != LDAP_SUCCESS) { lderr = LDAP_ENCODING_ERROR; ber_free(ber, 1); } else { /* try to send the message */ lderr = nsldapi_send_abandon_message(ld, lr->lr_conn, ber, abandon_msgid); } } } if (lr != NULL) { /* * Always call nsldapi_free_connection() so that the connection's * ref count is correctly decremented. It is OK to always pass * 1 for the "unbind" parameter because doing so will only affect * connections that resulted from a child request (because the * default connection's ref count never goes to zero). */ nsldapi_free_connection(ld, lr->lr_conn, NULL, NULL, 0 /* do not force */, 1 /* send unbind before closing */); /* * Free the entire request chain if we finished abandoning everything. */ if (origid == msgid) { nsldapi_free_request(ld, lr, 0); } } /* * Record the abandoned message ID (used to discard any server responses * that arrive later). */ LDAP_MUTEX_LOCK(ld, LDAP_ABANDON_LOCK); if (ld->ld_abandoned == NULL) { if ((ld->ld_abandoned = (int *)NSLDAPI_MALLOC(2 * sizeof(int))) == NULL) { lderr = LDAP_NO_MEMORY; LDAP_MUTEX_UNLOCK(ld, LDAP_ABANDON_LOCK); goto set_errorcode_and_return; } i = 0; } else { for (i = 0; ld->ld_abandoned[i] != -1; i++) ; /* NULL */ if ((ld->ld_abandoned = (int *)NSLDAPI_REALLOC( (char *)ld->ld_abandoned, (i + 2) * sizeof(int))) == NULL) { lderr = LDAP_NO_MEMORY; LDAP_MUTEX_UNLOCK(ld, LDAP_ABANDON_LOCK); goto set_errorcode_and_return; } } ld->ld_abandoned[i] = msgid; ld->ld_abandoned[i + 1] = -1; LDAP_MUTEX_UNLOCK(ld, LDAP_ABANDON_LOCK); set_errorcode_and_return: LDAP_SET_LDERRNO(ld, lderr, NULL, NULL); return (lderr); }
/* * simple_bindifnot_s() is like ldap_simple_bind_s() except that it only does * a bind if the default connection is not currently bound. * If a successful bind using the same DN has already taken place we just * return LDAP_SUCCESS without conversing with the server at all. */ static int simple_bindifnot_s( LDAP *ld, const char *dn, const char *passwd ) { int msgid, rc; LDAPMessage *result; char *binddn; LDAPDebug( LDAP_DEBUG_TRACE, "simple_bindifnot_s\n", 0, 0, 0 ); if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) { return( LDAP_PARAM_ERROR ); } if ( dn == NULL ) { dn = ""; /* to make comparisons simpler */ } /* * if we are already bound using the same DN, just return LDAP_SUCCESS. */ if ( NULL != ( binddn = nsldapi_get_binddn( ld )) && 0 == strcmp( dn, binddn )) { rc = LDAP_SUCCESS; LDAP_SET_LDERRNO( ld, rc, NULL, NULL ); return rc; } /* * if the default connection has been lost and is now marked dead, * dispose of the default connection so it will get re-established. * * if not, clear the bind DN and status to ensure that we don't * report the wrong bind DN to a different thread while waiting * for our bind result to return from the server. */ LDAP_MUTEX_LOCK( ld, LDAP_CONN_LOCK ); if ( NULL != ld->ld_defconn ) { if ( LDAP_CONNST_DEAD == ld->ld_defconn->lconn_status ) { nsldapi_free_connection( ld, ld->ld_defconn, NULL, NULL, 1, 0 ); ld->ld_defconn = NULL; } else if ( ld->ld_defconn->lconn_binddn != NULL ) { NSLDAPI_FREE( ld->ld_defconn->lconn_binddn ); ld->ld_defconn->lconn_binddn = NULL; ld->ld_defconn->lconn_bound = 0; } } LDAP_MUTEX_UNLOCK( ld, LDAP_CONN_LOCK ); /* * finally, bind (this will open a new connection if necessary) * * do everything under the protection of the result lock to * ensure that only one thread will be in this code at a time. * XXXmcs: we should use a condition variable instead? */ LDAP_MUTEX_LOCK( ld, LDAP_RESULT_LOCK ); if ( (msgid = simple_bind_nolock( ld, dn, passwd, 0 )) == -1 ) { rc = LDAP_GET_LDERRNO( ld, NULL, NULL ); goto unlock_and_return; } /* * Note that at this point the bind request is on its way to the * server and at any time now we will either be bound as the new * DN (if the bind succeeded) or we will be bound as anonymous (if * the bind failed). */ /* * Wait for the bind result. Code inside result.c:read1msg() * takes care of setting the connection's bind DN and status. */ if ( nsldapi_result_nolock( ld, msgid, 1, 0, (struct timeval *) 0, &result ) == -1 ) { rc = LDAP_GET_LDERRNO( ld, NULL, NULL ); goto unlock_and_return; } rc = ldap_result2error( ld, result, 1 ); unlock_and_return: LDAP_MUTEX_UNLOCK( ld, LDAP_RESULT_LOCK ); return( rc ); }