s3/utils: Add search client
authorNoel Power <noel.power@suse.com>
Thu, 21 Jul 2016 15:53:17 +0000 (16:53 +0100)
committerAndrew Bartlett <abartlet@samba.org>
Wed, 25 Oct 2023 22:23:38 +0000 (22:23 +0000)
Simple cli client for doing a basic windows search.

example:

  wspsearch -U$(USER)%$(PASSWD) //$(SERVER)/$(SHARE) --search='DSC' --kind=Picture

Signed-off-by: Noel Power <noel.power@suse.com>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
source3/utils/wscript_build
source3/utils/wspsearch.c [new file with mode: 0644]

index 90656b3d9658c2dc34d9509878b5b84b2084adb6..7a28de857e11e2a68e68b1a1f8a09ee3ea64685a 100644 (file)
@@ -337,6 +337,23 @@ bld.SAMBA3_BINARY('mdsearch',
                  mdssvc
                  ''')
 
+bld.SAMBA3_BINARY('wspsearch',
+                 source='wspsearch.c',
+                 deps='''
+                 talloc
+                 tevent
+                 smbconf
+                 CMDLINE_S3
+                 cmdline_contexts
+                 libsmb
+                 msrpc3
+                 LIBSAMBA_WSP
+                 RPCCLI_WSP
+                 WSP_UTIL
+                 dcerpc
+                 ''',
+                  enabled=bld.env.with_wsp)
+
 pytalloc_util = bld.pyembed_libname('pytalloc-util')
 pyrpc_util = bld.pyembed_libname('pyrpc_util')
 bld.SAMBA3_PYTHON('python_net_s3',
diff --git a/source3/utils/wspsearch.c b/source3/utils/wspsearch.c
new file mode 100644 (file)
index 0000000..3650fe1
--- /dev/null
@@ -0,0 +1,820 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *
+ *  Window Search Service
+ *
+ *  Copyright (c) Noel Power
+ *
+ *  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 "lib/util/debug.h"
+#include "lib/cmdline/cmdline.h"
+#include "lib/cmdline_contexts.h"
+#include "param.h"
+#include "client.h"
+#include "libsmb/proto.h"
+#include "librpc/rpc/rpc_common.h"
+#include "librpc/wsp/wsp_util.h"
+#include "rpc_client/cli_pipe.h"
+#include "rpc_client/wsp_cli.h"
+#include "libcli/wsp/wsp_aqs.h"
+#include "librpc/gen_ndr/ndr_wsp.h"
+#include "librpc/gen_ndr/ndr_wsp_data.h"
+#include "dcerpc.h"
+
+#define WIN_VERSION_64 0x10000
+
+/* send connectin message */
+static NTSTATUS wsp_connect(TALLOC_CTX *ctx,
+                       struct wsp_client_ctx *wsp_ctx,
+                       const char* clientmachine,
+                       const char* clientuser,
+                       const char* server,
+                       bool *is_64bit)
+{
+       struct wsp_request *request = NULL;
+       struct wsp_response *response = NULL;
+       uint32_t client_ver;
+       uint32_t server_ver;
+       DATA_BLOB unread = data_blob_null;
+       NTSTATUS status;
+       TALLOC_CTX *local_ctx = talloc_new(ctx);
+
+
+       if (local_ctx == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto out;
+       }
+
+       response = talloc_zero(local_ctx, struct wsp_response);
+       if (!response) {
+               status = NT_STATUS_NO_MEMORY;
+               goto out;
+       }
+
+       request = talloc_zero(local_ctx, struct wsp_request);
+       if (!request) {
+               status = NT_STATUS_NO_MEMORY;
+               goto out;
+       }
+
+       if (!init_connectin_request(local_ctx, request,
+                              clientmachine, clientuser, server)) {
+               DBG_ERR("Failed in initialise connection message\n");
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto out;
+       }
+
+       status =  wsp_request_response(local_ctx, wsp_ctx,
+                       request, response, &unread);
+       if (NT_STATUS_IS_OK(status)) {
+               client_ver = request->message.cpmconnect.iclientversion;
+               server_ver = response->message.cpmconnect.server_version;
+               *is_64bit =
+                       (server_ver & WIN_VERSION_64)
+                       && (client_ver & WIN_VERSION_64);
+       }
+
+out:
+       data_blob_free(&unread);
+       TALLOC_FREE(local_ctx);
+       return status;
+}
+
+static NTSTATUS create_query(TALLOC_CTX *ctx,
+                            struct wsp_client_ctx *wsp_ctx,
+                            uint32_t limit,
+                            t_select_stmt *select,
+                            uint32_t *single_cursor)
+{
+       struct wsp_request *request = NULL;
+       struct wsp_response *response = NULL;
+       NTSTATUS status;
+       DATA_BLOB unread = data_blob_null;
+       TALLOC_CTX *local_ctx = talloc_new(ctx);
+
+       if (local_ctx == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto out;
+       }
+
+       request = talloc_zero(local_ctx, struct wsp_request);
+       if (!request) {
+               status = NT_STATUS_NO_MEMORY;
+               goto out;
+       }
+
+       response = talloc_zero(local_ctx, struct wsp_response);
+       if (!response) {
+               status = NT_STATUS_NO_MEMORY;
+               goto out;;
+       }
+
+       if (!create_querysearch_request(ctx, request, select)) {
+               DBG_ERR("error setting up query request message\n");
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto out;
+       }
+
+       request->message.cpmcreatequery.rowsetproperties.cmaxresults = limit;
+
+       status = wsp_request_response(local_ctx,
+                       wsp_ctx,
+                       request,
+                       response,
+                       &unread);
+       if (NT_STATUS_IS_OK(status)) {
+               if (unread.length == 4) {
+                       *single_cursor = IVAL(unread.data, 0);
+               }
+       }
+
+out:
+       data_blob_free(&unread);
+       TALLOC_FREE(local_ctx);
+       return status;
+}
+
+static NTSTATUS create_bindings(TALLOC_CTX *ctx,
+                               struct wsp_client_ctx *wsp_ctx,
+                               t_select_stmt *select,
+                               uint32_t cursor,
+                               struct wsp_cpmsetbindingsin *bindings_out,
+                               bool is_64bit)
+{
+       struct wsp_request *request = NULL;
+       struct wsp_response *response = NULL;
+       NTSTATUS status;
+       DATA_BLOB unread = data_blob_null;
+
+       request = talloc_zero(ctx, struct wsp_request);
+       if (!request) {
+               status = NT_STATUS_NO_MEMORY;
+               goto out;
+       }
+
+       response = talloc_zero(ctx, struct wsp_response);
+       if (!response) {
+               status = NT_STATUS_NO_MEMORY;
+               goto out;
+       }
+
+       if (!create_setbindings_request(ctx,
+                               request,
+                               select,
+                               cursor,
+                               is_64bit)) {
+               DBG_ERR("Failed to create setbindings message\n");
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto out;
+       }
+
+       status = wsp_request_response(ctx,
+                       wsp_ctx,
+                       request,
+                       response,
+                       &unread);
+       if (NT_STATUS_IS_OK(status)) {
+               *bindings_out = request->message.cpmsetbindings;
+       }
+
+out:
+       data_blob_free(&unread);
+       return status;
+}
+
+static NTSTATUS create_querystatusex(TALLOC_CTX *ctx,
+                               struct wsp_client_ctx *wsp_ctx,
+                               uint32_t cursor,
+                               uint32_t *nrows)
+{
+       struct wsp_request *request = NULL;
+       struct wsp_response *response = NULL;
+       struct wsp_cpmgetquerystatusexin *statusexin = NULL;
+       NTSTATUS status;
+       DATA_BLOB unread = data_blob_null;
+       TALLOC_CTX *local_ctx = talloc_new(ctx);
+
+       if (local_ctx == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto out;
+       }
+
+       request = talloc_zero(local_ctx, struct wsp_request);
+       if (!request) {
+               status = NT_STATUS_NO_MEMORY;
+               goto out;
+       }
+
+       response = talloc_zero(local_ctx, struct wsp_response);
+       if (!response) {
+               status = NT_STATUS_NO_MEMORY;
+               goto out;
+       }
+
+       statusexin = &request->message.cpmgetquerystatusex;
+
+       request->header.msg = CPMGETQUERYSTATUSEX;
+       statusexin->hcursor = cursor;
+       statusexin->bmk = 0xfffffffc;
+       status = wsp_request_response(local_ctx,
+                       wsp_ctx,
+                       request,
+                       response,
+                       &unread);
+       if (NT_STATUS_IS_OK(status)) {
+               *nrows = response->message.cpmgetquerystatusex.resultsfound;
+       }
+
+out:
+       data_blob_free(&unread);
+       TALLOC_FREE(local_ctx);
+       return status;
+}
+
+static NTSTATUS print_rowsreturned(
+                               TALLOC_CTX *ctx,
+                               DATA_BLOB *buffer,
+                               bool is_64bit,
+                               bool disp_all_cols,
+                               struct wsp_cpmsetbindingsin *bindings,
+                               uint32_t cbreserved,
+                               uint64_t address,
+                               uint32_t rowsreturned,
+                               uint32_t *rows_processed)
+{
+       NTSTATUS status;
+       int row;
+       TALLOC_CTX *local_ctx = NULL;
+       struct wsp_cbasestoragevariant **rowsarray = NULL;
+       enum ndr_err_code err;
+
+       local_ctx = talloc_init("results");
+       if (local_ctx == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto out;
+       }
+
+       rowsarray = talloc_zero_array(local_ctx,
+                       struct wsp_cbasestoragevariant*,
+                       rowsreturned);
+       if (rowsarray == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto out;
+       }
+
+       err = extract_rowsarray(rowsarray,
+                       buffer,
+                       is_64bit,
+                       bindings,
+                       cbreserved,
+                       address,
+                       rowsreturned,
+                       rowsarray);
+       if (err) {
+               DBG_ERR("failed to extract rows from getrows response\n");
+               status = NT_STATUS_UNSUCCESSFUL;
+               goto out;
+       }
+
+       for(row = 0; row < rowsreturned; row++) {
+               TALLOC_CTX *row_ctx = NULL;
+               const char *col_str = NULL;
+
+               row_ctx = talloc_init("row");
+               if (row_ctx == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
+                       goto out;
+               }
+
+               if (disp_all_cols) {
+                       int i;
+                       for (i = 0; i < bindings->ccolumns; i++){
+                               col_str =
+                                       variant_as_string(
+                                               row_ctx,
+                                               &rowsarray[row][i],
+                                               true);
+                               if (col_str) {
+                                       printf("%s%s",
+                                               i ? ", " : "", col_str);
+                               } else {
+                                       printf("%sN/A",
+                                               i ? ", " : "");
+                               }
+                       }
+               } else {
+                       col_str = variant_as_string(
+                                       row_ctx,
+                                       &rowsarray[row][0],
+                                       true);
+                       printf("%s", col_str);
+               }
+               printf("\n");
+               TALLOC_FREE(row_ctx);
+       }
+       status = NT_STATUS_OK;
+out:
+       TALLOC_FREE(local_ctx);
+       *rows_processed = row;
+       return status;
+}
+
+static NTSTATUS create_getrows(TALLOC_CTX *ctx,
+                              struct wsp_client_ctx *wsp_ctx,
+                              struct wsp_cpmsetbindingsin *bindings,
+                              uint32_t cursor,
+                              uint32_t nrows,
+                              bool disp_all_cols,
+                              bool is_64bit)
+{
+       struct wsp_request *request = NULL;
+       struct wsp_response *response = NULL;
+       NTSTATUS status;
+       DATA_BLOB unread = data_blob_null;
+       uint32_t bmk = 0xfffffffc;
+       uint32_t skip = 0;
+       uint32_t total_rows = 0;
+       uint32_t INITIAL_ROWS = 32;
+       uint32_t requested_rows = INITIAL_ROWS;
+       uint32_t rows_printed;
+       uint32_t current_row = 0;
+       TALLOC_CTX *row_ctx;
+       bool loop_again;
+
+       do {
+               row_ctx = talloc_new(NULL);
+               if (!row_ctx) {
+                       status = NT_STATUS_UNSUCCESSFUL;
+                       goto out;
+               }
+               request = talloc_zero(row_ctx, struct wsp_request);
+               if (!request) {
+                       status = NT_STATUS_NO_MEMORY;
+                       goto out;
+               }
+               response = talloc_zero(row_ctx, struct wsp_response);
+               if (!response) {
+                       status = NT_STATUS_NO_MEMORY;
+                       goto out;
+               }
+
+               create_seekat_getrows_request(request,
+                                       request,
+                                       cursor,
+                                       bmk,
+                                       skip,
+                                       requested_rows,
+                                       40,
+                                       0xDEAbd860,
+                                       bindings->brow,
+                                       0);
+
+               status = wsp_request_response(request,
+                               wsp_ctx,
+                               request,
+                               response,
+                               &unread);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto out;
+               }
+
+               total_rows += response->message.cpmgetrows.rowsreturned;
+               if (response->message.cpmgetrows.rowsreturned
+                  != requested_rows) {
+                       uint32_t rowsreturned =
+                               response->message.cpmgetrows.rowsreturned;
+                       if (response->message.cpmgetrows.etype == EROWSEEKAT) {
+                               struct wsp_cpmgetrowsout *resp;
+                               struct wsp_crowseekat *seekat;
+                               resp = &response->message.cpmgetrows;
+                               seekat =
+                                       &resp->seekdescription.crowseekat;
+                               bmk = seekat->bmkoffset;
+                               skip = seekat->cskip;
+                       } else {
+                               bmk = 0xfffffffc;
+                               skip = total_rows;
+                       }
+                       requested_rows = requested_rows - rowsreturned;
+               } else {
+                       requested_rows = INITIAL_ROWS;
+                       bmk = 0xfffffffc;
+                       skip = total_rows;
+               }
+
+               if (response->message.cpmgetrows.rowsreturned) {
+                       status = print_rowsreturned(row_ctx, &unread,
+                               is_64bit,
+                               disp_all_cols,
+                               bindings, 40,
+                               0xDEAbd860,
+                               response->message.cpmgetrows.rowsreturned,
+                               &rows_printed);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               goto out;
+                       }
+                       current_row += rows_printed;
+                       data_blob_free(&unread);
+               }
+
+               /*
+                * response is a talloc child of row_ctz so we need to
+                * assign loop_again before we delete row_ctx
+                */
+               loop_again = response->message.cpmgetrows.rowsreturned;
+
+               TALLOC_FREE(row_ctx);
+               if (nrows && total_rows > nrows) {
+                       DBG_ERR("Something is wrong, results returned %d "
+                               "exceed expected number of results %d\n",
+                               total_rows, nrows);
+                       status = NT_STATUS_UNSUCCESSFUL;
+                       goto out;
+               }
+       } while (loop_again);
+out:
+       data_blob_free(&unread);
+       TALLOC_FREE(row_ctx);
+       return status;
+}
+
+const char *default_column = "System.ItemUrl";
+
+static bool is_valid_kind(const char *kind)
+{
+       const char* kinds[] = {"calendar",
+               "communication",
+               "contact",
+               "document",
+               "email",
+               "feed",
+               "folder",
+               "game",
+               "instantMessage",
+               "journal",
+               "link",
+               "movie",
+               "music",
+               "note",
+               "picture",
+               "program",
+               "recordedtv",
+               "searchfolder",
+               "task",
+               "video",
+               "webhistory"};
+       char* search_kind = NULL;
+       int i;
+       bool found = false;
+
+       search_kind = strlower_talloc(NULL, kind);
+       if (search_kind == NULL) {
+               DBG_ERR("couldn't convert %s to lower case\n",
+                               kind);
+               return NULL;
+       }
+
+       for (i=0; i<ARRAY_SIZE(kinds); i++) {
+               if (strequal(search_kind, kinds[i])) {
+                       found = true;
+                       break;
+               }
+       }
+
+       if (found == false) {
+               DBG_ERR("Invalid kind %s\n", kind);
+       }
+       TALLOC_FREE(search_kind);
+       return found;
+}
+
+static char * build_default_sql(TALLOC_CTX *ctx,
+                               const char *kind,
+                               const char *phrase,
+                               const char *location)
+{
+       char *sql = NULL;
+       /* match what windows clients do */
+       sql = talloc_asprintf(ctx,
+               "Scope:\"%s\"  AND NOT System.Shell.SFGAOFlagsStrings:hidden"
+               "  AND NOT System.Shell.OmitFromView:true", location);
+
+       if (kind) {
+               if (!is_valid_kind(kind)) {
+                       return NULL;
+               }
+               sql = talloc_asprintf(ctx, "System.Kind:%s AND %s",
+                                       kind, sql);
+       }
+
+       if (phrase) {
+               sql = talloc_asprintf(ctx,
+                               "All:$=\"%s\" OR All:$<\"%s\""
+                               " AND %s", phrase, phrase, sql);
+       }
+       sql =  talloc_asprintf(ctx, "SELECT %s"
+                               " WHERE %s", default_column, sql);
+       return sql;
+}
+
+int main(int argc, char **argv)
+{
+       int opt;
+       int result = 0;
+       NTSTATUS status = NT_STATUS_OK;
+       poptContext pc;
+       char* server = NULL;
+       char* share = NULL;
+       char* path = NULL;
+       char* location = NULL;
+       char* query = NULL;
+       bool custom_query = false;
+       const char* phrase = NULL;
+       const char* kind = NULL;
+       uint32_t limit = 500;
+       uint32_t nrows = 0;
+       struct wsp_cpmsetbindingsin bindings_used = {0};
+       bool is_64bit = false;
+       struct poptOption long_options[] = {
+                POPT_AUTOHELP
+               { "limit",
+                       0,
+                       POPT_ARG_INT,
+                       &limit,
+                       0,
+                       "limit results",
+                       "default is 500, specifying 0 means unlimited" },
+               { "search",
+                       0,
+                       POPT_ARG_STRING,
+                       &phrase,
+                       0,
+                       "Search phrase",
+                       "phrase" },
+               { "kind", 0, POPT_ARG_STRING, &kind, 0,
+                       "Kind of thing to search for [Calendar|Communication|"
+                       "Contact|Document|Email|Feed|Folder|Game|"
+                       "InstantMessage|Journal|Link|Movie|Music|Note|Picture|"
+                       "Program|RecordedTV|SearchFolder|Task|Video"
+                       "|WebHistory]",
+                       "kind" },
+               { "query",
+                       0,
+                       POPT_ARG_STRING,
+                       &query,
+                       0,
+                       "specify a more complex query",
+                       "query" },
+                POPT_COMMON_SAMBA
+                POPT_COMMON_CONNECTION
+                POPT_COMMON_CREDENTIALS
+                POPT_TABLEEND
+       };
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct tevent_context *ev_ctx
+               =  samba_tevent_context_init(talloc_tos());
+       uint32_t cursor = 0;
+       struct wsp_client_ctx *wsp_ctx = NULL;
+       t_select_stmt *select_stmt = NULL;
+       const char **const_argv = discard_const_p(const char *, argv);
+       struct dcerpc_binding_handle *h = NULL;
+       struct cli_state *c = NULL;
+       uint32_t flags = CLI_FULL_CONNECTION_IPC;
+
+       samba_cmdline_init(frame,
+                          SAMBA_CMDLINE_CONFIG_CLIENT,
+                          false /* require_smbconf */);
+
+       pc = samba_popt_get_context("wspsearch",
+                       argc,
+                       const_argv,
+                       long_options,
+                       0);
+       poptSetOtherOptionHelp(pc, "[OPTIONS] //server1/share1");
+
+       while ((opt = poptGetNextOpt(pc)) != -1) ;
+
+       if(!poptPeekArg(pc)) {
+               poptPrintUsage(pc, stderr, 0);
+               result = -1;
+               goto out;
+       }
+
+       path = talloc_strdup(talloc_tos(), poptGetArg(pc));
+       if (!path || limit < 0) {
+               DBG_ERR("Invalid argument\n");
+               result = -1;
+               goto out;
+       }
+
+       string_replace(path,'/','\\');
+       server = talloc_strdup(talloc_tos(), path+2);
+       if (!server) {
+               DBG_ERR("Invalid argument\n");
+               return -1;
+       }
+
+       if (server) {
+               /*
+                * if we specify --query then we don't need actually need the
+                * share part, if it is specified then we don't care as we
+                * expect the scope to be part of the query (and if it isn't
+                * then it will probably fail anyway)
+                */
+               share = strchr_m(server,'\\');
+               if (!query && !share) {
+                       DBG_ERR("Invalid argument\n");
+                       return -1;
+               }
+               if (share) {
+                       *share = 0;
+                       share++;
+               }
+       }
+
+       DBG_INFO("server name is %s\n", server ? server : "N/A");
+       DBG_INFO("share name is %s\n", share ? share : "N/A");
+       DBG_INFO("search phrase is %s\n", phrase ? phrase : "N/A");
+       DBG_INFO("search kind is %s\n", kind ? kind : "N/A");
+
+       if (!query && (kind == NULL && phrase == NULL)) {
+               poptPrintUsage(pc, stderr, 0);
+               result = -1;
+               goto out;
+       }
+
+       if (!query) {
+               location = talloc_asprintf(talloc_tos(),
+                               "FILE://%s/%s", server, share);
+               query = build_default_sql(talloc_tos(), kind, phrase, location);
+               if (!query) {
+                       result = -1;
+                       goto out;
+               }
+       } else {
+               custom_query = true;
+       }
+
+       printf("custom_query %d\n", custom_query);
+       select_stmt = get_wsp_sql_tree(query);
+
+       poptFreeContext(pc);
+
+       if (select_stmt == NULL) {
+               DBG_ERR("query failed\n");
+               result = -1;
+               goto out;
+       }
+
+       if (select_stmt->cols == NULL) {
+               select_stmt->cols = talloc_zero(select_stmt, t_col_list);
+               if (select_stmt->cols == NULL) {
+                       DBG_ERR("out of memory\n");
+                       result = -1;
+                       goto out;
+               }
+               select_stmt->cols->num_cols = 1;
+               select_stmt->cols->cols =
+                       talloc_zero_array(select_stmt->cols, char*, 1);
+               if (select_stmt->cols->cols == NULL) {
+                       DBG_ERR("out of memory\n");
+                       result = -1;
+                       goto out;
+               }
+               select_stmt->cols->cols[0] =
+                       talloc_strdup(select_stmt->cols, default_column);
+       }
+
+       status =  cli_full_connection_creds(&c,
+                                  lp_netbios_name(),
+                                  server,
+                                  NULL,
+                                  0,
+                                  "IPC$",
+                                  "IPC",
+                                  samba_cmdline_get_creds(),
+                                  flags);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("failed to connect to IPC$: %s\n",
+                     nt_errstr(status));
+               result = -1;
+               goto out;
+       }
+
+       status = wsp_server_connect(talloc_tos(),
+                       server,
+                       ev_ctx,
+                       samba_cmdline_get_lp_ctx(),
+                       samba_cmdline_get_creds(),
+                       c,
+                       &wsp_ctx);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("failed to connect to wsp: %s\n",
+                     nt_errstr(status));
+               result = -1;
+               goto out;
+       }
+
+       h = get_wsp_pipe(wsp_ctx);
+       if (h == NULL) {
+               DBG_ERR("Failed to communicate with server, no pipe\n");
+               result = -1;
+               goto out;
+       }
+
+       dcerpc_binding_handle_set_timeout(h,
+                                         DCERPC_REQUEST_TIMEOUT * 1000);
+
+       /* connect */
+       DBG_INFO("sending connect\n");
+       status = wsp_connect(talloc_tos(),
+                        wsp_ctx,
+                        lpcfg_netbios_name(samba_cmdline_get_lp_ctx()),
+                        cli_credentials_get_username(
+                                samba_cmdline_get_creds()),
+                        server,
+                        &is_64bit);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("failed to connect to wsp: %s\n",
+                     nt_errstr(status));
+               result = -1;
+               goto out;
+       }
+
+       DBG_INFO("sending query\n");
+
+       status = create_query(talloc_tos(),
+                       wsp_ctx,
+                       limit,
+                       select_stmt,
+                       &cursor);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("failed to send query: %s)\n",
+                     nt_errstr(status));
+               result = -1;
+               goto out;
+       }
+
+       DBG_INFO("sending createbindings\n");
+       /* set bindings */
+       status = create_bindings(talloc_tos(),
+                       wsp_ctx,
+                       select_stmt,
+                       cursor,
+                       &bindings_used,
+                       is_64bit);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("failed to setbindings: %s)\n",
+                     nt_errstr(status));
+               result = -1;
+               goto out;
+       }
+
+       status = create_querystatusex(talloc_tos(),
+                                     wsp_ctx,
+                                     bindings_used.hcursor,
+                                     &nrows);
+       if (!nrows) {
+               result = 0;
+               DBG_ERR("no results found\n");
+               goto out;
+       }
+
+       printf("found %d results, returning %d \n",
+                       nrows,
+                       limit ? MIN(nrows, limit) : nrows);
+       status = create_getrows(talloc_tos(),
+                               wsp_ctx,
+                               &bindings_used,
+                               bindings_used.hcursor,
+                               limit ? MIN(nrows, limit) : nrows,
+                               custom_query,
+                               is_64bit);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("Failed to retrieve rows, error: %s\n",
+                       nt_errstr(status));
+               result = -1;
+               goto out;
+       }
+       result = 0;
+out:
+       TALLOC_FREE(frame);
+       return result;
+}