]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
libads: Add netlogon_pings()
authorVolker Lendecke <vl@samba.org>
Thu, 24 Oct 2024 11:55:35 +0000 (13:55 +0200)
committerVolker Lendecke <vl@samba.org>
Mon, 11 Nov 2024 14:03:04 +0000 (14:03 +0000)
This encapsulates our logic that we send CLDAP requests on UDP/389,
sending them with 100msec timeouts until someone replies. It also
contains the code to do this over LDAP/389 or LDAPS/636.

It also contains code to filter for domain controller flags like
DS_ONLY_LDAP_NEEDED, this logic exists in several places right now.

Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
source3/libads/netlogon_ping.c [new file with mode: 0644]
source3/libads/netlogon_ping.h [new file with mode: 0644]
source3/wscript_build

diff --git a/source3/libads/netlogon_ping.c b/source3/libads/netlogon_ping.c
new file mode 100644 (file)
index 0000000..4fd776a
--- /dev/null
@@ -0,0 +1,830 @@
+/*
+ * Samba Unix/Linux SMB client library
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include <tevent.h>
+#include "netlogon_ping.h"
+#include "libcli/netlogon/netlogon_proto.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "libcli/ldap/ldap_message.h"
+#include "libcli/cldap/cldap.h"
+#include "source3/include/tldap.h"
+#include "source3/include/tldap_util.h"
+#include "source3/lib/tldap_tls_connect.h"
+#include "lib/util/tevent_unix.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "source4/lib/tls/tls.h"
+#include "source3/libads/cldap.h"
+
+struct ldap_netlogon_state {
+       struct tevent_context *ev;
+       struct tsocket_address *local;
+       struct tsocket_address *remote;
+       enum client_netlogon_ping_protocol proto;
+       const char *filter;
+
+       struct tstream_context *plain;
+       struct tldap_context *tldap;
+       struct tstream_tls_params *tls_params;
+
+       struct netlogon_samlogon_response *response;
+};
+
+static void ldap_netlogon_connected(struct tevent_req *subreq);
+static void ldap_netlogon_starttls_done(struct tevent_req *subreq);
+static void ldap_netlogon_tls_set_up(struct tevent_req *subreq);
+static void ldap_netlogon_search(struct tevent_req *req);
+static void ldap_netlogon_searched(struct tevent_req *subreq);
+
+static struct tevent_req *ldap_netlogon_send(
+       TALLOC_CTX *mem_ctx,
+       struct tevent_context *ev,
+       const struct tsocket_address *server,
+       enum client_netlogon_ping_protocol proto,
+       const char *filter)
+{
+       struct tevent_req *req = NULL, *subreq = NULL;
+       struct ldap_netlogon_state *state = NULL;
+       uint16_t port;
+       int ret;
+
+       req = tevent_req_create(mem_ctx, &state, struct ldap_netlogon_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->filter = filter;
+       state->proto = proto;
+
+       state->remote = tsocket_address_copy(server, state);
+       if (tevent_req_nomem(state->remote, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       port = (proto == CLIENT_NETLOGON_PING_LDAPS) ? 636 : 389;
+
+       ret = tsocket_address_inet_set_port(state->remote, port);
+       if (ret != 0) {
+               tevent_req_nterror(req, map_nt_error_from_unix_common(errno));
+               return tevent_req_post(req, ev);
+       }
+
+       ret = tsocket_address_inet_from_strings(
+               state, "ip", NULL, 0, &state->local);
+       if (ret != 0) {
+               tevent_req_nterror(req, map_nt_error_from_unix_common(errno));
+               return tevent_req_post(req, ev);
+       }
+
+       subreq = tstream_inet_tcp_connect_send(state,
+                                              state->ev,
+                                              state->local,
+                                              state->remote);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, ldap_netlogon_connected, req);
+
+       return req;
+}
+
+static void ldap_netlogon_connected(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                         struct tevent_req);
+       struct ldap_netlogon_state *state = tevent_req_data(
+               req, struct ldap_netlogon_state);
+       int ret, err;
+       NTSTATUS status;
+
+       ret = tstream_inet_tcp_connect_recv(
+               subreq, &err, state, &state->plain, NULL);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               tevent_req_nterror(req, map_nt_error_from_unix_common(err));
+               return;
+       }
+
+       state->tldap = tldap_context_create_from_plain_stream(
+               state, &state->plain);
+       if (tevent_req_nomem(state->tldap, req)) {
+               return;
+       }
+
+       if (state->proto == CLIENT_NETLOGON_PING_LDAP) {
+               ldap_netlogon_search(req);
+               return;
+       }
+
+       status = tstream_tls_params_client(state,
+                                          false,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          "NORMAL",
+                                          TLS_VERIFY_PEER_NO_CHECK,
+                                          NULL,
+                                          &state->tls_params);
+       if (tevent_req_nterror(req, status)) {
+               DBG_ERR("tstream_tls_params_client(NO_CHECK): %s\n",
+                       nt_errstr(status));
+               return;
+       }
+
+       if (state->proto == CLIENT_NETLOGON_PING_STARTTLS) {
+               subreq = tldap_extended_send(state,
+                                            state->ev,
+                                            state->tldap,
+                                            LDB_EXTENDED_START_TLS_OID,
+                                            NULL,
+                                            NULL,
+                                            0,
+                                            NULL,
+                                            0);
+               if (tevent_req_nomem(subreq, req)) {
+                       return;
+               }
+               tevent_req_set_callback(subreq,
+                                       ldap_netlogon_starttls_done,
+                                       req);
+               return;
+       }
+
+       subreq = tldap_tls_connect_send(state,
+                                       state->ev,
+                                       state->tldap,
+                                       state->tls_params);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, ldap_netlogon_tls_set_up, req);
+}
+
+static void ldap_netlogon_starttls_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                         struct tevent_req);
+       struct ldap_netlogon_state *state = tevent_req_data(
+               req, struct ldap_netlogon_state);
+       TLDAPRC rc;
+
+       rc = tldap_extended_recv(subreq, NULL, NULL, NULL);
+       TALLOC_FREE(subreq);
+       if (!TLDAP_RC_IS_SUCCESS(rc)) {
+               tevent_req_nterror(req, NT_STATUS_LDAP(TLDAP_RC_V(rc)));
+               return;
+       }
+
+       subreq = tldap_tls_connect_send(state,
+                                       state->ev,
+                                       state->tldap,
+                                       state->tls_params);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, ldap_netlogon_tls_set_up, req);
+}
+
+static void ldap_netlogon_tls_set_up(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                         struct tevent_req);
+       TLDAPRC rc;
+
+       rc = tldap_tls_connect_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!TLDAP_RC_IS_SUCCESS(rc)) {
+               tevent_req_nterror(req, NT_STATUS_LDAP(TLDAP_RC_V(rc)));
+               return;
+       }
+
+       ldap_netlogon_search(req);
+}
+
+static void ldap_netlogon_search(struct tevent_req *req)
+{
+       struct ldap_netlogon_state *state = tevent_req_data(
+               req, struct ldap_netlogon_state);
+       static const char *attrs[] = {"netlogon"};
+       struct tevent_req *subreq = NULL;
+
+       subreq = tldap_search_all_send(state,
+                                      state->ev,
+                                      state->tldap,
+                                      "",
+                                      TLDAP_SCOPE_BASE,
+                                      state->filter,
+                                      attrs,
+                                      ARRAY_SIZE(attrs),
+                                      0,
+                                      NULL,
+                                      0,
+                                      NULL,
+                                      0,
+                                      0,
+                                      0,
+                                      0);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, ldap_netlogon_searched, req);
+}
+
+static void ldap_netlogon_searched(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                         struct tevent_req);
+       struct ldap_netlogon_state *state = tevent_req_data(
+               req, struct ldap_netlogon_state);
+       struct tldap_message **msgs = NULL;
+       DATA_BLOB blob = {.data = NULL};
+       NTSTATUS status;
+       TLDAPRC rc;
+       bool ok;
+
+       rc = tldap_search_all_recv(subreq, state, &msgs, NULL);
+       TALLOC_FREE(subreq);
+       if (!TLDAP_RC_IS_SUCCESS(rc)) {
+               tevent_req_nterror(req, NT_STATUS_LDAP(TLDAP_RC_V(rc)));
+               return;
+       }
+
+       if (talloc_array_length(msgs) != 1) {
+               tevent_req_nterror(req,
+                                  NT_STATUS_LDAP(TLDAP_RC_V(
+                                          TLDAP_NO_RESULTS_RETURNED)));
+               return;
+       }
+
+       ok = tldap_get_single_valueblob(msgs[0], "netlogon", &blob);
+       if (!ok) {
+               tevent_req_nterror(req,
+                                  NT_STATUS_LDAP(TLDAP_RC_V(
+                                          TLDAP_NO_RESULTS_RETURNED)));
+               return;
+       }
+
+       state->response = talloc(state, struct netlogon_samlogon_response);
+       if (tevent_req_nomem(state->response, req)) {
+               return;
+       }
+
+       status = pull_netlogon_samlogon_response(&blob,
+                                                state->response,
+                                                state->response);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+static NTSTATUS ldap_netlogon_recv(
+       struct tevent_req *req,
+       TALLOC_CTX *mem_ctx,
+       struct netlogon_samlogon_response **response)
+{
+       struct ldap_netlogon_state *state = tevent_req_data(
+               req, struct ldap_netlogon_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+       *response = talloc_move(mem_ctx, &state->response);
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+struct cldap_netlogon_ping_state {
+       struct cldap_socket *sock;
+       struct cldap_search search;
+       struct netlogon_samlogon_response *response;
+};
+
+static void cldap_netlogon_ping_done(struct tevent_req *subreq);
+
+static struct tevent_req *cldap_netlogon_ping_send(
+       TALLOC_CTX *mem_ctx,
+       struct tevent_context *ev,
+       const struct tsocket_address *server,
+       const char *filter)
+{
+       struct tevent_req *req = NULL, *subreq = NULL;
+       struct cldap_netlogon_ping_state *state = NULL;
+       struct tsocket_address *server_389 = NULL;
+       static const char *const attr[] = {"NetLogon", NULL};
+       int ret;
+       NTSTATUS status;
+
+       req = tevent_req_create(mem_ctx,
+                               &state,
+                               struct cldap_netlogon_ping_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       server_389 = tsocket_address_copy(server, state);
+       if (tevent_req_nomem(server_389, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       ret = tsocket_address_inet_set_port(server_389, 389);
+       if (ret != 0) {
+               tevent_req_nterror(req, map_nt_error_from_unix_common(errno));
+               return tevent_req_post(req, ev);
+       }
+
+       status = cldap_socket_init(state, NULL, server_389, &state->sock);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->search = (struct cldap_search){
+               .in.filter = filter,
+               .in.attributes = attr,
+               .in.timeout = 2,
+               .in.retries = 2,
+       };
+
+       subreq = cldap_search_send(state, ev, state->sock, &state->search);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cldap_netlogon_ping_done, req);
+       return req;
+}
+
+static void cldap_netlogon_ping_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                         struct tevent_req);
+       struct cldap_netlogon_ping_state *state = tevent_req_data(
+               req, struct cldap_netlogon_ping_state);
+       struct ldap_SearchResEntry *resp = NULL;
+       NTSTATUS status;
+
+       status = cldap_search_recv(subreq, state, &state->search);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       TALLOC_FREE(state->sock);
+
+       resp = state->search.out.response;
+
+       if (resp == NULL) {
+               tevent_req_nterror(req, NT_STATUS_NOT_FOUND);
+               return;
+       }
+
+       if (resp->num_attributes != 1 ||
+           !strequal(resp->attributes[0].name, "netlogon") ||
+           resp->attributes[0].num_values != 1 ||
+           resp->attributes[0].values->length < 2)
+       {
+               tevent_req_nterror(req, NT_STATUS_UNEXPECTED_NETWORK_ERROR);
+               return;
+       }
+
+       state->response = talloc(state, struct netlogon_samlogon_response);
+       if (tevent_req_nomem(state->response, req)) {
+               return;
+       }
+
+       status = pull_netlogon_samlogon_response(resp->attributes[0].values,
+                                                state->response,
+                                                state->response);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+       tevent_req_done(req);
+}
+
+static NTSTATUS cldap_netlogon_ping_recv(
+       struct tevent_req *req,
+       TALLOC_CTX *mem_ctx,
+       struct netlogon_samlogon_response **response)
+{
+       struct cldap_netlogon_ping_state *state = tevent_req_data(
+               req, struct cldap_netlogon_ping_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+       *response = talloc_move(mem_ctx, &state->response);
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+struct netlogon_ping_state {
+       struct netlogon_samlogon_response *response;
+};
+
+static void netlogon_ping_done_cldap(struct tevent_req *subreq);
+static void netlogon_ping_done_ldaps(struct tevent_req *subreq);
+
+static struct tevent_req *netlogon_ping_send(
+       TALLOC_CTX *mem_ctx,
+       struct tevent_context *ev,
+       struct tsocket_address *server,
+       enum client_netlogon_ping_protocol proto,
+       const char *filter,
+       struct timeval timeout)
+{
+       struct tevent_req *req = NULL, *subreq = NULL;
+       struct netlogon_ping_state *state = NULL;
+
+       req = tevent_req_create(mem_ctx, &state, struct netlogon_ping_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       switch (proto) {
+       case CLIENT_NETLOGON_PING_CLDAP:
+               subreq = cldap_netlogon_ping_send(state, ev, server, filter);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq, netlogon_ping_done_cldap, req);
+               break;
+       case CLIENT_NETLOGON_PING_LDAP:
+       case CLIENT_NETLOGON_PING_LDAPS:
+       case CLIENT_NETLOGON_PING_STARTTLS:
+               subreq = ldap_netlogon_send(state, ev, server, proto, filter);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq, netlogon_ping_done_ldaps, req);
+               break;
+       default:
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return tevent_req_post(req, ev);
+               break;
+       }
+
+       return req;
+}
+
+static void netlogon_ping_done_cldap(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                         struct tevent_req);
+       struct netlogon_ping_state *state = tevent_req_data(
+               req, struct netlogon_ping_state);
+       NTSTATUS status;
+
+       status = cldap_netlogon_ping_recv(subreq, state, &state->response);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+       tevent_req_done(req);
+}
+
+static void netlogon_ping_done_ldaps(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                         struct tevent_req);
+       struct netlogon_ping_state *state = tevent_req_data(
+               req, struct netlogon_ping_state);
+       NTSTATUS status;
+
+       status = ldap_netlogon_recv(subreq, state, &state->response);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+       tevent_req_done(req);
+}
+
+static NTSTATUS netlogon_ping_recv(
+       struct tevent_req *req,
+       TALLOC_CTX *mem_ctx,
+       struct netlogon_samlogon_response **response)
+{
+       struct netlogon_ping_state *state = tevent_req_data(
+               req, struct netlogon_ping_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+       *response = talloc_move(mem_ctx, &state->response);
+       return NT_STATUS_OK;
+}
+
+struct netlogon_pings_state {
+       struct tevent_context *ev;
+
+       struct tsocket_address **servers;
+       size_t num_servers;
+       size_t min_servers;
+       struct timeval timeout;
+       enum client_netlogon_ping_protocol proto;
+       uint32_t required_flags;
+
+       char *filter;
+       size_t num_sent;
+       size_t num_received;
+       size_t num_good_received;
+       struct tevent_req **reqs;
+       struct netlogon_samlogon_response **responses;
+};
+
+static void netlogon_pings_next(struct tevent_req *subreq);
+static void netlogon_pings_done(struct tevent_req *subreq);
+
+struct tevent_req *netlogon_pings_send(TALLOC_CTX *mem_ctx,
+                                      struct tevent_context *ev,
+                                      enum client_netlogon_ping_protocol proto,
+                                      struct tsocket_address **servers,
+                                      size_t num_servers,
+                                      struct netlogon_ping_filter filter,
+                                      size_t min_servers,
+                                      struct timeval timeout)
+{
+       struct tevent_req *req = NULL;
+       struct netlogon_pings_state *state = NULL;
+       char *filter_str = NULL;
+       size_t i;
+
+       req = tevent_req_create(mem_ctx, &state, struct netlogon_pings_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->proto = proto;
+       state->servers = servers;
+       state->num_servers = num_servers;
+       state->min_servers = min_servers;
+       state->timeout = timeout;
+       state->required_flags = filter.required_flags;
+
+       state->reqs = talloc_zero_array(state,
+                                       struct tevent_req *,
+                                       num_servers);
+       if (tevent_req_nomem(state->reqs, 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);
+       }
+
+       filter_str = talloc_asprintf(state,
+                                    "(&(NtVer=%s)",
+                                    ldap_encode_ndr_uint32(state,
+                                                           filter.ntversion));
+       if (filter.domain != NULL) {
+               talloc_asprintf_addbuf(&filter_str,
+                                      "(DnsDomain=%s)",
+                                      filter.domain);
+       }
+       if (filter.acct_ctrl != -1) {
+               talloc_asprintf_addbuf(
+                       &filter_str,
+                       "(AAC=%s)",
+                       ldap_encode_ndr_uint32(mem_ctx, filter.acct_ctrl));
+       }
+       if (filter.domain_sid != NULL) {
+               talloc_asprintf_addbuf(
+                       &filter_str,
+                       "(domainSid=%s)",
+                       ldap_encode_ndr_dom_sid(mem_ctx, filter.domain_sid));
+       }
+       if (filter.domain_guid != NULL) {
+               talloc_asprintf_addbuf(
+                       &filter_str,
+                       "(DomainGuid=%s)",
+                       ldap_encode_ndr_GUID(mem_ctx, filter.domain_guid));
+       }
+       if (filter.hostname != NULL) {
+               talloc_asprintf_addbuf(&filter_str,
+                                      "(Host=%s)",
+                                      filter.hostname);
+       }
+       if (filter.user != NULL) {
+               talloc_asprintf_addbuf(&filter_str, "(User=%s)", filter.user);
+       }
+       talloc_asprintf_addbuf(&filter_str, ")");
+
+       if (tevent_req_nomem(filter_str, req)) {
+               return tevent_req_post(req, ev);
+       }
+       state->filter = filter_str;
+
+       for (i = 0; i < min_servers; i++) {
+               state->reqs[i] = netlogon_ping_send(state->reqs,
+                                                   state->ev,
+                                                   state->servers[i],
+                                                   state->proto,
+                                                   state->filter,
+                                                   state->timeout);
+               if (tevent_req_nomem(state->reqs[i], req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(state->reqs[i],
+                                       netlogon_pings_done,
+                                       req);
+       }
+       state->num_sent = min_servers;
+       if (state->num_sent < state->num_servers) {
+               /*
+                * After 100 milliseconds fire the next one
+                */
+               struct tevent_req *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, netlogon_pings_next, req);
+       }
+
+       return req;
+}
+
+static void netlogon_pings_next(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                         struct tevent_req);
+       struct netlogon_pings_state *state = tevent_req_data(
+               req, struct netlogon_pings_state);
+       bool ret;
+
+       ret = tevent_wakeup_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!ret) {
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       subreq = netlogon_ping_send(state->reqs,
+                                   state->ev,
+                                   state->servers[state->num_sent],
+                                   state->proto,
+                                   state->filter,
+                                   state->timeout);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, netlogon_pings_done, req);
+       state->reqs[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, netlogon_pings_next, req);
+       }
+}
+
+static void netlogon_pings_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                         struct tevent_req);
+       struct netlogon_pings_state *state = tevent_req_data(
+               req, struct netlogon_pings_state);
+       struct netlogon_samlogon_response *response = NULL;
+       NTSTATUS status;
+       size_t i;
+
+       for (i = 0; i < state->num_sent; i++) {
+               if (state->reqs[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->reqs[i] = NULL;
+
+       status = netlogon_ping_recv(subreq, state, &response);
+       TALLOC_FREE(subreq);
+       state->num_received += 1;
+
+       if (NT_STATUS_IS_OK(status)) {
+               uint32_t ret_flags;
+               bool ok;
+
+               switch (response->ntver) {
+               case NETLOGON_NT_VERSION_5EX:
+                       ret_flags = response->data.nt5_ex.server_type;
+                       break;
+               case NETLOGON_NT_VERSION_5:
+                       ret_flags = response->data.nt5.server_type;
+                       break;
+               default:
+                       ret_flags = 0;
+                       break;
+               }
+
+               ok = check_cldap_reply_required_flags(ret_flags,
+                                                     state->required_flags);
+               if (ok) {
+                       state->responses[i] = talloc_move(state->responses,
+                                                         &response);
+                       state->num_good_received += 1;
+               }
+       }
+
+       if (state->num_good_received >= state->min_servers) {
+               tevent_req_done(req);
+               return;
+       }
+       if (state->num_received == state->num_servers) {
+               /*
+                * Everybody replied, but we did not get enough good
+                * answers (see above)
+                */
+               tevent_req_nterror(req, NT_STATUS_NOT_FOUND);
+               return;
+       }
+       /*
+        * Wait for more answers
+        */
+}
+
+NTSTATUS netlogon_pings_recv(struct tevent_req *req,
+                            TALLOC_CTX *mem_ctx,
+                            struct netlogon_samlogon_response ***responses)
+{
+       struct netlogon_pings_state *state = tevent_req_data(
+               req, struct netlogon_pings_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+       *responses = talloc_move(mem_ctx, &state->responses);
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS netlogon_pings(TALLOC_CTX *mem_ctx,
+                       enum client_netlogon_ping_protocol proto,
+                       struct tsocket_address **servers,
+                       int num_servers,
+                       struct netlogon_ping_filter filter,
+                       int min_servers,
+                       struct timeval timeout,
+                       struct netlogon_samlogon_response ***responses)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct tevent_context *ev = NULL;
+       struct tevent_req *req = NULL;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       ev = samba_tevent_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = netlogon_pings_send(frame,
+                                 ev,
+                                 proto,
+                                 servers,
+                                 num_servers,
+                                 filter,
+                                 min_servers,
+                                 timeout);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = netlogon_pings_recv(req, mem_ctx, responses);
+ fail:
+       TALLOC_FREE(frame);
+       return status;
+}
diff --git a/source3/libads/netlogon_ping.h b/source3/libads/netlogon_ping.h
new file mode 100644 (file)
index 0000000..dc238bd
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Samba Unix/Linux SMB client library
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _LIBADS_NETLOGON_PING_H_
+#define _LIBADS_NETLOGON_PING_H_
+
+#include "replace.h"
+#include "lib/tsocket/tsocket.h"
+#include "librpc/gen_ndr/nbt.h"
+#include "libcli/util/ntstatus.h"
+#include "lib/param/loadparm.h"
+
+struct netlogon_samlogon_response;
+
+struct netlogon_ping_filter {
+       unsigned ntversion;
+       const char *domain;
+       const struct dom_sid *domain_sid;
+       const struct GUID *domain_guid;
+       const char *hostname;
+       const char *user;
+       int acct_ctrl;
+       uint32_t required_flags;
+};
+
+struct tevent_req *netlogon_pings_send(TALLOC_CTX *mem_ctx,
+                                      struct tevent_context *ev,
+                                      enum client_netlogon_ping_protocol proto,
+                                      struct tsocket_address **servers,
+                                      size_t num_servers,
+                                      struct netlogon_ping_filter filter,
+                                      size_t min_servers,
+                                      struct timeval timeout);
+NTSTATUS netlogon_pings_recv(struct tevent_req *req,
+                            TALLOC_CTX *mem_ctx,
+                            struct netlogon_samlogon_response ***responses);
+NTSTATUS netlogon_pings(TALLOC_CTX *mem_ctx,
+                       enum client_netlogon_ping_protocol proto,
+                       struct tsocket_address **servers,
+                       int num_servers,
+                       struct netlogon_ping_filter filter,
+                       int min_servers,
+                       struct timeval timeout,
+                       struct netlogon_samlogon_response ***responses);
+
+#endif /* _LIBADS_NETLOGON_PING_H_ */
index 93dffdebd90b138faac1924b94b2fb6dbc3a59c8..0c9f6ad3143a739e87650740ae6497f694363ba5 100644 (file)
@@ -469,9 +469,13 @@ bld.SAMBA3_LIBRARY('libsmb',
                         ''',
                    private_library=True)
 
+bld.SAMBA3_SUBSYSTEM('NETLOGON_PING',
+                     source='libads/netlogon_ping.c',
+                     deps='TLDAP cli_cldap')
+
 bld.SAMBA3_SUBSYSTEM('CLDAP',
                     source='libads/cldap.c',
-                    deps='cli-ldap-common cli_cldap LIBTSOCKET')
+                    deps='cli-ldap-common cli_cldap LIBTSOCKET NETLOGON_PING')
 
 # NOTE: The secrets3 library is a low level library used by several subsystems.
 # PLEASE DO NOT make it depend on high level libraries like PDB, if you are