s4:libcli/ldap: send AbandonRequests for cancelled requests
[Samba.git] / source4 / libcli / ldap / ldap_client.c
blob97a83cec4ab38988c98ec4d40c7d1cd607357b2b
1 /*
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/>.
25 #include "includes.h"
26 #include <tevent.h>
27 #include "lib/socket/socket.h"
28 #include "lib/tsocket/tsocket.h"
29 #include "libcli/util/tstream.h"
30 #include "../lib/util/asn1.h"
31 #include "../lib/util/dlinklist.h"
32 #include "libcli/ldap/libcli_ldap.h"
33 #include "libcli/ldap/ldap_proto.h"
34 #include "libcli/ldap/ldap_client.h"
35 #include "libcli/composite/composite.h"
36 #include "lib/tls/tls.h"
37 #include "auth/gensec/gensec.h"
38 #include "system/time.h"
39 #include "param/param.h"
40 #include "libcli/resolve/resolve.h"
42 static void ldap_connection_dead(struct ldap_connection *conn, NTSTATUS status);
44 static int ldap_connection_destructor(struct ldap_connection *conn)
47 * NT_STATUS_OK means that callbacks of pending requests are not
48 * triggered
50 ldap_connection_dead(conn, NT_STATUS_OK);
51 return 0;
54 /**
55 create a new ldap_connection stucture. The event context is optional
58 _PUBLIC_ struct ldap_connection *ldap4_new_connection(TALLOC_CTX *mem_ctx,
59 struct loadparm_context *lp_ctx,
60 struct tevent_context *ev)
62 struct ldap_connection *conn;
64 if (ev == NULL) {
65 return NULL;
68 conn = talloc_zero(mem_ctx, struct ldap_connection);
69 if (conn == NULL) {
70 return NULL;
73 conn->next_messageid = 1;
74 conn->event.event_ctx = ev;
76 conn->sockets.send_queue = tevent_queue_create(conn,
77 "ldap_connection send_queue");
78 if (conn->sockets.send_queue == NULL) {
79 TALLOC_FREE(conn);
80 return NULL;
83 conn->lp_ctx = lp_ctx;
85 /* set a reasonable request timeout */
86 conn->timeout = 60;
88 /* explicitly avoid reconnections by default */
89 conn->reconnect.max_retries = 0;
91 talloc_set_destructor(conn, ldap_connection_destructor);
92 return conn;
96 the connection is dead
98 static void ldap_connection_dead(struct ldap_connection *conn, NTSTATUS status)
100 struct ldap_request *req;
102 tevent_queue_stop(conn->sockets.send_queue);
103 TALLOC_FREE(conn->sockets.recv_subreq);
104 conn->sockets.active = NULL;
105 TALLOC_FREE(conn->sockets.sasl);
106 TALLOC_FREE(conn->sockets.tls);
107 TALLOC_FREE(conn->sockets.raw);
109 /* return an error for any pending request ... */
110 while (conn->pending) {
111 req = conn->pending;
112 DLIST_REMOVE(req->conn->pending, req);
113 req->conn = NULL;
114 req->state = LDAP_REQUEST_DONE;
115 if (NT_STATUS_IS_OK(status)) {
116 continue;
118 req->status = status;
119 if (req->async.fn) {
120 req->async.fn(req);
125 static void ldap_reconnect(struct ldap_connection *conn);
128 handle packet errors
130 static void ldap_error_handler(struct ldap_connection *conn, NTSTATUS status)
132 ldap_connection_dead(conn, status);
134 /* but try to reconnect so that the ldb client can go on */
135 ldap_reconnect(conn);
140 match up with a pending message, adding to the replies list
142 static void ldap_match_message(struct ldap_connection *conn, struct ldap_message *msg)
144 struct ldap_request *req;
145 int i;
147 for (req=conn->pending; req; req=req->next) {
148 if (req->messageid == msg->messageid) break;
150 /* match a zero message id to the last request sent.
151 It seems that servers send 0 if unable to parse */
152 if (req == NULL && msg->messageid == 0) {
153 req = conn->pending;
155 if (req == NULL) {
156 DEBUG(0,("ldap: no matching message id for %u\n",
157 msg->messageid));
158 TALLOC_FREE(msg);
159 return;
162 /* Check for undecoded critical extensions */
163 for (i=0; msg->controls && msg->controls[i]; i++) {
164 if (!msg->controls_decoded[i] &&
165 msg->controls[i]->critical) {
166 TALLOC_FREE(msg);
167 req->status = NT_STATUS_LDAP(LDAP_UNAVAILABLE_CRITICAL_EXTENSION);
168 req->state = LDAP_REQUEST_DONE;
169 DLIST_REMOVE(conn->pending, req);
170 if (req->async.fn) {
171 req->async.fn(req);
173 return;
177 /* add to the list of replies received */
178 req->replies = talloc_realloc(req, req->replies,
179 struct ldap_message *, req->num_replies+1);
180 if (req->replies == NULL) {
181 TALLOC_FREE(msg);
182 req->status = NT_STATUS_NO_MEMORY;
183 req->state = LDAP_REQUEST_DONE;
184 DLIST_REMOVE(conn->pending, req);
185 if (req->async.fn) {
186 req->async.fn(req);
188 return;
191 req->replies[req->num_replies] = talloc_steal(req->replies, msg);
192 req->num_replies++;
194 if (msg->type != LDAP_TAG_SearchResultEntry &&
195 msg->type != LDAP_TAG_SearchResultReference) {
196 /* currently only search results expect multiple
197 replies */
198 req->state = LDAP_REQUEST_DONE;
199 DLIST_REMOVE(conn->pending, req);
202 if (req->async.fn) {
203 req->async.fn(req);
207 static void ldap_connection_recv_done(struct tevent_req *subreq);
209 static void ldap_connection_recv_next(struct ldap_connection *conn)
211 struct tevent_req *subreq = NULL;
213 if (conn->sockets.recv_subreq != NULL) {
214 return;
217 if (conn->sockets.active == NULL) {
218 return;
221 if (conn->pending == NULL) {
222 return;
226 * The minimum size of a LDAP pdu is 7 bytes
228 * dumpasn1 -hh ldap-unbind-min.dat
230 * <30 05 02 01 09 42 00>
231 * 0 5: SEQUENCE {
232 * <02 01 09>
233 * 2 1: INTEGER 9
234 * <42 00>
235 * 5 0: [APPLICATION 2]
236 * : Error: Object has zero length.
237 * : }
239 * dumpasn1 -hh ldap-unbind-windows.dat
241 * <30 84 00 00 00 05 02 01 09 42 00>
242 * 0 5: SEQUENCE {
243 * <02 01 09>
244 * 6 1: INTEGER 9
245 * <42 00>
246 * 9 0: [APPLICATION 2]
247 * : Error: Object has zero length.
248 * : }
250 * This means using an initial read size
251 * of 7 is ok.
253 subreq = tstream_read_pdu_blob_send(conn,
254 conn->event.event_ctx,
255 conn->sockets.active,
256 7, /* initial_read_size */
257 ldap_full_packet,
258 conn);
259 if (subreq == NULL) {
260 ldap_error_handler(conn, NT_STATUS_NO_MEMORY);
261 return;
263 tevent_req_set_callback(subreq, ldap_connection_recv_done, conn);
264 conn->sockets.recv_subreq = subreq;
265 return;
269 decode/process LDAP data
271 static void ldap_connection_recv_done(struct tevent_req *subreq)
273 NTSTATUS status;
274 struct ldap_connection *conn =
275 tevent_req_callback_data(subreq,
276 struct ldap_connection);
277 struct ldap_message *msg;
278 struct asn1_data *asn1;
279 DATA_BLOB blob;
281 msg = talloc_zero(conn, struct ldap_message);
282 if (msg == NULL) {
283 ldap_error_handler(conn, NT_STATUS_NO_MEMORY);
284 return;
287 asn1 = asn1_init(conn);
288 if (asn1 == NULL) {
289 TALLOC_FREE(msg);
290 ldap_error_handler(conn, NT_STATUS_NO_MEMORY);
291 return;
294 conn->sockets.recv_subreq = NULL;
296 status = tstream_read_pdu_blob_recv(subreq,
297 asn1,
298 &blob);
299 TALLOC_FREE(subreq);
300 if (!NT_STATUS_IS_OK(status)) {
301 TALLOC_FREE(msg);
302 asn1_free(asn1);
303 ldap_error_handler(conn, status);
304 return;
307 asn1_load_nocopy(asn1, blob.data, blob.length);
309 status = ldap_decode(asn1, samba_ldap_control_handlers(), msg);
310 asn1_free(asn1);
311 if (!NT_STATUS_IS_OK(status)) {
312 TALLOC_FREE(msg);
313 ldap_error_handler(conn, status);
314 return;
317 ldap_match_message(conn, msg);
318 ldap_connection_recv_next(conn);
320 return;
324 parse a ldap URL
326 static NTSTATUS ldap_parse_basic_url(TALLOC_CTX *mem_ctx, const char *url,
327 char **host, uint16_t *port, bool *ldaps)
329 int tmp_port = 0;
330 char protocol[11];
331 char tmp_host[1025];
332 int ret;
334 /* Paranoia check */
335 SMB_ASSERT(sizeof(protocol)>10 && sizeof(tmp_host)>254);
337 ret = sscanf(url, "%10[^:]://%254[^:/]:%d", protocol, tmp_host, &tmp_port);
338 if (ret < 2) {
339 return NT_STATUS_INVALID_PARAMETER;
342 if (strequal(protocol, "ldap")) {
343 *port = 389;
344 *ldaps = false;
345 } else if (strequal(protocol, "ldaps")) {
346 *port = 636;
347 *ldaps = true;
348 } else {
349 DEBUG(0, ("unrecognised ldap protocol (%s)!\n", protocol));
350 return NT_STATUS_PROTOCOL_UNREACHABLE;
353 if (tmp_port != 0)
354 *port = tmp_port;
356 *host = talloc_strdup(mem_ctx, tmp_host);
357 NT_STATUS_HAVE_NO_MEMORY(*host);
359 return NT_STATUS_OK;
363 connect to a ldap server
366 struct ldap_connect_state {
367 struct composite_context *ctx;
368 struct ldap_connection *conn;
369 struct socket_context *sock;
370 struct tstream_context *raw;
371 struct tstream_tls_params *tls_params;
372 struct tstream_context *tls;
375 static void ldap_connect_recv_unix_conn(struct composite_context *ctx);
376 static void ldap_connect_recv_tcp_conn(struct composite_context *ctx);
378 _PUBLIC_ struct composite_context *ldap_connect_send(struct ldap_connection *conn,
379 const char *url)
381 struct composite_context *result, *ctx;
382 struct ldap_connect_state *state;
383 char protocol[11];
384 int ret;
386 result = talloc_zero(conn, struct composite_context);
387 if (result == NULL) goto failed;
388 result->state = COMPOSITE_STATE_IN_PROGRESS;
389 result->async.fn = NULL;
390 result->event_ctx = conn->event.event_ctx;
392 state = talloc(result, struct ldap_connect_state);
393 if (state == NULL) goto failed;
394 state->ctx = result;
395 result->private_data = state;
397 state->conn = conn;
399 if (conn->reconnect.url == NULL) {
400 conn->reconnect.url = talloc_strdup(conn, url);
401 if (conn->reconnect.url == NULL) goto failed;
404 /* Paranoia check */
405 SMB_ASSERT(sizeof(protocol)>10);
407 ret = sscanf(url, "%10[^:]://", protocol);
408 if (ret < 1) {
409 return NULL;
412 if (strequal(protocol, "ldapi")) {
413 struct socket_address *unix_addr;
414 char path[1025];
416 NTSTATUS status = socket_create("unix", SOCKET_TYPE_STREAM, &state->sock, 0);
417 if (!NT_STATUS_IS_OK(status)) {
418 return NULL;
420 talloc_steal(state, state->sock);
421 SMB_ASSERT(sizeof(protocol)>10);
422 SMB_ASSERT(sizeof(path)>1024);
424 /* LDAPI connections are to localhost, so give the
425 * local host name as the target for gensec's
426 * DIGEST-MD5 mechanism */
427 conn->host = talloc_asprintf(conn, "%s.%s",
428 lpcfg_netbios_name(conn->lp_ctx),
429 lpcfg_dnsdomain(conn->lp_ctx));
430 if (composite_nomem(conn->host, state->ctx)) {
431 return result;
434 /* The %c specifier doesn't null terminate :-( */
435 ZERO_STRUCT(path);
436 ret = sscanf(url, "%10[^:]://%1025c", protocol, path);
437 if (ret < 2) {
438 composite_error(state->ctx, NT_STATUS_INVALID_PARAMETER);
439 return result;
442 rfc1738_unescape(path);
444 unix_addr = socket_address_from_strings(state, state->sock->backend_name,
445 path, 0);
446 if (composite_nomem(unix_addr, result)) {
447 return result;
451 ctx = socket_connect_send(state->sock, NULL, unix_addr,
452 0, result->event_ctx);
453 ctx->async.fn = ldap_connect_recv_unix_conn;
454 ctx->async.private_data = state;
455 return result;
456 } else {
457 NTSTATUS status = ldap_parse_basic_url(conn, url, &conn->host,
458 &conn->port, &conn->ldaps);
459 if (!NT_STATUS_IS_OK(status)) {
460 composite_error(result, status);
461 return result;
464 if (conn->ldaps) {
465 char *ca_file = lpcfg_tls_cafile(state, conn->lp_ctx);
466 char *crl_file = lpcfg_tls_crlfile(state, conn->lp_ctx);
467 const char *tls_priority = lpcfg_tls_priority(conn->lp_ctx);
468 if (!ca_file || !*ca_file) {
469 composite_error(result,
470 NT_STATUS_INVALID_PARAMETER_MIX);
471 return result;
474 status = tstream_tls_params_client(state,
475 ca_file,
476 crl_file,
477 tls_priority,
478 &state->tls_params);
479 if (!NT_STATUS_IS_OK(status)) {
480 composite_error(result, status);
481 return result;
485 ctx = socket_connect_multi_send(state, conn->host, 1, &conn->port,
486 lpcfg_resolve_context(conn->lp_ctx),
487 result->event_ctx);
488 if (composite_nomem(ctx, result)) {
489 return result;
492 ctx->async.fn = ldap_connect_recv_tcp_conn;
493 ctx->async.private_data = state;
494 return result;
496 failed:
497 talloc_free(result);
498 return NULL;
501 static void ldap_connect_got_tls(struct tevent_req *subreq);
503 static void ldap_connect_got_sock(struct composite_context *ctx,
504 struct ldap_connection *conn)
506 struct ldap_connect_state *state =
507 talloc_get_type_abort(ctx->private_data,
508 struct ldap_connect_state);
509 struct tevent_req *subreq = NULL;
510 int fd;
511 int ret;
513 socket_set_flags(state->sock, SOCKET_FLAG_NOCLOSE);
514 fd = socket_get_fd(state->sock);
515 TALLOC_FREE(state->sock);
517 smb_set_close_on_exec(fd);
519 ret = set_blocking(fd, false);
520 if (ret == -1) {
521 NTSTATUS status = map_nt_error_from_unix_common(errno);
522 composite_error(state->ctx, status);
523 return;
526 ret = tstream_bsd_existing_socket(state, fd, &state->raw);
527 if (ret == -1) {
528 NTSTATUS status = map_nt_error_from_unix_common(errno);
529 composite_error(state->ctx, status);
530 return;
533 if (!conn->ldaps) {
534 conn->sockets.raw = talloc_move(conn, &state->raw);
535 conn->sockets.active = conn->sockets.raw;
536 composite_done(state->ctx);
537 return;
540 subreq = tstream_tls_connect_send(state, state->ctx->event_ctx,
541 state->raw, state->tls_params);
542 if (composite_nomem(subreq, state->ctx)) {
543 return;
545 tevent_req_set_callback(subreq, ldap_connect_got_tls, state);
548 static void ldap_connect_got_tls(struct tevent_req *subreq)
550 struct ldap_connect_state *state =
551 tevent_req_callback_data(subreq,
552 struct ldap_connect_state);
553 int err;
554 int ret;
556 ret = tstream_tls_connect_recv(subreq, &err, state, &state->tls);
557 TALLOC_FREE(subreq);
558 if (ret == -1) {
559 NTSTATUS status = map_nt_error_from_unix_common(err);
560 composite_error(state->ctx, status);
561 return;
564 talloc_steal(state->tls, state->tls_params);
566 state->conn->sockets.raw = talloc_move(state->conn, &state->raw);
567 state->conn->sockets.tls = talloc_move(state->conn->sockets.raw,
568 &state->tls);
569 state->conn->sockets.active = state->conn->sockets.tls;
570 composite_done(state->ctx);
573 static void ldap_connect_recv_tcp_conn(struct composite_context *ctx)
575 struct ldap_connect_state *state =
576 talloc_get_type_abort(ctx->async.private_data,
577 struct ldap_connect_state);
578 struct ldap_connection *conn = state->conn;
579 uint16_t port;
580 NTSTATUS status = socket_connect_multi_recv(ctx, state, &state->sock,
581 &port);
582 if (!NT_STATUS_IS_OK(status)) {
583 composite_error(state->ctx, status);
584 return;
587 ldap_connect_got_sock(state->ctx, conn);
590 static void ldap_connect_recv_unix_conn(struct composite_context *ctx)
592 struct ldap_connect_state *state =
593 talloc_get_type_abort(ctx->async.private_data,
594 struct ldap_connect_state);
595 struct ldap_connection *conn = state->conn;
597 NTSTATUS status = socket_connect_recv(ctx);
599 if (!NT_STATUS_IS_OK(state->ctx->status)) {
600 composite_error(state->ctx, status);
601 return;
604 ldap_connect_got_sock(state->ctx, conn);
607 _PUBLIC_ NTSTATUS ldap_connect_recv(struct composite_context *ctx)
609 NTSTATUS status = composite_wait(ctx);
610 talloc_free(ctx);
611 return status;
614 _PUBLIC_ NTSTATUS ldap_connect(struct ldap_connection *conn, const char *url)
616 struct composite_context *ctx = ldap_connect_send(conn, url);
617 return ldap_connect_recv(ctx);
620 /* set reconnect parameters */
622 _PUBLIC_ void ldap_set_reconn_params(struct ldap_connection *conn, int max_retries)
624 if (conn) {
625 conn->reconnect.max_retries = max_retries;
626 conn->reconnect.retries = 0;
627 conn->reconnect.previous = time_mono(NULL);
631 /* Actually this function is NOT ASYNC safe, FIXME? */
632 static void ldap_reconnect(struct ldap_connection *conn)
634 NTSTATUS status;
635 time_t now = time_mono(NULL);
637 /* do we have set up reconnect ? */
638 if (conn->reconnect.max_retries == 0) return;
640 /* is the retry time expired ? */
641 if (now > conn->reconnect.previous + 30) {
642 conn->reconnect.retries = 0;
643 conn->reconnect.previous = now;
646 /* are we reconnectind too often and too fast? */
647 if (conn->reconnect.retries > conn->reconnect.max_retries) return;
649 /* keep track of the number of reconnections */
650 conn->reconnect.retries++;
652 /* reconnect */
653 status = ldap_connect(conn, conn->reconnect.url);
654 if ( ! NT_STATUS_IS_OK(status)) {
655 return;
658 /* rebind */
659 status = ldap_rebind(conn);
660 if ( ! NT_STATUS_IS_OK(status)) {
661 ldap_connection_dead(conn, status);
665 static void ldap_request_destructor_abandon(struct ldap_request *abandon)
667 TALLOC_FREE(abandon);
670 /* destroy an open ldap request */
671 static int ldap_request_destructor(struct ldap_request *req)
673 if (req->state == LDAP_REQUEST_PENDING) {
674 struct ldap_message msg = {
675 .type = LDAP_TAG_AbandonRequest,
676 .r.AbandonRequest.messageid = req->messageid,
678 struct ldap_request *abandon = NULL;
680 DLIST_REMOVE(req->conn->pending, req);
682 abandon = ldap_request_send(req->conn, &msg);
683 if (abandon == NULL) {
684 ldap_error_handler(req->conn, NT_STATUS_NO_MEMORY);
685 return 0;
687 abandon->async.fn = ldap_request_destructor_abandon;
688 abandon->async.private_data = NULL;
691 return 0;
694 static void ldap_request_timeout_abandon(struct ldap_request *abandon)
696 struct ldap_request *req =
697 talloc_get_type_abort(abandon->async.private_data,
698 struct ldap_request);
700 if (req->state == LDAP_REQUEST_PENDING) {
701 DLIST_REMOVE(req->conn->pending, req);
703 req->state = LDAP_REQUEST_DONE;
704 if (req->async.fn) {
705 req->async.fn(req);
710 called on timeout of a ldap request
712 static void ldap_request_timeout(struct tevent_context *ev, struct tevent_timer *te,
713 struct timeval t, void *private_data)
715 struct ldap_request *req =
716 talloc_get_type_abort(private_data,
717 struct ldap_request);
719 req->status = NT_STATUS_IO_TIMEOUT;
720 if (req->state == LDAP_REQUEST_PENDING) {
721 struct ldap_message msg = {
722 .type = LDAP_TAG_AbandonRequest,
723 .r.AbandonRequest.messageid = req->messageid,
725 struct ldap_request *abandon = NULL;
727 abandon = ldap_request_send(req->conn, &msg);
728 if (abandon == NULL) {
729 ldap_error_handler(req->conn, NT_STATUS_NO_MEMORY);
730 return;
732 talloc_reparent(req->conn, req, abandon);
733 abandon->async.fn = ldap_request_timeout_abandon;
734 abandon->async.private_data = req;
735 DLIST_REMOVE(req->conn->pending, req);
736 return;
738 req->state = LDAP_REQUEST_DONE;
739 if (req->async.fn) {
740 req->async.fn(req);
746 called on completion of a failed ldap request
748 static void ldap_request_failed_complete(struct tevent_context *ev, struct tevent_timer *te,
749 struct timeval t, void *private_data)
751 struct ldap_request *req =
752 talloc_get_type_abort(private_data,
753 struct ldap_request);
755 if (req->async.fn) {
756 req->async.fn(req);
760 static void ldap_request_written(struct tevent_req *subreq);
763 send a ldap message - async interface
765 _PUBLIC_ struct ldap_request *ldap_request_send(struct ldap_connection *conn,
766 struct ldap_message *msg)
768 struct ldap_request *req;
769 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
770 struct tevent_req *subreq = NULL;
772 req = talloc_zero(conn, struct ldap_request);
773 if (req == NULL) return NULL;
775 if (conn->sockets.active == NULL) {
776 status = NT_STATUS_INVALID_CONNECTION;
777 goto failed;
780 req->state = LDAP_REQUEST_SEND;
781 req->conn = conn;
782 req->messageid = conn->next_messageid++;
783 if (conn->next_messageid == 0) {
784 conn->next_messageid = 1;
786 req->type = msg->type;
787 if (req->messageid == -1) {
788 goto failed;
791 talloc_set_destructor(req, ldap_request_destructor);
793 msg->messageid = req->messageid;
795 if (!ldap_encode(msg, samba_ldap_control_handlers(), &req->data, req)) {
796 status = NT_STATUS_INTERNAL_ERROR;
797 goto failed;
800 /* put a timeout on the request */
801 req->time_event = tevent_add_timer(conn->event.event_ctx, req,
802 timeval_current_ofs(conn->timeout, 0),
803 ldap_request_timeout, req);
804 if (req->time_event == NULL) {
805 status = NT_STATUS_NO_MEMORY;
806 goto failed;
809 req->write_iov.iov_base = req->data.data;
810 req->write_iov.iov_len = req->data.length;
812 subreq = tstream_writev_queue_send(req, conn->event.event_ctx,
813 conn->sockets.active,
814 conn->sockets.send_queue,
815 &req->write_iov, 1);
816 if (subreq == NULL) {
817 status = NT_STATUS_NO_MEMORY;
818 goto failed;
820 tevent_req_set_callback(subreq, ldap_request_written, req);
822 req->state = LDAP_REQUEST_PENDING;
823 DLIST_ADD(conn->pending, req);
825 return req;
827 failed:
828 req->status = status;
829 req->state = LDAP_REQUEST_ERROR;
830 tevent_add_timer(conn->event.event_ctx, req, timeval_zero(),
831 ldap_request_failed_complete, req);
833 return req;
836 static void ldap_request_written(struct tevent_req *subreq)
838 struct ldap_request *req =
839 tevent_req_callback_data(subreq,
840 struct ldap_request);
841 int err;
842 ssize_t ret;
844 ret = tstream_writev_queue_recv(subreq, &err);
845 TALLOC_FREE(subreq);
846 if (ret == -1) {
847 NTSTATUS error = map_nt_error_from_unix_common(err);
848 ldap_error_handler(req->conn, error);
849 return;
852 if (req->type == LDAP_TAG_AbandonRequest ||
853 req->type == LDAP_TAG_UnbindRequest)
855 if (req->state == LDAP_REQUEST_PENDING) {
856 DLIST_REMOVE(req->conn->pending, req);
858 req->state = LDAP_REQUEST_DONE;
859 if (req->async.fn) {
860 req->async.fn(req);
862 return;
865 ldap_connection_recv_next(req->conn);
870 wait for a request to complete
871 note that this does not destroy the request
873 _PUBLIC_ NTSTATUS ldap_request_wait(struct ldap_request *req)
875 while (req->state < LDAP_REQUEST_DONE) {
876 if (tevent_loop_once(req->conn->event.event_ctx) != 0) {
877 req->state = LDAP_REQUEST_ERROR;
878 req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
879 break;
882 return req->status;
887 a mapping of ldap response code to strings
889 static const struct {
890 enum ldap_result_code code;
891 const char *str;
892 } ldap_code_map[] = {
893 #define _LDAP_MAP_CODE(c) { c, #c }
894 _LDAP_MAP_CODE(LDAP_SUCCESS),
895 _LDAP_MAP_CODE(LDAP_OPERATIONS_ERROR),
896 _LDAP_MAP_CODE(LDAP_PROTOCOL_ERROR),
897 _LDAP_MAP_CODE(LDAP_TIME_LIMIT_EXCEEDED),
898 _LDAP_MAP_CODE(LDAP_SIZE_LIMIT_EXCEEDED),
899 _LDAP_MAP_CODE(LDAP_COMPARE_FALSE),
900 _LDAP_MAP_CODE(LDAP_COMPARE_TRUE),
901 _LDAP_MAP_CODE(LDAP_AUTH_METHOD_NOT_SUPPORTED),
902 _LDAP_MAP_CODE(LDAP_STRONG_AUTH_REQUIRED),
903 _LDAP_MAP_CODE(LDAP_REFERRAL),
904 _LDAP_MAP_CODE(LDAP_ADMIN_LIMIT_EXCEEDED),
905 _LDAP_MAP_CODE(LDAP_UNAVAILABLE_CRITICAL_EXTENSION),
906 _LDAP_MAP_CODE(LDAP_CONFIDENTIALITY_REQUIRED),
907 _LDAP_MAP_CODE(LDAP_SASL_BIND_IN_PROGRESS),
908 _LDAP_MAP_CODE(LDAP_NO_SUCH_ATTRIBUTE),
909 _LDAP_MAP_CODE(LDAP_UNDEFINED_ATTRIBUTE_TYPE),
910 _LDAP_MAP_CODE(LDAP_INAPPROPRIATE_MATCHING),
911 _LDAP_MAP_CODE(LDAP_CONSTRAINT_VIOLATION),
912 _LDAP_MAP_CODE(LDAP_ATTRIBUTE_OR_VALUE_EXISTS),
913 _LDAP_MAP_CODE(LDAP_INVALID_ATTRIBUTE_SYNTAX),
914 _LDAP_MAP_CODE(LDAP_NO_SUCH_OBJECT),
915 _LDAP_MAP_CODE(LDAP_ALIAS_PROBLEM),
916 _LDAP_MAP_CODE(LDAP_INVALID_DN_SYNTAX),
917 _LDAP_MAP_CODE(LDAP_ALIAS_DEREFERENCING_PROBLEM),
918 _LDAP_MAP_CODE(LDAP_INAPPROPRIATE_AUTHENTICATION),
919 _LDAP_MAP_CODE(LDAP_INVALID_CREDENTIALS),
920 _LDAP_MAP_CODE(LDAP_INSUFFICIENT_ACCESS_RIGHTS),
921 _LDAP_MAP_CODE(LDAP_BUSY),
922 _LDAP_MAP_CODE(LDAP_UNAVAILABLE),
923 _LDAP_MAP_CODE(LDAP_UNWILLING_TO_PERFORM),
924 _LDAP_MAP_CODE(LDAP_LOOP_DETECT),
925 _LDAP_MAP_CODE(LDAP_NAMING_VIOLATION),
926 _LDAP_MAP_CODE(LDAP_OBJECT_CLASS_VIOLATION),
927 _LDAP_MAP_CODE(LDAP_NOT_ALLOWED_ON_NON_LEAF),
928 _LDAP_MAP_CODE(LDAP_NOT_ALLOWED_ON_RDN),
929 _LDAP_MAP_CODE(LDAP_ENTRY_ALREADY_EXISTS),
930 _LDAP_MAP_CODE(LDAP_OBJECT_CLASS_MODS_PROHIBITED),
931 _LDAP_MAP_CODE(LDAP_AFFECTS_MULTIPLE_DSAS),
932 _LDAP_MAP_CODE(LDAP_OTHER)
936 used to setup the status code from a ldap response
938 _PUBLIC_ NTSTATUS ldap_check_response(struct ldap_connection *conn, struct ldap_Result *r)
940 int i;
941 const char *codename = "unknown";
943 if (r->resultcode == LDAP_SUCCESS) {
944 return NT_STATUS_OK;
947 if (conn->last_error) {
948 talloc_free(conn->last_error);
951 for (i=0;i<ARRAY_SIZE(ldap_code_map);i++) {
952 if (r->resultcode == ldap_code_map[i].code) {
953 codename = ldap_code_map[i].str;
954 break;
958 conn->last_error = talloc_asprintf(conn, "LDAP error %u %s - %s <%s> <%s>",
959 r->resultcode,
960 codename,
961 r->dn?r->dn:"(NULL)",
962 r->errormessage?r->errormessage:"",
963 r->referral?r->referral:"");
965 return NT_STATUS_LDAP(r->resultcode);
969 return error string representing the last error
971 _PUBLIC_ const char *ldap_errstr(struct ldap_connection *conn,
972 TALLOC_CTX *mem_ctx,
973 NTSTATUS status)
975 if (NT_STATUS_IS_LDAP(status) && conn->last_error != NULL) {
976 return talloc_strdup(mem_ctx, conn->last_error);
978 return talloc_asprintf(mem_ctx, "LDAP client internal error: %s", nt_errstr(status));
983 return the Nth result message, waiting if necessary
985 _PUBLIC_ NTSTATUS ldap_result_n(struct ldap_request *req, int n, struct ldap_message **msg)
987 *msg = NULL;
989 NT_STATUS_HAVE_NO_MEMORY(req);
991 while (req->state < LDAP_REQUEST_DONE && n >= req->num_replies) {
992 if (tevent_loop_once(req->conn->event.event_ctx) != 0) {
993 return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
997 if (n < req->num_replies) {
998 *msg = req->replies[n];
999 return NT_STATUS_OK;
1002 if (!NT_STATUS_IS_OK(req->status)) {
1003 return req->status;
1006 return NT_STATUS_NO_MORE_ENTRIES;
1011 return a single result message, checking if it is of the expected LDAP type
1013 _PUBLIC_ NTSTATUS ldap_result_one(struct ldap_request *req, struct ldap_message **msg, int type)
1015 NTSTATUS status;
1016 status = ldap_result_n(req, 0, msg);
1017 if (!NT_STATUS_IS_OK(status)) {
1018 return status;
1020 if ((*msg)->type != type) {
1021 *msg = NULL;
1022 return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
1024 return status;
1028 a simple ldap transaction, for single result requests that only need a status code
1029 this relies on single valued requests having the response type == request type + 1
1031 _PUBLIC_ NTSTATUS ldap_transaction(struct ldap_connection *conn, struct ldap_message *msg)
1033 struct ldap_request *req = ldap_request_send(conn, msg);
1034 struct ldap_message *res;
1035 NTSTATUS status;
1036 status = ldap_result_n(req, 0, &res);
1037 if (!NT_STATUS_IS_OK(status)) {
1038 talloc_free(req);
1039 return status;
1041 if (res->type != msg->type + 1) {
1042 talloc_free(req);
1043 return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
1045 status = ldap_check_response(conn, &res->r.GeneralResult);
1046 talloc_free(req);
1047 return status;