Add cldap_multi_netlogon_send/recv
authorVolker Lendecke <vl@samba.org>
Thu, 29 Sep 2011 11:30:30 +0000 (13:30 +0200)
committerStefan Metzmacher <metze@samba.org>
Mon, 17 Oct 2011 07:52:29 +0000 (09:52 +0200)
Make ads_cldap_netlogon use it. It does not need the fancy multi stuff, but
excercising that code more often is better. And because we have to ask over the
network, the additional load should be neglectable.

Ported to master by Stefan Metzmacher <metze@samba.org>

source3/libads/cldap.c
source3/libads/cldap.h

index 4f725a093eadb0f9662018df59c0517baf5f1959..02412369fa31038148b71cab90ad93be9817ec39 100644 (file)
 #include "includes.h"
 #include "../libcli/cldap/cldap.h"
 #include "../lib/tsocket/tsocket.h"
+#include "../lib/util/tevent_ntstatus.h"
 #include "libads/cldap.h"
 
+struct cldap_multi_netlogon_state {
+       struct tevent_context *ev;
+       const struct tsocket_address * const *servers;
+       int num_servers;
+       const char *domain;
+       const char *hostname;
+       unsigned ntversion;
+       int min_servers;
+
+       struct cldap_socket **cldap;
+       struct tevent_req **subreqs;
+       int num_sent;
+       int num_received;
+       int num_good_received;
+       struct cldap_netlogon *ios;
+       struct netlogon_samlogon_response **responses;
+};
+
+static void cldap_multi_netlogon_done(struct tevent_req *subreq);
+static void cldap_multi_netlogon_next(struct tevent_req *subreq);
+
+/*
+ * Do a parallel cldap ping to the servers. The first "min_servers"
+ * are fired directly, the remaining ones in 100msec intervals. If
+ * "min_servers" responses came in successfully, we immediately reply,
+ * not waiting for the remaining ones.
+ */
+
+struct tevent_req *cldap_multi_netlogon_send(
+       TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+       const struct tsocket_address * const *servers, int num_servers,
+       const char *domain, const char *hostname, unsigned ntversion,
+       int min_servers)
+{
+       struct tevent_req *req, *subreq;
+       struct cldap_multi_netlogon_state *state;
+       int i;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cldap_multi_netlogon_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->servers = servers;
+       state->num_servers = num_servers;
+       state->domain = domain;
+       state->hostname = hostname;
+       state->ntversion = ntversion;
+       state->min_servers = min_servers;
+
+       if (min_servers > num_servers) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return tevent_req_post(req, ev);
+       }
+
+       state->subreqs = talloc_zero_array(state,
+                                          struct tevent_req *,
+                                          num_servers);
+       if (tevent_req_nomem(state->subreqs, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->cldap = talloc_zero_array(state,
+                                        struct cldap_socket *,
+                                        num_servers);
+       if (tevent_req_nomem(state->cldap, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->responses = talloc_zero_array(state,
+                               struct netlogon_samlogon_response *,
+                               num_servers);
+       if (tevent_req_nomem(state->responses, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->ios = talloc_zero_array(state->responses,
+                                      struct cldap_netlogon,
+                                      num_servers);
+       if (tevent_req_nomem(state->ios, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       for (i=0; i<num_servers; i++) {
+               NTSTATUS status;
+
+               status = cldap_socket_init(state->cldap,
+                                          NULL, /* local_addr */
+                                          state->servers[i],
+                                          &state->cldap[i]);
+               if (tevent_req_nterror(req, status)) {
+                       return tevent_req_post(req, ev);
+               }
+
+               state->ios[i].in.dest_address   = NULL;
+               state->ios[i].in.dest_port      = 0;
+               state->ios[i].in.realm          = domain;
+               state->ios[i].in.host           = NULL;
+               state->ios[i].in.user           = NULL;
+               state->ios[i].in.domain_guid    = NULL;
+               state->ios[i].in.domain_sid     = NULL;
+               state->ios[i].in.acct_control   = 0;
+               state->ios[i].in.version        = ntversion;
+               state->ios[i].in.map_response   = false;
+       }
+
+       for (i=0; i<min_servers; i++) {
+               state->subreqs[i] = cldap_netlogon_send(state->subreqs,
+                                                       state->ev,
+                                                       state->cldap[i],
+                                                       &state->ios[i]);
+               if (tevent_req_nomem(state->subreqs[i], req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(
+                       state->subreqs[i], cldap_multi_netlogon_done, req);
+       }
+       state->num_sent = min_servers;
+
+       if (state->num_sent < state->num_servers) {
+               /*
+                * After 100 milliseconds fire the next one
+                */
+               subreq = tevent_wakeup_send(state, state->ev,
+                                           timeval_current_ofs(0, 100000));
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq, cldap_multi_netlogon_next,
+                                       req);
+       }
+
+       return req;
+}
+
+static void cldap_multi_netlogon_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cldap_multi_netlogon_state *state = tevent_req_data(
+               req, struct cldap_multi_netlogon_state);
+       NTSTATUS status;
+       struct netlogon_samlogon_response *response;
+       int i;
+
+       for (i=0; i<state->num_sent; i++) {
+               if (state->subreqs[i] == subreq) {
+                       break;
+               }
+       }
+       if (i == state->num_sent) {
+               /*
+                * Got a response we did not fire...
+                */
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+       state->subreqs[i] = NULL;
+
+       response = talloc_zero(state, struct netlogon_samlogon_response);
+       if (tevent_req_nomem(response, req)) {
+               return;
+       }
+
+       status = cldap_netlogon_recv(subreq, response,
+                                    &state->ios[i]);
+       TALLOC_FREE(subreq);
+       state->num_received += 1;
+
+       if (NT_STATUS_IS_OK(status)) {
+               *response = state->ios[i].out.netlogon;
+               state->responses[i] = talloc_move(state->responses,
+                                                 &response);
+               state->num_good_received += 1;
+       }
+
+       if ((state->num_received == state->num_servers) ||
+           (state->num_good_received >= state->min_servers)) {
+               tevent_req_done(req);
+               return;
+       }
+}
+
+static void cldap_multi_netlogon_next(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cldap_multi_netlogon_state *state = tevent_req_data(
+               req, struct cldap_multi_netlogon_state);
+       bool ret;
+
+       ret = tevent_wakeup_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!ret) {
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       subreq = cldap_netlogon_send(state->subreqs,
+                                    state->ev,
+                                    state->cldap[state->num_sent],
+                                    &state->ios[state->num_sent]);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, cldap_multi_netlogon_done, req);
+       state->subreqs[state->num_sent] = subreq;
+       state->num_sent += 1;
+
+       if (state->num_sent < state->num_servers) {
+               /*
+                * After 100 milliseconds fire the next one
+                */
+               subreq = tevent_wakeup_send(state, state->ev,
+                                           timeval_current_ofs(0, 100000));
+               if (tevent_req_nomem(subreq, req)) {
+                       return;
+               }
+               tevent_req_set_callback(subreq, cldap_multi_netlogon_next,
+                                       req);
+       }
+}
+
+NTSTATUS cldap_multi_netlogon_recv(
+       struct tevent_req *req, TALLOC_CTX *mem_ctx,
+       struct netlogon_samlogon_response ***responses)
+{
+       struct cldap_multi_netlogon_state *state = tevent_req_data(
+               req, struct cldap_multi_netlogon_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+               return status;
+       }
+       /*
+        * If we timeout, give back what we have so far
+        */
+       *responses = talloc_move(mem_ctx, &state->responses);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS cldap_multi_netlogon(
+       TALLOC_CTX *mem_ctx,
+       const struct tsocket_address * const *servers,
+       int num_servers,
+       const char *domain, const char *hostname, unsigned ntversion,
+       int min_servers, struct timeval timeout,
+       struct netlogon_samlogon_response ***responses)
+{
+       struct tevent_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       ev = tevent_context_init(talloc_tos());
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = cldap_multi_netlogon_send(
+               ev, ev, servers, num_servers, domain, hostname, ntversion,
+               min_servers);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_set_endtime(req, ev, timeout)) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = cldap_multi_netlogon_recv(req, mem_ctx, responses);
+fail:
+       TALLOC_FREE(ev);
+       return status;
+}
+
 /*******************************************************************
   do a cldap netlogon query.  Always 389/udp
 *******************************************************************/
@@ -35,14 +313,13 @@ bool ads_cldap_netlogon(TALLOC_CTX *mem_ctx,
                        uint32_t nt_version,
                        struct netlogon_samlogon_response **_reply)
 {
-       struct cldap_socket *cldap;
-       struct cldap_netlogon io;
-       struct netlogon_samlogon_response *reply;
        NTSTATUS status;
        char addrstr[INET6_ADDRSTRLEN];
        const char *dest_str;
-       int ret;
        struct tsocket_address *dest_addr;
+       const struct tsocket_address * const *dest_addrs;
+       struct netlogon_samlogon_response **responses = NULL;
+       int ret;
 
        dest_str = print_sockaddr(addrstr, sizeof(addrstr), ss);
 
@@ -56,50 +333,27 @@ bool ads_cldap_netlogon(TALLOC_CTX *mem_ctx,
                return false;
        }
 
-       /*
-        * as we use a connected udp socket
-        */
-       status = cldap_socket_init(mem_ctx, NULL, dest_addr, &cldap);
-       TALLOC_FREE(dest_addr);
+       dest_addrs = (const struct tsocket_address * const *)&dest_addr;
+
+       status = cldap_multi_netlogon(talloc_tos(),
+                               dest_addrs, 1,
+                               realm, NULL,
+                               nt_version, 1,
+                               timeval_current_ofs(MAX(3,lp_ldap_timeout()/2), 0),
+                               &responses);
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(2,("Failed to create cldap socket to %s: %s\n",
-                        dest_str, nt_errstr(status)));
+               DEBUG(2, ("ads_cldap_netlogon: cldap_multi_netlogon "
+                         "failed: %s\n", nt_errstr(status)));
                return false;
        }
-
-       reply = talloc(cldap, struct netlogon_samlogon_response);
-       if (!reply) {
-               goto failed;
-       }
-
-       /*
-        * as we use a connected socket, so we don't need to specify the
-        * destination
-        */
-       io.in.dest_address      = NULL;
-       io.in.dest_port         = 0;
-       io.in.realm             = realm;
-       io.in.host              = NULL;
-       io.in.user              = NULL;
-       io.in.domain_guid       = NULL;
-       io.in.domain_sid        = NULL;
-       io.in.acct_control      = 0;
-       io.in.version           = nt_version;
-       io.in.map_response      = false;
-
-       status = cldap_netlogon(cldap, reply, &io);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(2,("cldap_netlogon() failed: %s\n", nt_errstr(status)));
-               goto failed;
+       if (responses[0] == NULL) {
+               DEBUG(2, ("ads_cldap_netlogon: did not get a reply\n"));
+               TALLOC_FREE(responses);
+               return false;
        }
+       *_reply = talloc_move(mem_ctx, &responses[0]);
 
-       *reply = io.out.netlogon;
-       *_reply = talloc_move(mem_ctx, &reply);
-       TALLOC_FREE(cldap);
        return true;
-failed:
-       TALLOC_FREE(cldap);
-       return false;
 }
 
 /*******************************************************************
index 60e1c56038dc2616d2420d02de186ba0de30c761..9e42782052a560a0cd1e2703196769711c3a7822 100644 (file)
 #include "../libcli/netlogon/netlogon.h"
 
 /* The following definitions come from libads/cldap.c  */
+
+struct tevent_req *cldap_multi_netlogon_send(
+       TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+       const struct tsocket_address * const *servers,
+       int num_servers,
+       const char *domain, const char *hostname, unsigned ntversion,
+       int min_servers);
+NTSTATUS cldap_multi_netlogon_recv(
+       struct tevent_req *req, TALLOC_CTX *mem_ctx,
+       struct netlogon_samlogon_response ***responses);
+NTSTATUS cldap_multi_netlogon(
+       TALLOC_CTX *mem_ctx,
+       const struct tsocket_address * const *servers,
+       int num_servers,
+       const char *domain, const char *hostname, unsigned ntversion,
+       int min_servers, struct timeval timeout,
+       struct netlogon_samlogon_response ***responses);
+
 bool ads_cldap_netlogon(TALLOC_CTX *mem_ctx,
                        struct sockaddr_storage *ss,
                        const char *realm,