summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVolker Lendecke <vl@samba.org>2011-09-29 13:30:30 +0200
committerStefan Metzmacher <metze@samba.org>2011-10-17 09:52:29 +0200
commit41a0e96724dc05752b1bdb86fc946c820be16632 (patch)
tree058bf0a6fdafc88ccf05f1311d9f688a2744afe3
parent8c076862ad0b1b14bb6addef323c5678368051af (diff)
downloadsamba-41a0e96724dc05752b1bdb86fc946c820be16632.tar.gz
samba-41a0e96724dc05752b1bdb86fc946c820be16632.tar.bz2
samba-41a0e96724dc05752b1bdb86fc946c820be16632.zip
Add cldap_multi_netlogon_send/recv
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>
-rw-r--r--source3/libads/cldap.c338
-rw-r--r--source3/libads/cldap.h18
2 files changed, 314 insertions, 42 deletions
diff --git a/source3/libads/cldap.c b/source3/libads/cldap.c
index 4f725a093e..02412369fa 100644
--- a/source3/libads/cldap.c
+++ b/source3/libads/cldap.c
@@ -23,8 +23,286 @@
#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;
}
/*******************************************************************
diff --git a/source3/libads/cldap.h b/source3/libads/cldap.h
index 60e1c56038..9e42782052 100644
--- a/source3/libads/cldap.h
+++ b/source3/libads/cldap.h
@@ -26,6 +26,24 @@
#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,