diff options
Diffstat (limited to 'source3/libads')
-rw-r--r-- | source3/libads/cldap.c | 338 | ||||
-rw-r--r-- | source3/libads/cldap.h | 18 |
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, |