r11369: Implement socket_connect_multi: Connect to multiple ipv4 tcp ports in
[samba.git] / source4 / libcli / ldap / ldap_client.c
1 /* 
2    Unix SMB/CIFS mplementation.
3    LDAP protocol helper functions for SAMBA
4    
5    Copyright (C) Andrew Tridgell  2004
6    Copyright (C) Volker Lendecke 2004
7    Copyright (C) Stefan Metzmacher 2004
8    Copyright (C) Simo Sorce 2004
9     
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 2 of the License, or
13    (at your option) any later version.
14    
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.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23    
24 */
25
26 #include "includes.h"
27 #include "asn_1.h"
28 #include "dlinklist.h"
29 #include "lib/events/events.h"
30 #include "lib/socket/socket.h"
31 #include "lib/tls/tls.h"
32 #include "libcli/ldap/ldap.h"
33 #include "libcli/ldap/ldap_client.h"
34 #include "libcli/composite/composite.h"
35
36
37 /*
38   create a new ldap_connection stucture. The event context is optional
39 */
40 struct ldap_connection *ldap_new_connection(TALLOC_CTX *mem_ctx, 
41                                             struct event_context *ev)
42 {
43         struct ldap_connection *conn;
44
45         conn = talloc_zero(mem_ctx, struct ldap_connection);
46         if (conn == NULL) {
47                 return NULL;
48         }
49
50         if (ev == NULL) {
51                 ev = event_context_init(conn);
52                 if (ev == NULL) {
53                         talloc_free(conn);
54                         return NULL;
55                 }
56         }
57
58         conn->next_messageid  = 1;
59         conn->event.event_ctx = ev;
60
61         /* set a reasonable request timeout */
62         conn->timeout = 60;
63
64         return conn;
65 }
66
67
68 /*
69   the connection is dead
70 */
71 static void ldap_connection_dead(struct ldap_connection *conn)
72 {
73         struct ldap_request *req;
74
75         while (conn->pending) {
76                 req = conn->pending;
77                 DLIST_REMOVE(req->conn->pending, req);
78                 req->state = LDAP_REQUEST_DONE;
79                 req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
80                 if (req->async.fn) {
81                         req->async.fn(req);
82                 }
83         }       
84
85         while (conn->send_queue) {
86                 req = conn->send_queue;
87                 DLIST_REMOVE(req->conn->send_queue, req);
88                 req->state = LDAP_REQUEST_DONE;
89                 req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
90                 if (req->async.fn) {
91                         req->async.fn(req);
92                 }
93         }
94
95         talloc_free(conn->tls);
96         conn->tls = NULL;
97 }
98
99
100 /*
101   match up with a pending message, adding to the replies list
102 */
103 static void ldap_match_message(struct ldap_connection *conn, struct ldap_message *msg)
104 {
105         struct ldap_request *req;
106
107         for (req=conn->pending; req; req=req->next) {
108                 if (req->messageid == msg->messageid) break;
109         }
110         /* match a zero message id to the last request sent.
111            It seems that servers send 0 if unable to parse */
112         if (req == NULL && msg->messageid == 0) {
113                 req = conn->pending;
114         }
115         if (req == NULL) {
116                 DEBUG(0,("ldap: no matching message id for %u\n",
117                          msg->messageid));
118                 talloc_free(msg);
119                 return;
120         }
121
122         /* add to the list of replies received */
123         talloc_steal(req, msg);
124         req->replies = talloc_realloc(req, req->replies, 
125                                       struct ldap_message *, req->num_replies+1);
126         if (req->replies == NULL) {
127                 req->status = NT_STATUS_NO_MEMORY;
128                 req->state = LDAP_REQUEST_DONE;
129                 DLIST_REMOVE(conn->pending, req);
130                 if (req->async.fn) {
131                         req->async.fn(req);
132                 }
133                 return;
134         }
135
136         req->replies[req->num_replies] = talloc_steal(req->replies, msg);
137         req->num_replies++;
138
139         if (msg->type != LDAP_TAG_SearchResultEntry &&
140             msg->type != LDAP_TAG_SearchResultReference) {
141                 /* currently only search results expect multiple
142                    replies */
143                 req->state = LDAP_REQUEST_DONE;
144                 DLIST_REMOVE(conn->pending, req);
145         }
146
147         if (req->async.fn) {
148                 req->async.fn(req);
149         }
150 }
151
152 /*
153   try and decode/process plain data
154 */
155 static void ldap_try_decode_plain(struct ldap_connection *conn)
156 {
157         struct asn1_data asn1;
158
159         if (!asn1_load(&asn1, conn->partial)) {
160                 ldap_connection_dead(conn);
161                 return;
162         }
163
164         /* try and decode - this will fail if we don't have a full packet yet */
165         while (asn1.ofs < asn1.length) {
166                 struct ldap_message *msg = talloc(conn, struct ldap_message);
167                 off_t saved_ofs = asn1.ofs;
168                         
169                 if (msg == NULL) {
170                         ldap_connection_dead(conn);
171                         return;
172                 }
173
174                 if (ldap_decode(&asn1, msg)) {
175                         ldap_match_message(conn, msg);
176                 } else {
177                         asn1.ofs = saved_ofs;
178                         talloc_free(msg);
179                         break;
180                 }
181         }
182
183         /* keep any remaining data in conn->partial */
184         data_blob_free(&conn->partial);
185         if (asn1.ofs != asn1.length) {
186                 conn->partial = data_blob_talloc(conn, 
187                                                  asn1.data + asn1.ofs, 
188                                                  asn1.length - asn1.ofs);
189         }
190         asn1_free(&asn1);
191 }
192
193 /*
194   try and decode/process wrapped data
195 */
196 static void ldap_try_decode_wrapped(struct ldap_connection *conn)
197 {
198         uint32_t len;
199
200         /* keep decoding while we have a full wrapped packet */
201         while (conn->partial.length >= 4 &&
202                (len=RIVAL(conn->partial.data, 0)) <= conn->partial.length-4) {
203                 DATA_BLOB wrapped, unwrapped;
204                 struct asn1_data asn1;
205                 struct ldap_message *msg = talloc(conn, struct ldap_message);
206                 NTSTATUS status;
207
208                 if (msg == NULL) {
209                         ldap_connection_dead(conn);
210                         return;
211                 }
212
213                 wrapped.data   = conn->partial.data+4;
214                 wrapped.length = len;
215
216                 status = gensec_unwrap(conn->gensec, msg, &wrapped, &unwrapped);
217                 if (!NT_STATUS_IS_OK(status)) {
218                         ldap_connection_dead(conn);
219                         return;
220                 }
221
222                 if (!asn1_load(&asn1, unwrapped)) {
223                         ldap_connection_dead(conn);
224                         return;
225                 }
226
227                 while (ldap_decode(&asn1, msg)) {
228                         ldap_match_message(conn, msg);
229                         msg = talloc(conn, struct ldap_message);
230                 }
231                 
232                 talloc_free(msg);
233                 asn1_free(&asn1);
234
235                 if (conn->partial.length == len + 4) {
236                         data_blob_free(&conn->partial);
237                 } else {
238                         memmove(conn->partial.data, conn->partial.data+len+4,
239                                 conn->partial.length - (len+4));
240                         conn->partial.length -= len + 4;
241                 }
242         }
243 }
244
245
246 /*
247   handle ldap recv events
248 */
249 static void ldap_recv_handler(struct ldap_connection *conn)
250 {
251         NTSTATUS status;
252         size_t npending=0, nread;
253
254         /* work out how much data is pending */
255         status = tls_socket_pending(conn->tls, &npending);
256         if (!NT_STATUS_IS_OK(status) || npending == 0) {
257                 ldap_connection_dead(conn);
258                 return;
259         }
260
261         conn->partial.data = talloc_realloc_size(conn, conn->partial.data, 
262                                                  conn->partial.length + npending);
263         if (conn->partial.data == NULL) {
264                 ldap_connection_dead(conn);
265                 return;
266         }
267
268         /* receive the pending data */
269         status = tls_socket_recv(conn->tls, conn->partial.data + conn->partial.length,
270                                  npending, &nread);
271         if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
272                 return;
273         }
274         if (!NT_STATUS_IS_OK(status)) {
275                 ldap_connection_dead(conn);
276                 return;
277         }
278         conn->partial.length += nread;
279
280         /* see if we can decode what we have */
281         if (conn->enable_wrap) {
282                 ldap_try_decode_wrapped(conn);
283         } else {
284                 ldap_try_decode_plain(conn);
285         }
286 }
287
288
289 /*
290   handle ldap send events
291 */
292 static void ldap_send_handler(struct ldap_connection *conn)
293 {
294         while (conn->send_queue) {
295                 struct ldap_request *req = conn->send_queue;
296                 size_t nsent;
297                 NTSTATUS status;
298
299                 status = tls_socket_send(conn->tls, &req->data, &nsent);
300                 if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
301                         break;
302                 }
303                 if (!NT_STATUS_IS_OK(status)) {
304                         ldap_connection_dead(conn);
305                         return;
306                 }
307
308                 req->data.data += nsent;
309                 req->data.length -= nsent;
310                 if (req->data.length == 0) {
311                         req->state = LDAP_REQUEST_PENDING;
312                         DLIST_REMOVE(conn->send_queue, req);
313
314                         /* some types of requests don't expect a reply */
315                         if (req->type == LDAP_TAG_AbandonRequest ||
316                             req->type == LDAP_TAG_UnbindRequest) {
317                                 req->status = NT_STATUS_OK;
318                                 req->state = LDAP_REQUEST_DONE;
319                                 if (req->async.fn) {
320                                         req->async.fn(req);
321                                 }
322                         } else {
323                                 DLIST_ADD(conn->pending, req);
324                         }
325                 }
326         }
327         if (conn->send_queue == NULL) {
328                 EVENT_FD_NOT_WRITEABLE(conn->event.fde);
329         }
330 }
331
332
333 /*
334   handle ldap socket events
335 */
336 static void ldap_io_handler(struct event_context *ev, struct fd_event *fde, 
337                             uint16_t flags, void *private)
338 {
339         struct ldap_connection *conn = talloc_get_type(private, struct ldap_connection);
340         if (flags & EVENT_FD_WRITE) {
341                 ldap_send_handler(conn);
342                 if (conn->tls == NULL) return;
343         }
344         if (flags & EVENT_FD_READ) {
345                 ldap_recv_handler(conn);
346         }
347 }
348
349 /*
350   parse a ldap URL
351 */
352 static NTSTATUS ldap_parse_basic_url(TALLOC_CTX *mem_ctx, const char *url,
353                                      char **host, uint16_t *port, BOOL *ldaps)
354 {
355         int tmp_port = 0;
356         char protocol[11];
357         char tmp_host[255];
358         const char *p = url;
359         int ret;
360
361         /* skip leading "URL:" (if any) */
362         if (strncasecmp(p, "URL:", 4) == 0) {
363                 p += 4;
364         }
365
366         /* Paranoia check */
367         SMB_ASSERT(sizeof(protocol)>10 && sizeof(tmp_host)>254);
368                 
369         ret = sscanf(p, "%10[^:]://%254[^:/]:%d", protocol, tmp_host, &tmp_port);
370         if (ret < 2) {
371                 return NT_STATUS_INVALID_PARAMETER;
372         }
373
374         if (strequal(protocol, "ldap")) {
375                 *port = 389;
376                 *ldaps = False;
377         } else if (strequal(protocol, "ldaps")) {
378                 *port = 636;
379                 *ldaps = True;
380         } else {
381                 DEBUG(0, ("unrecognised ldap protocol (%s)!\n", protocol));
382                 return NT_STATUS_PROTOCOL_UNREACHABLE;
383         }
384
385         if (tmp_port != 0)
386                 *port = tmp_port;
387
388         *host = talloc_strdup(mem_ctx, tmp_host);
389         NT_STATUS_HAVE_NO_MEMORY(*host);
390
391         return NT_STATUS_OK;
392 }
393
394 /*
395   connect to a ldap server
396 */
397
398 struct ldap_connect_state {
399         struct composite_context *ctx;
400         struct ldap_connection *conn;
401 };
402
403 static void ldap_connect_recv_conn(struct composite_context *ctx);
404
405 struct composite_context *ldap_connect_send(struct ldap_connection *conn,
406                                             const char *url)
407 {
408         struct composite_context *result, *ctx;
409         struct ldap_connect_state *state;
410
411         result = talloc_zero(NULL, struct composite_context);
412         if (result == NULL) goto failed;
413         result->state = COMPOSITE_STATE_IN_PROGRESS;
414         result->async.fn = NULL;
415         result->event_ctx = conn->event.event_ctx;
416
417         state = talloc(result, struct ldap_connect_state);
418         if (state == NULL) goto failed;
419         state->ctx = result;
420         result->private_data = state;
421
422         state->conn = conn;
423
424         state->ctx->status = ldap_parse_basic_url(conn, url, &conn->host,
425                                                   &conn->port, &conn->ldaps);
426         if (!NT_STATUS_IS_OK(state->ctx->status)) {
427                 composite_trigger_error(state->ctx);
428                 return result;
429         }
430
431         ctx = socket_connect_multi_send(state, conn->host, 1, &conn->port,
432                                         conn->event.event_ctx);
433         if (ctx == NULL) goto failed;
434
435         ctx->async.fn = ldap_connect_recv_conn;
436         ctx->async.private_data = state;
437         return result;
438
439  failed:
440         talloc_free(result);
441         return NULL;
442 }
443
444 static void ldap_connect_recv_conn(struct composite_context *ctx)
445 {
446         struct ldap_connect_state *state =
447                 talloc_get_type(ctx->async.private_data,
448                                 struct ldap_connect_state);
449         struct ldap_connection *conn = state->conn;
450         uint16_t port;
451
452         state->ctx->status = socket_connect_multi_recv(ctx, state, &conn->sock,
453                                                        &port);
454         if (!composite_is_ok(state->ctx)) return;
455
456         /* setup a handler for events on this socket */
457         conn->event.fde = event_add_fd(conn->event.event_ctx, conn->sock, 
458                                        socket_get_fd(conn->sock), 
459                                        EVENT_FD_READ, ldap_io_handler, conn);
460         if (conn->event.fde == NULL) {
461                 composite_error(state->ctx, NT_STATUS_INTERNAL_ERROR);
462                 return;
463         }
464
465         conn->tls = tls_init_client(conn->sock, conn->event.fde, conn->ldaps);
466         if (conn->tls == NULL) {
467                 talloc_free(conn->sock);
468                 return;
469         }
470         talloc_steal(conn, conn->tls);
471         talloc_steal(conn->tls, conn->sock);
472
473         composite_done(state->ctx);
474
475         return;
476 }
477
478 NTSTATUS ldap_connect_recv(struct composite_context *ctx)
479 {
480         NTSTATUS status = composite_wait(ctx);
481         talloc_free(ctx);
482         return status;
483 }
484
485 NTSTATUS ldap_connect(struct ldap_connection *conn, const char *url)
486 {
487         struct composite_context *ctx = ldap_connect_send(conn, url);
488         return ldap_connect_recv(ctx);
489 }
490
491 /* destroy an open ldap request */
492 static int ldap_request_destructor(void *ptr)
493 {
494         struct ldap_request *req = talloc_get_type(ptr, struct ldap_request);
495         if (req->state == LDAP_REQUEST_SEND) {
496                 DLIST_REMOVE(req->conn->send_queue, req);
497         }
498         if (req->state == LDAP_REQUEST_PENDING) {
499                 DLIST_REMOVE(req->conn->pending, req);
500         }
501         return 0;
502 }
503
504 /*
505   called on timeout of a ldap request
506 */
507 static void ldap_request_timeout(struct event_context *ev, struct timed_event *te, 
508                                       struct timeval t, void *private)
509 {
510         struct ldap_request *req = talloc_get_type(private, struct ldap_request);
511         req->status = NT_STATUS_IO_TIMEOUT;
512         if (req->state == LDAP_REQUEST_SEND) {
513                 DLIST_REMOVE(req->conn->send_queue, req);
514         }
515         if (req->state == LDAP_REQUEST_PENDING) {
516                 DLIST_REMOVE(req->conn->pending, req);
517         }
518         req->state = LDAP_REQUEST_DONE;
519         if (req->async.fn) {
520                 req->async.fn(req);
521         }
522 }
523
524 /*
525   send a ldap message - async interface
526 */
527 struct ldap_request *ldap_request_send(struct ldap_connection *conn,
528                                        struct ldap_message *msg)
529 {
530         struct ldap_request *req;
531
532         if (conn->tls == NULL) {
533                 return NULL;
534         }
535
536         req = talloc_zero(conn, struct ldap_request);
537         if (req == NULL) goto failed;
538
539         req->state       = LDAP_REQUEST_SEND;
540         req->conn        = conn;
541         req->messageid   = conn->next_messageid++;
542         if (conn->next_messageid == 0) {
543                 conn->next_messageid = 1;
544         }
545         req->type        = msg->type;
546         if (req->messageid == -1) {
547                 goto failed;
548         }
549
550         talloc_set_destructor(req, ldap_request_destructor);
551
552         msg->messageid = req->messageid;
553
554         if (!ldap_encode(msg, &req->data, req)) {
555                 goto failed;            
556         }
557
558         /* possibly encrypt/sign the request */
559         if (conn->enable_wrap) {
560                 DATA_BLOB wrapped;
561                 NTSTATUS status;
562
563                 status = gensec_wrap(conn->gensec, req, &req->data, &wrapped);
564                 if (!NT_STATUS_IS_OK(status)) {
565                         goto failed;
566                 }
567                 data_blob_free(&req->data);
568                 req->data = data_blob_talloc(req, NULL, wrapped.length + 4);
569                 if (req->data.data == NULL) {
570                         goto failed;
571                 }
572                 RSIVAL(req->data.data, 0, wrapped.length);
573                 memcpy(req->data.data+4, wrapped.data, wrapped.length);
574                 data_blob_free(&wrapped);
575         }
576
577
578         if (conn->send_queue == NULL) {
579                 EVENT_FD_WRITEABLE(conn->event.fde);
580         }
581         DLIST_ADD_END(conn->send_queue, req, struct ldap_request *);
582
583         /* put a timeout on the request */
584         event_add_timed(conn->event.event_ctx, req, 
585                         timeval_current_ofs(conn->timeout, 0),
586                         ldap_request_timeout, req);
587
588         return req;
589
590 failed:
591         talloc_free(req);
592         return NULL;
593 }
594
595
596 /*
597   wait for a request to complete
598   note that this does not destroy the request
599 */
600 NTSTATUS ldap_request_wait(struct ldap_request *req)
601 {
602         while (req->state != LDAP_REQUEST_DONE) {
603                 if (event_loop_once(req->conn->event.event_ctx) != 0) {
604                         req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
605                         break;
606                 }
607         }
608         return req->status;
609 }
610
611
612 /*
613   a mapping of ldap response code to strings
614 */
615 static const struct {
616         enum ldap_result_code code;
617         const char *str;
618 } ldap_code_map[] = {
619 #define _LDAP_MAP_CODE(c) { c, #c }
620         _LDAP_MAP_CODE(LDAP_SUCCESS),
621         _LDAP_MAP_CODE(LDAP_OPERATIONS_ERROR),
622         _LDAP_MAP_CODE(LDAP_PROTOCOL_ERROR),
623         _LDAP_MAP_CODE(LDAP_TIME_LIMIT_EXCEEDED),
624         _LDAP_MAP_CODE(LDAP_SIZE_LIMIT_EXCEEDED),
625         _LDAP_MAP_CODE(LDAP_COMPARE_FALSE),
626         _LDAP_MAP_CODE(LDAP_COMPARE_TRUE),
627         _LDAP_MAP_CODE(LDAP_AUTH_METHOD_NOT_SUPPORTED),
628         _LDAP_MAP_CODE(LDAP_STRONG_AUTH_REQUIRED),
629         _LDAP_MAP_CODE(LDAP_REFERRAL),
630         _LDAP_MAP_CODE(LDAP_ADMIN_LIMIT_EXCEEDED),
631         _LDAP_MAP_CODE(LDAP_UNAVAILABLE_CRITICAL_EXTENSION),
632         _LDAP_MAP_CODE(LDAP_CONFIDENTIALITY_REQUIRED),
633         _LDAP_MAP_CODE(LDAP_SASL_BIND_IN_PROGRESS),
634         _LDAP_MAP_CODE(LDAP_NO_SUCH_ATTRIBUTE),
635         _LDAP_MAP_CODE(LDAP_UNDEFINED_ATTRIBUTE_TYPE),
636         _LDAP_MAP_CODE(LDAP_INAPPROPRIATE_MATCHING),
637         _LDAP_MAP_CODE(LDAP_CONSTRAINT_VIOLATION),
638         _LDAP_MAP_CODE(LDAP_ATTRIBUTE_OR_VALUE_EXISTS),
639         _LDAP_MAP_CODE(LDAP_INVALID_ATTRIBUTE_SYNTAX),
640         _LDAP_MAP_CODE(LDAP_NO_SUCH_OBJECT),
641         _LDAP_MAP_CODE(LDAP_ALIAS_PROBLEM),
642         _LDAP_MAP_CODE(LDAP_INVALID_DN_SYNTAX),
643         _LDAP_MAP_CODE(LDAP_ALIAS_DEREFERENCING_PROBLEM),
644         _LDAP_MAP_CODE(LDAP_INAPPROPRIATE_AUTHENTICATION),
645         _LDAP_MAP_CODE(LDAP_INVALID_CREDENTIALS),
646         _LDAP_MAP_CODE(LDAP_INSUFFICIENT_ACCESS_RIGHTs),
647         _LDAP_MAP_CODE(LDAP_BUSY),
648         _LDAP_MAP_CODE(LDAP_UNAVAILABLE),
649         _LDAP_MAP_CODE(LDAP_UNWILLING_TO_PERFORM),
650         _LDAP_MAP_CODE(LDAP_LOOP_DETECT),
651         _LDAP_MAP_CODE(LDAP_NAMING_VIOLATION),
652         _LDAP_MAP_CODE(LDAP_OBJECT_CLASS_VIOLATION),
653         _LDAP_MAP_CODE(LDAP_NOT_ALLOWED_ON_NON_LEAF),
654         _LDAP_MAP_CODE(LDAP_NOT_ALLOWED_ON_RDN),
655         _LDAP_MAP_CODE(LDAP_ENTRY_ALREADY_EXISTS),
656         _LDAP_MAP_CODE(LDAP_OBJECT_CLASS_MODS_PROHIBITED),
657         _LDAP_MAP_CODE(LDAP_AFFECTS_MULTIPLE_DSAS),
658         _LDAP_MAP_CODE(LDAP_OTHER)
659 };
660
661 /*
662   used to setup the status code from a ldap response
663 */
664 NTSTATUS ldap_check_response(struct ldap_connection *conn, struct ldap_Result *r)
665 {
666         int i;
667         const char *codename = "unknown";
668
669         if (r->resultcode == LDAP_SUCCESS) {
670                 return NT_STATUS_OK;
671         }
672
673         if (conn->last_error) {
674                 talloc_free(conn->last_error);
675         }
676
677         for (i=0;i<ARRAY_SIZE(ldap_code_map);i++) {
678                 if (r->resultcode == ldap_code_map[i].code) {
679                         codename = ldap_code_map[i].str;
680                         break;
681                 }
682         }
683
684         conn->last_error = talloc_asprintf(conn, "LDAP error %u %s - %s <%s> <%s>", 
685                                            r->resultcode,
686                                            codename,
687                                            r->dn?r->dn:"(NULL)", 
688                                            r->errormessage?r->errormessage:"", 
689                                            r->referral?r->referral:"");
690         
691         return NT_STATUS_LDAP(r->resultcode);
692 }
693
694 /*
695   return error string representing the last error
696 */
697 const char *ldap_errstr(struct ldap_connection *conn, NTSTATUS status)
698 {
699         if (NT_STATUS_IS_LDAP(status) && conn->last_error != NULL) {
700                 return conn->last_error;
701         }
702         return nt_errstr(status);
703 }
704
705
706 /*
707   return the Nth result message, waiting if necessary
708 */
709 NTSTATUS ldap_result_n(struct ldap_request *req, int n, struct ldap_message **msg)
710 {
711         *msg = NULL;
712
713         NT_STATUS_HAVE_NO_MEMORY(req);
714
715         while (req->state != LDAP_REQUEST_DONE && n >= req->num_replies) {
716                 if (event_loop_once(req->conn->event.event_ctx) != 0) {
717                         return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
718                 }
719         }
720
721         if (n < req->num_replies) {
722                 *msg = req->replies[n];
723                 return NT_STATUS_OK;
724         }
725
726         if (!NT_STATUS_IS_OK(req->status)) {
727                 return req->status;
728         }
729
730         return NT_STATUS_NO_MORE_ENTRIES;
731 }
732
733
734 /*
735   return a single result message, checking if it is of the expected LDAP type
736 */
737 NTSTATUS ldap_result_one(struct ldap_request *req, struct ldap_message **msg, int type)
738 {
739         NTSTATUS status;
740         status = ldap_result_n(req, 0, msg);
741         if (!NT_STATUS_IS_OK(status)) {
742                 return status;
743         }
744         if ((*msg)->type != type) {
745                 *msg = NULL;
746                 return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
747         }
748         return status;
749 }
750
751 /*
752   a simple ldap transaction, for single result requests that only need a status code
753   this relies on single valued requests having the response type == request type + 1
754 */
755 NTSTATUS ldap_transaction(struct ldap_connection *conn, struct ldap_message *msg)
756 {
757         struct ldap_request *req = ldap_request_send(conn, msg);
758         struct ldap_message *res;
759         NTSTATUS status;
760         status = ldap_result_n(req, 0, &res);
761         if (!NT_STATUS_IS_OK(status)) {
762                 talloc_free(req);
763                 return status;
764         }
765         if (res->type != msg->type + 1) {
766                 talloc_free(req);
767                 return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
768         }
769         status = ldap_check_response(conn, &res->r.GeneralResult);
770         talloc_free(req);
771         return status;
772 }