s3:rpc_client: add a mdssvc client library
authorRalph Boehme <slow@samba.org>
Thu, 2 May 2019 05:45:39 +0000 (07:45 +0200)
committerRalph Boehme <slow@samba.org>
Wed, 9 Oct 2019 14:35:28 +0000 (14:35 +0000)
Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Noel Power <noel.power@suse.com>
source3/rpc_client/cli_mdssvc.c [new file with mode: 0644]
source3/rpc_client/cli_mdssvc.h [new file with mode: 0644]
source3/rpc_client/cli_mdssvc_private.h [new file with mode: 0644]
source3/rpc_client/cli_mdssvc_util.c [new file with mode: 0644]
source3/rpc_client/cli_mdssvc_util.h [new file with mode: 0644]
source3/wscript_build

diff --git a/source3/rpc_client/cli_mdssvc.c b/source3/rpc_client/cli_mdssvc.c
new file mode 100644 (file)
index 0000000..76cceb4
--- /dev/null
@@ -0,0 +1,1039 @@
+/*
+   Unix SMB/CIFS implementation.
+   Main metadata server / Spotlight client functions
+
+   Copyright (C) Ralph Boehme 2019
+
+   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 "includes.h"
+#include "rpc_client.h"
+#include "../librpc/gen_ndr/ndr_mdssvc_c.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "rpc_server/mdssvc/dalloc.h"
+#include "rpc_server/mdssvc/marshalling.h"
+#include "cli_mdssvc.h"
+#include "cli_mdssvc_private.h"
+#include "cli_mdssvc_util.h"
+
+struct mdsctx_id mdscli_new_ctx_id(struct mdscli_ctx *mdscli_ctx)
+{
+       mdscli_ctx->ctx_id.id++;
+       return mdscli_ctx->ctx_id;
+}
+
+char *mdscli_get_basepath(TALLOC_CTX *mem_ctx,
+                         struct mdscli_ctx *mdscli_ctx)
+{
+       return talloc_strdup(mem_ctx, mdscli_ctx->mdscmd_open.share_path);
+}
+
+struct mdscli_connect_state {
+       struct tevent_context *ev;
+       struct mdscli_ctx *mdscli_ctx;
+};
+
+static void mdscli_connect_open_done(struct tevent_req *subreq);
+static void mdscli_connect_unknown1_done(struct tevent_req *subreq);
+
+struct tevent_req *mdscli_connect_send(TALLOC_CTX *mem_ctx,
+                                      struct tevent_context *ev,
+                                      struct dcerpc_binding_handle *bh,
+                                      const char *share_name,
+                                      const char *mount_path)
+{
+       struct tevent_req *req = NULL;
+       struct mdscli_connect_state *state = NULL;
+       struct tevent_req *subreq = NULL;
+       struct mdscli_ctx *ctx = NULL;
+
+       req = tevent_req_create(req, &state, struct mdscli_connect_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       ctx = talloc_zero(state, struct mdscli_ctx);
+       if (tevent_req_nomem(ctx, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       *state = (struct mdscli_connect_state) {
+               .ev = ev,
+               .mdscli_ctx = ctx,
+       };
+
+       *ctx = (struct mdscli_ctx) {
+               .bh = bh,
+               .max_fragment_size = 64 * 1024,
+               /*
+                * The connection id is a per tcon value sent by the client,
+                * 0x6b000060 is a value used most of the times for the first
+                * tcon.
+                */
+               .ctx_id.connection = UINT64_C(0x6b000060),
+       };
+
+       subreq = dcerpc_mdssvc_open_send(state,
+                                        state->ev,
+                                        ctx->bh,
+                                        &ctx->dev,
+                                        &ctx->mdscmd_open.unkn2,
+                                        &ctx->mdscmd_open.unkn3,
+                                        mount_path,
+                                        share_name,
+                                        ctx->mdscmd_open.share_path,
+                                        &ctx->ph);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, state->ev);
+       }
+       tevent_req_set_callback(subreq, mdscli_connect_open_done, req);
+       ctx->async_pending++;
+
+       return req;
+}
+
+static void mdscli_connect_open_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct mdscli_connect_state *state = tevent_req_data(
+               req, struct mdscli_connect_state);
+       struct mdscli_ctx *mdscli_ctx = state->mdscli_ctx;
+       NTSTATUS status;
+
+       status = dcerpc_mdssvc_open_recv(subreq, state);
+       TALLOC_FREE(subreq);
+       state->mdscli_ctx->async_pending--;
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       subreq = dcerpc_mdssvc_unknown1_send(
+                       state,
+                       state->ev,
+                       mdscli_ctx->bh,
+                       mdscli_ctx->ph,
+                       0,
+                       mdscli_ctx->dev,
+                       mdscli_ctx->mdscmd_open.unkn2,
+                       0,
+                       geteuid(),
+                       getegid(),
+                       &mdscli_ctx->mdscmd_unknown1.status,
+                       &mdscli_ctx->flags,
+                       &mdscli_ctx->mdscmd_unknown1.unkn7);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, mdscli_connect_unknown1_done, req);
+}
+
+static void mdscli_connect_unknown1_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct mdscli_connect_state *state = tevent_req_data(
+               req, struct mdscli_connect_state);
+       NTSTATUS status;
+
+       status = dcerpc_mdssvc_unknown1_recv(subreq, state);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+NTSTATUS mdscli_connect_recv(struct tevent_req *req,
+                            TALLOC_CTX *mem_ctx,
+                            struct mdscli_ctx **mdscli_ctx)
+{
+       struct mdscli_connect_state *state = tevent_req_data(
+               req, struct mdscli_connect_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       *mdscli_ctx = talloc_move(mem_ctx, &state->mdscli_ctx);
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS mdscli_connect(TALLOC_CTX *mem_ctx,
+                       struct dcerpc_binding_handle *bh,
+                       const char *share_name,
+                       const char *mount_path,
+                       struct mdscli_ctx **mdscli_ctx)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct tevent_req *req = NULL;
+       struct tevent_context *ev = NULL;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       ev = samba_tevent_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+
+       req = mdscli_connect_send(frame,
+                                 ev,
+                                 bh,
+                                 share_name,
+                                 mount_path);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+
+       status = mdscli_connect_recv(req, mem_ctx, mdscli_ctx);
+fail:
+       TALLOC_FREE(frame);
+       return status;
+}
+
+struct mdscli_search_state {
+       struct mdscli_search_ctx *search;
+       struct mdssvc_blob request_blob;
+       struct mdssvc_blob response_blob;
+};
+
+static void mdscli_search_cmd_done(struct tevent_req *subreq);
+
+struct tevent_req *mdscli_search_send(TALLOC_CTX *mem_ctx,
+                                     struct tevent_context *ev,
+                                     struct mdscli_ctx *mdscli_ctx,
+                                     const char *mds_query,
+                                     const char *path_scope_in,
+                                     bool live)
+{
+       struct tevent_req *req = NULL;
+       struct mdscli_search_state *state = NULL;
+       struct tevent_req *subreq = NULL;
+       struct mdscli_search_ctx *search = NULL;
+       char *path_scope = NULL;
+       NTSTATUS status;
+
+       req = tevent_req_create(req, &state, struct mdscli_search_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       search = talloc_zero(state, struct mdscli_search_ctx);
+       if (tevent_req_nomem(search, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       if (path_scope_in[0] == '/') {
+               path_scope = talloc_strdup(search, path_scope_in);
+       } else {
+               path_scope = talloc_asprintf(search,
+                                            "%s/%s",
+                                            mdscli_ctx->mdscmd_open.share_path,
+                                            path_scope_in);
+       }
+       if (tevent_req_nomem(path_scope, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       *search = (struct mdscli_search_ctx) {
+               .mdscli_ctx = mdscli_ctx,
+               .ctx_id = mdscli_new_ctx_id(mdscli_ctx),
+               .unique_id = generate_random_u64(),
+               .live = live,
+               .path_scope = path_scope,
+               .mds_query = talloc_strdup(search, mds_query),
+       };
+       if (tevent_req_nomem(search->mds_query, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       *state = (struct mdscli_search_state) {
+               .search = search,
+       };
+
+       status = mdscli_blob_search(state,
+                                   search,
+                                   &state->request_blob);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->response_blob.spotlight_blob = talloc_array(
+               state,
+               uint8_t,
+               mdscli_ctx->max_fragment_size);
+       if (tevent_req_nomem(state->response_blob.spotlight_blob, req)) {
+               return tevent_req_post(req, ev);
+       }
+       state->response_blob.size = mdscli_ctx->max_fragment_size;
+
+       subreq = dcerpc_mdssvc_cmd_send(state,
+                                       ev,
+                                       mdscli_ctx->bh,
+                                       mdscli_ctx->ph,
+                                       0,
+                                       mdscli_ctx->dev,
+                                       mdscli_ctx->mdscmd_open.unkn2,
+                                       0,
+                                       mdscli_ctx->flags,
+                                       state->request_blob,
+                                       0,
+                                       mdscli_ctx->max_fragment_size,
+                                       1,
+                                       mdscli_ctx->max_fragment_size,
+                                       0,
+                                       0,
+                                       &mdscli_ctx->mdscmd_cmd.fragment,
+                                       &state->response_blob,
+                                       &mdscli_ctx->mdscmd_cmd.unkn9);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, mdscli_search_cmd_done, req);
+       mdscli_ctx->async_pending++;
+       return req;
+}
+
+static void mdscli_search_cmd_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct mdscli_search_state *state = tevent_req_data(
+               req, struct mdscli_search_state);
+       DALLOC_CTX *d = NULL;
+       uint64_t *uint64p = NULL;
+       NTSTATUS status;
+       bool ok;
+
+       status = dcerpc_mdssvc_cmd_recv(subreq, state);
+       TALLOC_FREE(subreq);
+       state->search->mdscli_ctx->async_pending--;
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       d = dalloc_new(state);
+       if (tevent_req_nomem(d, req)) {
+               return;
+       }
+
+       ok = sl_unpack(d,
+                      (char *)state->response_blob.spotlight_blob,
+                      state->response_blob.length);
+       if (!ok) {
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       uint64p = dalloc_get(d,
+                            "DALLOC_CTX", 0,
+                            "uint64_t", 0);
+       if (uint64p == NULL) {
+               DBG_DEBUG("Unexpected mds reponse: %s", dalloc_dump(d, 0));
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       if (*uint64p != 0) {
+               DBG_DEBUG("Unexpected mds result: 0x%" PRIx64, *uint64p);
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       tevent_req_done(req);
+       return;
+}
+
+NTSTATUS mdscli_search_recv(struct tevent_req *req,
+                           TALLOC_CTX *mem_ctx,
+                           struct mdscli_search_ctx **search)
+{
+       struct mdscli_search_state *state = tevent_req_data(
+               req, struct mdscli_search_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       *search = talloc_move(mem_ctx, &state->search);
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS mdscli_search(TALLOC_CTX *mem_ctx,
+                      struct mdscli_ctx *mdscli_ctx,
+                      const char *mds_query,
+                      const char *path_scope,
+                      bool live,
+                      struct mdscli_search_ctx **search)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct tevent_req *req = NULL;
+       struct tevent_context *ev = NULL;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       if (mdscli_ctx->async_pending != 0) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+
+       ev = samba_tevent_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+
+       req = mdscli_search_send(frame,
+                                ev,
+                                mdscli_ctx,
+                                mds_query,
+                                path_scope,
+                                live);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+
+       status = mdscli_search_recv(req, mem_ctx, search);
+fail:
+       TALLOC_FREE(frame);
+       return status;
+}
+
+struct mdscli_get_results_state {
+       struct mdscli_search_ctx *search;
+       struct mdssvc_blob request_blob;
+       struct mdssvc_blob response_blob;
+       uint64_t *cnids;
+};
+
+static void mdscli_get_results_cmd_done(struct tevent_req *subreq);
+
+struct tevent_req *mdscli_get_results_send(
+                       TALLOC_CTX *mem_ctx,
+                       struct tevent_context *ev,
+                       struct mdscli_search_ctx *search)
+{
+       struct tevent_req *req = NULL;
+       struct mdscli_get_results_state *state = NULL;
+       struct tevent_req *subreq = NULL;
+       struct mdscli_ctx *mdscli_ctx = search->mdscli_ctx;
+       NTSTATUS status;
+
+       req = tevent_req_create(req, &state, struct mdscli_get_results_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       *state = (struct mdscli_get_results_state) {
+               .search = search,
+       };
+
+       status = mdscli_blob_get_results(state,
+                                        search,
+                                        &state->request_blob);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->response_blob.spotlight_blob = talloc_array(
+               state,
+               uint8_t,
+               mdscli_ctx->max_fragment_size);
+       if (tevent_req_nomem(state->response_blob.spotlight_blob, req)) {
+               return tevent_req_post(req, ev);
+       }
+       state->response_blob.size = mdscli_ctx->max_fragment_size;
+
+       subreq = dcerpc_mdssvc_cmd_send(state,
+                                       ev,
+                                       mdscli_ctx->bh,
+                                       mdscli_ctx->ph,
+                                       0,
+                                       mdscli_ctx->dev,
+                                       mdscli_ctx->mdscmd_open.unkn2,
+                                       0,
+                                       mdscli_ctx->flags,
+                                       state->request_blob,
+                                       0,
+                                       mdscli_ctx->max_fragment_size,
+                                       1,
+                                       mdscli_ctx->max_fragment_size,
+                                       0,
+                                       0,
+                                       &mdscli_ctx->mdscmd_cmd.fragment,
+                                       &state->response_blob,
+                                       &mdscli_ctx->mdscmd_cmd.unkn9);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, mdscli_get_results_cmd_done, req);
+       mdscli_ctx->async_pending++;
+       return req;
+}
+
+static void mdscli_get_results_cmd_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct mdscli_get_results_state *state = tevent_req_data(
+               req, struct mdscli_get_results_state);
+       DALLOC_CTX *d = NULL;
+       uint64_t *uint64p = NULL;
+       sl_cnids_t *cnids = NULL;
+       size_t ncnids;
+       size_t i;
+       NTSTATUS status;
+       bool ok;
+
+       status = dcerpc_mdssvc_cmd_recv(subreq, state);
+       TALLOC_FREE(subreq);
+       state->search->mdscli_ctx->async_pending--;
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       d = dalloc_new(state);
+       if (tevent_req_nomem(d, req)) {
+               return;
+       }
+
+       ok = sl_unpack(d,
+                      (char *)state->response_blob.spotlight_blob,
+                      state->response_blob.length);
+       if (!ok) {
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       uint64p = dalloc_get(d,
+                            "DALLOC_CTX", 0,
+                            "uint64_t", 0);
+       if (uint64p == NULL) {
+               DBG_DEBUG("Unexpected mds reponse: %s", dalloc_dump(d, 0));
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       if (*uint64p == 35) {
+               DBG_DEBUG("search done: %s", dalloc_dump(d, 0));
+               tevent_req_done(req);
+               return;
+       }
+
+       cnids = dalloc_get(d, "DALLOC_CTX", 0, "sl_cnids_t", 1);
+       if (cnids == NULL) {
+               DBG_DEBUG("cnids error: %s", dalloc_dump(d, 0));
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       ncnids = dalloc_size(cnids->ca_cnids);
+       if (ncnids == 0) {
+               tevent_req_nterror(req, NT_STATUS_NO_MORE_MATCHES);
+               return;
+       }
+
+       if (cnids->ca_unkn1 != 0xadd) {
+               /*
+                * Whatever 0xadd means... but it seems to be the standard value
+                * macOS mdssvc returns here.
+                */
+               DBG_DEBUG("unexpected ca_unkn1: %s", dalloc_dump(d, 0));
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       if (cnids->ca_context != state->search->ctx_id.connection ) {
+               DBG_DEBUG("unexpected ca_context: %s", dalloc_dump(d, 0));
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       state->cnids = talloc_zero_array(state, uint64_t, ncnids);
+       if (tevent_req_nomem(state->cnids, req)) {
+               return;
+       }
+
+       for (i = 0; i < ncnids; i++) {
+               uint64_t *cnid = NULL;
+
+               cnid = dalloc_get(cnids->ca_cnids, "uint64_t", i);
+               if (cnid == NULL) {
+                       DBG_DEBUG("cnids error: %s", dalloc_dump(d, 0));
+                       tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+                       return;
+               }
+               state->cnids[i] = *cnid;
+       }
+
+       tevent_req_done(req);
+       return;
+}
+
+NTSTATUS mdscli_get_results_recv(struct tevent_req *req,
+                                TALLOC_CTX *mem_ctx,
+                                uint64_t **cnids)
+{
+       struct mdscli_get_results_state *state = tevent_req_data(
+               req, struct mdscli_get_results_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       *cnids = talloc_move(mem_ctx, &state->cnids);
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS mdscli_get_results(TALLOC_CTX *mem_ctx,
+                           struct mdscli_search_ctx *search,
+                           uint64_t **_cnids)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct tevent_req *req = NULL;
+       struct tevent_context *ev = NULL;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       if (search->mdscli_ctx->async_pending != 0) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+
+       ev = samba_tevent_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+
+       req = mdscli_get_results_send(frame,
+                                     ev,
+                                     search);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+
+       status = mdscli_get_results_recv(req, mem_ctx, _cnids);
+fail:
+       TALLOC_FREE(frame);
+       return status;
+}
+
+struct mdscli_get_path_state {
+       struct mdscli_ctx *mdscli_ctx;
+       struct mdssvc_blob request_blob;
+       struct mdssvc_blob response_blob;
+       char *path;
+};
+
+static void mdscli_get_path_done(struct tevent_req *subreq);
+
+struct tevent_req *mdscli_get_path_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct mdscli_ctx *mdscli_ctx,
+                                       uint64_t cnid)
+{
+       struct tevent_req *req = NULL;
+       struct mdscli_get_path_state *state = NULL;
+       struct tevent_req *subreq = NULL;
+       NTSTATUS status;
+
+       req = tevent_req_create(req, &state, struct mdscli_get_path_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       *state = (struct mdscli_get_path_state) {
+               .mdscli_ctx = mdscli_ctx,
+       };
+
+       status = mdscli_blob_get_path(state,
+                                     mdscli_ctx,
+                                     cnid,
+                                     &state->request_blob);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->response_blob.spotlight_blob = talloc_array(
+               state,
+               uint8_t,
+               mdscli_ctx->max_fragment_size);
+       if (tevent_req_nomem(state->response_blob.spotlight_blob, req)) {
+               return tevent_req_post(req, ev);
+       }
+       state->response_blob.size = mdscli_ctx->max_fragment_size;
+
+       subreq = dcerpc_mdssvc_cmd_send(state,
+                                       ev,
+                                       mdscli_ctx->bh,
+                                       mdscli_ctx->ph,
+                                       0,
+                                       mdscli_ctx->dev,
+                                       mdscli_ctx->mdscmd_open.unkn2,
+                                       0,
+                                       mdscli_ctx->flags,
+                                       state->request_blob,
+                                       0,
+                                       mdscli_ctx->max_fragment_size,
+                                       1,
+                                       mdscli_ctx->max_fragment_size,
+                                       0,
+                                       0,
+                                       &mdscli_ctx->mdscmd_cmd.fragment,
+                                       &state->response_blob,
+                                       &mdscli_ctx->mdscmd_cmd.unkn9);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, mdscli_get_path_done, req);
+       mdscli_ctx->async_pending++;
+       return req;
+}
+
+static void mdscli_get_path_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct mdscli_get_path_state *state = tevent_req_data(
+               req, struct mdscli_get_path_state);
+       DALLOC_CTX *d = NULL;
+       char *path = NULL;
+       NTSTATUS status;
+       bool ok;
+
+       status = dcerpc_mdssvc_cmd_recv(subreq, state);
+       TALLOC_FREE(subreq);
+       state->mdscli_ctx->async_pending--;
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       d = dalloc_new(state);
+       if (tevent_req_nomem(d, req)) {
+               return;
+       }
+
+       ok = sl_unpack(d,
+                      (char *)state->response_blob.spotlight_blob,
+                      state->response_blob.length);
+       if (!ok) {
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       path = dalloc_get(d,
+                         "DALLOC_CTX", 0,
+                         "DALLOC_CTX", 2,
+                         "DALLOC_CTX", 0,
+                         "DALLOC_CTX", 1,
+                         "char *", 0);
+       if (path == NULL) {
+               DBG_DEBUG("No path in mds reponse: %s", dalloc_dump(d, 0));
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+       state->path = talloc_move(state, &path);
+       DBG_DEBUG("path: %s\n", state->path);
+
+       tevent_req_done(req);
+       return;
+}
+
+NTSTATUS mdscli_get_path_recv(struct tevent_req *req,
+                             TALLOC_CTX *mem_ctx,
+                             char **path)
+{
+       struct mdscli_get_path_state *state = tevent_req_data(
+               req, struct mdscli_get_path_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               tevent_req_received(req);
+               return status;
+       }
+
+       *path = talloc_move(mem_ctx, &state->path);
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS mdscli_get_path(TALLOC_CTX *mem_ctx,
+                        struct mdscli_ctx *mdscli_ctx,
+                        uint64_t cnid,
+                        char **path)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct tevent_req *req = NULL;
+       struct tevent_context *ev = NULL;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       if (mdscli_ctx->async_pending != 0) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+
+       ev = samba_tevent_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+
+       req = mdscli_get_path_send(frame, ev, mdscli_ctx, cnid);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+
+       status = mdscli_get_path_recv(req, mem_ctx, path);
+fail:
+       TALLOC_FREE(frame);
+       return status;
+}
+
+struct mdscli_close_search_state {
+       struct mdscli_search_ctx *search;
+       struct mdssvc_blob request_blob;
+       struct mdssvc_blob response_blob;
+};
+
+static void mdscli_close_search_done(struct tevent_req *subreq);
+
+struct tevent_req *mdscli_close_search_send(TALLOC_CTX *mem_ctx,
+                                           struct tevent_context *ev,
+                                           struct mdscli_search_ctx **search)
+{
+       struct mdscli_ctx *mdscli_ctx = NULL;
+       struct tevent_req *req = NULL;
+       struct mdscli_close_search_state *state = NULL;
+       struct tevent_req *subreq = NULL;
+       NTSTATUS status;
+
+       req = tevent_req_create(req, &state, struct mdscli_close_search_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       *state = (struct mdscli_close_search_state) {
+               .search = talloc_move(state, search),
+       };
+       mdscli_ctx = state->search->mdscli_ctx;
+
+       status = mdscli_blob_close_search(state,
+                                         state->search,
+                                         &state->request_blob);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
+
+       state->response_blob.spotlight_blob = talloc_array(
+               state,
+               uint8_t,
+               mdscli_ctx->max_fragment_size);
+       if (tevent_req_nomem(state->response_blob.spotlight_blob, req)) {
+               return tevent_req_post(req, ev);
+       }
+       state->response_blob.size = mdscli_ctx->max_fragment_size;
+
+       subreq = dcerpc_mdssvc_cmd_send(state,
+                                       ev,
+                                       mdscli_ctx->bh,
+                                       mdscli_ctx->ph,
+                                       0,
+                                       mdscli_ctx->dev,
+                                       mdscli_ctx->mdscmd_open.unkn2,
+                                       0,
+                                       mdscli_ctx->flags,
+                                       state->request_blob,
+                                       0,
+                                       mdscli_ctx->max_fragment_size,
+                                       1,
+                                       mdscli_ctx->max_fragment_size,
+                                       0,
+                                       0,
+                                       &mdscli_ctx->mdscmd_cmd.fragment,
+                                       &state->response_blob,
+                                       &mdscli_ctx->mdscmd_cmd.unkn9);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, mdscli_close_search_done, req);
+       mdscli_ctx->async_pending++;
+       return req;
+}
+
+static void mdscli_close_search_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct mdscli_close_search_state *state = tevent_req_data(
+               req, struct mdscli_close_search_state);
+       NTSTATUS status;
+
+       status = dcerpc_mdssvc_cmd_recv(subreq, state);
+       TALLOC_FREE(subreq);
+       state->search->mdscli_ctx->async_pending--;
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       tevent_req_done(req);
+       return;
+}
+
+NTSTATUS mdscli_close_search_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS mdscli_close_search(struct mdscli_search_ctx **search)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct tevent_req *req = NULL;
+       struct tevent_context *ev = NULL;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       if ((*search)->mdscli_ctx->async_pending != 0) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+
+       ev = samba_tevent_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+
+       req = mdscli_close_search_send(frame, ev, search);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+
+       status = mdscli_close_search_recv(req);
+fail:
+       TALLOC_FREE(frame);
+       return status;
+}
+
+struct mdscli_disconnect_state {
+       struct mdscli_ctx *mdscli_ctx;
+};
+
+static void mdscli_disconnect_done(struct tevent_req *subreq);
+
+struct tevent_req *mdscli_disconnect_send(TALLOC_CTX *mem_ctx,
+                                         struct tevent_context *ev,
+                                         struct mdscli_ctx *mdscli_ctx)
+{
+       struct tevent_req *req = NULL;
+       struct mdscli_disconnect_state *state = NULL;
+       struct tevent_req *subreq = NULL;
+
+       req = tevent_req_create(req, &state, struct mdscli_disconnect_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       *state = (struct mdscli_disconnect_state) {
+               .mdscli_ctx = mdscli_ctx,
+       };
+
+       subreq = dcerpc_mdssvc_close_send(state,
+                                         ev,
+                                         mdscli_ctx->bh,
+                                         mdscli_ctx->ph,
+                                         0,
+                                         mdscli_ctx->dev,
+                                         mdscli_ctx->mdscmd_open.unkn2,
+                                         0,
+                                         &mdscli_ctx->ph,
+                                         &mdscli_ctx->mdscmd_close.status);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, mdscli_disconnect_done, req);
+       mdscli_ctx->async_pending++;
+       return req;
+}
+
+static void mdscli_disconnect_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct mdscli_disconnect_state *state = tevent_req_data(
+               req, struct mdscli_disconnect_state);
+       NTSTATUS status;
+
+       status = dcerpc_mdssvc_close_recv(subreq, state);
+       TALLOC_FREE(subreq);
+       state->mdscli_ctx->async_pending--;
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       tevent_req_done(req);
+       return;
+}
+
+NTSTATUS mdscli_disconnect_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS mdscli_disconnect(struct mdscli_ctx *mdscli_ctx)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct tevent_req *req = NULL;
+       struct tevent_context *ev = NULL;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       if (mdscli_ctx->async_pending != 0) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+
+       ev = samba_tevent_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+
+       req = mdscli_disconnect_send(frame, ev, mdscli_ctx);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+
+       status = mdscli_disconnect_recv(req);
+fail:
+       TALLOC_FREE(frame);
+       return status;
+}
diff --git a/source3/rpc_client/cli_mdssvc.h b/source3/rpc_client/cli_mdssvc.h
new file mode 100644 (file)
index 0000000..f8b1582
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+   Unix SMB/CIFS implementation.
+   mdssvc client functions
+
+   Copyright (C) Ralph Boehme                  2019
+
+   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 _MDSCLI_H_
+#define _MDSCLI_H_
+
+struct mdscli_ctx;
+struct mdscli_search_ctx;
+
+struct mdsctx_id mdscli_new_ctx_id(struct mdscli_ctx *mdscli_ctx);
+char *mdscli_get_basepath(TALLOC_CTX *mem_ctx,
+                         struct mdscli_ctx *mdscli_ctx);
+struct mdscli_ctx *mdscli_search_get_ctx(struct mdscli_search_ctx *search);
+
+struct tevent_req *mdscli_connect_send(TALLOC_CTX *mem_ctx,
+                                      struct tevent_context *ev,
+                                      struct dcerpc_binding_handle *bh,
+                                      const char *share_name,
+                                      const char *mount_path);
+NTSTATUS mdscli_connect_recv(struct tevent_req *req,
+                            TALLOC_CTX *mem_ctx,
+                            struct mdscli_ctx **mdscli_ctx);
+NTSTATUS mdscli_connect(TALLOC_CTX *mem_ctx,
+                       struct dcerpc_binding_handle *bh,
+                       const char *share_name,
+                       const char *mount_path,
+                       struct mdscli_ctx **mdscli_ctx);
+
+struct tevent_req *mdscli_search_send(TALLOC_CTX *mem_ctx,
+                                     struct tevent_context *ev,
+                                     struct mdscli_ctx *mdscli_ctx,
+                                     const char *mds_query,
+                                     const char *path_scope,
+                                     bool live);
+NTSTATUS mdscli_search_recv(struct tevent_req *req,
+                           TALLOC_CTX *mem_ctx,
+                           struct mdscli_search_ctx **search);
+NTSTATUS mdscli_search(TALLOC_CTX *mem_ctx,
+                      struct mdscli_ctx *mdscli_ctx,
+                      const char *mds_query,
+                      const char *path_scope,
+                      bool live,
+                      struct mdscli_search_ctx **search);
+
+struct tevent_req *mdscli_get_results_send(
+                       TALLOC_CTX *mem_ctx,
+                       struct tevent_context *ev,
+                       struct mdscli_search_ctx *search);
+NTSTATUS mdscli_get_results_recv(struct tevent_req *req,
+                                TALLOC_CTX *mem_ctx,
+                                uint64_t **cnids);
+NTSTATUS mdscli_get_results(TALLOC_CTX *mem_ctx,
+                           struct mdscli_search_ctx *search,
+                           uint64_t **_cnids);
+
+struct tevent_req *mdscli_get_path_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct mdscli_ctx *mdscli_ctx,
+                                       uint64_t cnid);
+NTSTATUS mdscli_get_path_recv(struct tevent_req *req,
+                             TALLOC_CTX *mem_ctx,
+                             char **path);
+NTSTATUS mdscli_get_path(TALLOC_CTX *mem_ctx,
+                        struct mdscli_ctx *mdscli_ctx,
+                        uint64_t cnid,
+                        char **path);
+
+struct tevent_req *mdscli_close_search_send(TALLOC_CTX *mem_ctx,
+                                           struct tevent_context *ev,
+                                           struct mdscli_search_ctx **_search);
+NTSTATUS mdscli_close_search_recv(struct tevent_req *req);
+NTSTATUS mdscli_close_search(struct mdscli_search_ctx **search);
+
+struct tevent_req *mdscli_disconnect_send(TALLOC_CTX *mem_ctx,
+                                         struct tevent_context *ev,
+                                         struct mdscli_ctx *mdscli_ctx);
+NTSTATUS mdscli_disconnect_recv(struct tevent_req *req);
+NTSTATUS mdscli_disconnect(struct mdscli_ctx *mdscli_ctx);
+
+#endif /* _MDSCLI_H_ */
diff --git a/source3/rpc_client/cli_mdssvc_private.h b/source3/rpc_client/cli_mdssvc_private.h
new file mode 100644 (file)
index 0000000..031af85
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+   Unix SMB/CIFS implementation.
+   mdssvc client functions
+
+   Copyright (C) Ralph Boehme                  2019
+
+   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 _MDSCLI_PRIVATE_H_
+#define _MDSCLI_PRIVATE_H_
+
+struct mdsctx_id {
+       uint64_t id;
+       uint64_t connection;
+};
+
+struct mdscli_ctx {
+       uint64_t async_pending;
+
+       struct dcerpc_binding_handle *bh;
+       struct policy_handle ph;
+
+       struct mdsctx_id ctx_id;
+       size_t max_fragment_size;
+
+       /* Known fields used across multiple commands */
+       uint32_t dev;
+       uint32_t flags;
+
+       /* cmd specific or unknown fields */
+       struct {
+               char share_path[1025];
+               uint32_t unkn2;
+               uint32_t unkn3;
+       } mdscmd_open;
+       struct {
+               uint32_t status;
+               uint32_t unkn7;
+       } mdscmd_unknown1;
+       struct {
+               uint32_t fragment;
+               uint32_t unkn9;
+       } mdscmd_cmd;
+       struct {
+               uint32_t status;
+       } mdscmd_close;
+};
+
+struct mdscli_search_ctx {
+       struct mdscli_ctx *mdscli_ctx;
+       struct mdsctx_id ctx_id;
+       uint64_t unique_id;
+       bool live;
+       char *path_scope;
+       char *mds_query;
+};
+
+#endif /* _MDSCLI_PRIVATE_H_ */
diff --git a/source3/rpc_client/cli_mdssvc_util.c b/source3/rpc_client/cli_mdssvc_util.c
new file mode 100644 (file)
index 0000000..fe5092c
--- /dev/null
@@ -0,0 +1,531 @@
+/*
+   Unix SMB/CIFS implementation.
+   Main metadata server / Spotlight client functions
+
+   Copyright (C) Ralph Boehme 2019
+
+   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 "includes.h"
+#include "rpc_client.h"
+#include "librpc/gen_ndr/mdssvc.h"
+#include "cli_mdssvc.h"
+#include "cli_mdssvc_private.h"
+#include "cli_mdssvc_util.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "rpc_server/mdssvc/dalloc.h"
+#include "rpc_server/mdssvc/marshalling.h"
+
+NTSTATUS mdscli_blob_search(TALLOC_CTX *mem_ctx,
+                           struct mdscli_search_ctx *search,
+                           struct mdssvc_blob *blob)
+{
+       struct mdscli_ctx *ctx = search->mdscli_ctx;
+       DALLOC_CTX *d = NULL;
+       uint64_t *uint64p = NULL;
+       sl_array_t *array = NULL;
+       sl_array_t *cmd_array = NULL;
+       sl_dict_t *query_dict = NULL;
+       sl_array_t *attr_array = NULL;
+       sl_array_t *scope_array = NULL;
+       double dval;
+       uint64_t uint64val;
+       ssize_t len;
+       int ret;
+
+       d = dalloc_new(mem_ctx);
+       if (d == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       array = dalloc_zero(d, sl_array_t);
+       if (array == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_add(d, array, sl_array_t);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       cmd_array = dalloc_zero(d, sl_array_t);
+       if (cmd_array == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_add(array, cmd_array, sl_array_t);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_stradd(cmd_array, "openQueryWithParams:forContext:");
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       uint64p = talloc_zero_array(cmd_array, uint64_t, 2);
+       if (uint64p == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       talloc_set_name(uint64p, "uint64_t *");
+
+       uint64p[0] = search->ctx_id.id;
+       uint64p[1] = search->ctx_id.connection;
+
+       ret = dalloc_add(cmd_array, uint64p, uint64_t *);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       query_dict = dalloc_zero(array, sl_dict_t);
+       if (query_dict == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_add(array, query_dict, sl_dict_t);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_stradd(query_dict, "kMDQueryBatchFirstDelay");
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+       dval = 1;
+       ret = dalloc_add_copy(query_dict, &dval, double);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_stradd(query_dict, "kMDQueryUniqueId");
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+       ret = dalloc_add_copy(query_dict, &search->unique_id, uint64_t);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_stradd(query_dict, "kMDAttributeArray");
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+       attr_array = dalloc_zero(query_dict, sl_array_t);
+       if (attr_array == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+       ret = dalloc_add(query_dict, attr_array, sl_array_t);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+       ret = dalloc_stradd(attr_array, "kMDItemFSName");
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_stradd(query_dict, "kMDQueryBatchFirstCount");
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+       uint64val = 10;
+       ret = dalloc_add_copy(query_dict, &uint64val, uint64_t);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_stradd(query_dict, "kMDQueryBatchUpdateCount");
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+       uint64val = 100;
+       ret = dalloc_add_copy(query_dict, &uint64val, uint64_t);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_stradd(query_dict, "kMDQueryString");
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+       ret = dalloc_stradd(query_dict, search->mds_query);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_stradd(query_dict, "kMDScopeArray");
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+       scope_array = dalloc_zero(query_dict, sl_array_t);
+       if (scope_array == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+       ret = dalloc_add(query_dict, scope_array, sl_array_t);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+       ret = dalloc_stradd(scope_array, search->path_scope);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       blob->spotlight_blob = talloc_array(d,
+                                           uint8_t,
+                                           ctx->max_fragment_size);
+       if (blob->spotlight_blob == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+       blob->size = ctx->max_fragment_size;
+
+       len = sl_pack(d, (char *)blob->spotlight_blob, blob->size);
+       TALLOC_FREE(d);
+       if (len == -1) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       blob->length = len;
+       blob->size = len;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS mdscli_blob_get_results(TALLOC_CTX *mem_ctx,
+                                struct mdscli_search_ctx *search,
+                                struct mdssvc_blob *blob)
+{
+       struct mdscli_ctx *ctx = search->mdscli_ctx;
+       DALLOC_CTX *d = NULL;
+       uint64_t *uint64p = NULL;
+       sl_array_t *array = NULL;
+       sl_array_t *cmd_array = NULL;
+       ssize_t len;
+       int ret;
+
+       d = dalloc_new(mem_ctx);
+       if (d == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       array = dalloc_zero(d, sl_array_t);
+       if (array == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_add(d, array, sl_array_t);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       cmd_array = dalloc_zero(d, sl_array_t);
+       if (cmd_array == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_add(array, cmd_array, sl_array_t);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_stradd(cmd_array, "fetchQueryResultsForContext:");
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       uint64p = talloc_zero_array(cmd_array, uint64_t, 2);
+       if (uint64p == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       talloc_set_name(uint64p, "uint64_t *");
+
+       uint64p[0] = search->ctx_id.id;
+       uint64p[1] = search->ctx_id.connection;
+
+       ret = dalloc_add(cmd_array, uint64p, uint64_t *);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       blob->spotlight_blob = talloc_array(d,
+                                           uint8_t,
+                                           ctx->max_fragment_size);
+       if (blob->spotlight_blob == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+       blob->size = ctx->max_fragment_size;
+
+       len = sl_pack(d, (char *)blob->spotlight_blob, blob->size);
+       TALLOC_FREE(d);
+       if (len == -1) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       blob->length = len;
+       blob->size = len;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS mdscli_blob_get_path(TALLOC_CTX *mem_ctx,
+                             struct mdscli_ctx *ctx,
+                             uint64_t cnid,
+                             struct mdssvc_blob *blob)
+{
+       struct mdsctx_id ctx_id = mdscli_new_ctx_id(ctx);
+       DALLOC_CTX *d = NULL;
+       uint64_t *uint64var = NULL;
+       sl_array_t *array = NULL;
+       sl_array_t *cmd_array = NULL;
+       sl_array_t *attr_array = NULL;
+       sl_cnids_t *cnids = NULL;
+       ssize_t len;
+       int ret;
+
+       d = dalloc_new(mem_ctx);
+       if (d == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       array = dalloc_zero(d, sl_array_t);
+       if (array == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_add(d, array, sl_array_t);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       cmd_array = dalloc_zero(d, sl_array_t);
+       if (cmd_array == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_add(array, cmd_array, sl_array_t);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_stradd(cmd_array, "fetchAttributes:forOIDArray:context:");
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       uint64var = talloc_zero_array(cmd_array, uint64_t, 2);
+       if (uint64var == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       talloc_set_name(uint64var, "uint64_t *");
+
+       uint64var[0] = ctx_id.id;
+       uint64var[1] = 0;
+
+       ret = dalloc_add(cmd_array, &uint64var[0], uint64_t *);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       attr_array = dalloc_zero(d, sl_array_t);
+       if (attr_array == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_add(array, attr_array, sl_array_t);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_stradd(attr_array, "kMDItemPath");
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* CNIDs */
+       cnids = talloc_zero(array, sl_cnids_t);
+       if (cnids == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       cnids->ca_cnids = dalloc_new(cnids);
+       if (cnids->ca_cnids == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       cnids->ca_unkn1 = 0xadd;
+       cnids->ca_context = 0x6b000020;
+
+       ret = dalloc_add_copy(cnids->ca_cnids, &cnid, uint64_t);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_add(array, cnids, sl_cnids_t);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       blob->spotlight_blob = talloc_array(d,
+                                           uint8_t,
+                                           ctx->max_fragment_size);
+       if (blob->spotlight_blob == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+       blob->size = ctx->max_fragment_size;
+
+       len = sl_pack(d, (char *)blob->spotlight_blob, blob->size);
+       TALLOC_FREE(d);
+       if (len == -1) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       blob->length = len;
+       blob->size = len;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS mdscli_blob_close_search(TALLOC_CTX *mem_ctx,
+                                 struct mdscli_search_ctx *search,
+                                 struct mdssvc_blob *blob)
+{
+       struct mdscli_ctx *ctx = search->mdscli_ctx;
+       DALLOC_CTX *d = NULL;
+       uint64_t *uint64p = NULL;
+       sl_array_t *array = NULL;
+       sl_array_t *cmd_array = NULL;
+       ssize_t len;
+       int ret;
+
+       d = dalloc_new(mem_ctx);
+       if (d == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       array = dalloc_zero(d, sl_array_t);
+       if (array == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_add(d, array, sl_array_t);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       cmd_array = dalloc_zero(d, sl_array_t);
+       if (cmd_array == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_add(array, cmd_array, sl_array_t);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dalloc_stradd(cmd_array, "closeQueryForContext:");
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       uint64p = talloc_zero_array(cmd_array, uint64_t, 2);
+       if (uint64p == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       talloc_set_name(uint64p, "uint64_t *");
+
+       uint64p[0] = search->ctx_id.id;
+       uint64p[1] = search->ctx_id.connection;
+
+       ret = dalloc_add(cmd_array, uint64p, uint64_t *);
+       if (ret != 0) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       blob->spotlight_blob = talloc_array(d,
+                                           uint8_t,
+                                           ctx->max_fragment_size);
+       if (blob->spotlight_blob == NULL) {
+               TALLOC_FREE(d);
+               return NT_STATUS_NO_MEMORY;
+       }
+       blob->size = ctx->max_fragment_size;
+
+       len = sl_pack(d, (char *)blob->spotlight_blob, blob->size);
+       TALLOC_FREE(d);
+       if (len == -1) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       blob->length = len;
+       blob->size = len;
+       return NT_STATUS_OK;
+}
diff --git a/source3/rpc_client/cli_mdssvc_util.h b/source3/rpc_client/cli_mdssvc_util.h
new file mode 100644 (file)
index 0000000..7a98c85
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+   Unix SMB/CIFS implementation.
+   Main metadata server / Spotlight client functions
+
+   Copyright (C) Ralph Boehme 2019
+
+   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 _MDSCLI_UTIL_H_
+#define _MDSCLI_UTIL_H_
+
+NTSTATUS mdscli_blob_search(TALLOC_CTX *mem_ctx,
+                           struct mdscli_search_ctx *search,
+                           struct mdssvc_blob *blob);
+
+NTSTATUS mdscli_blob_get_results(TALLOC_CTX *mem_ctx,
+                                struct mdscli_search_ctx *search,
+                                struct mdssvc_blob *blob);
+
+NTSTATUS mdscli_blob_get_path(TALLOC_CTX *mem_ctx,
+                             struct mdscli_ctx *mdscli_ctx,
+                             uint64_t cnid,
+                             struct mdssvc_blob *blob);
+
+NTSTATUS mdscli_blob_close_search(TALLOC_CTX *mem_ctx,
+                                 struct mdscli_search_ctx *search,
+                                 struct mdssvc_blob *blob);
+
+#endif /* _MDSCLI_UTIL_H_ */
index 6707b4bb1464818b7988881e884615533493ad7e..51d24ee4098eb51364740bf0f0580033e40c1dfc 100644 (file)
@@ -1053,6 +1053,13 @@ bld.SAMBA3_SUBSYSTEM('RPC_CLIENT_SCHANNEL',
                     source='rpc_client/cli_pipe_schannel.c',
                     deps='samba-util krb5samba')
 
+bld.SAMBA3_SUBSYSTEM('RPCCLI_MDSSVC',
+                    source='''
+                    rpc_client/cli_mdssvc.c
+                    rpc_client/cli_mdssvc_util.c
+                    ''',
+                    deps='mdssvc RPC_NDR_MDSSVC')
+
 bld.SAMBA3_SUBSYSTEM('INIT_LSA',
                     source='rpc_client/init_lsa.c',
                     deps='samba-util')