2004-11-06 23:15:39 +03:00
/*
Unix SMB / CIFS mplementation .
LDAP protocol helper functions for SAMBA
Copyright ( C ) Andrew Tridgell 2004
Copyright ( C ) Volker Lendecke 2004
Copyright ( C ) Stefan Metzmacher 2004
Copyright ( C ) Simo Sorce 2004
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
# include "asn_1.h"
# include "dlinklist.h"
2005-06-16 09:39:40 +04:00
# include "lib/events/events.h"
# include "lib/socket/socket.h"
2005-06-20 05:17:29 +04:00
# include "lib/tls/tls.h"
2005-02-10 10:08:40 +03:00
# include "libcli/ldap/ldap.h"
2005-06-16 09:39:40 +04:00
# include "libcli/ldap/ldap_client.h"
2004-11-06 23:15:39 +03:00
2005-01-30 10:22:16 +03:00
2005-06-16 09:39:40 +04:00
/*
create a new ldap_connection stucture . The event context is optional
*/
struct ldap_connection * ldap_new_connection ( TALLOC_CTX * mem_ctx ,
struct event_context * ev )
2005-01-30 10:22:16 +03:00
{
2005-06-16 09:39:40 +04:00
struct ldap_connection * conn ;
2005-01-30 10:22:16 +03:00
2005-06-16 09:39:40 +04:00
conn = talloc_zero ( mem_ctx , struct ldap_connection ) ;
if ( conn = = NULL ) {
return NULL ;
2005-01-30 10:22:16 +03:00
}
2005-06-16 09:39:40 +04:00
if ( ev = = NULL ) {
ev = event_context_init ( conn ) ;
if ( ev = = NULL ) {
talloc_free ( conn ) ;
return NULL ;
2005-01-30 10:22:16 +03:00
}
}
2005-06-16 09:39:40 +04:00
conn - > next_messageid = 1 ;
conn - > event . event_ctx = ev ;
2005-01-30 10:22:16 +03:00
2005-06-16 09:39:40 +04:00
/* set a reasonable request timeout */
conn - > timeout = 60 ;
2005-01-30 10:22:16 +03:00
2005-06-16 09:39:40 +04:00
return conn ;
2005-01-30 10:22:16 +03:00
}
2005-06-16 09:39:40 +04:00
/*
the connection is dead
*/
static void ldap_connection_dead ( struct ldap_connection * conn )
{
struct ldap_request * req ;
2005-01-30 10:22:16 +03:00
2005-06-16 09:39:40 +04:00
while ( conn - > pending ) {
req = conn - > pending ;
DLIST_REMOVE ( req - > conn - > pending , req ) ;
req - > state = LDAP_REQUEST_DONE ;
req - > status = NT_STATUS_UNEXPECTED_NETWORK_ERROR ;
if ( req - > async . fn ) {
req - > async . fn ( req ) ;
}
}
while ( conn - > send_queue ) {
req = conn - > send_queue ;
DLIST_REMOVE ( req - > conn - > send_queue , req ) ;
req - > state = LDAP_REQUEST_DONE ;
req - > status = NT_STATUS_UNEXPECTED_NETWORK_ERROR ;
if ( req - > async . fn ) {
req - > async . fn ( req ) ;
2005-01-30 10:22:16 +03:00
}
}
2005-06-20 05:17:29 +04:00
talloc_free ( conn - > tls ) ;
conn - > tls = NULL ;
2005-01-30 10:22:16 +03:00
}
2005-06-16 09:39:40 +04:00
/*
match up with a pending message , adding to the replies list
*/
static void ldap_match_message ( struct ldap_connection * conn , struct ldap_message * msg )
2004-11-06 23:15:39 +03:00
{
2005-06-16 09:39:40 +04:00
struct ldap_request * req ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
for ( req = conn - > pending ; req ; req = req - > next ) {
if ( req - > messageid = = msg - > messageid ) break ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
if ( req = = NULL ) {
DEBUG ( 0 , ( " ldap: no matching message id for %u \n " ,
msg - > messageid ) ) ;
talloc_free ( msg ) ;
return ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/* add to the list of replies received */
talloc_steal ( req , msg ) ;
req - > replies = talloc_realloc ( req , req - > replies ,
struct ldap_message * , req - > num_replies + 1 ) ;
if ( req - > replies = = NULL ) {
req - > status = NT_STATUS_NO_MEMORY ;
req - > state = LDAP_REQUEST_DONE ;
DLIST_REMOVE ( conn - > pending , req ) ;
if ( req - > async . fn ) {
req - > async . fn ( req ) ;
}
return ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
req - > replies [ req - > num_replies ] = talloc_steal ( req - > replies , msg ) ;
req - > num_replies + + ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
if ( msg - > type ! = LDAP_TAG_SearchResultEntry ) {
/* currently only search results expect multiple
replies */
req - > state = LDAP_REQUEST_DONE ;
DLIST_REMOVE ( conn - > pending , req ) ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
if ( req - > async . fn ) {
req - > async . fn ( req ) ;
}
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/*
try and decode / process plain data
*/
static void ldap_try_decode_plain ( struct ldap_connection * conn )
2004-11-06 23:15:39 +03:00
{
2005-06-16 09:39:40 +04:00
struct asn1_data asn1 ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
if ( ! asn1_load ( & asn1 , conn - > partial ) ) {
ldap_connection_dead ( conn ) ;
return ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/* try and decode - this will fail if we don't have a full packet yet */
while ( asn1 . ofs < asn1 . length ) {
struct ldap_message * msg = talloc ( conn , struct ldap_message ) ;
2005-06-18 17:15:09 +04:00
off_t saved_ofs = asn1 . ofs ;
2005-06-16 09:39:40 +04:00
if ( msg = = NULL ) {
ldap_connection_dead ( conn ) ;
return ;
}
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
if ( ldap_decode ( & asn1 , msg ) ) {
ldap_match_message ( conn , msg ) ;
} else {
2005-06-18 17:15:09 +04:00
asn1 . ofs = saved_ofs ;
2005-06-16 09:39:40 +04:00
talloc_free ( msg ) ;
break ;
}
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/* keep any remaining data in conn->partial */
data_blob_free ( & conn - > partial ) ;
2005-06-19 13:31:34 +04:00
if ( asn1 . ofs ! = asn1 . length ) {
2005-06-16 09:39:40 +04:00
conn - > partial = data_blob_talloc ( conn ,
asn1 . data + asn1 . ofs ,
asn1 . length - asn1 . ofs ) ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
asn1_free ( & asn1 ) ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/*
try and decode / process wrapped data
*/
static void ldap_try_decode_wrapped ( struct ldap_connection * conn )
2004-11-06 23:15:39 +03:00
{
2005-06-16 09:39:40 +04:00
uint32_t len ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
/* keep decoding while we have a full wrapped packet */
while ( conn - > partial . length > = 4 & &
( len = RIVAL ( conn - > partial . data , 0 ) ) < = conn - > partial . length - 4 ) {
DATA_BLOB wrapped , unwrapped ;
struct asn1_data asn1 ;
struct ldap_message * msg = talloc ( conn , struct ldap_message ) ;
NTSTATUS status ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
if ( msg = = NULL ) {
ldap_connection_dead ( conn ) ;
return ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
wrapped . data = conn - > partial . data + 4 ;
wrapped . length = len ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
status = gensec_unwrap ( conn - > gensec , msg , & wrapped , & unwrapped ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
ldap_connection_dead ( conn ) ;
2004-11-06 23:15:39 +03:00
return ;
}
2005-06-16 09:39:40 +04:00
if ( ! asn1_load ( & asn1 , unwrapped ) ) {
ldap_connection_dead ( conn ) ;
return ;
}
2004-11-06 23:15:39 +03:00
2005-06-18 13:08:08 +04:00
while ( ldap_decode ( & asn1 , msg ) ) {
2005-06-16 09:39:40 +04:00
ldap_match_message ( conn , msg ) ;
2005-06-18 13:08:08 +04:00
msg = talloc ( conn , struct ldap_message ) ;
2005-06-16 09:39:40 +04:00
}
2005-06-18 13:08:08 +04:00
talloc_free ( msg ) ;
2005-06-16 09:39:40 +04:00
asn1_free ( & asn1 ) ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
if ( conn - > partial . length = = len + 4 ) {
data_blob_free ( & conn - > partial ) ;
2004-11-06 23:15:39 +03:00
} else {
2005-06-16 09:39:40 +04:00
memmove ( conn - > partial . data , conn - > partial . data + len + 4 ,
conn - > partial . length - ( len + 4 ) ) ;
conn - > partial . length - = len + 4 ;
2004-11-06 23:15:39 +03:00
}
}
}
2005-06-16 09:39:40 +04:00
2005-05-11 18:38:13 +04:00
/*
2005-06-16 09:39:40 +04:00
handle ldap recv events
2005-05-11 18:38:13 +04:00
*/
2005-06-16 09:39:40 +04:00
static void ldap_recv_handler ( struct ldap_connection * conn )
2005-05-11 18:38:13 +04:00
{
2005-06-16 09:39:40 +04:00
NTSTATUS status ;
size_t npending = 0 , nread ;
2005-05-11 18:38:13 +04:00
2005-06-16 09:39:40 +04:00
/* work out how much data is pending */
2005-06-20 05:17:29 +04:00
status = tls_socket_pending ( conn - > tls , & npending ) ;
2005-06-16 09:39:40 +04:00
if ( ! NT_STATUS_IS_OK ( status ) | | npending = = 0 ) {
2005-06-18 16:44:36 +04:00
ldap_connection_dead ( conn ) ;
2005-06-16 09:39:40 +04:00
return ;
}
2005-05-11 18:38:13 +04:00
2005-06-16 09:39:40 +04:00
conn - > partial . data = talloc_realloc_size ( conn , conn - > partial . data ,
conn - > partial . length + npending ) ;
if ( conn - > partial . data = = NULL ) {
ldap_connection_dead ( conn ) ;
return ;
}
2005-05-11 18:38:13 +04:00
2005-06-16 09:39:40 +04:00
/* receive the pending data */
2005-06-20 05:17:29 +04:00
status = tls_socket_recv ( conn - > tls , conn - > partial . data + conn - > partial . length ,
npending , & nread ) ;
2005-06-16 09:39:40 +04:00
if ( NT_STATUS_EQUAL ( status , STATUS_MORE_ENTRIES ) ) {
return ;
2005-05-11 18:38:13 +04:00
}
2005-06-16 09:39:40 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
ldap_connection_dead ( conn ) ;
return ;
}
conn - > partial . length + = nread ;
2005-05-11 18:38:13 +04:00
2005-06-16 09:39:40 +04:00
/* see if we can decode what we have */
if ( conn - > enable_wrap ) {
ldap_try_decode_wrapped ( conn ) ;
} else {
ldap_try_decode_plain ( conn ) ;
}
2005-05-11 18:38:13 +04:00
}
/*
2005-06-16 09:39:40 +04:00
handle ldap send events
2005-05-11 18:38:13 +04:00
*/
2005-06-16 09:39:40 +04:00
static void ldap_send_handler ( struct ldap_connection * conn )
2005-05-11 18:38:13 +04:00
{
2005-06-16 09:39:40 +04:00
while ( conn - > send_queue ) {
struct ldap_request * req = conn - > send_queue ;
size_t nsent ;
NTSTATUS status ;
2005-05-11 18:38:13 +04:00
2005-06-20 05:17:29 +04:00
status = tls_socket_send ( conn - > tls , & req - > data , & nsent ) ;
2005-06-16 09:39:40 +04:00
if ( NT_STATUS_EQUAL ( status , STATUS_MORE_ENTRIES ) ) {
break ;
2005-05-11 18:38:13 +04:00
}
2005-06-16 09:39:40 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
ldap_connection_dead ( conn ) ;
return ;
2005-05-11 18:38:13 +04:00
}
2005-06-16 09:39:40 +04:00
req - > data . data + = nsent ;
req - > data . length - = nsent ;
if ( req - > data . length = = 0 ) {
req - > state = LDAP_REQUEST_PENDING ;
DLIST_REMOVE ( conn - > send_queue , req ) ;
/* some types of requests don't expect a reply */
if ( req - > type = = LDAP_TAG_AbandonRequest | |
req - > type = = LDAP_TAG_UnbindRequest ) {
req - > status = NT_STATUS_OK ;
req - > state = LDAP_REQUEST_DONE ;
if ( req - > async . fn ) {
req - > async . fn ( req ) ;
}
} else {
DLIST_ADD ( conn - > pending , req ) ;
}
}
2005-05-11 18:38:13 +04:00
}
2005-06-16 09:39:40 +04:00
if ( conn - > send_queue = = NULL ) {
EVENT_FD_NOT_WRITEABLE ( conn - > event . fde ) ;
2005-05-11 18:38:13 +04:00
}
}
2005-06-16 09:39:40 +04:00
/*
handle ldap socket events
*/
static void ldap_io_handler ( struct event_context * ev , struct fd_event * fde ,
uint16_t flags , void * private )
2004-11-06 23:15:39 +03:00
{
2005-06-16 09:39:40 +04:00
struct ldap_connection * conn = talloc_get_type ( private , struct ldap_connection ) ;
if ( flags & EVENT_FD_WRITE ) {
ldap_send_handler ( conn ) ;
2005-06-20 05:17:29 +04:00
if ( conn - > tls = = NULL ) return ;
2005-06-16 09:39:40 +04:00
}
if ( flags & EVENT_FD_READ ) {
ldap_recv_handler ( conn ) ;
2005-05-11 18:38:13 +04:00
}
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/*
parse a ldap URL
*/
static NTSTATUS ldap_parse_basic_url ( TALLOC_CTX * mem_ctx , const char * url ,
char * * host , uint16_t * port , BOOL * ldaps )
2004-11-06 23:15:39 +03:00
{
2005-06-16 09:39:40 +04:00
int tmp_port = 0 ;
char protocol [ 11 ] ;
char tmp_host [ 255 ] ;
const char * p = url ;
int ret ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
/* skip leading "URL:" (if any) */
if ( strncasecmp ( p , " URL: " , 4 ) = = 0 ) {
p + = 4 ;
}
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
/* Paranoia check */
SMB_ASSERT ( sizeof ( protocol ) > 10 & & sizeof ( tmp_host ) > 254 ) ;
ret = sscanf ( p , " %10[^:]://%254[^:/]:%d " , protocol , tmp_host , & tmp_port ) ;
if ( ret < 2 ) {
return NT_STATUS_INVALID_PARAMETER ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
if ( strequal ( protocol , " ldap " ) ) {
* port = 389 ;
* ldaps = False ;
} else if ( strequal ( protocol , " ldaps " ) ) {
* port = 636 ;
* ldaps = True ;
2004-11-06 23:15:39 +03:00
} else {
2005-06-16 09:39:40 +04:00
DEBUG ( 0 , ( " unrecognised ldap protocol (%s)! \n " , protocol ) ) ;
return NT_STATUS_PROTOCOL_UNREACHABLE ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
if ( tmp_port ! = 0 )
* port = tmp_port ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
* host = talloc_strdup ( mem_ctx , tmp_host ) ;
NT_STATUS_HAVE_NO_MEMORY ( * host ) ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
return NT_STATUS_OK ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/*
connect to a ldap server
*/
NTSTATUS ldap_connect ( struct ldap_connection * conn , const char * url )
2004-11-06 23:15:39 +03:00
{
NTSTATUS status ;
2005-06-16 09:39:40 +04:00
status = ldap_parse_basic_url ( conn , url , & conn - > host ,
& conn - > port , & conn - > ldaps ) ;
NT_STATUS_NOT_OK_RETURN ( status ) ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
status = socket_create ( " ipv4 " , SOCKET_TYPE_STREAM , & conn - > sock , 0 ) ;
NT_STATUS_NOT_OK_RETURN ( status ) ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
talloc_steal ( conn , conn - > sock ) ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
/* connect in a event friendly way */
status = socket_connect_ev ( conn - > sock , NULL , 0 , conn - > host , conn - > port , 0 ,
conn - > event . event_ctx ) ;
2004-12-29 02:59:22 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2005-06-16 09:39:40 +04:00
talloc_free ( conn - > sock ) ;
return status ;
2004-12-29 02:59:22 +03:00
}
2005-06-16 09:39:40 +04:00
/* setup a handler for events on this socket */
conn - > event . fde = event_add_fd ( conn - > event . event_ctx , conn - > sock ,
socket_get_fd ( conn - > sock ) ,
EVENT_FD_READ , ldap_io_handler , conn ) ;
if ( conn - > event . fde = = NULL ) {
talloc_free ( conn - > sock ) ;
return NT_STATUS_INTERNAL_ERROR ;
2004-11-06 23:15:39 +03:00
}
2005-06-20 05:17:29 +04:00
conn - > tls = tls_init_client ( conn - > sock , conn - > event . fde , conn - > ldaps ) ;
if ( conn - > tls = = NULL ) {
talloc_free ( conn - > sock ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
talloc_steal ( conn , conn - > tls ) ;
talloc_steal ( conn - > tls , conn - > sock ) ;
2005-06-16 09:39:40 +04:00
return NT_STATUS_OK ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/* destroy an open ldap request */
static int ldap_request_destructor ( void * ptr )
2004-11-06 23:15:39 +03:00
{
2005-06-16 09:39:40 +04:00
struct ldap_request * req = talloc_get_type ( ptr , struct ldap_request ) ;
if ( req - > state = = LDAP_REQUEST_SEND ) {
DLIST_REMOVE ( req - > conn - > send_queue , req ) ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
if ( req - > state = = LDAP_REQUEST_PENDING ) {
DLIST_REMOVE ( req - > conn - > pending , req ) ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
return 0 ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/*
called on timeout of a ldap request
*/
static void ldap_request_timeout ( struct event_context * ev , struct timed_event * te ,
struct timeval t , void * private )
2004-11-06 23:15:39 +03:00
{
2005-06-16 09:39:40 +04:00
struct ldap_request * req = talloc_get_type ( private , struct ldap_request ) ;
req - > status = NT_STATUS_IO_TIMEOUT ;
if ( req - > state = = LDAP_REQUEST_SEND ) {
DLIST_REMOVE ( req - > conn - > send_queue , req ) ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
if ( req - > state = = LDAP_REQUEST_PENDING ) {
DLIST_REMOVE ( req - > conn - > pending , req ) ;
}
req - > state = LDAP_REQUEST_DONE ;
if ( req - > async . fn ) {
req - > async . fn ( req ) ;
2004-11-06 23:15:39 +03:00
}
}
2005-06-16 09:39:40 +04:00
/*
send a ldap message - async interface
*/
struct ldap_request * ldap_request_send ( struct ldap_connection * conn ,
struct ldap_message * msg )
2004-11-06 23:15:39 +03:00
{
2005-06-16 09:39:40 +04:00
struct ldap_request * req ;
2004-11-06 23:15:39 +03:00
2005-06-20 05:17:29 +04:00
if ( conn - > tls = = NULL ) {
2005-06-16 09:39:40 +04:00
return NULL ;
}
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
req = talloc_zero ( conn , struct ldap_request ) ;
if ( req = = NULL ) goto failed ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
req - > state = LDAP_REQUEST_SEND ;
req - > conn = conn ;
req - > messageid = conn - > next_messageid + + ;
req - > type = msg - > type ;
if ( req - > messageid = = - 1 ) {
goto failed ;
}
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
talloc_set_destructor ( req , ldap_request_destructor ) ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
msg - > messageid = req - > messageid ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
if ( ! ldap_encode ( msg , & req - > data ) ) {
goto failed ;
}
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
/* possibly encrypt/sign the request */
if ( conn - > enable_wrap ) {
DATA_BLOB wrapped ;
NTSTATUS status ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
status = gensec_wrap ( conn - > gensec , req , & req - > data , & wrapped ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto failed ;
}
data_blob_free ( & req - > data ) ;
req - > data = data_blob_talloc ( req , NULL , wrapped . length + 4 ) ;
if ( req - > data . data = = NULL ) {
goto failed ;
}
RSIVAL ( req - > data . data , 0 , wrapped . length ) ;
memcpy ( req - > data . data + 4 , wrapped . data , wrapped . length ) ;
data_blob_free ( & wrapped ) ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
if ( conn - > send_queue = = NULL ) {
EVENT_FD_WRITEABLE ( conn - > event . fde ) ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
DLIST_ADD_END ( conn - > send_queue , req , struct ldap_request * ) ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
/* put a timeout on the request */
event_add_timed ( conn - > event . event_ctx , req ,
timeval_current_ofs ( conn - > timeout , 0 ) ,
ldap_request_timeout , req ) ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
return req ;
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
failed :
talloc_free ( req ) ;
2004-11-06 23:15:39 +03:00
return NULL ;
}
2005-06-16 09:39:40 +04:00
/*
wait for a request to complete
note that this does not destroy the request
*/
NTSTATUS ldap_request_wait ( struct ldap_request * req )
{
while ( req - > state ! = LDAP_REQUEST_DONE ) {
if ( event_loop_once ( req - > conn - > event . event_ctx ) ! = 0 ) {
req - > status = NT_STATUS_UNEXPECTED_NETWORK_ERROR ;
break ;
}
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
return req - > status ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/*
used to setup the status code from a ldap response
*/
NTSTATUS ldap_check_response ( struct ldap_connection * conn , struct ldap_Result * r )
{
if ( r - > resultcode = = LDAP_SUCCESS ) {
return NT_STATUS_OK ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
if ( conn - > last_error ) {
talloc_free ( conn - > last_error ) ;
}
conn - > last_error = talloc_asprintf ( conn , " LDAP error %u - %s <%s> <%s> " ,
r - > resultcode ,
2005-06-18 13:01:51 +04:00
r - > dn ? r - > dn : " (NULL) " ,
r - > errormessage ? r - > errormessage : " " ,
r - > referral ? r - > referral : " " ) ;
2005-06-16 09:39:40 +04:00
return NT_STATUS_LDAP ( r - > resultcode ) ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/*
return error string representing the last error
*/
const char * ldap_errstr ( struct ldap_connection * conn , NTSTATUS status )
2004-11-06 23:15:39 +03:00
{
2005-06-16 09:39:40 +04:00
if ( NT_STATUS_IS_LDAP ( status ) & & conn - > last_error ! = NULL ) {
return conn - > last_error ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
return nt_errstr ( status ) ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/*
return the Nth result message , waiting if necessary
*/
NTSTATUS ldap_result_n ( struct ldap_request * req , int n , struct ldap_message * * msg )
2004-11-06 23:15:39 +03:00
{
2005-06-16 09:39:40 +04:00
* msg = NULL ;
2004-11-06 23:15:39 +03:00
2005-06-17 06:45:40 +04:00
NT_STATUS_HAVE_NO_MEMORY ( req ) ;
2005-06-16 09:39:40 +04:00
while ( req - > state ! = LDAP_REQUEST_DONE & & n > = req - > num_replies ) {
if ( event_loop_once ( req - > conn - > event . event_ctx ) ! = 0 ) {
return NT_STATUS_UNEXPECTED_NETWORK_ERROR ;
}
}
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
if ( n < req - > num_replies ) {
* msg = req - > replies [ n ] ;
return NT_STATUS_OK ;
}
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
if ( ! NT_STATUS_IS_OK ( req - > status ) ) {
return req - > status ;
}
2004-11-06 23:15:39 +03:00
2005-06-16 09:39:40 +04:00
return NT_STATUS_NO_MORE_ENTRIES ;
2004-11-06 23:15:39 +03:00
}
2005-06-16 09:39:40 +04:00
/*
return a single result message , checking if it is of the expected LDAP type
*/
NTSTATUS ldap_result_one ( struct ldap_request * req , struct ldap_message * * msg , int type )
2004-11-06 23:15:39 +03:00
{
2005-06-16 09:39:40 +04:00
NTSTATUS status ;
status = ldap_result_n ( req , 0 , msg ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
if ( ( * msg ) - > type ! = type ) {
* msg = NULL ;
return NT_STATUS_UNEXPECTED_NETWORK_ERROR ;
}
return status ;
2004-11-06 23:15:39 +03:00
}
2005-06-17 06:45:40 +04:00
/*
a simple ldap transaction , for single result requests that only need a status code
this relies on single valued requests having the response type = = request type + 1
*/
NTSTATUS ldap_transaction ( struct ldap_connection * conn , struct ldap_message * msg )
{
struct ldap_request * req = ldap_request_send ( conn , msg ) ;
struct ldap_message * res ;
NTSTATUS status ;
status = ldap_result_n ( req , 0 , & res ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
talloc_free ( req ) ;
return status ;
}
if ( res - > type ! = msg - > type + 1 ) {
talloc_free ( req ) ;
return NT_STATUS_LDAP ( LDAP_PROTOCOL_ERROR ) ;
}
status = ldap_check_response ( conn , & res - > r . GeneralResult ) ;
talloc_free ( req ) ;
return status ;
}