2 Unix SMB/CIFS mplementation.
3 LDAP protocol helper functions for SAMBA
5 Copyright (C) Andrew Tridgell 2004
6 Copyright (C) Volker Lendecke 2004
7 Copyright (C) Stefan Metzmacher 2004
8 Copyright (C) Simo Sorce 2004
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 #include "lib/socket/socket.h"
28 #include "../lib/util/asn1.h"
29 #include "../lib/util/dlinklist.h"
30 #include "libcli/ldap/ldap.h"
31 #include "libcli/ldap/ldap_proto.h"
32 #include "libcli/ldap/ldap_client.h"
33 #include "libcli/composite/composite.h"
34 #include "lib/stream/packet.h"
35 #include "lib/tls/tls.h"
36 #include "auth/gensec/gensec.h"
37 #include "system/time.h"
38 #include "param/param.h"
39 #include "libcli/resolve/resolve.h"
42 create a new ldap_connection stucture. The event context is optional
44 _PUBLIC_
struct ldap_connection
*ldap4_new_connection(TALLOC_CTX
*mem_ctx
,
45 struct loadparm_context
*lp_ctx
,
46 struct tevent_context
*ev
)
48 struct ldap_connection
*conn
;
54 conn
= talloc_zero(mem_ctx
, struct ldap_connection
);
59 conn
->next_messageid
= 1;
60 conn
->event
.event_ctx
= ev
;
62 conn
->lp_ctx
= lp_ctx
;
64 /* set a reasonable request timeout */
67 /* explicitly avoid reconnections by default */
68 conn
->reconnect
.max_retries
= 0;
74 the connection is dead
76 static void ldap_connection_dead(struct ldap_connection
*conn
)
78 struct ldap_request
*req
;
80 talloc_free(conn
->sock
); /* this will also free event.fde */
81 talloc_free(conn
->packet
);
83 conn
->event
.fde
= NULL
;
86 /* return an error for any pending request ... */
87 while (conn
->pending
) {
89 DLIST_REMOVE(req
->conn
->pending
, req
);
90 req
->state
= LDAP_REQUEST_DONE
;
91 req
->status
= NT_STATUS_UNEXPECTED_NETWORK_ERROR
;
98 static void ldap_reconnect(struct ldap_connection
*conn
);
103 static void ldap_error_handler(void *private_data
, NTSTATUS status
)
105 struct ldap_connection
*conn
= talloc_get_type(private_data
,
106 struct ldap_connection
);
107 ldap_connection_dead(conn
);
109 /* but try to reconnect so that the ldb client can go on */
110 ldap_reconnect(conn
);
115 match up with a pending message, adding to the replies list
117 static void ldap_match_message(struct ldap_connection
*conn
, struct ldap_message
*msg
)
119 struct ldap_request
*req
;
122 for (req
=conn
->pending
; req
; req
=req
->next
) {
123 if (req
->messageid
== msg
->messageid
) break;
125 /* match a zero message id to the last request sent.
126 It seems that servers send 0 if unable to parse */
127 if (req
== NULL
&& msg
->messageid
== 0) {
131 DEBUG(0,("ldap: no matching message id for %u\n",
137 /* Check for undecoded critical extensions */
138 for (i
=0; msg
->controls
&& msg
->controls
[i
]; i
++) {
139 if (!msg
->controls_decoded
[i
] &&
140 msg
->controls
[i
]->critical
) {
141 req
->status
= NT_STATUS_LDAP(LDAP_UNAVAILABLE_CRITICAL_EXTENSION
);
142 req
->state
= LDAP_REQUEST_DONE
;
143 DLIST_REMOVE(conn
->pending
, req
);
151 /* add to the list of replies received */
152 talloc_steal(req
, msg
);
153 req
->replies
= talloc_realloc(req
, req
->replies
,
154 struct ldap_message
*, req
->num_replies
+1);
155 if (req
->replies
== NULL
) {
156 req
->status
= NT_STATUS_NO_MEMORY
;
157 req
->state
= LDAP_REQUEST_DONE
;
158 DLIST_REMOVE(conn
->pending
, req
);
165 req
->replies
[req
->num_replies
] = talloc_steal(req
->replies
, msg
);
168 if (msg
->type
!= LDAP_TAG_SearchResultEntry
&&
169 msg
->type
!= LDAP_TAG_SearchResultReference
) {
170 /* currently only search results expect multiple
172 req
->state
= LDAP_REQUEST_DONE
;
173 DLIST_REMOVE(conn
->pending
, req
);
183 decode/process LDAP data
185 static NTSTATUS
ldap_recv_handler(void *private_data
, DATA_BLOB blob
)
188 struct ldap_connection
*conn
= talloc_get_type(private_data
,
189 struct ldap_connection
);
190 struct ldap_message
*msg
= talloc(conn
, struct ldap_message
);
191 struct asn1_data
*asn1
= asn1_init(conn
);
193 if (asn1
== NULL
|| msg
== NULL
) {
194 return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR
);
197 if (!asn1_load(asn1
, blob
)) {
200 return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR
);
203 status
= ldap_decode(asn1
, samba_ldap_control_handlers(), msg
);
204 if (!NT_STATUS_IS_OK(status
)) {
209 ldap_match_message(conn
, msg
);
211 data_blob_free(&blob
);
216 /* Handle read events, from the GENSEC socket callback, or real events */
217 void ldap_read_io_handler(void *private_data
, uint16_t flags
)
219 struct ldap_connection
*conn
= talloc_get_type(private_data
,
220 struct ldap_connection
);
221 packet_recv(conn
->packet
);
225 handle ldap socket events
227 static void ldap_io_handler(struct tevent_context
*ev
, struct tevent_fd
*fde
,
228 uint16_t flags
, void *private_data
)
230 struct ldap_connection
*conn
= talloc_get_type(private_data
,
231 struct ldap_connection
);
232 if (flags
& TEVENT_FD_WRITE
) {
233 packet_queue_run(conn
->packet
);
234 if (!tls_enabled(conn
->sock
)) return;
236 if (flags
& TEVENT_FD_READ
) {
237 ldap_read_io_handler(private_data
, flags
);
244 static NTSTATUS
ldap_parse_basic_url(TALLOC_CTX
*mem_ctx
, const char *url
,
245 char **host
, uint16_t *port
, bool *ldaps
)
253 SMB_ASSERT(sizeof(protocol
)>10 && sizeof(tmp_host
)>254);
255 ret
= sscanf(url
, "%10[^:]://%254[^:/]:%d", protocol
, tmp_host
, &tmp_port
);
257 return NT_STATUS_INVALID_PARAMETER
;
260 if (strequal(protocol
, "ldap")) {
263 } else if (strequal(protocol
, "ldaps")) {
267 DEBUG(0, ("unrecognised ldap protocol (%s)!\n", protocol
));
268 return NT_STATUS_PROTOCOL_UNREACHABLE
;
274 *host
= talloc_strdup(mem_ctx
, tmp_host
);
275 NT_STATUS_HAVE_NO_MEMORY(*host
);
281 connect to a ldap server
284 struct ldap_connect_state
{
285 struct composite_context
*ctx
;
286 struct ldap_connection
*conn
;
289 static void ldap_connect_recv_unix_conn(struct composite_context
*ctx
);
290 static void ldap_connect_recv_tcp_conn(struct composite_context
*ctx
);
292 _PUBLIC_
struct composite_context
*ldap_connect_send(struct ldap_connection
*conn
,
295 struct composite_context
*result
, *ctx
;
296 struct ldap_connect_state
*state
;
300 result
= talloc_zero(conn
, struct composite_context
);
301 if (result
== NULL
) goto failed
;
302 result
->state
= COMPOSITE_STATE_IN_PROGRESS
;
303 result
->async
.fn
= NULL
;
304 result
->event_ctx
= conn
->event
.event_ctx
;
306 state
= talloc(result
, struct ldap_connect_state
);
307 if (state
== NULL
) goto failed
;
309 result
->private_data
= state
;
313 if (conn
->reconnect
.url
== NULL
) {
314 conn
->reconnect
.url
= talloc_strdup(conn
, url
);
315 if (conn
->reconnect
.url
== NULL
) goto failed
;
319 SMB_ASSERT(sizeof(protocol
)>10);
321 ret
= sscanf(url
, "%10[^:]://", protocol
);
326 if (strequal(protocol
, "ldapi")) {
327 struct socket_address
*unix_addr
;
330 NTSTATUS status
= socket_create("unix", SOCKET_TYPE_STREAM
, &conn
->sock
, 0);
331 if (!NT_STATUS_IS_OK(status
)) {
334 talloc_steal(conn
, conn
->sock
);
335 SMB_ASSERT(sizeof(protocol
)>10);
336 SMB_ASSERT(sizeof(path
)>1024);
338 /* LDAPI connections are to localhost, so give the local host name as the target for gensec */
339 conn
->host
= talloc_asprintf(conn
, "%s.%s", lp_netbios_name(conn
->lp_ctx
), lp_realm(conn
->lp_ctx
));
340 if (composite_nomem(conn
->host
, state
->ctx
)) {
344 /* The %c specifier doesn't null terminate :-( */
346 ret
= sscanf(url
, "%10[^:]://%1025c", protocol
, path
);
348 composite_error(state
->ctx
, NT_STATUS_INVALID_PARAMETER
);
352 rfc1738_unescape(path
);
354 unix_addr
= socket_address_from_strings(conn
, conn
->sock
->backend_name
,
360 ctx
= socket_connect_send(conn
->sock
, NULL
, unix_addr
,
361 0, conn
->event
.event_ctx
);
362 ctx
->async
.fn
= ldap_connect_recv_unix_conn
;
363 ctx
->async
.private_data
= state
;
366 NTSTATUS status
= ldap_parse_basic_url(conn
, url
, &conn
->host
,
367 &conn
->port
, &conn
->ldaps
);
368 if (!NT_STATUS_IS_OK(state
->ctx
->status
)) {
369 composite_error(state
->ctx
, status
);
373 ctx
= socket_connect_multi_send(state
, conn
->host
, 1, &conn
->port
,
374 lp_resolve_context(conn
->lp_ctx
), conn
->event
.event_ctx
);
375 if (ctx
== NULL
) goto failed
;
377 ctx
->async
.fn
= ldap_connect_recv_tcp_conn
;
378 ctx
->async
.private_data
= state
;
386 static void ldap_connect_got_sock(struct composite_context
*ctx
,
387 struct ldap_connection
*conn
)
389 /* setup a handler for events on this socket */
390 conn
->event
.fde
= tevent_add_fd(conn
->event
.event_ctx
, conn
->sock
,
391 socket_get_fd(conn
->sock
),
392 TEVENT_FD_READ
, ldap_io_handler
, conn
);
393 if (conn
->event
.fde
== NULL
) {
394 composite_error(ctx
, NT_STATUS_INTERNAL_ERROR
);
398 tevent_fd_set_close_fn(conn
->event
.fde
, socket_tevent_fd_close_fn
);
399 socket_set_flags(conn
->sock
, SOCKET_FLAG_NOCLOSE
);
401 talloc_steal(conn
, conn
->sock
);
403 struct socket_context
*tls_socket
;
404 struct socket_context
*tmp_socket
;
405 char *cafile
= lp_tls_cafile(conn
->sock
, conn
->lp_ctx
);
407 if (!cafile
|| !*cafile
) {
408 talloc_free(conn
->sock
);
412 tls_socket
= tls_init_client(conn
->sock
, conn
->event
.fde
, cafile
);
415 if (tls_socket
== NULL
) {
416 talloc_free(conn
->sock
);
420 /* the original socket, must become a child of the tls socket */
421 tmp_socket
= conn
->sock
;
422 conn
->sock
= talloc_steal(conn
, tls_socket
);
423 talloc_steal(conn
->sock
, tmp_socket
);
426 conn
->packet
= packet_init(conn
);
427 if (conn
->packet
== NULL
) {
428 talloc_free(conn
->sock
);
432 packet_set_private(conn
->packet
, conn
);
433 packet_set_socket(conn
->packet
, conn
->sock
);
434 packet_set_callback(conn
->packet
, ldap_recv_handler
);
435 packet_set_full_request(conn
->packet
, ldap_full_packet
);
436 packet_set_error_handler(conn
->packet
, ldap_error_handler
);
437 packet_set_event_context(conn
->packet
, conn
->event
.event_ctx
);
438 packet_set_fde(conn
->packet
, conn
->event
.fde
);
439 /* packet_set_serialise(conn->packet); */
442 packet_set_unreliable_select(conn
->packet
);
448 static void ldap_connect_recv_tcp_conn(struct composite_context
*ctx
)
450 struct ldap_connect_state
*state
=
451 talloc_get_type(ctx
->async
.private_data
,
452 struct ldap_connect_state
);
453 struct ldap_connection
*conn
= state
->conn
;
455 NTSTATUS status
= socket_connect_multi_recv(ctx
, state
, &conn
->sock
,
457 if (!NT_STATUS_IS_OK(status
)) {
458 composite_error(state
->ctx
, status
);
462 ldap_connect_got_sock(state
->ctx
, conn
);
465 static void ldap_connect_recv_unix_conn(struct composite_context
*ctx
)
467 struct ldap_connect_state
*state
=
468 talloc_get_type(ctx
->async
.private_data
,
469 struct ldap_connect_state
);
470 struct ldap_connection
*conn
= state
->conn
;
472 NTSTATUS status
= socket_connect_recv(ctx
);
474 if (!NT_STATUS_IS_OK(state
->ctx
->status
)) {
475 composite_error(state
->ctx
, status
);
479 ldap_connect_got_sock(state
->ctx
, conn
);
482 _PUBLIC_ NTSTATUS
ldap_connect_recv(struct composite_context
*ctx
)
484 NTSTATUS status
= composite_wait(ctx
);
489 _PUBLIC_ NTSTATUS
ldap_connect(struct ldap_connection
*conn
, const char *url
)
491 struct composite_context
*ctx
= ldap_connect_send(conn
, url
);
492 return ldap_connect_recv(ctx
);
495 /* set reconnect parameters */
497 _PUBLIC_
void ldap_set_reconn_params(struct ldap_connection
*conn
, int max_retries
)
500 conn
->reconnect
.max_retries
= max_retries
;
501 conn
->reconnect
.retries
= 0;
502 conn
->reconnect
.previous
= time(NULL
);
506 /* Actually this function is NOT ASYNC safe, FIXME? */
507 static void ldap_reconnect(struct ldap_connection
*conn
)
510 time_t now
= time(NULL
);
512 /* do we have set up reconnect ? */
513 if (conn
->reconnect
.max_retries
== 0) return;
515 /* is the retry time expired ? */
516 if (now
> conn
->reconnect
.previous
+ 30) {
517 conn
->reconnect
.retries
= 0;
518 conn
->reconnect
.previous
= now
;
521 /* are we reconnectind too often and too fast? */
522 if (conn
->reconnect
.retries
> conn
->reconnect
.max_retries
) return;
524 /* keep track of the number of reconnections */
525 conn
->reconnect
.retries
++;
528 status
= ldap_connect(conn
, conn
->reconnect
.url
);
529 if ( ! NT_STATUS_IS_OK(status
)) {
534 status
= ldap_rebind(conn
);
535 if ( ! NT_STATUS_IS_OK(status
)) {
536 ldap_connection_dead(conn
);
540 /* destroy an open ldap request */
541 static int ldap_request_destructor(struct ldap_request
*req
)
543 if (req
->state
== LDAP_REQUEST_PENDING
) {
544 DLIST_REMOVE(req
->conn
->pending
, req
);
550 called on timeout of a ldap request
552 static void ldap_request_timeout(struct tevent_context
*ev
, struct tevent_timer
*te
,
553 struct timeval t
, void *private_data
)
555 struct ldap_request
*req
= talloc_get_type(private_data
, struct ldap_request
);
556 req
->status
= NT_STATUS_IO_TIMEOUT
;
557 if (req
->state
== LDAP_REQUEST_PENDING
) {
558 DLIST_REMOVE(req
->conn
->pending
, req
);
560 req
->state
= LDAP_REQUEST_DONE
;
568 called on completion of a one-way ldap request
570 static void ldap_request_complete(struct tevent_context
*ev
, struct tevent_timer
*te
,
571 struct timeval t
, void *private_data
)
573 struct ldap_request
*req
= talloc_get_type(private_data
, struct ldap_request
);
580 send a ldap message - async interface
582 _PUBLIC_
struct ldap_request
*ldap_request_send(struct ldap_connection
*conn
,
583 struct ldap_message
*msg
)
585 struct ldap_request
*req
;
586 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
588 req
= talloc_zero(conn
, struct ldap_request
);
589 if (req
== NULL
) return NULL
;
591 if (conn
->sock
== NULL
) {
592 status
= NT_STATUS_INVALID_CONNECTION
;
596 req
->state
= LDAP_REQUEST_SEND
;
598 req
->messageid
= conn
->next_messageid
++;
599 if (conn
->next_messageid
== 0) {
600 conn
->next_messageid
= 1;
602 req
->type
= msg
->type
;
603 if (req
->messageid
== -1) {
607 talloc_set_destructor(req
, ldap_request_destructor
);
609 msg
->messageid
= req
->messageid
;
611 if (!ldap_encode(msg
, samba_ldap_control_handlers(), &req
->data
, req
)) {
612 status
= NT_STATUS_INTERNAL_ERROR
;
616 status
= packet_send(conn
->packet
, req
->data
);
617 if (!NT_STATUS_IS_OK(status
)) {
621 /* some requests don't expect a reply, so don't add those to the
623 if (req
->type
== LDAP_TAG_AbandonRequest
||
624 req
->type
== LDAP_TAG_UnbindRequest
) {
625 req
->status
= NT_STATUS_OK
;
626 req
->state
= LDAP_REQUEST_DONE
;
627 /* we can't call the async callback now, as it isn't setup, so
628 call it as next event */
629 tevent_add_timer(conn
->event
.event_ctx
, req
, timeval_zero(),
630 ldap_request_complete
, req
);
634 req
->state
= LDAP_REQUEST_PENDING
;
635 DLIST_ADD(conn
->pending
, req
);
637 /* put a timeout on the request */
638 req
->time_event
= tevent_add_timer(conn
->event
.event_ctx
, req
,
639 timeval_current_ofs(conn
->timeout
, 0),
640 ldap_request_timeout
, req
);
645 req
->status
= status
;
646 req
->state
= LDAP_REQUEST_ERROR
;
647 tevent_add_timer(conn
->event
.event_ctx
, req
, timeval_zero(),
648 ldap_request_complete
, req
);
655 wait for a request to complete
656 note that this does not destroy the request
658 _PUBLIC_ NTSTATUS
ldap_request_wait(struct ldap_request
*req
)
660 while (req
->state
< LDAP_REQUEST_DONE
) {
661 if (tevent_loop_once(req
->conn
->event
.event_ctx
) != 0) {
662 req
->status
= NT_STATUS_UNEXPECTED_NETWORK_ERROR
;
671 a mapping of ldap response code to strings
673 static const struct {
674 enum ldap_result_code code
;
676 } ldap_code_map
[] = {
677 #define _LDAP_MAP_CODE(c) { c, #c }
678 _LDAP_MAP_CODE(LDAP_SUCCESS
),
679 _LDAP_MAP_CODE(LDAP_OPERATIONS_ERROR
),
680 _LDAP_MAP_CODE(LDAP_PROTOCOL_ERROR
),
681 _LDAP_MAP_CODE(LDAP_TIME_LIMIT_EXCEEDED
),
682 _LDAP_MAP_CODE(LDAP_SIZE_LIMIT_EXCEEDED
),
683 _LDAP_MAP_CODE(LDAP_COMPARE_FALSE
),
684 _LDAP_MAP_CODE(LDAP_COMPARE_TRUE
),
685 _LDAP_MAP_CODE(LDAP_AUTH_METHOD_NOT_SUPPORTED
),
686 _LDAP_MAP_CODE(LDAP_STRONG_AUTH_REQUIRED
),
687 _LDAP_MAP_CODE(LDAP_REFERRAL
),
688 _LDAP_MAP_CODE(LDAP_ADMIN_LIMIT_EXCEEDED
),
689 _LDAP_MAP_CODE(LDAP_UNAVAILABLE_CRITICAL_EXTENSION
),
690 _LDAP_MAP_CODE(LDAP_CONFIDENTIALITY_REQUIRED
),
691 _LDAP_MAP_CODE(LDAP_SASL_BIND_IN_PROGRESS
),
692 _LDAP_MAP_CODE(LDAP_NO_SUCH_ATTRIBUTE
),
693 _LDAP_MAP_CODE(LDAP_UNDEFINED_ATTRIBUTE_TYPE
),
694 _LDAP_MAP_CODE(LDAP_INAPPROPRIATE_MATCHING
),
695 _LDAP_MAP_CODE(LDAP_CONSTRAINT_VIOLATION
),
696 _LDAP_MAP_CODE(LDAP_ATTRIBUTE_OR_VALUE_EXISTS
),
697 _LDAP_MAP_CODE(LDAP_INVALID_ATTRIBUTE_SYNTAX
),
698 _LDAP_MAP_CODE(LDAP_NO_SUCH_OBJECT
),
699 _LDAP_MAP_CODE(LDAP_ALIAS_PROBLEM
),
700 _LDAP_MAP_CODE(LDAP_INVALID_DN_SYNTAX
),
701 _LDAP_MAP_CODE(LDAP_ALIAS_DEREFERENCING_PROBLEM
),
702 _LDAP_MAP_CODE(LDAP_INAPPROPRIATE_AUTHENTICATION
),
703 _LDAP_MAP_CODE(LDAP_INVALID_CREDENTIALS
),
704 _LDAP_MAP_CODE(LDAP_INSUFFICIENT_ACCESS_RIGHTS
),
705 _LDAP_MAP_CODE(LDAP_BUSY
),
706 _LDAP_MAP_CODE(LDAP_UNAVAILABLE
),
707 _LDAP_MAP_CODE(LDAP_UNWILLING_TO_PERFORM
),
708 _LDAP_MAP_CODE(LDAP_LOOP_DETECT
),
709 _LDAP_MAP_CODE(LDAP_NAMING_VIOLATION
),
710 _LDAP_MAP_CODE(LDAP_OBJECT_CLASS_VIOLATION
),
711 _LDAP_MAP_CODE(LDAP_NOT_ALLOWED_ON_NON_LEAF
),
712 _LDAP_MAP_CODE(LDAP_NOT_ALLOWED_ON_RDN
),
713 _LDAP_MAP_CODE(LDAP_ENTRY_ALREADY_EXISTS
),
714 _LDAP_MAP_CODE(LDAP_OBJECT_CLASS_MODS_PROHIBITED
),
715 _LDAP_MAP_CODE(LDAP_AFFECTS_MULTIPLE_DSAS
),
716 _LDAP_MAP_CODE(LDAP_OTHER
)
720 used to setup the status code from a ldap response
722 _PUBLIC_ NTSTATUS
ldap_check_response(struct ldap_connection
*conn
, struct ldap_Result
*r
)
725 const char *codename
= "unknown";
727 if (r
->resultcode
== LDAP_SUCCESS
) {
731 if (conn
->last_error
) {
732 talloc_free(conn
->last_error
);
735 for (i
=0;i
<ARRAY_SIZE(ldap_code_map
);i
++) {
736 if (r
->resultcode
== ldap_code_map
[i
].code
) {
737 codename
= ldap_code_map
[i
].str
;
742 conn
->last_error
= talloc_asprintf(conn
, "LDAP error %u %s - %s <%s> <%s>",
745 r
->dn
?r
->dn
:"(NULL)",
746 r
->errormessage
?r
->errormessage
:"",
747 r
->referral
?r
->referral
:"");
749 return NT_STATUS_LDAP(r
->resultcode
);
753 return error string representing the last error
755 _PUBLIC_
const char *ldap_errstr(struct ldap_connection
*conn
,
759 if (NT_STATUS_IS_LDAP(status
) && conn
->last_error
!= NULL
) {
760 return talloc_strdup(mem_ctx
, conn
->last_error
);
762 return talloc_asprintf(mem_ctx
, "LDAP client internal error: %s", nt_errstr(status
));
767 return the Nth result message, waiting if necessary
769 _PUBLIC_ NTSTATUS
ldap_result_n(struct ldap_request
*req
, int n
, struct ldap_message
**msg
)
773 NT_STATUS_HAVE_NO_MEMORY(req
);
775 while (req
->state
< LDAP_REQUEST_DONE
&& n
>= req
->num_replies
) {
776 if (tevent_loop_once(req
->conn
->event
.event_ctx
) != 0) {
777 return NT_STATUS_UNEXPECTED_NETWORK_ERROR
;
781 if (n
< req
->num_replies
) {
782 *msg
= req
->replies
[n
];
786 if (!NT_STATUS_IS_OK(req
->status
)) {
790 return NT_STATUS_NO_MORE_ENTRIES
;
795 return a single result message, checking if it is of the expected LDAP type
797 _PUBLIC_ NTSTATUS
ldap_result_one(struct ldap_request
*req
, struct ldap_message
**msg
, int type
)
800 status
= ldap_result_n(req
, 0, msg
);
801 if (!NT_STATUS_IS_OK(status
)) {
804 if ((*msg
)->type
!= type
) {
806 return NT_STATUS_UNEXPECTED_NETWORK_ERROR
;
812 a simple ldap transaction, for single result requests that only need a status code
813 this relies on single valued requests having the response type == request type + 1
815 _PUBLIC_ NTSTATUS
ldap_transaction(struct ldap_connection
*conn
, struct ldap_message
*msg
)
817 struct ldap_request
*req
= ldap_request_send(conn
, msg
);
818 struct ldap_message
*res
;
820 status
= ldap_result_n(req
, 0, &res
);
821 if (!NT_STATUS_IS_OK(status
)) {
825 if (res
->type
!= msg
->type
+ 1) {
827 return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR
);
829 status
= ldap_check_response(conn
, &res
->r
.GeneralResult
);