76cceb42fe0e32e8820264d74c1babd0e61ebae4
[samba.git] / source3 / rpc_client / cli_mdssvc.c
1 /*
2    Unix SMB/CIFS implementation.
3    Main metadata server / Spotlight client functions
4
5    Copyright (C) Ralph Boehme 2019
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "rpc_client.h"
23 #include "../librpc/gen_ndr/ndr_mdssvc_c.h"
24 #include "lib/util/tevent_ntstatus.h"
25 #include "rpc_server/mdssvc/dalloc.h"
26 #include "rpc_server/mdssvc/marshalling.h"
27 #include "cli_mdssvc.h"
28 #include "cli_mdssvc_private.h"
29 #include "cli_mdssvc_util.h"
30
31 struct mdsctx_id mdscli_new_ctx_id(struct mdscli_ctx *mdscli_ctx)
32 {
33         mdscli_ctx->ctx_id.id++;
34         return mdscli_ctx->ctx_id;
35 }
36
37 char *mdscli_get_basepath(TALLOC_CTX *mem_ctx,
38                           struct mdscli_ctx *mdscli_ctx)
39 {
40         return talloc_strdup(mem_ctx, mdscli_ctx->mdscmd_open.share_path);
41 }
42
43 struct mdscli_connect_state {
44         struct tevent_context *ev;
45         struct mdscli_ctx *mdscli_ctx;
46 };
47
48 static void mdscli_connect_open_done(struct tevent_req *subreq);
49 static void mdscli_connect_unknown1_done(struct tevent_req *subreq);
50
51 struct tevent_req *mdscli_connect_send(TALLOC_CTX *mem_ctx,
52                                        struct tevent_context *ev,
53                                        struct dcerpc_binding_handle *bh,
54                                        const char *share_name,
55                                        const char *mount_path)
56 {
57         struct tevent_req *req = NULL;
58         struct mdscli_connect_state *state = NULL;
59         struct tevent_req *subreq = NULL;
60         struct mdscli_ctx *ctx = NULL;
61
62         req = tevent_req_create(req, &state, struct mdscli_connect_state);
63         if (req == NULL) {
64                 return NULL;
65         }
66
67         ctx = talloc_zero(state, struct mdscli_ctx);
68         if (tevent_req_nomem(ctx, req)) {
69                 return tevent_req_post(req, ev);
70         }
71
72         *state = (struct mdscli_connect_state) {
73                 .ev = ev,
74                 .mdscli_ctx = ctx,
75         };
76
77         *ctx = (struct mdscli_ctx) {
78                 .bh = bh,
79                 .max_fragment_size = 64 * 1024,
80                 /*
81                  * The connection id is a per tcon value sent by the client,
82                  * 0x6b000060 is a value used most of the times for the first
83                  * tcon.
84                  */
85                 .ctx_id.connection = UINT64_C(0x6b000060),
86         };
87
88         subreq = dcerpc_mdssvc_open_send(state,
89                                          state->ev,
90                                          ctx->bh,
91                                          &ctx->dev,
92                                          &ctx->mdscmd_open.unkn2,
93                                          &ctx->mdscmd_open.unkn3,
94                                          mount_path,
95                                          share_name,
96                                          ctx->mdscmd_open.share_path,
97                                          &ctx->ph);
98         if (tevent_req_nomem(subreq, req)) {
99                 return tevent_req_post(req, state->ev);
100         }
101         tevent_req_set_callback(subreq, mdscli_connect_open_done, req);
102         ctx->async_pending++;
103
104         return req;
105 }
106
107 static void mdscli_connect_open_done(struct tevent_req *subreq)
108 {
109         struct tevent_req *req = tevent_req_callback_data(
110                 subreq, struct tevent_req);
111         struct mdscli_connect_state *state = tevent_req_data(
112                 req, struct mdscli_connect_state);
113         struct mdscli_ctx *mdscli_ctx = state->mdscli_ctx;
114         NTSTATUS status;
115
116         status = dcerpc_mdssvc_open_recv(subreq, state);
117         TALLOC_FREE(subreq);
118         state->mdscli_ctx->async_pending--;
119         if (tevent_req_nterror(req, status)) {
120                 return;
121         }
122
123         subreq = dcerpc_mdssvc_unknown1_send(
124                         state,
125                         state->ev,
126                         mdscli_ctx->bh,
127                         mdscli_ctx->ph,
128                         0,
129                         mdscli_ctx->dev,
130                         mdscli_ctx->mdscmd_open.unkn2,
131                         0,
132                         geteuid(),
133                         getegid(),
134                         &mdscli_ctx->mdscmd_unknown1.status,
135                         &mdscli_ctx->flags,
136                         &mdscli_ctx->mdscmd_unknown1.unkn7);
137         if (tevent_req_nomem(subreq, req)) {
138                 return;
139         }
140         tevent_req_set_callback(subreq, mdscli_connect_unknown1_done, req);
141 }
142
143 static void mdscli_connect_unknown1_done(struct tevent_req *subreq)
144 {
145         struct tevent_req *req = tevent_req_callback_data(
146                 subreq, struct tevent_req);
147         struct mdscli_connect_state *state = tevent_req_data(
148                 req, struct mdscli_connect_state);
149         NTSTATUS status;
150
151         status = dcerpc_mdssvc_unknown1_recv(subreq, state);
152         TALLOC_FREE(subreq);
153         if (tevent_req_nterror(req, status)) {
154                 return;
155         }
156
157         tevent_req_done(req);
158 }
159
160 NTSTATUS mdscli_connect_recv(struct tevent_req *req,
161                              TALLOC_CTX *mem_ctx,
162                              struct mdscli_ctx **mdscli_ctx)
163 {
164         struct mdscli_connect_state *state = tevent_req_data(
165                 req, struct mdscli_connect_state);
166         NTSTATUS status;
167
168         if (tevent_req_is_nterror(req, &status)) {
169                 tevent_req_received(req);
170                 return status;
171         }
172
173         *mdscli_ctx = talloc_move(mem_ctx, &state->mdscli_ctx);
174         tevent_req_received(req);
175         return NT_STATUS_OK;
176 }
177
178 NTSTATUS mdscli_connect(TALLOC_CTX *mem_ctx,
179                         struct dcerpc_binding_handle *bh,
180                         const char *share_name,
181                         const char *mount_path,
182                         struct mdscli_ctx **mdscli_ctx)
183 {
184         TALLOC_CTX *frame = talloc_stackframe();
185         struct tevent_req *req = NULL;
186         struct tevent_context *ev = NULL;
187         NTSTATUS status = NT_STATUS_NO_MEMORY;
188
189         ev = samba_tevent_context_init(frame);
190         if (ev == NULL) {
191                 goto fail;
192         }
193
194         req = mdscli_connect_send(frame,
195                                   ev,
196                                   bh,
197                                   share_name,
198                                   mount_path);
199         if (req == NULL) {
200                 goto fail;
201         }
202         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
203                 goto fail;
204         }
205
206         status = mdscli_connect_recv(req, mem_ctx, mdscli_ctx);
207 fail:
208         TALLOC_FREE(frame);
209         return status;
210 }
211
212 struct mdscli_search_state {
213         struct mdscli_search_ctx *search;
214         struct mdssvc_blob request_blob;
215         struct mdssvc_blob response_blob;
216 };
217
218 static void mdscli_search_cmd_done(struct tevent_req *subreq);
219
220 struct tevent_req *mdscli_search_send(TALLOC_CTX *mem_ctx,
221                                       struct tevent_context *ev,
222                                       struct mdscli_ctx *mdscli_ctx,
223                                       const char *mds_query,
224                                       const char *path_scope_in,
225                                       bool live)
226 {
227         struct tevent_req *req = NULL;
228         struct mdscli_search_state *state = NULL;
229         struct tevent_req *subreq = NULL;
230         struct mdscli_search_ctx *search = NULL;
231         char *path_scope = NULL;
232         NTSTATUS status;
233
234         req = tevent_req_create(req, &state, struct mdscli_search_state);
235         if (req == NULL) {
236                 return NULL;
237         }
238
239         search = talloc_zero(state, struct mdscli_search_ctx);
240         if (tevent_req_nomem(search, req)) {
241                 return tevent_req_post(req, ev);
242         }
243
244         if (path_scope_in[0] == '/') {
245                 path_scope = talloc_strdup(search, path_scope_in);
246         } else {
247                 path_scope = talloc_asprintf(search,
248                                              "%s/%s",
249                                              mdscli_ctx->mdscmd_open.share_path,
250                                              path_scope_in);
251         }
252         if (tevent_req_nomem(path_scope, req)) {
253                 return tevent_req_post(req, ev);
254         }
255
256         *search = (struct mdscli_search_ctx) {
257                 .mdscli_ctx = mdscli_ctx,
258                 .ctx_id = mdscli_new_ctx_id(mdscli_ctx),
259                 .unique_id = generate_random_u64(),
260                 .live = live,
261                 .path_scope = path_scope,
262                 .mds_query = talloc_strdup(search, mds_query),
263         };
264         if (tevent_req_nomem(search->mds_query, req)) {
265                 return tevent_req_post(req, ev);
266         }
267
268         *state = (struct mdscli_search_state) {
269                 .search = search,
270         };
271
272         status = mdscli_blob_search(state,
273                                     search,
274                                     &state->request_blob);
275         if (tevent_req_nterror(req, status)) {
276                 return tevent_req_post(req, ev);
277         }
278
279         state->response_blob.spotlight_blob = talloc_array(
280                 state,
281                 uint8_t,
282                 mdscli_ctx->max_fragment_size);
283         if (tevent_req_nomem(state->response_blob.spotlight_blob, req)) {
284                 return tevent_req_post(req, ev);
285         }
286         state->response_blob.size = mdscli_ctx->max_fragment_size;
287
288         subreq = dcerpc_mdssvc_cmd_send(state,
289                                         ev,
290                                         mdscli_ctx->bh,
291                                         mdscli_ctx->ph,
292                                         0,
293                                         mdscli_ctx->dev,
294                                         mdscli_ctx->mdscmd_open.unkn2,
295                                         0,
296                                         mdscli_ctx->flags,
297                                         state->request_blob,
298                                         0,
299                                         mdscli_ctx->max_fragment_size,
300                                         1,
301                                         mdscli_ctx->max_fragment_size,
302                                         0,
303                                         0,
304                                         &mdscli_ctx->mdscmd_cmd.fragment,
305                                         &state->response_blob,
306                                         &mdscli_ctx->mdscmd_cmd.unkn9);
307         if (tevent_req_nomem(subreq, req)) {
308                 return tevent_req_post(req, ev);
309         }
310         tevent_req_set_callback(subreq, mdscli_search_cmd_done, req);
311         mdscli_ctx->async_pending++;
312         return req;
313 }
314
315 static void mdscli_search_cmd_done(struct tevent_req *subreq)
316 {
317         struct tevent_req *req = tevent_req_callback_data(
318                 subreq, struct tevent_req);
319         struct mdscli_search_state *state = tevent_req_data(
320                 req, struct mdscli_search_state);
321         DALLOC_CTX *d = NULL;
322         uint64_t *uint64p = NULL;
323         NTSTATUS status;
324         bool ok;
325
326         status = dcerpc_mdssvc_cmd_recv(subreq, state);
327         TALLOC_FREE(subreq);
328         state->search->mdscli_ctx->async_pending--;
329         if (tevent_req_nterror(req, status)) {
330                 return;
331         }
332
333         d = dalloc_new(state);
334         if (tevent_req_nomem(d, req)) {
335                 return;
336         }
337
338         ok = sl_unpack(d,
339                        (char *)state->response_blob.spotlight_blob,
340                        state->response_blob.length);
341         if (!ok) {
342                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
343                 return;
344         }
345
346         uint64p = dalloc_get(d,
347                              "DALLOC_CTX", 0,
348                              "uint64_t", 0);
349         if (uint64p == NULL) {
350                 DBG_DEBUG("Unexpected mds reponse: %s", dalloc_dump(d, 0));
351                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
352                 return;
353         }
354
355         if (*uint64p != 0) {
356                 DBG_DEBUG("Unexpected mds result: 0x%" PRIx64, *uint64p);
357                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
358                 return;
359         }
360
361         tevent_req_done(req);
362         return;
363 }
364
365 NTSTATUS mdscli_search_recv(struct tevent_req *req,
366                             TALLOC_CTX *mem_ctx,
367                             struct mdscli_search_ctx **search)
368 {
369         struct mdscli_search_state *state = tevent_req_data(
370                 req, struct mdscli_search_state);
371         NTSTATUS status;
372
373         if (tevent_req_is_nterror(req, &status)) {
374                 tevent_req_received(req);
375                 return status;
376         }
377
378         *search = talloc_move(mem_ctx, &state->search);
379         tevent_req_received(req);
380         return NT_STATUS_OK;
381 }
382
383 NTSTATUS mdscli_search(TALLOC_CTX *mem_ctx,
384                        struct mdscli_ctx *mdscli_ctx,
385                        const char *mds_query,
386                        const char *path_scope,
387                        bool live,
388                        struct mdscli_search_ctx **search)
389 {
390         TALLOC_CTX *frame = talloc_stackframe();
391         struct tevent_req *req = NULL;
392         struct tevent_context *ev = NULL;
393         NTSTATUS status = NT_STATUS_NO_MEMORY;
394
395         if (mdscli_ctx->async_pending != 0) {
396                 status = NT_STATUS_INVALID_PARAMETER;
397                 goto fail;
398         }
399
400         ev = samba_tevent_context_init(frame);
401         if (ev == NULL) {
402                 goto fail;
403         }
404
405         req = mdscli_search_send(frame,
406                                  ev,
407                                  mdscli_ctx,
408                                  mds_query,
409                                  path_scope,
410                                  live);
411         if (req == NULL) {
412                 goto fail;
413         }
414         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
415                 goto fail;
416         }
417
418         status = mdscli_search_recv(req, mem_ctx, search);
419 fail:
420         TALLOC_FREE(frame);
421         return status;
422 }
423
424 struct mdscli_get_results_state {
425         struct mdscli_search_ctx *search;
426         struct mdssvc_blob request_blob;
427         struct mdssvc_blob response_blob;
428         uint64_t *cnids;
429 };
430
431 static void mdscli_get_results_cmd_done(struct tevent_req *subreq);
432
433 struct tevent_req *mdscli_get_results_send(
434                         TALLOC_CTX *mem_ctx,
435                         struct tevent_context *ev,
436                         struct mdscli_search_ctx *search)
437 {
438         struct tevent_req *req = NULL;
439         struct mdscli_get_results_state *state = NULL;
440         struct tevent_req *subreq = NULL;
441         struct mdscli_ctx *mdscli_ctx = search->mdscli_ctx;
442         NTSTATUS status;
443
444         req = tevent_req_create(req, &state, struct mdscli_get_results_state);
445         if (req == NULL) {
446                 return NULL;
447         }
448
449         *state = (struct mdscli_get_results_state) {
450                 .search = search,
451         };
452
453         status = mdscli_blob_get_results(state,
454                                          search,
455                                          &state->request_blob);
456         if (tevent_req_nterror(req, status)) {
457                 return tevent_req_post(req, ev);
458         }
459
460         state->response_blob.spotlight_blob = talloc_array(
461                 state,
462                 uint8_t,
463                 mdscli_ctx->max_fragment_size);
464         if (tevent_req_nomem(state->response_blob.spotlight_blob, req)) {
465                 return tevent_req_post(req, ev);
466         }
467         state->response_blob.size = mdscli_ctx->max_fragment_size;
468
469         subreq = dcerpc_mdssvc_cmd_send(state,
470                                         ev,
471                                         mdscli_ctx->bh,
472                                         mdscli_ctx->ph,
473                                         0,
474                                         mdscli_ctx->dev,
475                                         mdscli_ctx->mdscmd_open.unkn2,
476                                         0,
477                                         mdscli_ctx->flags,
478                                         state->request_blob,
479                                         0,
480                                         mdscli_ctx->max_fragment_size,
481                                         1,
482                                         mdscli_ctx->max_fragment_size,
483                                         0,
484                                         0,
485                                         &mdscli_ctx->mdscmd_cmd.fragment,
486                                         &state->response_blob,
487                                         &mdscli_ctx->mdscmd_cmd.unkn9);
488         if (tevent_req_nomem(subreq, req)) {
489                 return tevent_req_post(req, ev);
490         }
491         tevent_req_set_callback(subreq, mdscli_get_results_cmd_done, req);
492         mdscli_ctx->async_pending++;
493         return req;
494 }
495
496 static void mdscli_get_results_cmd_done(struct tevent_req *subreq)
497 {
498         struct tevent_req *req = tevent_req_callback_data(
499                 subreq, struct tevent_req);
500         struct mdscli_get_results_state *state = tevent_req_data(
501                 req, struct mdscli_get_results_state);
502         DALLOC_CTX *d = NULL;
503         uint64_t *uint64p = NULL;
504         sl_cnids_t *cnids = NULL;
505         size_t ncnids;
506         size_t i;
507         NTSTATUS status;
508         bool ok;
509
510         status = dcerpc_mdssvc_cmd_recv(subreq, state);
511         TALLOC_FREE(subreq);
512         state->search->mdscli_ctx->async_pending--;
513         if (tevent_req_nterror(req, status)) {
514                 return;
515         }
516
517         d = dalloc_new(state);
518         if (tevent_req_nomem(d, req)) {
519                 return;
520         }
521
522         ok = sl_unpack(d,
523                        (char *)state->response_blob.spotlight_blob,
524                        state->response_blob.length);
525         if (!ok) {
526                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
527                 return;
528         }
529
530         uint64p = dalloc_get(d,
531                              "DALLOC_CTX", 0,
532                              "uint64_t", 0);
533         if (uint64p == NULL) {
534                 DBG_DEBUG("Unexpected mds reponse: %s", dalloc_dump(d, 0));
535                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
536                 return;
537         }
538
539         if (*uint64p == 35) {
540                 DBG_DEBUG("search done: %s", dalloc_dump(d, 0));
541                 tevent_req_done(req);
542                 return;
543         }
544
545         cnids = dalloc_get(d, "DALLOC_CTX", 0, "sl_cnids_t", 1);
546         if (cnids == NULL) {
547                 DBG_DEBUG("cnids error: %s", dalloc_dump(d, 0));
548                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
549                 return;
550         }
551
552         ncnids = dalloc_size(cnids->ca_cnids);
553         if (ncnids == 0) {
554                 tevent_req_nterror(req, NT_STATUS_NO_MORE_MATCHES);
555                 return;
556         }
557
558         if (cnids->ca_unkn1 != 0xadd) {
559                 /*
560                  * Whatever 0xadd means... but it seems to be the standard value
561                  * macOS mdssvc returns here.
562                  */
563                 DBG_DEBUG("unexpected ca_unkn1: %s", dalloc_dump(d, 0));
564                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
565                 return;
566         }
567
568         if (cnids->ca_context != state->search->ctx_id.connection ) {
569                 DBG_DEBUG("unexpected ca_context: %s", dalloc_dump(d, 0));
570                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
571                 return;
572         }
573
574         state->cnids = talloc_zero_array(state, uint64_t, ncnids);
575         if (tevent_req_nomem(state->cnids, req)) {
576                 return;
577         }
578
579         for (i = 0; i < ncnids; i++) {
580                 uint64_t *cnid = NULL;
581
582                 cnid = dalloc_get(cnids->ca_cnids, "uint64_t", i);
583                 if (cnid == NULL) {
584                         DBG_DEBUG("cnids error: %s", dalloc_dump(d, 0));
585                         tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
586                         return;
587                 }
588                 state->cnids[i] = *cnid;
589         }
590
591         tevent_req_done(req);
592         return;
593 }
594
595 NTSTATUS mdscli_get_results_recv(struct tevent_req *req,
596                                  TALLOC_CTX *mem_ctx,
597                                  uint64_t **cnids)
598 {
599         struct mdscli_get_results_state *state = tevent_req_data(
600                 req, struct mdscli_get_results_state);
601         NTSTATUS status;
602
603         if (tevent_req_is_nterror(req, &status)) {
604                 tevent_req_received(req);
605                 return status;
606         }
607
608         *cnids = talloc_move(mem_ctx, &state->cnids);
609
610         tevent_req_received(req);
611         return NT_STATUS_OK;
612 }
613
614 NTSTATUS mdscli_get_results(TALLOC_CTX *mem_ctx,
615                             struct mdscli_search_ctx *search,
616                             uint64_t **_cnids)
617 {
618         TALLOC_CTX *frame = talloc_stackframe();
619         struct tevent_req *req = NULL;
620         struct tevent_context *ev = NULL;
621         NTSTATUS status = NT_STATUS_NO_MEMORY;
622
623         if (search->mdscli_ctx->async_pending != 0) {
624                 status = NT_STATUS_INVALID_PARAMETER;
625                 goto fail;
626         }
627
628         ev = samba_tevent_context_init(frame);
629         if (ev == NULL) {
630                 goto fail;
631         }
632
633         req = mdscli_get_results_send(frame,
634                                       ev,
635                                       search);
636         if (req == NULL) {
637                 goto fail;
638         }
639         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
640                 goto fail;
641         }
642
643         status = mdscli_get_results_recv(req, mem_ctx, _cnids);
644 fail:
645         TALLOC_FREE(frame);
646         return status;
647 }
648
649 struct mdscli_get_path_state {
650         struct mdscli_ctx *mdscli_ctx;
651         struct mdssvc_blob request_blob;
652         struct mdssvc_blob response_blob;
653         char *path;
654 };
655
656 static void mdscli_get_path_done(struct tevent_req *subreq);
657
658 struct tevent_req *mdscli_get_path_send(TALLOC_CTX *mem_ctx,
659                                         struct tevent_context *ev,
660                                         struct mdscli_ctx *mdscli_ctx,
661                                         uint64_t cnid)
662 {
663         struct tevent_req *req = NULL;
664         struct mdscli_get_path_state *state = NULL;
665         struct tevent_req *subreq = NULL;
666         NTSTATUS status;
667
668         req = tevent_req_create(req, &state, struct mdscli_get_path_state);
669         if (req == NULL) {
670                 return NULL;
671         }
672         *state = (struct mdscli_get_path_state) {
673                 .mdscli_ctx = mdscli_ctx,
674         };
675
676         status = mdscli_blob_get_path(state,
677                                       mdscli_ctx,
678                                       cnid,
679                                       &state->request_blob);
680         if (tevent_req_nterror(req, status)) {
681                 return tevent_req_post(req, ev);
682         }
683
684         state->response_blob.spotlight_blob = talloc_array(
685                 state,
686                 uint8_t,
687                 mdscli_ctx->max_fragment_size);
688         if (tevent_req_nomem(state->response_blob.spotlight_blob, req)) {
689                 return tevent_req_post(req, ev);
690         }
691         state->response_blob.size = mdscli_ctx->max_fragment_size;
692
693         subreq = dcerpc_mdssvc_cmd_send(state,
694                                         ev,
695                                         mdscli_ctx->bh,
696                                         mdscli_ctx->ph,
697                                         0,
698                                         mdscli_ctx->dev,
699                                         mdscli_ctx->mdscmd_open.unkn2,
700                                         0,
701                                         mdscli_ctx->flags,
702                                         state->request_blob,
703                                         0,
704                                         mdscli_ctx->max_fragment_size,
705                                         1,
706                                         mdscli_ctx->max_fragment_size,
707                                         0,
708                                         0,
709                                         &mdscli_ctx->mdscmd_cmd.fragment,
710                                         &state->response_blob,
711                                         &mdscli_ctx->mdscmd_cmd.unkn9);
712         if (tevent_req_nomem(subreq, req)) {
713                 return tevent_req_post(req, ev);
714         }
715         tevent_req_set_callback(subreq, mdscli_get_path_done, req);
716         mdscli_ctx->async_pending++;
717         return req;
718 }
719
720 static void mdscli_get_path_done(struct tevent_req *subreq)
721 {
722         struct tevent_req *req = tevent_req_callback_data(
723                 subreq, struct tevent_req);
724         struct mdscli_get_path_state *state = tevent_req_data(
725                 req, struct mdscli_get_path_state);
726         DALLOC_CTX *d = NULL;
727         char *path = NULL;
728         NTSTATUS status;
729         bool ok;
730
731         status = dcerpc_mdssvc_cmd_recv(subreq, state);
732         TALLOC_FREE(subreq);
733         state->mdscli_ctx->async_pending--;
734         if (tevent_req_nterror(req, status)) {
735                 return;
736         }
737
738         d = dalloc_new(state);
739         if (tevent_req_nomem(d, req)) {
740                 return;
741         }
742
743         ok = sl_unpack(d,
744                        (char *)state->response_blob.spotlight_blob,
745                        state->response_blob.length);
746         if (!ok) {
747                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
748                 return;
749         }
750
751         path = dalloc_get(d,
752                           "DALLOC_CTX", 0,
753                           "DALLOC_CTX", 2,
754                           "DALLOC_CTX", 0,
755                           "DALLOC_CTX", 1,
756                           "char *", 0);
757         if (path == NULL) {
758                 DBG_DEBUG("No path in mds reponse: %s", dalloc_dump(d, 0));
759                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
760                 return;
761         }
762         state->path = talloc_move(state, &path);
763         DBG_DEBUG("path: %s\n", state->path);
764
765         tevent_req_done(req);
766         return;
767 }
768
769 NTSTATUS mdscli_get_path_recv(struct tevent_req *req,
770                               TALLOC_CTX *mem_ctx,
771                               char **path)
772 {
773         struct mdscli_get_path_state *state = tevent_req_data(
774                 req, struct mdscli_get_path_state);
775         NTSTATUS status;
776
777         if (tevent_req_is_nterror(req, &status)) {
778                 tevent_req_received(req);
779                 return status;
780         }
781
782         *path = talloc_move(mem_ctx, &state->path);
783         tevent_req_received(req);
784         return NT_STATUS_OK;
785 }
786
787 NTSTATUS mdscli_get_path(TALLOC_CTX *mem_ctx,
788                          struct mdscli_ctx *mdscli_ctx,
789                          uint64_t cnid,
790                          char **path)
791 {
792         TALLOC_CTX *frame = talloc_stackframe();
793         struct tevent_req *req = NULL;
794         struct tevent_context *ev = NULL;
795         NTSTATUS status = NT_STATUS_NO_MEMORY;
796
797         if (mdscli_ctx->async_pending != 0) {
798                 status = NT_STATUS_INVALID_PARAMETER;
799                 goto fail;
800         }
801
802         ev = samba_tevent_context_init(frame);
803         if (ev == NULL) {
804                 goto fail;
805         }
806
807         req = mdscli_get_path_send(frame, ev, mdscli_ctx, cnid);
808         if (req == NULL) {
809                 goto fail;
810         }
811         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
812                 goto fail;
813         }
814
815         status = mdscli_get_path_recv(req, mem_ctx, path);
816 fail:
817         TALLOC_FREE(frame);
818         return status;
819 }
820
821 struct mdscli_close_search_state {
822         struct mdscli_search_ctx *search;
823         struct mdssvc_blob request_blob;
824         struct mdssvc_blob response_blob;
825 };
826
827 static void mdscli_close_search_done(struct tevent_req *subreq);
828
829 struct tevent_req *mdscli_close_search_send(TALLOC_CTX *mem_ctx,
830                                             struct tevent_context *ev,
831                                             struct mdscli_search_ctx **search)
832 {
833         struct mdscli_ctx *mdscli_ctx = NULL;
834         struct tevent_req *req = NULL;
835         struct mdscli_close_search_state *state = NULL;
836         struct tevent_req *subreq = NULL;
837         NTSTATUS status;
838
839         req = tevent_req_create(req, &state, struct mdscli_close_search_state);
840         if (req == NULL) {
841                 return NULL;
842         }
843         *state = (struct mdscli_close_search_state) {
844                 .search = talloc_move(state, search),
845         };
846         mdscli_ctx = state->search->mdscli_ctx;
847
848         status = mdscli_blob_close_search(state,
849                                           state->search,
850                                           &state->request_blob);
851         if (tevent_req_nterror(req, status)) {
852                 return tevent_req_post(req, ev);
853         }
854
855         state->response_blob.spotlight_blob = talloc_array(
856                 state,
857                 uint8_t,
858                 mdscli_ctx->max_fragment_size);
859         if (tevent_req_nomem(state->response_blob.spotlight_blob, req)) {
860                 return tevent_req_post(req, ev);
861         }
862         state->response_blob.size = mdscli_ctx->max_fragment_size;
863
864         subreq = dcerpc_mdssvc_cmd_send(state,
865                                         ev,
866                                         mdscli_ctx->bh,
867                                         mdscli_ctx->ph,
868                                         0,
869                                         mdscli_ctx->dev,
870                                         mdscli_ctx->mdscmd_open.unkn2,
871                                         0,
872                                         mdscli_ctx->flags,
873                                         state->request_blob,
874                                         0,
875                                         mdscli_ctx->max_fragment_size,
876                                         1,
877                                         mdscli_ctx->max_fragment_size,
878                                         0,
879                                         0,
880                                         &mdscli_ctx->mdscmd_cmd.fragment,
881                                         &state->response_blob,
882                                         &mdscli_ctx->mdscmd_cmd.unkn9);
883         if (tevent_req_nomem(subreq, req)) {
884                 return tevent_req_post(req, ev);
885         }
886         tevent_req_set_callback(subreq, mdscli_close_search_done, req);
887         mdscli_ctx->async_pending++;
888         return req;
889 }
890
891 static void mdscli_close_search_done(struct tevent_req *subreq)
892 {
893         struct tevent_req *req = tevent_req_callback_data(
894                 subreq, struct tevent_req);
895         struct mdscli_close_search_state *state = tevent_req_data(
896                 req, struct mdscli_close_search_state);
897         NTSTATUS status;
898
899         status = dcerpc_mdssvc_cmd_recv(subreq, state);
900         TALLOC_FREE(subreq);
901         state->search->mdscli_ctx->async_pending--;
902         if (tevent_req_nterror(req, status)) {
903                 return;
904         }
905
906         tevent_req_done(req);
907         return;
908 }
909
910 NTSTATUS mdscli_close_search_recv(struct tevent_req *req)
911 {
912         return tevent_req_simple_recv_ntstatus(req);
913 }
914
915 NTSTATUS mdscli_close_search(struct mdscli_search_ctx **search)
916 {
917         TALLOC_CTX *frame = talloc_stackframe();
918         struct tevent_req *req = NULL;
919         struct tevent_context *ev = NULL;
920         NTSTATUS status = NT_STATUS_NO_MEMORY;
921
922         if ((*search)->mdscli_ctx->async_pending != 0) {
923                 status = NT_STATUS_INVALID_PARAMETER;
924                 goto fail;
925         }
926
927         ev = samba_tevent_context_init(frame);
928         if (ev == NULL) {
929                 goto fail;
930         }
931
932         req = mdscli_close_search_send(frame, ev, search);
933         if (req == NULL) {
934                 goto fail;
935         }
936         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
937                 goto fail;
938         }
939
940         status = mdscli_close_search_recv(req);
941 fail:
942         TALLOC_FREE(frame);
943         return status;
944 }
945
946 struct mdscli_disconnect_state {
947         struct mdscli_ctx *mdscli_ctx;
948 };
949
950 static void mdscli_disconnect_done(struct tevent_req *subreq);
951
952 struct tevent_req *mdscli_disconnect_send(TALLOC_CTX *mem_ctx,
953                                           struct tevent_context *ev,
954                                           struct mdscli_ctx *mdscli_ctx)
955 {
956         struct tevent_req *req = NULL;
957         struct mdscli_disconnect_state *state = NULL;
958         struct tevent_req *subreq = NULL;
959
960         req = tevent_req_create(req, &state, struct mdscli_disconnect_state);
961         if (req == NULL) {
962                 return NULL;
963         }
964         *state = (struct mdscli_disconnect_state) {
965                 .mdscli_ctx = mdscli_ctx,
966         };
967
968         subreq = dcerpc_mdssvc_close_send(state,
969                                           ev,
970                                           mdscli_ctx->bh,
971                                           mdscli_ctx->ph,
972                                           0,
973                                           mdscli_ctx->dev,
974                                           mdscli_ctx->mdscmd_open.unkn2,
975                                           0,
976                                           &mdscli_ctx->ph,
977                                           &mdscli_ctx->mdscmd_close.status);
978         if (tevent_req_nomem(subreq, req)) {
979                 return tevent_req_post(req, ev);
980         }
981         tevent_req_set_callback(subreq, mdscli_disconnect_done, req);
982         mdscli_ctx->async_pending++;
983         return req;
984 }
985
986 static void mdscli_disconnect_done(struct tevent_req *subreq)
987 {
988         struct tevent_req *req = tevent_req_callback_data(
989                 subreq, struct tevent_req);
990         struct mdscli_disconnect_state *state = tevent_req_data(
991                 req, struct mdscli_disconnect_state);
992         NTSTATUS status;
993
994         status = dcerpc_mdssvc_close_recv(subreq, state);
995         TALLOC_FREE(subreq);
996         state->mdscli_ctx->async_pending--;
997         if (tevent_req_nterror(req, status)) {
998                 return;
999         }
1000
1001         tevent_req_done(req);
1002         return;
1003 }
1004
1005 NTSTATUS mdscli_disconnect_recv(struct tevent_req *req)
1006 {
1007         return tevent_req_simple_recv_ntstatus(req);
1008 }
1009
1010 NTSTATUS mdscli_disconnect(struct mdscli_ctx *mdscli_ctx)
1011 {
1012         TALLOC_CTX *frame = talloc_stackframe();
1013         struct tevent_req *req = NULL;
1014         struct tevent_context *ev = NULL;
1015         NTSTATUS status = NT_STATUS_NO_MEMORY;
1016
1017         if (mdscli_ctx->async_pending != 0) {
1018                 status = NT_STATUS_INVALID_PARAMETER;
1019                 goto fail;
1020         }
1021
1022         ev = samba_tevent_context_init(frame);
1023         if (ev == NULL) {
1024                 goto fail;
1025         }
1026
1027         req = mdscli_disconnect_send(frame, ev, mdscli_ctx);
1028         if (req == NULL) {
1029                 goto fail;
1030         }
1031         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
1032                 goto fail;
1033         }
1034
1035         status = mdscli_disconnect_recv(req);
1036 fail:
1037         TALLOC_FREE(frame);
1038         return status;
1039 }