/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
- * Copyright 1998-2006 The OpenLDAP Foundation.
+ * Copyright 1998-2008 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* LDAPResult ::= SEQUENCE {
* resultCode ENUMERATED { ... },
* matchedDN LDAPDN,
- * diagnosticMessage LDAPString,
+ * diagnosticMessage LDAPString,
* referral [3] Referral OPTIONAL
* }
* Referral ::= SEQUENCE OF LDAPURL (one or more)
- * LDAPURL ::= LDAPString (limited to URL chars)
+ * LDAPURL ::= LDAPString (limited to URL chars)
*/
#include "portable.h"
* search references, followed by an ldap result). An extension to
* LDAPv3 allows partial extended responses to be returned in response
* to any request. The type of the first message received is returned.
- * When waiting, any messages that have been abandoned are discarded.
+ * When waiting, any messages that have been abandoned/discarded are
+ * discarded.
*
* Example:
* ldap_result( s, msgid, all, timeout, result )
"ldap_chkResponseList returns ld %p NULL\n", (void *)ld, 0, 0);
} else {
Debug( LDAP_DEBUG_TRACE,
- "ldap_chkResponseList returns ld %p msgid %d, type 0x%02lu\n",
+ "ldap_chkResponseList returns ld %p msgid %d, type 0x%02lx\n",
(void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype );
}
#endif
int rc;
struct timeval tv = { 0 },
tv0 = { 0 },
- *tvp;
- time_t start_time = 0;
- time_t tmp_time;
+ start_time_tv = { 0 },
+ *tvp = NULL;
LDAPConn *lc;
assert( ld != NULL );
LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
#endif
+ if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) {
+ tv = ld->ld_options.ldo_tm_api;
+ timeout = &tv;
+ }
+
#ifdef LDAP_DEBUG
if ( timeout == NULL ) {
Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n",
}
#endif /* LDAP_DEBUG */
- if ( timeout == NULL ) {
- tvp = NULL;
- } else {
+ if ( timeout != NULL ) {
tv0 = *timeout;
tv = *timeout;
tvp = &tv;
- start_time = time( NULL );
+#ifdef HAVE_GETTIMEOFDAY
+ gettimeofday( &start_time_tv, NULL );
+#else /* ! HAVE_GETTIMEOFDAY */
+ time( &start_time_tv.tv_sec );
+ start_time_tv.tv_usec = 0;
+#endif /* ! HAVE_GETTIMEOFDAY */
}
rc = LDAP_MSG_X_KEEP_LOOKING;
}
#endif /* LDAP_DEBUG */
- if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) {
+ if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) {
rc = (*result)->lm_msgtype;
} else {
ldap_pvt_thread_mutex_unlock( &ld->ld_conn_mutex );
#endif
- if ( !lc_ready ) {
+ if ( !lc_ready ) {
rc = ldap_int_select( ld, tvp );
#ifdef LDAP_DEBUG
if ( rc == -1 ) {
}
if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
- tmp_time = time( NULL );
- tv0.tv_sec -= ( tmp_time - start_time );
- if ( tv0.tv_sec <= 0 ) {
- rc = 0; /* timed out */
+ struct timeval curr_time_tv = { 0 },
+ delta_time_tv = { 0 };
+
+#ifdef HAVE_GETTIMEOFDAY
+ gettimeofday( &curr_time_tv, NULL );
+#else /* ! HAVE_GETTIMEOFDAY */
+ time( &curr_time_tv.tv_sec );
+ curr_time_tv.tv_usec = 0;
+#endif /* ! HAVE_GETTIMEOFDAY */
+
+ /* delta_time = tmp_time - start_time */
+ delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
+ delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
+ if ( delta_time_tv.tv_usec < 0 ) {
+ delta_time_tv.tv_sec--;
+ delta_time_tv.tv_usec += 1000000;
+ }
+
+ /* tv0 < delta_time ? */
+ if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) ||
+ ( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) )
+ {
+ rc = 0; /* timed out */
ld->ld_errno = LDAP_TIMEOUT;
break;
}
+
+ /* tv0 -= delta_time */
+ tv0.tv_sec -= delta_time_tv.tv_sec;
+ tv0.tv_usec -= delta_time_tv.tv_usec;
+ if ( tv0.tv_usec < 0 ) {
+ tv0.tv_sec--;
+ tv0.tv_usec += 1000000;
+ }
+
tv.tv_sec = tv0.tv_sec;
+ tv.tv_usec = tv0.tv_usec;
- Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld secs to go\n",
- (void *)ld, (long) tv.tv_sec, 0 );
- start_time = tmp_time;
+ Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n",
+ (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec );
+
+ start_time_tv.tv_sec = curr_time_tv.tv_sec;
+ start_time_tv.tv_usec = curr_time_tv.tv_usec;
}
}
ber_tag_t tag;
ber_len_t len;
int foundit = 0;
- LDAPRequest *lr, *tmplr;
+ LDAPRequest *lr, *tmplr, dummy_lr = { 0 };
LDAPConn *lc;
BerElement tmpber;
int rc, refer_cnt, hadref, simple_request;
if ( sock_errno() == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
#endif
ld->ld_errno = LDAP_SERVER_DOWN;
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
+#endif
+ ldap_free_connection( ld, lc, 1, 0 );
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
+#endif
+ lc = *lcp = NULL;
return -1;
default:
return( -1 );
}
+ /* id == 0 iff unsolicited notification message (RFC 4511) */
+
/* if it's been abandoned, toss it */
- if ( ldap_abandoned( ld, id, &idx ) ) {
- /* the message type */
- tag = ber_peek_tag( ber, &len );
- switch ( tag ) {
- case LDAP_RES_SEARCH_ENTRY:
- case LDAP_RES_SEARCH_REFERENCE:
- case LDAP_RES_INTERMEDIATE:
- case LBER_ERROR:
- break;
+ if ( id > 0 ) {
+ if ( ldap_abandoned( ld, id, &idx ) ) {
+ /* the message type */
+ tag = ber_peek_tag( ber, &len );
+ switch ( tag ) {
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_REFERENCE:
+ case LDAP_RES_INTERMEDIATE:
+ case LBER_ERROR:
+ break;
- default:
- /* there's no need to keep the id
- * in the abandoned list any longer */
- ldap_mark_abandoned( ld, id, idx );
- break;
- }
+ default:
+ /* there's no need to keep the id
+ * in the abandoned list any longer */
+ ldap_mark_abandoned( ld, id, idx );
+ break;
+ }
- Debug( LDAP_DEBUG_ANY,
- "abandoned/discarded ld %p msgid %ld message type %s\n",
- (void *)ld, (long)id, ldap_int_msgtype2str( tag ) );
+ Debug( LDAP_DEBUG_ANY,
+ "abandoned/discarded ld %p msgid %ld message type %s\n",
+ (void *)ld, (long)id, ldap_int_msgtype2str( tag ) );
retry_ber:
- ber_free( ber, 1 );
- if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
- goto retry;
+ ber_free( ber, 1 );
+ if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
+ goto retry;
+ }
+ return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
}
- return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
- }
- lr = ldap_find_request_by_msgid( ld, id );
- if ( lr == NULL ) {
- const char *msg = "unknown";
+ lr = ldap_find_request_by_msgid( ld, id );
+ if ( lr == NULL ) {
+ const char *msg = "unknown";
- /* the message type */
- tag = ber_peek_tag( ber, &len );
- switch ( tag ) {
- case LBER_ERROR:
- break;
+ /* the message type */
+ tag = ber_peek_tag( ber, &len );
+ switch ( tag ) {
+ case LBER_ERROR:
+ break;
- default:
- msg = ldap_int_msgtype2str( tag );
- break;
- }
+ default:
+ msg = ldap_int_msgtype2str( tag );
+ break;
+ }
- Debug( LDAP_DEBUG_ANY,
- "no request for response on ld %p msgid %ld message type %s (tossing)\n",
- (void *)ld, (long)id, msg );
+ Debug( LDAP_DEBUG_ANY,
+ "no request for response on ld %p msgid %ld message type %s (tossing)\n",
+ (void *)ld, (long)id, msg );
+
+ goto retry_ber;
+ }
- goto retry_ber;
- }
#ifdef LDAP_CONNECTIONLESS
- if ( LDAP_IS_UDP(ld) && isv2 ) {
- ber_scanf(ber, "x{");
- }
+ if ( LDAP_IS_UDP(ld) && isv2 ) {
+ ber_scanf(ber, "x{");
+ }
nextresp2:
#endif
+ }
+
/* the message type */
tag = ber_peek_tag( ber, &len );
if ( tag == LBER_ERROR ) {
"read1msg: ld %p msgid %ld message type %s\n",
(void *)ld, (long)lr->lr_msgid, ldap_int_msgtype2str( tag ) );
+ if ( id == 0 ) {
+ /* unsolicited notification message (RFC 4511) */
+ if ( tag != LDAP_RES_EXTENDED ) {
+ /* toss it */
+ goto retry_ber;
+
+ /* strictly speaking, it's an error; from RFC 4511:
+
+4.4. Unsolicited Notification
+
+ An unsolicited notification is an LDAPMessage sent from the server to
+ the client that is not in response to any LDAPMessage received by the
+ server. It is used to signal an extraordinary condition in the
+ server or in the LDAP session between the client and the server. The
+ notification is of an advisory nature, and the server will not expect
+ any response to be returned from the client.
+
+ The unsolicited notification is structured as an LDAPMessage in which
+ the messageID is zero and protocolOp is set to the extendedResp
+ choice using the ExtendedResponse type (See Section 4.12). The
+ responseName field of the ExtendedResponse always contains an LDAPOID
+ that is unique for this notification.
+
+ * however, since unsolicited responses
+ * are of advisory nature, better
+ * toss it, right now
+ */
+
+#if 0
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 1 );
+ return( -1 );
+#endif
+ }
+
+ lr = &dummy_lr;
+ }
+
id = lr->lr_origid;
refer_cnt = 0;
hadref = simple_request = 0;
}
}
}
- } else
- if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
- /* All results that just return a status, i.e. don't return data
- * go through the following code. This code also chases V2 referrals
- * and checks if all referrals have been chased.
- */
+
+ } else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
+ /* All results that just return a status, i.e. don't return data
+ * go through the following code. This code also chases V2 referrals
+ * and checks if all referrals have been chased.
+ */
char *lr_res_error = NULL;
tmpber = *ber; /* struct copy */
break;
default:
- if ( lr->lr_res_error == NULL ||
- lr->lr_res_error[ 0 ] == '\0' )
- {
+ if ( lr->lr_res_error == NULL ) {
break;
}
+ /* pedantic, should never happen */
+ if ( lr->lr_res_error[ 0 ] == '\0' ) {
+ LDAP_FREE( lr->lr_res_error );
+ lr->lr_res_error = NULL;
+ break;
+ }
+
/* V2 referrals are in error string */
refer_cnt = ldap_chase_referrals( ld, lr,
&lr->lr_res_error, -1, &hadref );
{
id = lr->lr_msgid;
tag = lr->lr_res_msgtype;
- Debug( LDAP_DEBUG_ANY, "request done: ld %p msgid %ld\n",
+ Debug( LDAP_DEBUG_TRACE, "request done: ld %p msgid %ld\n",
(void *)ld, (long) id, 0 );
Debug( LDAP_DEBUG_TRACE,
"res_errno: %d, res_error: <%s>, "
}
}
- ldap_return_request( ld, lr, 1 );
+ if ( lr != &dummy_lr ) {
+ ldap_return_request( ld, lr, 1 );
+ }
lr = NULL;
}
- if ( lc != NULL ) {
+ /*
+ * RF 4511 unsolicited (id == 0) responses
+ * shouldn't necessarily end the connection
+ */
+ if ( lc != NULL && id != 0 ) {
#ifdef LDAP_R_COMPILE
ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
#endif
}
if ( lr != NULL ) {
- ldap_return_request( ld, lr, 0 );
+ if ( lr != &dummy_lr ) {
+ ldap_return_request( ld, lr, 0 );
+ }
lr = NULL;
}
return( rc );
}
+ /* try to handle unsolicited responses as appropriate */
+ if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
+ int is_nod = 0;
+
+ tag = ber_peek_tag( &tmpber, &len );
+
+ /* we have a res oid */
+ if ( tag == LDAP_TAG_EXOP_RES_OID ) {
+ static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
+ struct berval resoid = BER_BVNULL;
+
+ if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 1 );
+ return -1;
+ }
+
+ assert( !BER_BVISEMPTY( &resoid ) );
+
+ is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
+
+ tag = ber_peek_tag( &tmpber, &len );
+ }
+
+#if 0 /* don't need right now */
+ /* we have res data */
+ if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
+ struct berval resdata;
+
+ if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 0 );
+ return ld->ld_errno;
+ }
+
+ /* use it... */
+ }
+#endif
+
+ /* handle RFC 4511 "Notice of Disconnection" locally */
+
+ if ( is_nod ) {
+ if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 1 );
+ return -1;
+ }
+
+ /* get rid of the connection... */
+ if ( lc != NULL ) {
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex );
+#endif
+ ldap_free_connection( ld, lc, 0, 1 );
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex );
+#endif
+ lc = *lcp = NULL;
+ }
+
+ /* need to return -1, because otherwise
+ * a valid result is expected */
+ return -1;
+ }
+ }
+
/* make a new ldap message */
newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
if ( newmsg == NULL ) {
prev = NULL;
for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
- if ( l->lm_msgid == newmsg->lm_msgid )
+ if ( l->lm_msgid == newmsg->lm_msgid ) {
break;
+ }
prev = l;
}
/* return the whole chain if that's what we were looking for */
if ( foundit ) {
- if ( prev == NULL )
+ if ( prev == NULL ) {
ld->ld_responses = l->lm_next;
- else
+ } else {
prev->lm_next = l->lm_next;
+ }
*result = l;
}
assert( msgid >= 0 );
assert( ld->ld_nabandoned >= 0 );
- return lutil_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, idxp );
+ return ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, idxp );
}
/*
LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
#endif
- /* NOTE: those assertions are repeated in lutil_bisect_delete() */
+ /* NOTE: those assertions are repeated in ldap_int_bisect_delete() */
assert( idx >= 0 );
assert( idx < ld->ld_nabandoned );
assert( ld->ld_abandoned[ idx ] == msgid );
- return lutil_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,
+ return ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,
msgid, idx );
}