util:datablob: data_blob_pad checks its alignment assumption
[samba.git] / source3 / rpc_client / wsp_cli.c
blob20c054c14ca37de9900ec40565ca44c1b151953e
1 /*
2 * Unix SMB/CIFS implementation.
4 * Window Search Service
6 * Copyright (c) Noel Power
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "client.h"
24 #include "rpc_client/wsp_cli.h"
25 #include "rpc_client/rpc_client.h"
26 #include "param/param.h"
27 #include "auth/credentials/credentials.h"
28 #include <tevent.h>
29 #include <util/tevent_ntstatus.h>
30 #include "libcli/tstream_binding_handle/tstream_binding_handle.h"
31 #include "lib/tsocket/tsocket.h"
32 #include "librpc/wsp/wsp_util.h"
33 #include "librpc/gen_ndr/ndr_wsp.h"
34 #include "rpc_client/cli_pipe.h"
35 #include "libcli/smb/smbXcli_base.h"
37 #define MSG_HDR_SIZE 16
38 #define USED 1
40 * 32-bit Windows XP operating system, 32-bit Windows Server 2003 operating
41 * system, 32-bit Windows Home Server server software, 32-bit Windows Vista
42 * with Windows Search 4.0, 32-bit Windows Server 2003 with Windows
43 * Search 4.0. All of these versions of Windows are running
44 * Windows Search 4.0.
47 static const uint32_t CLIENTVERSION = 0x00010700;
50 * DBPROP_CI_SCOPE_FLAGS
51 * containing QUERY_DEEP
52 * QUERY_DEEP (0x1) indicates that files in the scope directory and all
53 * subdirectories are included in the results. If clear, only files in
54 * the scope directory are included in the results.
56 static int32_t scope_flags_vector[] = {0x00000001};
58 * Search everywhere "\\" is the root scope
60 static const char * root_scope_string_vector[] = {"\\"};
62 /* sets sensible defaults */
63 static void init_wsp_prop(struct wsp_cdbprop *prop)
65 *prop = (struct wsp_cdbprop){0};
66 prop->colid.ekind = DBKIND_GUID_PROPID;
70 static bool create_restriction_array(TALLOC_CTX *ctx,
71 struct wsp_crestriction **pelements,
72 uint32_t nnodes)
74 struct wsp_crestriction *elements = talloc_zero_array(ctx,
75 struct wsp_crestriction,
76 nnodes);
77 if (elements == NULL) {
78 return false;
80 *pelements = elements;
81 return true;
85 static bool create_noderestriction(TALLOC_CTX *ctx,
86 struct wsp_cnoderestriction *pnode,
87 uint32_t nnodes)
89 bool ok;
90 pnode->cnode = nnodes;
91 ok = create_restriction_array(ctx, &pnode->panode, nnodes);
92 return ok;
95 static bool fill_sortarray(TALLOC_CTX *ctx, struct wsp_csort **dest,
96 struct wsp_csort *src, uint32_t num)
98 uint32_t i;
99 struct wsp_csort *psort = talloc_zero_array(ctx, struct wsp_csort,
100 num);
101 if (psort == NULL) {
102 return false;
104 for (i = 0; i < num; i++) {
105 psort[i] = src[i];
107 *dest = psort;
108 return true;
113 static bool set_fullpropspec(TALLOC_CTX *ctx, struct wsp_cfullpropspec *prop,
114 const char* propname, uint32_t kind)
116 struct GUID guid = {0};
117 const struct full_propset_info *prop_info = NULL;
119 prop_info = get_propset_info_with_guid(propname, &guid);
120 if (!prop_info) {
121 DBG_ERR("Failed to handle property named %s\n",
122 propname);
123 return false;
125 prop->guidpropset = guid;
126 prop->ulkind = kind;
127 if (kind == PRSPEC_LPWSTR) {
128 prop->name_or_id.propname.vstring = talloc_strdup(ctx,
129 propname);
130 if (prop->name_or_id.propname.vstring == NULL) {
131 DBG_ERR("out of memory");
132 return false;
134 prop->name_or_id.propname.len = strlen(propname);
135 } else {
136 prop->name_or_id.prspec = prop_info->id;
138 return true;
141 struct binding
143 uint32_t status_off;
144 uint32_t value_off;
145 uint32_t len_off;
148 static bool set_ctablecolumn(TALLOC_CTX *ctx, struct wsp_ctablecolumn *tablecol,
149 const char* propname, struct binding *offsets,
150 uint32_t value_size)
152 struct wsp_cfullpropspec *prop = &tablecol->propspec;
154 if (!set_fullpropspec(ctx, prop, propname, PRSPEC_PROPID)) {
155 return false;
157 tablecol->vtype =VT_VARIANT ;
158 tablecol->aggregateused = USED;
159 tablecol->valueused = USED;
160 tablecol->valueoffset.value = offsets->value_off;
161 tablecol->valuesize.value = value_size;
162 tablecol->statusused = USED;
163 tablecol->statusoffset.value = offsets->status_off;
164 tablecol->lengthused = USED;
165 tablecol->lengthoffset.value = offsets->len_off;
166 return true;
170 static bool fill_uint32_vec(TALLOC_CTX* ctx,
171 uint32_t **pdest,
172 uint32_t* ivector, uint32_t elems)
174 uint32_t i;
175 uint32_t *dest = talloc_zero_array(ctx, uint32_t, elems);
176 if (dest == NULL) {
177 return false;
180 for ( i = 0; i < elems; i++ ) {
181 dest[ i ] = ivector[ i ];
183 *pdest = dest;
184 return true;
187 static bool init_propset1(TALLOC_CTX* tmp_ctx,
188 struct wsp_cdbpropset *propertyset)
190 uint32_t i;
191 GUID_from_string(DBPROPSET_FSCIFRMWRK_EXT,
192 &propertyset->guidpropertyset);
194 propertyset->cproperties = 4;
195 propertyset->aprops =
196 talloc_zero_array(tmp_ctx, struct wsp_cdbprop,
197 propertyset->cproperties);
198 if (propertyset->aprops == NULL) {
199 return false;
202 /* initialise first 4 props */
203 for( i = 0; i < propertyset->cproperties; i++) {
204 init_wsp_prop(&propertyset->aprops[i]);
208 * see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
209 * and also as seen in various windows network traces
210 * set value prop[0] - 'catalog to search'
213 propertyset->aprops[0].dbpropid = DBPROP_CI_CATALOG_NAME;
214 /* The name of the Catalog to Query */
215 set_variant_lpwstr(tmp_ctx, &propertyset->aprops[0].vvalue,
216 "Windows\\SystemIndex");
218 * set value prop[1] 'Regular Query'
221 propertyset->aprops[1].dbpropid = DBPROP_CI_QUERY_TYPE;
222 set_variant_i4(tmp_ctx, &propertyset->aprops[1].vvalue,
223 CINORMAL);
226 * set value prop[2] 'search subfolders'
228 propertyset->aprops[2].dbpropid = DBPROP_CI_SCOPE_FLAGS;
229 set_variant_i4_vector(tmp_ctx, &propertyset->aprops[2].vvalue,
230 scope_flags_vector, ARRAY_SIZE(scope_flags_vector));
233 * set value prop[3] 'root scope'
235 propertyset->aprops[3].dbpropid = DBPROP_CI_INCLUDE_SCOPES;
236 set_variant_lpwstr_vector(tmp_ctx,
237 &propertyset->aprops[3].vvalue,
238 root_scope_string_vector,
239 ARRAY_SIZE(root_scope_string_vector));
240 return true;
243 static bool init_propset2(TALLOC_CTX* tmp_ctx,
244 struct wsp_cdbpropset *propertyset,
245 const char* server)
247 uint32_t i;
249 GUID_from_string(DBPROPSET_CIFRMWRKCORE_EXT,
250 &propertyset->guidpropertyset);
252 propertyset->cproperties = 1;
253 propertyset->aprops =
254 talloc_zero_array(tmp_ctx, struct wsp_cdbprop,
255 propertyset->cproperties);
256 if (propertyset->aprops == NULL) {
257 return false;
260 /* initialise first 1 props */
261 for( i = 0; i < propertyset->cproperties; i++) {
262 init_wsp_prop(&propertyset->aprops[i]);
266 * see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
267 * and also as seen in various windows network traces
268 * set value prop[0] - 'machines to search'
270 propertyset->aprops[0].dbpropid = DBPROP_MACHINE;
271 set_variant_bstr(tmp_ctx, &propertyset->aprops[0].vvalue,
272 server);
273 return true;
276 static bool init_apropset0(TALLOC_CTX* tmp_ctx,
277 struct wsp_cdbpropset *propertyset)
279 uint32_t i;
281 GUID_from_string(DBPROPSET_MSIDXS_ROWSETEXT,
282 &propertyset->guidpropertyset);
284 propertyset->cproperties = 7;
285 propertyset->aprops =
286 talloc_zero_array(tmp_ctx, struct wsp_cdbprop,
287 propertyset->cproperties);
288 if (propertyset->aprops == NULL) {
289 return false;
292 /* initialise props */
293 for( i = 0; i < propertyset->cproperties; i++) {
294 init_wsp_prop(&propertyset->aprops[i]);
298 * see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
299 * set value prop[0]
300 * MSIDXSPROP_ROWSETQUERYSTATUS - 'ignored'
302 propertyset->aprops[0].dbpropid = MSIDXSPROP_ROWSETQUERYSTATUS;
303 set_variant_i4(tmp_ctx, &propertyset->aprops[0].vvalue, 0x00000000);
306 * set value prop[1]
307 * MSIDXSPROP_COMMAND_LOCALE_STRING - 'EN'
309 propertyset->aprops[1].dbpropid = MSIDXSPROP_COMMAND_LOCALE_STRING;
310 set_variant_bstr(tmp_ctx, &propertyset->aprops[1].vvalue,
311 "en-us");
314 * set value prop[2]
315 * MSIDXSPROP_QUERY_RESTRICTION - 'ignored'
317 propertyset->aprops[2].dbpropid = MSIDXSPROP_QUERY_RESTRICTION;
318 set_variant_bstr(tmp_ctx, &propertyset->aprops[2].vvalue,
319 "");
322 * set value prop[3]
323 * MSIDXSPROP_PARSE_TREE - 'ignored'
325 propertyset->aprops[3].dbpropid = MSIDXSPROP_PARSE_TREE;
326 set_variant_bstr(tmp_ctx, &propertyset->aprops[3].vvalue,
327 "");
330 * set value prop[4]
331 * MSIDXSPROP_MAX_RANK - 'ignored'
333 propertyset->aprops[4].dbpropid = MSIDXSPROP_MAX_RANK;
334 set_variant_i4(tmp_ctx, &propertyset->aprops[4].vvalue, 0x00000000);
337 * set value prop[5]
338 * MSIDXSPROP_RESULTS_FOUND - 'ignored'
340 propertyset->aprops[5].dbpropid = MSIDXSPROP_RESULTS_FOUND;
341 set_variant_i4(tmp_ctx, &propertyset->aprops[5].vvalue, 0x00000000);
344 * set value prop[6]
345 * ? - '' (unknown property id)
347 propertyset->aprops[6].dbpropid = 0x00000008;
348 set_variant_i4(tmp_ctx, &propertyset->aprops[6].vvalue, 0x00000000);
349 return true;
352 static bool init_apropset1(TALLOC_CTX* tmp_ctx,
353 struct wsp_cdbpropset *propertyset)
355 uint32_t i;
356 GUID_from_string(DBPROPSET_QUERYEXT,
357 &propertyset->guidpropertyset);
359 propertyset->cproperties = 11;
360 propertyset->aprops =
361 talloc_zero_array(tmp_ctx, struct wsp_cdbprop,
362 propertyset->cproperties);
363 if (propertyset->aprops == NULL) {
364 return false;
367 /* init properties */
368 for( i = 0; i < propertyset->cproperties; i++) {
369 init_wsp_prop(&propertyset->aprops[i]);
373 * see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
374 * set value prop[0]
375 * DBPROP_USECONTENTINDEX - 'forced use of the full text index
376 * is false.'
378 propertyset->aprops[0].dbpropid = DBPROP_USECONTENTINDEX;
379 set_variant_vt_bool(tmp_ctx, &propertyset->aprops[0].vvalue, false);
382 * set value prop[1]
383 * DBPROP_DEFERNONINDEXEDTRIMMING - 'trimming of security
384 * results will not be deferred'
386 propertyset->aprops[1].dbpropid = DBPROP_DEFERNONINDEXEDTRIMMING;
387 set_variant_vt_bool(tmp_ctx, &propertyset->aprops[1].vvalue, false);
390 * set value prop[2]
391 * DBPROP_USEEXTENDEDDBTYPES - 'extended DB types are not used'
393 propertyset->aprops[2].dbpropid = DBPROP_USEEXTENDEDDBTYPES;
394 set_variant_vt_bool(tmp_ctx, &propertyset->aprops[2].vvalue, false);
397 * set value prop[3]
398 * DBPROP_IGNORENOISEONLYCLAUSES = 'full text clauses consisting
399 * entirely of noise words will
400 * result in an error being returned'
402 propertyset->aprops[3].dbpropid = DBPROP_IGNORENOISEONLYCLAUSES;
403 set_variant_vt_bool(tmp_ctx, &propertyset->aprops[3].vvalue, false);
406 * set value prop[4]
407 * DBPROP_GENERICOPTIONS_STRING - 'no generic options set'
409 propertyset->aprops[4].dbpropid = DBPROP_GENERICOPTIONS_STRING;
410 set_variant_bstr(tmp_ctx, &propertyset->aprops[4].vvalue, "");
413 * set value prop[5]
414 * DBPROP_DEFERCATALOGVERIFICATION - 'catalog verification is not
415 * deferred.'
417 propertyset->aprops[5].dbpropid = DBPROP_DEFERCATALOGVERIFICATION;
418 set_variant_vt_bool(tmp_ctx, &propertyset->aprops[5].vvalue, false);
421 * set value prop[6]
422 * DBPROP_IGNORESBRI - 'query can use the sort-by-rank index
423 * optimization'
425 propertyset->aprops[6].dbpropid = DBPROP_IGNORESBRI;
426 set_variant_vt_bool(tmp_ctx, &propertyset->aprops[6].vvalue, false);
429 * set value prop[7]
430 * DBPROP_GENERATEPARSETREE - 'a parse tree is not generated for
431 * debugging.'
433 propertyset->aprops[7].dbpropid = DBPROP_GENERATEPARSETREE;
434 set_variant_vt_bool(tmp_ctx, &propertyset->aprops[7].vvalue, false);
437 * set value prop[8]
438 * DBPROP_FREETEXTANYTERM - 'all terms from a FREETEXT clause
439 * appear in every matching document'
441 propertyset->aprops[8].dbpropid = DBPROP_FREETEXTANYTERM;
442 set_variant_vt_bool(tmp_ctx, &propertyset->aprops[8].vvalue, false);
444 * set value prop[9]
445 * DBPROP_FREETEXTUSESTEMMING - 'stemming is not used when interpreting
446 * a FREETEXT clause'
448 propertyset->aprops[9].dbpropid = DBPROP_FREETEXTUSESTEMMING;
449 set_variant_vt_bool(tmp_ctx, &propertyset->aprops[9].vvalue, false);
452 * set value prop[10]
453 * ? - ''
455 propertyset->aprops[10].dbpropid = 0x0000000f; /* ??? */
456 set_variant_vt_bool(tmp_ctx, &propertyset->aprops[10].vvalue, false);
457 return true;
460 static bool init_apropset2(TALLOC_CTX* tmp_ctx,
461 struct wsp_cdbpropset *propertyset,
462 const char* server)
464 uint32_t i;
465 GUID_from_string(DBPROPSET_CIFRMWRKCORE_EXT,
466 &propertyset->guidpropertyset);
468 propertyset->cproperties = 1;
469 propertyset->aprops =
470 talloc_zero_array(tmp_ctx, struct wsp_cdbprop,
471 propertyset->cproperties);
472 if (propertyset->aprops == NULL) {
473 return false;
476 /* init properties */
477 for( i = 0; i < propertyset->cproperties; i++) {
478 init_wsp_prop(&propertyset->aprops[i]);
482 * see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
483 * and also as seen in various windows network traces
484 * set value prop[0]
485 * DBPROP_MACHINE - 'target server'
487 propertyset->aprops[0].dbpropid = DBPROP_MACHINE;
488 set_variant_bstr(tmp_ctx, &propertyset->aprops[0].vvalue, server);
489 return true;
493 static bool init_apropset3(TALLOC_CTX* tmp_ctx,
494 struct wsp_cdbpropset *propertyset)
496 uint32_t i;
498 GUID_from_string(DBPROPSET_FSCIFRMWRK_EXT,
499 &propertyset->guidpropertyset);
501 propertyset->cproperties = 3;
502 propertyset->aprops =
503 talloc_zero_array(tmp_ctx, struct wsp_cdbprop,
504 propertyset->cproperties);
505 if (propertyset->aprops == NULL) {
506 return false;
509 /* init properties */
510 for( i = 0; i < propertyset->cproperties; i++) {
511 init_wsp_prop(&propertyset->aprops[i]);
515 * see MS-WSP 2.2.1.31.1 & 4.1 Protocol examples, Example 1
516 * and also as seen in various windows network traces
517 * set value prop[0]
518 * DBPROP_CI_INCLUDE_SCOPES - 'search everywhere'
520 propertyset->aprops[0].dbpropid = DBPROP_CI_INCLUDE_SCOPES;
521 set_variant_array_bstr(tmp_ctx, &propertyset->aprops[0].vvalue,
522 root_scope_string_vector,
523 ARRAY_SIZE(root_scope_string_vector));
526 * set value prop[1]
527 * DBPROP_CI_SCOPE_FLAGS - 'QUERY_DEEP'
529 propertyset->aprops[1].dbpropid = DBPROP_CI_SCOPE_FLAGS;
530 set_variant_array_i4(tmp_ctx, &propertyset->aprops[1].vvalue,
531 scope_flags_vector,
532 ARRAY_SIZE(scope_flags_vector));
535 * set value prop[2]
536 * DBPROP_CI_CATALOG_NAME - 'index to use' (always the same)
538 propertyset->aprops[2].dbpropid = DBPROP_CI_CATALOG_NAME;
539 set_variant_bstr(tmp_ctx, &propertyset->aprops[2].vvalue,
540 "Windows\\SystemIndex");
541 return true;
544 bool init_connectin_request(TALLOC_CTX *ctx,
545 struct wsp_request* request,
546 const char* clientmachine,
547 const char* clientuser,
548 const char* server)
550 enum ndr_err_code err;
551 struct connectin_propsets *props = NULL;
552 struct connectin_extpropsets *ext_props = NULL;
553 DATA_BLOB props_blob = data_blob_null;
554 struct ndr_push *ndr_props = NULL;
555 ndr_flags_type ndr_flags = NDR_SCALARS | NDR_BUFFERS;
556 bool result;
557 struct wsp_cpmconnectin *connectin =
558 &request->message.cpmconnect;
560 props = talloc_zero(ctx, struct connectin_propsets);
561 if (props == NULL) {
562 result = false;
563 DBG_ERR("out of memory\n");
564 goto out;
567 ext_props = talloc_zero(ctx, struct connectin_extpropsets) ;
568 if (ext_props == NULL) {
569 result = false;
570 DBG_ERR("out of memory\n");
571 goto out;
574 request->header.msg = CPMCONNECT;
575 connectin->iclientversion = CLIENTVERSION;
577 * hmm just say the client is remote, if we
578 * are talking to windows it is, if not does
579 * it really matter?
581 connectin->fclientisremote = 0x00000001;
582 connectin->machinename = clientmachine;
583 connectin->username = clientuser;
584 props->cpropsets = 2;
586 /* =================== */
587 /* set up PropertySet1 */
588 /* =================== */
589 if (!init_propset1(ctx, &props->propertyset1)) {
590 result = false;
591 DBG_ERR("initialising propset1 failed\n");
592 goto out;
595 /* =================== */
596 /* set up PropertySet2 */
597 /* =================== */
598 if (!init_propset2(ctx, &props->propertyset2, server)) {
599 result = false;
600 DBG_ERR("initialising propset2 failed\n");
601 goto out;
604 /* 4 ExtPropSets */
605 ext_props->cextpropset = 4;
606 ext_props->apropertysets = talloc_zero_array(ctx, struct wsp_cdbpropset,
607 ext_props->cextpropset);
609 if (ext_props->apropertysets == NULL) {
610 result = false;
611 DBG_ERR("out of memory\n");
612 goto out;
615 /* ======================= */
616 /* set up aPropertySets[0] */
617 /* ======================= */
618 if (!init_apropset0(ctx, &ext_props->apropertysets[0])) {
619 result = false;
620 DBG_ERR("initialisation of apropset0 failed\n");
621 goto out;
624 /* ======================= */
625 /* set up aPropertySets[1] */
626 /* ======================= */
627 if (!init_apropset1(ctx, &ext_props->apropertysets[1])) {
628 result = false;
629 DBG_ERR("initialisation of apropset1 failed\n");
630 goto out;
633 /* ======================= */
634 /* set up aPropertySets[2] */
635 /* ======================= */
636 if (!init_apropset2(ctx, &ext_props->apropertysets[2], server)) {
637 result = false;
638 DBG_ERR("initialisation of apropset2 failed\n");
639 goto out;
642 /* ======================= */
643 /* set up aPropertySets[3] */
644 /* ======================= */
645 if (!init_apropset3(ctx, &ext_props->apropertysets[3])) {
646 result = false;
647 DBG_ERR("initialisation of apropset3 failed\n");
648 goto out;
651 /* we also have to fill the opaque blobs that contain the propsets */
652 ndr_props = ndr_push_init_ctx(ctx);
653 if (ndr_props == NULL) {
654 result = false;
655 DBG_ERR("out of memory\n");
656 goto out;
659 /* first connectin_propsets */
660 err = ndr_push_connectin_propsets(ndr_props, ndr_flags, props);
661 if (err) {
662 DBG_ERR("Failed to push propset, error %d\n", err);
663 result = false;
664 goto out;
666 props_blob = ndr_push_blob(ndr_props);
667 connectin->cbblob1 = props_blob.length;
668 connectin->propsets = talloc_zero_array(ctx, uint8_t,
669 connectin->cbblob1);
670 if (connectin->propsets == NULL) {
671 result = false;
672 DBG_ERR("out of memory\n");
673 goto out;
676 memcpy(connectin->propsets, props_blob.data, props_blob.length);
678 /* then connectin_extpropsets */
679 TALLOC_FREE(ndr_props);
680 ndr_props = ndr_push_init_ctx(ctx);
682 if (ndr_props == NULL) {
683 result = false;
684 DBG_ERR("out of memory\n");
685 goto out;
688 err = ndr_push_connectin_extpropsets(ndr_props, ndr_flags, ext_props);
690 if (err) {
691 DBG_ERR("Failed to push extpropset, error %d\n", err);
692 result = false;
693 goto out;
696 props_blob = ndr_push_blob(ndr_props);
697 connectin->cbblob2 = props_blob.length;
698 connectin->extpropsets = talloc_zero_array(ctx, uint8_t,
699 connectin->cbblob2);
701 if (connectin->extpropsets == NULL) {
702 result = false;
703 DBG_ERR("out of memory\n");
704 goto out;
707 memcpy(connectin->extpropsets, props_blob.data, props_blob.length);
708 TALLOC_FREE(ndr_props);
709 result = true;
710 out:
711 return result;
714 void create_seekat_getrows_request(TALLOC_CTX * ctx,
715 struct wsp_request* request,
716 uint32_t cursor,
717 uint32_t bookmark,
718 uint32_t skip,
719 uint32_t rows,
720 uint32_t cbreserved,
721 uint32_t ulclientbase,
722 uint32_t cbrowwidth,
723 uint32_t fbwdfetch)
725 struct wsp_cpmgetrowsin *getrows =
726 &request->message.cpmgetrows;
727 /* msg type */
728 request->header.msg = CPMGETROWS;
729 /* position */
730 getrows->hcursor = cursor;
731 /* max no. rows to receive */
732 getrows->crowstotransfer = rows;
734 * size (length) of row in bytes, determined from value set
735 * by CPMSetBindings message
737 getrows->cbrowWidth = cbrowwidth;
739 * according to we should calculate this (see MS-WSP 3.2.4.2.4)
740 * but it seems window always sets this to the max 16KB limit
741 * (most likely when any row value is variable size e.g. like a
742 * string/path)
744 getrows->cbreadbuffer = 0x00004000;
746 * base value of buffer pointer
748 getrows->ulclientbase = ulclientbase;
749 getrows->cbreserved = cbreserved;
750 /* fetch rows in forward order */
751 getrows->fbwdfetch = fbwdfetch;
752 /* eRowSeekAt */
753 getrows->etype = EROWSEEKAT;
754 /* we don't handle chapters */
755 getrows->chapt = 0;
756 /* CRowsSeekAt (MS-WSP 2.2.1.37) */
757 getrows->seekdescription.crowseekat.bmkoffset = bookmark;
758 getrows->seekdescription.crowseekat.cskip = skip;
759 getrows->seekdescription.crowseekat.hregion = 0;
762 static bool extract_rowbuf_variable_type(TALLOC_CTX *ctx,
763 uint16_t type,
764 uint64_t offset,
765 DATA_BLOB *rows_buf, uint32_t len,
766 struct wsp_cbasestoragevariant *val)
768 enum ndr_err_code err;
769 struct ndr_pull *ndr_pull = NULL;
770 ndr_flags_type ndr_flags = NDR_SCALARS | NDR_BUFFERS;
771 DATA_BLOB variant_blob = data_blob_null;
772 if (offset >= rows_buf->length) {
773 DBG_ERR("offset %"PRIu64" outside buffer range (buf len - %zu)",
774 offset,
775 rows_buf->length);
776 return false;
778 variant_blob.data = rows_buf->data + offset;
779 variant_blob.length = len;
780 ndr_pull = ndr_pull_init_blob(&variant_blob, ctx);
782 if (ndr_pull == NULL) {
783 DBG_ERR("out of memory\n");
784 return false;
787 switch (type) {
788 case VT_LPWSTR: {
789 const char *string = NULL;
790 ndr_set_flags(&ndr_pull->flags, LIBNDR_FLAG_STR_NULLTERM);
791 err = ndr_pull_string(ndr_pull, ndr_flags, &string);
792 if (err) {
793 DBG_ERR("error unmarshalling string from %p\n", variant_blob.data );
794 } else {
795 DBG_INFO("\tstring val ->%s<-\n", string );
796 val->vtype = type;
797 val->vvalue.vt_lpwstr.value = string;
799 break;
801 default:
802 DBG_ERR("#FIXME Unhandled variant type %s\n", get_vtype_name(type));
803 break;
805 return true;
808 static bool convert_variant_array_to_vector(TALLOC_CTX *ctx,
809 uint64_t count,
810 struct wsp_cbasestoragevariant **variant_array,
811 struct wsp_cbasestoragevariant *outval)
813 uint64_t i;
814 uint16_t vtype;
815 union variant_types vvalue = {0};
816 vtype = variant_array[0]->vtype;
818 if (outval == NULL) {
819 return false;
822 if (count) {
823 switch (vtype) {
824 case VT_BSTR:
825 vvalue.vt_bstr_v.vvector_elements = count;
826 vvalue.vt_bstr_v.vvector_data =
827 talloc_zero_array(ctx,
828 struct vt_bstr, count);
829 if (vvalue.vt_bstr_v.vvector_data == NULL) {
830 return false;
832 break;
833 case VT_LPWSTR:
834 vvalue.vt_lpwstr_v.vvector_elements = count;
835 vvalue.vt_lpwstr_v.vvector_data =
836 talloc_zero_array(ctx,
837 struct vt_lpwstr, count);
838 if (vvalue.vt_lpwstr_v.vvector_data == NULL) {
839 return false;
841 break;
842 case VT_COMPRESSED_LPWSTR:
843 vvalue.vt_compresseed_lpwstr_v.vvector_elements
844 = count;
845 vvalue.vt_compresseed_lpwstr_v.vvector_data =
846 talloc_zero_array(ctx,
847 struct vt_compressed_lpwstr,
848 count);
849 if (vvalue.vt_compresseed_lpwstr_v.vvector_data == NULL) {
850 return false;
852 break;
853 default:
854 DBG_ERR("Can't convert array of %s to VECTOR\n",
855 get_vtype_name(vtype));
856 return false;
859 for (i = 0; i < count; i++) {
860 if (variant_array[i]->vtype != vtype) {
861 DBG_ERR("array item type %s doesn't match extpected "
862 "type %s\n",
863 get_vtype_name(variant_array[i]->vtype),
864 get_vtype_name(vtype));
865 return false;
867 switch (variant_array[i]->vtype) {
868 case VT_BSTR:
869 vvalue.vt_bstr_v.vvector_data[i]
870 = variant_array[i]->vvalue.vt_bstr;
871 break;
872 case VT_LPWSTR:
873 vvalue.vt_lpwstr_v.vvector_data[i]
874 = variant_array[i]->vvalue.vt_lpwstr;
875 break;
876 case VT_COMPRESSED_LPWSTR:
877 vvalue.vt_compresseed_lpwstr_v.vvector_data[i]
878 = variant_array[i]->vvalue.vt_compressed_lpwstr;
879 break;
880 default:
881 DBG_ERR("Can't convert array of %s to VECTOR\n",
882 get_vtype_name(vtype));
883 return false;
886 outval->vtype = vtype | VT_VECTOR;
887 outval->vvalue = vvalue;
888 return true;
892 * get the addresses in rowbuf of variants to read from
893 * pvec_address will point to addresses,
894 * an array of n elements for a vector or array of 1 element
895 * if non-vector item.
897 * addresses stored in pvec_address
900 static enum ndr_err_code extract_variant_addresses(TALLOC_CTX *ctx,
901 struct wsp_ctablevariant *tablevar,
902 bool is_64bit,
903 struct ndr_pull *ndr_pull,
904 ndr_flags_type flags,
905 uint64_t baseaddress,
906 DATA_BLOB *rows_buf,
907 uint64_t *pcount,
908 uint64_t **pvec_address)
910 bool is_vector = tablevar->vtype & VT_VECTOR;
911 uint64_t count;
912 uint64_t addr;
913 uint64_t *vec_address = NULL;
914 enum ndr_err_code err;
916 /* read count (only if this is a vector) */
917 if (is_vector) {
918 if (is_64bit) {
919 err = ndr_pull_udlong(ndr_pull,
920 flags,
921 &count);
922 if (err) {
923 DBG_ERR("Failed to extract count\n");
924 goto out;
926 } else {
927 uint32_t count_32;
928 err = ndr_pull_uint32(ndr_pull,
929 flags,
930 &count_32);
931 if (err) {
932 DBG_ERR("Failed to extract count\n");
933 goto out;
935 count = (uint64_t)count_32;
937 } else {
938 count = 1;
941 /* ensure count is at least within buffer range */
942 if (count >= MAX_ROW_BUFF_SIZE || count >= rows_buf->length) {
943 DBG_ERR("count %"PRIu64" either exceeds max buffer size "
944 "or buffer size (%zu)",
945 count, rows_buf->length);
946 err = NDR_ERR_VALIDATE;
947 goto out;
950 /* read address */
951 if (is_64bit) {
952 err = ndr_pull_udlong(ndr_pull,
953 flags,
954 &addr);
955 if (err) {
956 DBG_ERR("Failed to extract address\n");
957 goto out;
959 } else {
960 uint32_t addr_32;
961 err = ndr_pull_uint32(ndr_pull, flags, &addr_32);
962 if (err) {
963 DBG_ERR("Failed to extract address\n");
964 goto out;
966 addr = addr_32;
969 if ((addr - baseaddress) >= rows_buf->length) {
970 DBG_ERR("offset %"PRIu64" outside buffer range "
971 "(buf len - %zu)\n",
972 addr - baseaddress,
973 rows_buf->length);
974 err = NDR_ERR_VALIDATE;
975 goto out;
978 vec_address = talloc_zero_array(ctx,
979 uint64_t, count);
981 if (vec_address == NULL) {
982 err = NDR_ERR_ALLOC;
983 goto out;
987 * non vector case addr points to value
988 * otherwise addr points to list of addresses
989 * for the values in vector
991 if (is_vector == false) {
992 vec_address[0] = addr;
993 } else {
994 uint64_t array_offset = addr - baseaddress;
995 uint64_t i;
996 uint32_t intsize;
998 if (is_64bit) {
999 intsize = 8;
1000 } else {
1001 intsize = 4;
1004 if (array_offset >= MAX_ROW_BUFF_SIZE
1005 || array_offset >= rows_buf->length) {
1006 DBG_ERR("offset %"PRIu64" either exceeds max buf size "
1007 "or buffer size (%zu)",
1008 array_offset, rows_buf->length);
1009 err = NDR_ERR_VALIDATE;
1010 goto out;
1013 /* addr points to a list of int32 or int64 addresses */
1014 for (i = 0; i < count; i++) {
1016 * read the addresses of the vector elements
1017 * note: we can safely convert the uint64_t
1018 * values here to uint32_t values as
1019 * we are sure they are within range
1020 * due to previous checks above.
1022 if (smb_buffer_oob((uint32_t)rows_buf->length,
1023 (uint32_t)array_offset,
1024 intsize)) {
1025 DBG_ERR("offset %"PRIu64" will be outside "
1026 "buffer range (buf len - %zu) after "
1027 "reading %s address\n",
1028 array_offset,
1029 rows_buf->length,
1030 is_64bit ? "64 bit" : "32 bit");
1031 err = NDR_ERR_VALIDATE;
1032 goto out;
1034 if (is_64bit) {
1035 vec_address[i] =
1036 PULL_LE_I64(rows_buf->data,
1037 array_offset);
1038 } else {
1039 vec_address[i] =
1040 (uint32_t)PULL_LE_I32(rows_buf->data,
1041 array_offset);
1043 array_offset += intsize;
1046 err = NDR_ERR_SUCCESS;
1047 *pcount = count;
1048 *pvec_address = vec_address;
1049 out:
1050 return err;
1053 static enum ndr_err_code extract_crowvariant_variable(TALLOC_CTX *ctx,
1054 struct wsp_ctablevariant *tablevar,
1055 bool is_64bit,
1056 struct ndr_pull *ndr_pull,
1057 ndr_flags_type flags,
1058 uint64_t baseaddress,
1059 DATA_BLOB *rows_buf,
1060 uint32_t len,
1061 struct wsp_cbasestoragevariant *val)
1063 enum ndr_err_code err;
1064 bool is_vector = tablevar->vtype & VT_VECTOR;
1065 uint64_t count = 0;
1067 uint64_t *vec_address = NULL;
1068 struct wsp_cbasestoragevariant **variant_array = NULL;
1069 int i;
1072 err = extract_variant_addresses(ctx,
1073 tablevar,
1074 is_64bit,
1075 ndr_pull,
1076 flags,
1077 baseaddress,
1078 rows_buf,
1079 &count,
1080 &vec_address);
1082 if (err) {
1083 DBG_ERR("Failed to extract address and/or count\n");
1084 goto out;
1087 variant_array = talloc_zero_array(ctx,
1088 struct wsp_cbasestoragevariant*,
1089 count);
1091 if (variant_array == NULL) {
1092 err = NDR_ERR_ALLOC;
1093 goto out;
1096 if (is_vector == false) {
1097 variant_array[0] = val;
1098 } else {
1099 for (i = 0; i < count; i++) {
1100 variant_array[i] = talloc_zero(ctx,
1101 struct wsp_cbasestoragevariant);
1102 if (variant_array[i] == NULL) {
1103 err = NDR_ERR_ALLOC;
1104 goto out;
1109 for (i = 0; i < count; i++) {
1110 uint32_t tmplen = len;
1111 uint64_t buf_offset;
1112 buf_offset = vec_address[i] - baseaddress;
1113 if (buf_offset >= rows_buf->length) {
1114 DBG_ERR("offset %"PRIu64" outside buffer range "
1115 "(buf len - %zu)\n",
1116 buf_offset,
1117 rows_buf->length);
1118 err = NDR_ERR_VALIDATE;
1119 goto out;
1121 if (is_64bit
1122 && (tablevar->vtype & ~(VT_VECTOR)) == VT_LPWSTR) {
1124 * we can't trust len if 64 bit mode
1125 * (in 32 bit mode the length reported at len offset
1126 * seem consistent and correct)
1127 * So in this case instead of using the len
1128 * at len offset we just use the full buffer
1129 * from the point the value is stored at
1130 * till the end of the buffer
1132 tmplen = rows_buf->length - buf_offset;
1134 if (!extract_rowbuf_variable_type(ctx,
1135 tablevar->vtype & ~VT_VECTOR,
1136 buf_offset,
1137 rows_buf,
1138 tmplen,
1139 variant_array[i])) {
1140 err = NDR_ERR_VALIDATE;
1141 goto out;
1145 if (is_vector) {
1146 if (!convert_variant_array_to_vector(ctx,
1147 count,
1148 variant_array,
1149 val)) {
1150 err = NDR_ERR_VALIDATE;
1151 goto out;
1154 err = NDR_ERR_SUCCESS;
1155 out:
1156 return err;
1159 static enum ndr_err_code extract_crowvariant(TALLOC_CTX *ctx,
1160 struct wsp_ctablevariant *tablevar,
1161 bool is_64bit,
1162 struct ndr_pull *ndr_pull,
1163 ndr_flags_type flags,
1164 uint64_t baseaddress,
1165 DATA_BLOB *rows_buf, uint32_t len,
1166 struct wsp_cbasestoragevariant *val)
1168 enum ndr_err_code err = NDR_ERR_SUCCESS;
1169 bool is_vector = tablevar->vtype & VT_VECTOR;
1170 bool is_array = tablevar->vtype & VT_ARRAY;
1172 if (is_array) {
1173 DBG_ERR("Not handling ARRAYs!!!\n");
1174 err = NDR_ERR_VALIDATE;
1175 goto out;
1178 if (is_variable_size((tablevar->vtype & ~(VT_VECTOR)))) {
1179 err = extract_crowvariant_variable(ctx,
1180 tablevar,
1181 is_64bit,
1182 ndr_pull,
1183 flags,
1184 baseaddress,
1185 rows_buf,
1186 len,
1187 val);
1189 } else {
1190 if (is_vector) {
1191 DBG_ERR("Not handling VECTORs of fixed size values!!!\n");
1192 err = NDR_ERR_VALIDATE;
1193 goto out;
1195 NDR_CHECK(ndr_pull_set_switch_value(ndr_pull,
1196 &val->vvalue,
1197 tablevar->vtype));
1198 NDR_CHECK(ndr_pull_variant_types(ndr_pull, NDR_SCALARS, &val->vvalue));
1199 val->vtype = tablevar->vtype;
1201 out:
1202 return err;
1205 static enum ndr_err_code process_columns(TALLOC_CTX *ctx,
1206 bool is_64bit,
1207 uint64_t baseaddress,
1208 struct wsp_cpmsetbindingsin *bindingin,
1209 DATA_BLOB *rows_buf,
1210 uint32_t nrow,
1211 struct wsp_cbasestoragevariant *cols)
1213 uint32_t i;
1214 enum ndr_err_code err = NDR_ERR_SUCCESS;
1215 struct ndr_pull *ndr_pull = NULL;
1216 ndr_flags_type ndr_flags = NDR_SCALARS | NDR_BUFFERS;
1217 uint64_t nrow_offset = (uint64_t)nrow * bindingin->brow;
1219 if (nrow_offset >= rows_buf->length) {
1220 DBG_ERR("offset %"PRIu64" outside buffer range (buf len - %zu)\n",
1221 nrow_offset,
1222 rows_buf->length);
1223 err = NDR_ERR_ALLOC;
1224 goto out;
1228 * process columns, column info is contained in cpmsetbindings
1229 * for more information see 'Rows' description MS-WSP 2.2.4.1.2
1230 * which describes how the server fills the buffer.
1232 for (i = 0; i < bindingin->ccolumns; i++) {
1233 struct wsp_ctablecolumn *tab_col = &bindingin->acolumns[i];
1234 DATA_BLOB col_val_blob = data_blob_null;
1235 uint64_t val_offset;
1236 struct wsp_ctablevariant tablevariant = {0};
1237 DBG_INFO("\nRow[%d]Col[%d] property %s type %s\n",nrow, i,
1238 prop_from_fullprop(ctx, &tab_col->propspec),
1239 get_vtype_name(tab_col->vtype));
1240 if (tab_col->statusused) {
1241 val_offset = nrow_offset + tab_col->statusoffset.value;
1242 if (val_offset >= rows_buf->length) {
1243 DBG_ERR("offset %"PRIu64" outside buffer range "
1244 "(buf len - %zu)\n",
1245 val_offset,
1246 rows_buf->length);
1247 err = NDR_ERR_ALLOC;
1248 goto out;
1250 DBG_INFO("\n\tstatusoffset 0x%x status is %s\n",
1251 tab_col->statusoffset.value,
1252 get_store_status(
1253 (uint8_t)*(rows_buf->data
1254 + val_offset)));
1256 if (tab_col->lengthused) {
1257 val_offset = nrow_offset + tab_col->lengthoffset.value;
1258 if (val_offset >= rows_buf->length) {
1259 DBG_ERR("offset %"PRIu64" outside buffer range "
1260 "(buf len - %zu)\n",
1261 val_offset,
1262 rows_buf->length);
1263 err = NDR_ERR_ALLOC;
1264 goto out;
1266 DBG_INFO("\n\tlen offset 0x%x value at length is 0x%x\n",
1267 tab_col->lengthoffset.value,
1268 PULL_LE_I32(rows_buf->data,
1269 val_offset));
1271 if (tab_col->valueused) {
1272 uint32_t len = 0;
1273 val_offset = nrow_offset + tab_col->valueoffset.value;
1274 if (val_offset >= rows_buf->length) {
1275 DBG_ERR("offset %"PRIu64" outside buffer range "
1276 "(buf len - %zu)\n",
1277 val_offset,
1278 rows_buf->length);
1279 err = NDR_ERR_ALLOC;
1280 goto out;
1282 DBG_INFO("\n\tvalueoffset:valuesize 0x%x:0x%x "
1283 "crowvariant address = 0x%"PRIx64"\n",
1284 tab_col->valueoffset.value,
1285 tab_col->valuesize.value,
1286 val_offset);
1288 col_val_blob.data = rows_buf->data + val_offset;
1289 col_val_blob.length = tab_col->valuesize.value;
1292 if (tab_col->vtype != VT_VARIANT) {
1293 DBG_ERR("Not handling non variant column "
1294 "values\n");
1295 err = NDR_ERR_VALIDATE;
1296 goto out;
1298 ndr_pull = ndr_pull_init_blob(&col_val_blob, ctx);
1299 if (ndr_pull == NULL) {
1300 err = NDR_ERR_ALLOC;
1301 DBG_ERR("out of memory\n");
1302 goto out;
1305 err = ndr_pull_wsp_ctablevariant(ndr_pull,
1306 ndr_flags,
1307 &tablevariant);
1308 if (err) {
1309 DBG_ERR("!!! failed to pull fixed part of variant data for col data\n");
1310 goto out;
1312 DBG_INFO("\n");
1313 DBG_INFO("\tcrowvariant contains %s \n",
1314 get_vtype_name(tablevariant.vtype));
1316 if (tab_col->lengthused) {
1318 * it seems the size is what's at
1319 * lengthoffset - tab_col->valuesize.value
1321 len = PULL_LE_I32(rows_buf->data,
1322 nrow_offset
1323 + tab_col->lengthoffset.value);
1324 len = len - tab_col->valuesize.value;
1326 err = extract_crowvariant(ctx,
1327 &tablevariant,
1328 is_64bit,
1329 ndr_pull,
1330 ndr_flags,
1331 baseaddress,
1332 rows_buf,
1333 len,
1334 &cols[i]);
1337 out:
1338 return err;
1342 * extracts values from rows_buf into rowsarray
1343 * based on the information in bindingsin
1345 enum ndr_err_code extract_rowsarray(
1346 TALLOC_CTX * ctx,
1347 DATA_BLOB *rows_buf,
1348 bool is_64bit,
1349 struct wsp_cpmsetbindingsin *bindingsin,
1350 uint32_t cbreserved,
1351 uint64_t baseaddress,
1352 uint32_t rows,
1353 struct wsp_cbasestoragevariant **rowsarray)
1355 uint32_t i;
1356 enum ndr_err_code err = NDR_ERR_SUCCESS;
1358 * limit check the size of rows_buf
1359 * see MS-WSP 2.2.3.11 which describes the size
1360 * of the rows buffer MUST not exceed 0x0004000 bytes.
1361 * This limit will ensure we can safely check
1362 * limits based on uint32_t offsets
1365 if (rows_buf->length > MAX_ROW_BUFF_SIZE) {
1366 DBG_ERR("Buffer size 0x%zx exceeds 0x%x max buffer size\n",
1367 rows_buf->length, MAX_ROW_BUFF_SIZE);
1368 return NDR_ERR_BUFSIZE;
1371 for (i = 0; i < rows; i++ ) {
1372 struct wsp_cbasestoragevariant *cols =
1373 talloc_zero_array(ctx,
1374 struct wsp_cbasestoragevariant,
1375 bindingsin->ccolumns);
1376 uint64_t adjusted_address;
1377 if (cols == NULL) {
1378 return NDR_ERR_ALLOC;
1382 * cater for paddingrows (see MS-WSP 2.2.3.12)
1383 * Rows buffer starts cbreserved bytes into messages
1385 adjusted_address = baseaddress + cbreserved;
1387 err = process_columns(ctx,
1388 is_64bit,
1389 adjusted_address,
1390 bindingsin,
1391 rows_buf,
1393 cols);
1394 if (err) {
1395 break;
1397 rowsarray[i] = cols;
1399 return err;
1402 static bool process_query_node(TALLOC_CTX *ctx,
1403 struct wsp_crestriction *crestriction,
1404 t_query *node);
1406 static bool process_andornot_node(TALLOC_CTX *ctx,
1407 struct wsp_crestriction *crestr,
1408 t_query *node,
1409 struct wsp_crestriction **left,
1410 struct wsp_crestriction **right)
1412 struct wsp_cnoderestriction *restriction_node = NULL;
1414 *left = NULL;
1415 *right = NULL;
1417 restriction_node =
1418 &crestr->restriction.cnoderestriction;
1420 crestr->weight = 1000;
1422 if (node->type == eAND || node->type == eOR) {
1423 if (node->type == eAND) {
1424 crestr->ultype = RTAND;
1425 } else {
1426 crestr->ultype = RTOR;
1428 if (!create_noderestriction(ctx, restriction_node, 2)) {
1429 return false;
1431 *left = &restriction_node->panode[0];
1432 *right = &restriction_node->panode[1];
1433 } else {
1434 crestr->ultype = RTNOT;
1435 crestr->restriction.restriction.restriction =
1436 talloc_zero(ctx, struct wsp_crestriction);
1437 if (crestr->restriction.restriction.restriction == NULL) {
1438 DBG_ERR("out of memory\n");
1439 return false;
1441 crestr =
1442 crestr->restriction.restriction.restriction;
1444 if (*left == NULL) {
1445 *left = crestr;
1447 if (*right == NULL) {
1448 *right = crestr;
1450 return true;
1453 static void process_value_node(TALLOC_CTX *ctx,
1454 struct wsp_crestriction *crestriction,
1455 t_query *node)
1457 *crestriction = *node->restriction;
1460 static bool process_query_node(TALLOC_CTX *ctx,
1461 struct wsp_crestriction *crestriction,
1462 t_query *node)
1464 struct wsp_crestriction *left = NULL, *right = NULL;
1465 if (node == NULL) {
1466 return true;
1468 switch (node->type) {
1469 case eAND:
1470 case eOR:
1471 case eNOT:
1472 if (!process_andornot_node(ctx, crestriction, node,
1473 &left, &right)) {
1474 return false;
1476 break;
1477 case eVALUE:
1478 process_value_node(ctx, crestriction, node);
1479 break;
1480 default:
1481 break;
1483 if (!process_query_node(ctx, left, node->left)) {
1484 return false;
1486 if (!process_query_node(ctx, right, node->right)) {
1487 return false;
1489 return true;
1492 bool create_querysearch_request(TALLOC_CTX * ctx,
1493 struct wsp_request* request,
1494 t_select_stmt *sql)
1496 uint32_t indices[sql->cols->num_cols];
1497 uint32_t i;
1498 uint32_t j;
1499 struct wsp_cpmcreatequeryin *createquery =
1500 &request->message.cpmcreatequery;
1502 for (i = 0; i < sql->cols->num_cols; i++) {
1503 indices[i] = i;
1506 request->header.msg = CPMCREATEQUERY;
1507 createquery->ccolumnsetpresent = 1;
1508 createquery->columnset.columnset.count = sql->cols->num_cols;
1509 if (!fill_uint32_vec(ctx, &createquery->columnset.columnset.indexes,
1510 indices,
1511 sql->cols->num_cols)) {
1512 return false;
1515 /* handle restrictions */
1516 createquery->crestrictionpresent = 1;
1517 createquery->restrictionarray.restrictionarray.count = 1;
1518 createquery->restrictionarray.restrictionarray.ispresent = 1;
1520 if (!create_restriction_array(ctx,
1521 &createquery->restrictionarray.restrictionarray.restrictions,
1522 createquery->restrictionarray.restrictionarray.count)) {
1523 return false;
1527 if (!process_query_node(ctx,
1528 &createquery->restrictionarray.restrictionarray.restrictions[0],
1529 sql->where)) {
1530 return false;
1534 /* handle rest */
1535 createquery->csortsetpresent = 1;
1536 if (createquery->csortsetpresent) {
1537 /* sort on first column */
1538 struct wsp_csort data[] = {
1539 {0x00000000, 0x00000000, 0x00000000, WSP_DEFAULT_LCID},
1541 struct wsp_csortset *sortset = NULL;
1542 struct wsp_cingroupsortaggregsets *aggregsets = NULL;
1544 aggregsets = &createquery->sortset.groupsortaggregsets;
1545 aggregsets->ccount = 1;
1546 aggregsets->sortsets =
1547 talloc_zero_array(ctx,
1548 struct wsp_cingroupsortaggregset,
1549 aggregsets->ccount);
1550 sortset = &aggregsets->sortsets[0].sortaggregset;
1551 sortset->count = ARRAY_SIZE(data);
1552 if (!fill_sortarray(ctx,
1553 &sortset->sortarray,
1554 data,sortset->count)) {
1555 return false;
1559 createquery->ccategorizationsetpresent = 0;
1561 createquery->rowsetproperties.ubooleanoptions = 0x00000203;
1562 createquery->rowsetproperties.ulmaxopenrows = 0x00000000;
1563 createquery->rowsetproperties.ulmemoryusage = 0x00000000;
1564 createquery->rowsetproperties.cmaxresults = 0x00000000;
1565 createquery->rowsetproperties.ccmdtimeout = 0x00000005;
1567 createquery->pidmapper.count = sql->cols->num_cols;
1568 createquery->pidmapper.apropspec = talloc_zero_array(ctx,
1569 struct wsp_cfullpropspec,
1570 createquery->pidmapper.count);
1572 if (createquery->pidmapper.apropspec == NULL) {
1573 DBG_ERR("out of memory\n");
1574 return false;
1577 for(i = 0, j = 0; i < sql->cols->num_cols; i++) {
1578 struct wsp_cfullpropspec *prop =
1579 &createquery->pidmapper.apropspec[j];
1580 char *propname = sql->cols->cols[i];
1582 * don't put RowID in pidmapper or windows will reject
1583 * the query.
1585 if (strequal(propname, "System.Search.RowID")) {
1586 continue;
1588 if (!set_fullpropspec(ctx,
1589 prop, sql->cols->cols[i],
1590 PRSPEC_PROPID)) {
1591 DBG_ERR("Failed to handle property named %s\n",
1592 sql->cols->cols[i]);
1593 continue;
1595 j++;
1597 createquery->columnset.columnset.count = j;
1598 createquery->pidmapper.count = j;
1599 createquery->lcid = WSP_DEFAULT_LCID;
1600 return true;
1603 static int32_t getNextAddress(int32_t value_off,
1604 int32_t status_off,
1605 int32_t len_off,
1606 int32_t max_value_size)
1608 return MAX(MAX(value_off + max_value_size, status_off + 1), len_off + 2);
1611 static void create_binding_offsets(struct binding *binding, int no_cols,
1612 int max_value_size)
1614 uint32_t buf_addr = 0x0;
1615 uint32_t i;
1617 uint32_t value_off = 0;
1618 uint32_t len_off = 0;
1620 /* initial state this will get incremented to the desired 0x2 */
1621 uint32_t status_off = 0x1;
1622 uint32_t avail = 0x4;
1623 int status_remain = 0x2;
1624 int len_remain = -1;
1626 const static uint32_t WINDOW = 0x8;
1627 const static uint32_t LEN_STAT_SIZE = 0x4;
1628 for (i = 0; i < no_cols; i++) {
1629 buf_addr = buf_addr + WINDOW;
1630 value_off = buf_addr;
1632 if (status_remain <= 0) {
1633 if (avail) {
1634 status_off = avail;
1635 status_remain = LEN_STAT_SIZE;
1636 avail = 0;
1637 } else {
1639 * we prepare the address to allocate
1640 * another block from here. It will
1641 * be allocated automatically when we
1642 * re-enter the loop
1644 status_off = getNextAddress(value_off,
1645 status_off,
1646 len_off,
1647 max_value_size) + WINDOW;
1648 status_remain = LEN_STAT_SIZE;
1649 buf_addr = status_off;
1650 avail = buf_addr + LEN_STAT_SIZE;
1652 } else {
1653 status_off++;
1654 buf_addr = getNextAddress(value_off,
1655 status_off,
1656 len_off,
1657 max_value_size);
1660 if (len_remain <= 0) {
1661 if (avail) {
1662 len_off = avail;
1663 len_remain = LEN_STAT_SIZE;
1664 avail = 0;
1665 } else {
1667 * we prepare the address to allocate
1668 * another block from here. It will
1669 * be allocated automatically when we
1670 * re-enter the loop
1672 len_off = getNextAddress(value_off,
1673 status_off,
1674 len_off,
1675 max_value_size) + WINDOW;
1676 len_remain = LEN_STAT_SIZE;
1677 buf_addr = len_off;
1678 avail = buf_addr + LEN_STAT_SIZE;
1680 } else {
1681 len_off += 0x4;
1682 buf_addr = getNextAddress(value_off,
1683 status_off,
1684 len_off,
1685 max_value_size);
1687 status_remain--;
1688 len_remain -= LEN_STAT_SIZE;
1689 binding[i].value_off = value_off;
1690 binding[i].status_off = status_off;
1691 binding[i].len_off = len_off;
1695 static bool fill_bindings(TALLOC_CTX *ctx,
1696 struct wsp_cpmsetbindingsin *bindingsin,
1697 char **col_names,
1698 bool is_64bit)
1700 uint32_t i;
1701 struct binding *offsets = NULL;
1702 uint32_t num_cols;
1703 int maxvalue = is_64bit ? 0x18 : 0x10;
1705 struct wsp_ctablecolumn *tablecols = bindingsin->acolumns;
1706 bindingsin->brow = 0x0;
1707 num_cols = bindingsin->ccolumns;
1709 offsets = talloc_zero_array(ctx, struct binding, num_cols);
1711 if (offsets == NULL) {
1712 DBG_ERR("out of memory\n");
1713 return false;
1716 create_binding_offsets(offsets,
1717 num_cols,
1718 maxvalue);
1720 for (i = 0; i < num_cols; i++) {
1721 uint32_t max_off;
1722 if (!set_ctablecolumn(ctx, &tablecols[i], col_names[i],
1723 &offsets[i], maxvalue)) {
1724 DBG_ERR("Failed to handle property named %s\n",
1725 col_names[i]);
1726 continue;
1728 max_off = MAX(offsets[i].value_off + maxvalue,
1729 offsets[i].status_off + 1);
1730 max_off = MAX(max_off, offsets[i].len_off + 2);
1731 if (max_off > bindingsin->brow) {
1732 bindingsin->brow = max_off;
1735 /* important */
1736 bindingsin->brow += ndr_align_size(bindingsin->brow,4);
1737 return true;
1740 bool create_setbindings_request(TALLOC_CTX * ctx,
1741 struct wsp_request* request,
1742 t_select_stmt *sql,
1743 uint32_t cursor,
1744 bool is_64bit)
1746 struct wsp_cpmsetbindingsin *bindingsin =
1747 &request->message.cpmsetbindings;
1749 request->header.msg = CPMSETBINDINGSIN;
1750 bindingsin->hcursor = cursor;
1751 bindingsin->ccolumns = sql->cols->num_cols;
1753 bindingsin->acolumns = talloc_zero_array(ctx,
1754 struct wsp_ctablecolumn,
1755 bindingsin->ccolumns);
1757 if (bindingsin->acolumns == NULL) {
1758 DBG_ERR("out of memory\n");
1759 return false;
1762 if (!fill_bindings(ctx, bindingsin, sql->cols->cols, is_64bit)) {
1763 return false;
1766 return true;
1769 enum search_kind get_kind(const char* kind_str)
1771 enum search_kind result = UNKNOWN;
1772 int i;
1773 const static struct {
1774 const char* str;
1775 enum search_kind search_kind;
1776 } kind_map[] = {
1777 {"Calendar", CALENDAR},
1778 {"Communication", COMMUNICATION},
1779 {"Contact", CONTACT},
1780 {"Document", DOCUMENT},
1781 {"Email", EMAIL},
1782 {"Feed", FEED},
1783 {"Folder", FOLDER},
1784 {"Game", GAME},
1785 {"InstantMessage", INSTANTMESSAGE},
1786 {"Journal", JOURNAL},
1787 {"Link", LINK},
1788 {"Movie", MOVIE},
1789 {"Music", MUSIC},
1790 {"Note", NOTE},
1791 {"Picture", PICTURE},
1792 {"Program", PROGRAM},
1793 {"RecordedTV", RECORDEDTV},
1794 {"SearchFolder", SEARCHFOLDER},
1795 {"Task", TASK},
1796 {"Video", VIDEO},
1797 {"WebHistory", WEBHISTORY},
1799 for (i = 0; i < ARRAY_SIZE(kind_map); i++) {
1800 if (strequal(kind_str, kind_map[i].str)) {
1801 result = kind_map[i].search_kind;
1802 break;
1805 return result;
1808 struct wsp_client_ctx
1810 struct dcerpc_binding_handle *h;
1813 static NTSTATUS wsp_resp_pdu_complete(struct tstream_context *stream,
1814 void *private_data,
1815 DATA_BLOB blob,
1816 size_t *packet_size)
1818 ssize_t to_read;
1820 to_read = tstream_pending_bytes(stream);
1821 if (to_read == -1) {
1822 return NT_STATUS_IO_DEVICE_ERROR;
1825 if (to_read > 0) {
1826 *packet_size = blob.length + to_read;
1827 return STATUS_MORE_ENTRIES;
1830 return NT_STATUS_OK;
1833 static NTSTATUS wsp_rpc_transport_np_connect(struct cli_state *cli,
1834 TALLOC_CTX *mem_ctx,
1835 struct rpc_cli_transport **presult)
1837 struct tevent_context *ev = NULL;
1838 struct tevent_req *req = NULL;
1839 NTSTATUS status = NT_STATUS_NO_MEMORY;
1841 ev = samba_tevent_context_init(mem_ctx);
1842 if (ev == NULL) {
1843 goto fail;
1845 req = rpc_transport_np_init_send(ev, ev, cli, "MsFteWds");
1846 if (req == NULL) {
1847 goto fail;
1849 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
1850 goto fail;
1852 status = rpc_transport_np_init_recv(req, mem_ctx, presult);
1853 fail:
1854 TALLOC_FREE(req);
1855 TALLOC_FREE(ev);
1856 return status;
1859 NTSTATUS wsp_server_connect(TALLOC_CTX *mem_ctx,
1860 const char *servername,
1861 struct tevent_context *ev_ctx,
1862 struct loadparm_context *lp_ctx,
1863 struct cli_credentials *credentials,
1864 struct cli_state *cli,
1865 struct wsp_client_ctx **wsp_ctx)
1867 struct wsp_client_ctx *ctx = NULL;
1868 struct rpc_cli_transport *transport = NULL;
1869 struct tstream_context *stream = NULL;
1870 NTSTATUS status;
1872 bool smb2_or_greater =
1873 (lpcfg_client_max_protocol(lp_ctx) >= PROTOCOL_SMB2_02);
1875 if (!smb2_or_greater) {
1876 return NT_STATUS_PROTOCOL_NOT_SUPPORTED;
1879 ctx = talloc_zero(mem_ctx, struct wsp_client_ctx);
1880 if (ctx == NULL) {
1881 return NT_STATUS_NO_MEMORY;
1884 status = smb2cli_ioctl_pipe_wait(
1885 cli->conn,
1886 cli->timeout,
1887 cli->smb2.session,
1888 cli->smb2.tcon,
1889 "MsFteWds",
1891 if (!NT_STATUS_IS_OK(status)) {
1892 DBG_ERR("wait for pipe failed: %s)\n",
1893 nt_errstr(status));
1894 TALLOC_FREE(ctx);
1895 return status;
1898 status = wsp_rpc_transport_np_connect(cli,
1899 cli,
1900 &transport);
1901 if (!NT_STATUS_IS_OK(status)) {
1902 DBG_ERR("failed to int the pipe)\n");
1903 TALLOC_FREE(ctx);
1904 return status;
1907 stream = rpc_transport_get_tstream(transport);
1908 ctx->h = tstream_binding_handle_create(ctx,
1909 NULL,
1910 &stream,
1911 MSG_HDR_SIZE,
1912 wsp_resp_pdu_complete,
1913 ctx, 42280);
1914 if (ctx->h == NULL) {
1915 DBG_ERR("failed to create the pipe handle)\n");
1916 TALLOC_FREE(ctx);
1917 return NT_STATUS_UNSUCCESSFUL;
1920 *wsp_ctx = ctx;
1922 return status;
1925 static NTSTATUS write_something(TALLOC_CTX* ctx,
1926 struct dcerpc_binding_handle *handle,
1927 DATA_BLOB *blob_in,
1928 DATA_BLOB *blob_out)
1930 uint32_t outflags;
1931 NTSTATUS status;
1933 status = dcerpc_binding_handle_raw_call(handle,
1934 NULL,
1937 blob_in->data,
1938 blob_in->length,
1939 ctx,
1940 &blob_out->data,
1941 &blob_out->length,
1942 &outflags);
1943 return status;
1946 /* msg is expected to be created on the heap with talloc */
1947 static enum ndr_err_code parse_blob(TALLOC_CTX *ctx, DATA_BLOB *blob,
1948 struct wsp_request *request,
1949 struct wsp_response *response,
1950 DATA_BLOB *unread)
1952 struct ndr_pull *ndr = NULL;
1953 enum ndr_err_code err;
1954 ndr_flags_type ndr_flags = NDR_SCALARS | NDR_BUFFERS;
1955 uint32_t status = 0;
1957 ndr = ndr_pull_init_blob(blob, ctx);
1959 if (ndr == NULL) {
1960 return NDR_ERR_ALLOC;
1963 /* peek at the status */
1964 status = PULL_LE_I32(blob->data, 4);
1966 /* is hard error ?*/
1967 if (status & 0x80000000 && blob->length == MSG_HDR_SIZE) {
1968 /* just pull the header */
1969 err = ndr_pull_wsp_header(ndr, ndr_flags, &response->header);
1970 DBG_ERR("error: %s\n", nt_errstr(NT_STATUS(status)));
1971 goto out;
1973 err = ndr_pull_wsp_response(ndr, ndr_flags, response);
1974 if (err) {
1975 DBG_ERR("Failed to pull header from response blob error %d\n", err);
1976 goto out;
1978 if (DEBUGLEVEL >=6) {
1979 NDR_PRINT_DEBUG(wsp_response, response);
1981 if (response->header.msg == CPMGETROWS) {
1982 if (request) {
1983 /* point to rows buffer */
1984 struct wsp_cpmgetrowsin *getrows =
1985 &request->message.cpmgetrows;
1986 ndr->offset = getrows->cbreserved;
1990 if (ndr->offset < blob->length) {
1991 int bytes = blob->length - ndr->offset;
1992 *unread = data_blob_named(blob->data + ndr->offset,
1993 bytes, "UNREAD");
1994 DBG_WARNING("\nThere are unprocessed bytes (len 0x%x) "
1995 "at end of message\n", bytes);
1998 out:
1999 return err;
2002 static void set_msg_checksum(DATA_BLOB *blob, struct wsp_header *hdr)
2004 /* point at payload */
2005 uint32_t i;
2006 uint8_t *buffer = blob->data + MSG_HDR_SIZE;
2007 uint32_t buf_size = blob->length - MSG_HDR_SIZE;
2008 uint32_t nwords = buf_size/4;
2009 uint32_t offset = 0;
2010 uint32_t checksum = 0;
2012 static const uint32_t xor_const = 0x59533959;
2013 for(i = 0; i < nwords; i++) {
2014 checksum += PULL_LE_I32(buffer, offset);
2015 offset += 4;
2018 checksum ^= xor_const;
2019 checksum -= hdr->msg;
2020 hdr->checksum = checksum;
2023 static enum ndr_err_code insert_header_and_checksum(TALLOC_CTX *ctx,
2024 DATA_BLOB* blob,
2025 struct wsp_header *header)
2027 enum ndr_err_code err;
2028 ndr_flags_type ndr_flags = NDR_SCALARS | NDR_BUFFERS;
2029 struct ndr_push *header_ndr = ndr_push_init_ctx(ctx);
2031 if (header_ndr == NULL) {
2032 return NDR_ERR_ALLOC;
2035 if (header->msg == CPMCONNECT
2036 || header->msg == CPMCREATEQUERY
2037 || header->msg == CPMSETBINDINGSIN
2038 || header->msg == CPMGETROWS
2039 || header->msg == CPMFETCHVALUE) {
2041 set_msg_checksum(blob, header);
2043 err = ndr_push_wsp_header(header_ndr, ndr_flags, header);
2044 if (err) {
2045 DBG_ERR("Failed to push header, error %d\n", err);
2046 return err;
2048 memcpy(blob->data, header_ndr->data, MSG_HDR_SIZE);
2049 return err;
2052 NTSTATUS wsp_request_response(TALLOC_CTX* ctx,
2053 struct wsp_client_ctx *wsp_ctx,
2054 struct wsp_request* request,
2055 struct wsp_response *response,
2056 DATA_BLOB *unread)
2058 NTSTATUS status = NT_STATUS_OK;
2060 ndr_flags_type ndr_flags = NDR_SCALARS | NDR_BUFFERS;
2061 struct ndr_push* push_ndr = NULL;
2063 enum ndr_err_code err;
2065 DATA_BLOB req_blob;
2066 DATA_BLOB resp_blob;
2068 ZERO_STRUCT(req_blob);
2069 ZERO_STRUCT(resp_blob);
2071 push_ndr = ndr_push_init_ctx(ctx);
2072 if (push_ndr == NULL) {
2073 return NT_STATUS_NO_MEMORY;
2076 /* write message payload first */
2077 push_ndr->offset = MSG_HDR_SIZE;
2078 DBG_INFO("\n");
2080 switch(request->header.msg) {
2081 case CPMCONNECT:
2083 struct wsp_cpmconnectin *connectin =
2084 &request->message.cpmconnect;
2085 err = ndr_push_wsp_cpmconnectin(push_ndr, ndr_flags,
2086 connectin);
2087 break;
2089 case CPMCREATEQUERY:
2091 struct wsp_cpmcreatequeryin* createquery =
2092 &request->message.cpmcreatequery;
2093 err = ndr_push_wsp_cpmcreatequeryin(push_ndr,
2094 ndr_flags,
2095 createquery);
2096 req_blob = ndr_push_blob(push_ndr);
2097 /* we need to set cpmcreatequery.size */
2098 createquery->size =
2099 req_blob.length - MSG_HDR_SIZE;
2100 PUSH_LE_U32(req_blob.data, MSG_HDR_SIZE,
2101 createquery->size);
2103 break;
2105 case CPMSETBINDINGSIN:
2107 struct wsp_cpmsetbindingsin *bindingsin =
2108 &request->message.cpmsetbindings;
2109 err = ndr_push_wsp_cpmsetbindingsin(push_ndr, ndr_flags,
2110 bindingsin);
2111 req_blob = ndr_push_blob(push_ndr);
2112 /* we need to set cpmsetbindings.bbindingdesc (size) */
2113 bindingsin->bbindingdesc =
2114 req_blob.length - MSG_HDR_SIZE - 16;
2115 PUSH_LE_U32(req_blob.data, MSG_HDR_SIZE + 8,
2116 bindingsin->bbindingdesc);
2117 break;
2119 case CPMGETROWS:
2121 struct wsp_cpmgetrowsin *getrows =
2122 &request->message.cpmgetrows;
2123 err = ndr_push_wsp_cpmgetrowsin(push_ndr, ndr_flags,
2124 getrows);
2125 req_blob = ndr_push_blob(push_ndr);
2126 getrows->cbseek = req_blob.length - MSG_HDR_SIZE - 32;
2127 /* we need to set cpmgetrowsin.cbseek (size) */
2128 PUSH_LE_U32(req_blob.data, MSG_HDR_SIZE + 12,
2129 getrows->cbseek);
2130 PUSH_LE_U32(req_blob.data, MSG_HDR_SIZE + 16,
2131 getrows->cbreserved);
2132 break;
2134 case CPMGETQUERYSTATUS:
2136 struct wsp_cpmgetquerystatusin *querystatus =
2137 &request->message.cpmgetquerystatus;
2138 err = ndr_push_wsp_cpmgetquerystatusin(
2139 push_ndr,
2140 ndr_flags,
2141 querystatus);
2142 break;
2144 case CPMGETQUERYSTATUSEX:
2146 struct wsp_cpmgetquerystatusexin *statusexin =
2147 &request->message.cpmgetquerystatusex;
2148 err = ndr_push_wsp_cpmgetquerystatusexin(
2149 push_ndr,
2150 ndr_flags,
2151 statusexin);
2152 break;
2154 case CPMFREECURSOR:
2156 struct wsp_cpmfreecursorin *freecursor =
2157 &request->message.cpmfreecursor;
2158 err = ndr_push_wsp_cpmfreecursorin(
2159 push_ndr,
2160 ndr_flags,
2161 freecursor);
2162 break;
2164 case CPMFETCHVALUE:
2166 struct wsp_cpmfetchvaluein *fetchvalue =
2167 &request->message.cpmfetchvalue;
2168 err = ndr_push_wsp_cpmfetchvaluein(
2169 push_ndr,
2170 ndr_flags,
2171 fetchvalue);
2172 break;
2175 case CPMGETAPPROXIMATEPOSITION:
2177 struct wsp_cpmgetapproximatepositionin *position =
2178 &request->message.getapproximateposition;
2179 err = ndr_push_wsp_cpmgetapproximatepositionin(
2180 push_ndr,
2181 ndr_flags,
2182 position);
2183 break;
2185 default:
2186 status = NT_STATUS_MESSAGE_NOT_FOUND;
2187 goto out;
2188 break;
2190 if (err) {
2191 DBG_ERR("failed to serialise message! (%d)\n", err);
2192 status = NT_STATUS_UNSUCCESSFUL;
2193 goto out;
2195 if (!req_blob.data) {
2196 req_blob = ndr_push_blob(push_ndr);
2198 err = insert_header_and_checksum(ctx, &req_blob, &request->header);
2200 DBG_NOTICE("\nsending raw message from client len %d\n", (int)req_blob.length);
2201 DBG_NOTICE("\nsending raw message from client\n");
2202 DBG_NOTICE( "===============================\n");
2204 dump_data(5, req_blob.data, req_blob.length);
2206 status = write_something(ctx, wsp_ctx->h, &req_blob, &resp_blob);
2208 if (!NT_STATUS_IS_OK(status)) {
2209 DBG_ERR("Failed to write message\n");
2210 goto out;
2212 DBG_NOTICE("\nraw response from server\n");
2213 DBG_NOTICE( "========================\n");
2214 dump_data(5, resp_blob.data, resp_blob.length);
2216 err = parse_blob(ctx,
2217 &resp_blob,
2218 request,
2219 response,
2220 unread);
2221 if (err) {
2222 DBG_ERR("Failed to parse response error %d\n", err);
2223 status = NT_STATUS_UNSUCCESSFUL;
2224 goto out;
2226 DBG_NOTICE("response status is 0x%x\n", response->header.status);
2227 /* propagate error status to return status */
2228 if (response->header.status & 0x80000000) {
2229 status = NT_STATUS_UNSUCCESSFUL;
2231 out:
2232 return status;
2235 struct dcerpc_binding_handle* get_wsp_pipe(struct wsp_client_ctx *ctx)
2237 return ctx->h;