s3: cldap: cldap_multi_netlogon_send() fails with one bad IPv6 address.
[sfrench/samba-autobuild/.git] / libcli / cldap / cldap.c
index 7436bea4073bead1f1708ebdd5ca4b99f798f109..87f82b9b0dec7238b555e6ef5253453542f5dc0c 100644 (file)
@@ -61,15 +61,6 @@ struct cldap_socket {
         */
        bool connected;
 
-       /*
-        * we allow sync requests only, if the caller
-        * did not pass an event context to cldap_socket_init()
-        */
-       struct {
-               bool allow_poll;
-               struct tevent_context *ctx;
-       } event;
-
        /* the queue for outgoing dgrams */
        struct tevent_queue *send_queue;
 
@@ -98,6 +89,7 @@ struct cldap_search_state {
        struct cldap_search_state *prev, *next;
 
        struct {
+               struct tevent_context *ev;
                struct cldap_socket *cldap;
        } caller;
 
@@ -137,6 +129,8 @@ static void cldap_recvfrom_done(struct tevent_req *subreq);
 
 static bool cldap_recvfrom_setup(struct cldap_socket *c)
 {
+       struct tevent_context *ev;
+
        if (c->recv_subreq) {
                return true;
        }
@@ -145,7 +139,12 @@ static bool cldap_recvfrom_setup(struct cldap_socket *c)
                return true;
        }
 
-       c->recv_subreq = tdgram_recvfrom_send(c, c->event.ctx, c->sock);
+       ev = c->incoming.ev;
+       if (ev == NULL) {
+               ev = c->searches.list->caller.ev;
+       }
+
+       c->recv_subreq = tdgram_recvfrom_send(c, ev, c->sock);
        if (!c->recv_subreq) {
                return false;
        }
@@ -213,8 +212,6 @@ static void cldap_recvfrom_done(struct tevent_req *subreq)
 nomem:
        talloc_free(subreq);
        talloc_free(in);
-       /*TODO: call a dead socket handler */
-       return;
 }
 
 /*
@@ -223,7 +220,6 @@ nomem:
 static bool cldap_socket_recv_dgram(struct cldap_socket *c,
                                    struct cldap_incoming *in)
 {
-       DATA_BLOB blob;
        struct asn1_data *asn1;
        void *p;
        struct cldap_search_state *search;
@@ -233,16 +229,12 @@ static bool cldap_socket_recv_dgram(struct cldap_socket *c,
                goto error;
        }
 
-       blob = data_blob_const(in->buf, in->len);
-
        asn1 = asn1_init(in);
        if (!asn1) {
                goto nomem;
        }
 
-       if (!asn1_load(asn1, blob)) {
-               goto nomem;
-       }
+       asn1_load_nocopy(asn1, in->buf, in->len);
 
        in->ldap_msg = talloc(in, struct ldap_message);
        if (in->ldap_msg == NULL) {
@@ -259,7 +251,8 @@ static bool cldap_socket_recv_dgram(struct cldap_socket *c,
        p = idr_find(c->searches.idr, in->ldap_msg->messageid);
        if (p == NULL) {
                if (!c->incoming.handler) {
-                       goto done;
+                       TALLOC_FREE(in);
+                       return true;
                }
 
                /* this function should free or steal 'in' */
@@ -267,37 +260,54 @@ static bool cldap_socket_recv_dgram(struct cldap_socket *c,
                return false;
        }
 
-       search = talloc_get_type(p, struct cldap_search_state);
+       search = talloc_get_type_abort(p, struct cldap_search_state);
        search->response.in = talloc_move(search, &in);
+
        search->response.asn1 = asn1;
-       search->response.asn1->ofs = 0;
+
+       asn1_load_nocopy(search->response.asn1,
+                        search->response.in->buf, search->response.in->len);
 
        DLIST_REMOVE(c->searches.list, search);
 
-       if (!cldap_recvfrom_setup(c)) {
-               goto nomem;
+       if (cldap_recvfrom_setup(c)) {
+               tevent_req_done(search->req);
+               return true;
        }
 
+       /*
+        * This request was ok, just defer the notify of the caller
+        * and then just fail the next request if needed
+        */
+       tevent_req_defer_callback(search->req, search->caller.ev);
        tevent_req_done(search->req);
-       talloc_free(in);
-       return true;
 
+       status = NT_STATUS_NO_MEMORY;
+       /* in is NULL it this point */
+       goto nterror;
 nomem:
        in->recv_errno = ENOMEM;
 error:
        status = map_nt_error_from_unix_common(in->recv_errno);
 nterror:
+       TALLOC_FREE(in);
        /* in connected mode the first pending search gets the error */
        if (!c->connected) {
                /* otherwise we just ignore the error */
-               goto done;
+               return false;
        }
        if (!c->searches.list) {
-               goto done;
+               return false;
        }
+       /*
+        * We might called tevent_req_done() for a successful
+        * search before, so we better deliver the failure
+        * after the success, that is why we better also
+        * use tevent_req_defer_callback() here.
+        */
+       tevent_req_defer_callback(c->searches.list->req,
+                                 c->searches.list->caller.ev);
        tevent_req_nterror(c->searches.list->req, status);
-done:
-       talloc_free(in);
        return false;
 }
 
@@ -305,7 +315,6 @@ done:
   initialise a cldap_sock
 */
 NTSTATUS cldap_socket_init(TALLOC_CTX *mem_ctx,
-                          struct tevent_context *ev,
                           const struct tsocket_address *local_addr,
                           const struct tsocket_address *remote_addr,
                           struct cldap_socket **_cldap)
@@ -314,27 +323,42 @@ NTSTATUS cldap_socket_init(TALLOC_CTX *mem_ctx,
        struct tsocket_address *any = NULL;
        NTSTATUS status;
        int ret;
+       const char *fam = NULL;
+
+       if (local_addr == NULL && remote_addr == NULL) {
+               return NT_STATUS_INVALID_PARAMETER_MIX;
+       }
+
+       if (remote_addr) {
+               bool is_ipv4;
+               bool is_ipv6;
+
+               is_ipv4 = tsocket_address_is_inet(remote_addr, "ipv4");
+               is_ipv6 = tsocket_address_is_inet(remote_addr, "ipv6");
+
+               if (is_ipv4) {
+                       fam = "ipv4";
+               } else if (is_ipv6) {
+                       fam = "ipv6";
+               } else {
+                       return NT_STATUS_INVALID_ADDRESS;
+               }
+       }
 
        c = talloc_zero(mem_ctx, struct cldap_socket);
        if (!c) {
                goto nomem;
        }
 
-       if (!ev) {
-               ev = tevent_context_init(c);
-               if (!ev) {
-                       goto nomem;
+       if (!local_addr) {
+               /*
+                * Here we know the address family of the remote address.
+                */
+               if (fam == NULL) {
+                       return NT_STATUS_INVALID_PARAMETER_MIX;
                }
-               c->event.allow_poll = true;
-       }
-       c->event.ctx = ev;
 
-       if (!local_addr) {
-               /* we use ipv4 here instead of ip, as otherwise we end
-                  up with a PF_INET6 socket, and sendto() for ipv4
-                  addresses will fail. That breaks cldap name
-                  resolution for winbind to IPv4 hosts. */
-               ret = tsocket_address_inet_from_strings(c, "ipv4",
+               ret = tsocket_address_inet_from_strings(c, fam,
                                                        NULL, 0,
                                                        &any);
                if (ret != 0) {
@@ -392,10 +416,6 @@ NTSTATUS cldap_set_incoming_handler(struct cldap_socket *c,
                return NT_STATUS_PIPE_CONNECTED;
        }
 
-       /* if sync requests are allowed, we don't allow an incoming handler */
-       if (c->event.allow_poll) {
-               return NT_STATUS_INVALID_PIPE_STATE;
-       }
        c->incoming.ev = ev;
        c->incoming.handler = handler;
        c->incoming.private_data = private_data;
@@ -430,6 +450,10 @@ NTSTATUS cldap_reply_send(struct cldap_socket *cldap, struct cldap_reply *io)
                return NT_STATUS_PIPE_CONNECTED;
        }
 
+       if (cldap->incoming.ev == NULL) {
+               return NT_STATUS_INVALID_PIPE_STATE;
+       }
+
        if (!io->dest) {
                return NT_STATUS_INVALID_ADDRESS;
        }
@@ -482,7 +506,7 @@ NTSTATUS cldap_reply_send(struct cldap_socket *cldap, struct cldap_reply *io)
        data_blob_free(&blob2);
 
        subreq = tdgram_sendto_queue_send(state,
-                                         cldap->event.ctx,
+                                         cldap->incoming.ev,
                                          cldap->sock,
                                          cldap->send_queue,
                                          state->blob.data,
@@ -535,8 +559,9 @@ static void cldap_search_state_wakeup_done(struct tevent_req *subreq);
   queue a cldap reply for send
 */
 struct tevent_req *cldap_search_send(TALLOC_CTX *mem_ctx,
-                                   struct cldap_socket *cldap,
-                                   const struct cldap_search *io)
+                                    struct tevent_context *ev,
+                                    struct cldap_socket *cldap,
+                                    const struct cldap_search *io)
 {
        struct tevent_req *req, *subreq;
        struct cldap_search_state *state = NULL;
@@ -553,12 +578,18 @@ struct tevent_req *cldap_search_send(TALLOC_CTX *mem_ctx,
                return NULL;
        }
        ZERO_STRUCTP(state);
+       state->caller.ev = ev;
        state->req = req;
        state->caller.cldap = cldap;
        state->message_id = -1;
 
        talloc_set_destructor(state, cldap_search_state_destructor);
 
+       if (state->caller.cldap == NULL) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               goto post;
+       }
+
        if (io->in.dest_address) {
                if (cldap->connected) {
                        tevent_req_nterror(req, NT_STATUS_PIPE_CONNECTED);
@@ -632,13 +663,13 @@ struct tevent_req *cldap_search_send(TALLOC_CTX *mem_ctx,
                                         state->request.delay % 1000000);
        }
 
-       if (!tevent_req_set_endtime(req, state->caller.cldap->event.ctx, end)) {
+       if (!tevent_req_set_endtime(req, state->caller.ev, end)) {
                tevent_req_oom(req);
                goto post;
        }
 
        subreq = tdgram_sendto_queue_send(state,
-                                         state->caller.cldap->event.ctx,
+                                         state->caller.ev,
                                          state->caller.cldap->sock,
                                          state->caller.cldap->send_queue,
                                          state->request.blob.data,
@@ -649,12 +680,12 @@ struct tevent_req *cldap_search_send(TALLOC_CTX *mem_ctx,
        }
        tevent_req_set_callback(subreq, cldap_search_state_queue_done, req);
 
-       DLIST_ADD_END(cldap->searches.list, state, struct cldap_search_state *);
+       DLIST_ADD_END(cldap->searches.list, state);
 
        return req;
 
  post:
-       return tevent_req_post(req, cldap->event.ctx);
+       return tevent_req_post(req, state->caller.ev);
 }
 
 static void cldap_search_state_queue_done(struct tevent_req *subreq)
@@ -694,7 +725,7 @@ static void cldap_search_state_queue_done(struct tevent_req *subreq)
        next = tevent_timeval_current_ofs(state->request.delay / 1000000,
                                          state->request.delay % 1000000);
        subreq = tevent_wakeup_send(state,
-                                   state->caller.cldap->event.ctx,
+                                   state->caller.ev,
                                    next);
        if (tevent_req_nomem(subreq, req)) {
                return;
@@ -718,7 +749,7 @@ static void cldap_search_state_wakeup_done(struct tevent_req *subreq)
        }
 
        subreq = tdgram_sendto_queue_send(state,
-                                         state->caller.cldap->event.ctx,
+                                         state->caller.ev,
                                          state->caller.cldap->sock,
                                          state->caller.cldap->send_queue,
                                          state->request.blob.data,
@@ -807,109 +838,133 @@ NTSTATUS cldap_search(struct cldap_socket *cldap,
                      TALLOC_CTX *mem_ctx,
                      struct cldap_search *io)
 {
+       TALLOC_CTX *frame;
        struct tevent_req *req;
+       struct tevent_context *ev;
        NTSTATUS status;
 
-       if (!cldap->event.allow_poll) {
+       if (cldap->searches.list) {
+               return NT_STATUS_PIPE_BUSY;
+       }
+
+       if (cldap->incoming.handler) {
                return NT_STATUS_INVALID_PIPE_STATE;
        }
 
-       if (cldap->searches.list) {
-               return NT_STATUS_PIPE_BUSY;
+       frame = talloc_stackframe();
+
+       ev = samba_tevent_context_init(frame);
+       if (ev == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
        }
 
-       req = cldap_search_send(mem_ctx, cldap, io);
-       NT_STATUS_HAVE_NO_MEMORY(req);
+       req = cldap_search_send(mem_ctx, ev, cldap, io);
+       if (req == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
 
-       if (!tevent_req_poll(req, cldap->event.ctx)) {
-               talloc_free(req);
-               return NT_STATUS_INTERNAL_ERROR;
+       if (!tevent_req_poll(req, ev)) {
+               status = map_nt_error_from_unix_common(errno);
+               TALLOC_FREE(frame);
+               return status;
        }
 
        status = cldap_search_recv(req, mem_ctx, io);
-       talloc_free(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return status;
+       }
 
-       return status;
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
 }
 
 struct cldap_netlogon_state {
        struct cldap_search search;
 };
 
-static void cldap_netlogon_state_done(struct tevent_req *subreq);
-/*
-  queue a cldap netlogon for send
-*/
-struct tevent_req *cldap_netlogon_send(TALLOC_CTX *mem_ctx,
-                                     struct cldap_socket *cldap,
-                                     const struct cldap_netlogon *io)
+char *cldap_netlogon_create_filter(TALLOC_CTX *mem_ctx,
+                                  const struct cldap_netlogon *io)
 {
-       struct tevent_req *req, *subreq;
-       struct cldap_netlogon_state *state;
        char *filter;
-       static const char * const attr[] = { "NetLogon", NULL };
 
-       req = tevent_req_create(mem_ctx, &state,
-                               struct cldap_netlogon_state);
-       if (!req) {
+       filter = talloc_asprintf(mem_ctx, "(&(NtVer=%s)",
+                                ldap_encode_ndr_uint32(mem_ctx, io->in.version));
+       if (filter == NULL)
                return NULL;
-       }
 
-       filter = talloc_asprintf(state, "(&(NtVer=%s)", 
-                                ldap_encode_ndr_uint32(state, io->in.version));
-       if (tevent_req_nomem(filter, req)) {
-               goto post;
-       }
        if (io->in.user) {
                filter = talloc_asprintf_append_buffer(filter, "(User=%s)", io->in.user);
-               if (tevent_req_nomem(filter, req)) {
-                       goto post;
+               if (filter == NULL) {
+                       return NULL;
                }
        }
        if (io->in.host) {
                filter = talloc_asprintf_append_buffer(filter, "(Host=%s)", io->in.host);
-               if (tevent_req_nomem(filter, req)) {
-                       goto post;
+               if (filter == NULL) {
+                       return NULL;
                }
        }
        if (io->in.realm) {
                filter = talloc_asprintf_append_buffer(filter, "(DnsDomain=%s)", io->in.realm);
-               if (tevent_req_nomem(filter, req)) {
-                       goto post;
+               if (filter == NULL) {
+                       return NULL;
                }
        }
        if (io->in.acct_control != -1) {
                filter = talloc_asprintf_append_buffer(filter, "(AAC=%s)", 
-                                               ldap_encode_ndr_uint32(state, io->in.acct_control));
-               if (tevent_req_nomem(filter, req)) {
-                       goto post;
+                                               ldap_encode_ndr_uint32(mem_ctx, io->in.acct_control));
+               if (filter == NULL) {
+                       return NULL;
                }
        }
        if (io->in.domain_sid) {
-               struct dom_sid *sid = dom_sid_parse_talloc(state, io->in.domain_sid);
-               if (tevent_req_nomem(sid, req)) {
-                       goto post;
-               }
+               struct dom_sid *sid = dom_sid_parse_talloc(mem_ctx, io->in.domain_sid);
+
                filter = talloc_asprintf_append_buffer(filter, "(domainSid=%s)",
-                                               ldap_encode_ndr_dom_sid(state, sid));
-               if (tevent_req_nomem(filter, req)) {
-                       goto post;
+                                               ldap_encode_ndr_dom_sid(mem_ctx, sid));
+               if (filter == NULL) {
+                       return NULL;
                }
        }
        if (io->in.domain_guid) {
                struct GUID guid;
-               NTSTATUS status;
-               status = GUID_from_string(io->in.domain_guid, &guid);
-               if (tevent_req_nterror(req, status)) {
-                       goto post;
-               }
+               GUID_from_string(io->in.domain_guid, &guid);
+
                filter = talloc_asprintf_append_buffer(filter, "(DomainGuid=%s)",
-                                               ldap_encode_ndr_GUID(state, &guid));
-               if (tevent_req_nomem(filter, req)) {
-                       goto post;
+                                               ldap_encode_ndr_GUID(mem_ctx, &guid));
+               if (filter == NULL) {
+                       return NULL;
                }
        }
        filter = talloc_asprintf_append_buffer(filter, ")");
+
+       return filter;
+}
+
+static void cldap_netlogon_state_done(struct tevent_req *subreq);
+/*
+  queue a cldap netlogon for send
+*/
+struct tevent_req *cldap_netlogon_send(TALLOC_CTX *mem_ctx,
+                                      struct tevent_context *ev,
+                                      struct cldap_socket *cldap,
+                                      const struct cldap_netlogon *io)
+{
+       struct tevent_req *req, *subreq;
+       struct cldap_netlogon_state *state;
+       char *filter;
+       static const char * const attr[] = { "NetLogon", NULL };
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cldap_netlogon_state);
+       if (!req) {
+               return NULL;
+       }
+
+       filter = cldap_netlogon_create_filter(state, io);
        if (tevent_req_nomem(filter, req)) {
                goto post;
        }
@@ -930,7 +985,7 @@ struct tevent_req *cldap_netlogon_send(TALLOC_CTX *mem_ctx,
        state->search.in.timeout        = 2;
        state->search.in.retries        = 2;
 
-       subreq = cldap_search_send(state, cldap, &state->search);
+       subreq = cldap_search_send(state, ev, cldap, &state->search);
        if (tevent_req_nomem(subreq, req)) {
                goto post;
        }
@@ -938,7 +993,7 @@ struct tevent_req *cldap_netlogon_send(TALLOC_CTX *mem_ctx,
 
        return req;
 post:
-       return tevent_req_post(req, cldap->event.ctx);
+       return tevent_req_post(req, ev);
 }
 
 static void cldap_netlogon_state_done(struct tevent_req *subreq)
@@ -968,7 +1023,7 @@ NTSTATUS cldap_netlogon_recv(struct tevent_req *req,
 {
        struct cldap_netlogon_state *state = tevent_req_data(req,
                                             struct cldap_netlogon_state);
-       NTSTATUS status;
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
        DATA_BLOB *data;
 
        if (tevent_req_is_nterror(req, &status)) {
@@ -1012,29 +1067,47 @@ NTSTATUS cldap_netlogon(struct cldap_socket *cldap,
                        TALLOC_CTX *mem_ctx,
                        struct cldap_netlogon *io)
 {
+       TALLOC_CTX *frame;
        struct tevent_req *req;
+       struct tevent_context *ev;
        NTSTATUS status;
 
-       if (!cldap->event.allow_poll) {
+       if (cldap->searches.list) {
+               return NT_STATUS_PIPE_BUSY;
+       }
+
+       if (cldap->incoming.handler) {
                return NT_STATUS_INVALID_PIPE_STATE;
        }
 
-       if (cldap->searches.list) {
-               return NT_STATUS_PIPE_BUSY;
+       frame = talloc_stackframe();
+
+       ev = samba_tevent_context_init(frame);
+       if (ev == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
        }
 
-       req = cldap_netlogon_send(mem_ctx, cldap, io);
-       NT_STATUS_HAVE_NO_MEMORY(req);
+       req = cldap_netlogon_send(mem_ctx, ev, cldap, io);
+       if (req == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
 
-       if (!tevent_req_poll(req, cldap->event.ctx)) {
-               talloc_free(req);
-               return NT_STATUS_INTERNAL_ERROR;
+       if (!tevent_req_poll(req, ev)) {
+               status = map_nt_error_from_unix_common(errno);
+               TALLOC_FREE(frame);
+               return status;
        }
 
        status = cldap_netlogon_recv(req, mem_ctx, io);
-       talloc_free(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return status;
+       }
 
-       return status;
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
 }