s3:utils: Fix 'Usage:' for 'net ads enctypes'
[samba4-gss.git] / source3 / rpc_server / mdssvc / marshalling.c
blobc85fae79e7eae5f9d3056a56ce4832432b4f2943
1 /*
2 Unix SMB/CIFS implementation.
3 Main metadata server / Spotlight routines
5 Copyright (C) Ralph Boehme 2012-2014
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.
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.
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/>.
21 #include "includes.h"
22 #include "dalloc.h"
23 #include "marshalling.h"
25 #undef DBGC_CLASS
26 #define DBGC_CLASS DBGC_RPC_SRV
29 * This is used to talloc an array that will hold the table of
30 * contents of a marshalled Spotlight RPC (S-RPC) reply. Each ToC
31 * entry is 8 bytes, so we allocate space for 1024 entries which
32 * should be sufficient for even the largest S-RPC replies.
34 * The total buffersize for S-RPC packets is typically limited to 64k,
35 * so we can only store so many elements there anyway.
37 #define MAX_SLQ_TOC 1024*64
38 #define MAX_SLQ_TOCIDX 1024*8
39 #define MAX_SLQ_COUNT 1024*64
40 #define MAX_SL_STRLEN 1024
42 /******************************************************************************
43 * RPC data marshalling and unmarshalling
44 ******************************************************************************/
46 /* Spotlight epoch is 1.1.2001 00:00 UTC */
47 #define SPOTLIGHT_TIME_DELTA 978307200 /* Diff from UNIX epoch to Spotlight epoch */
49 #define SQ_TYPE_NULL 0x0000
50 #define SQ_TYPE_COMPLEX 0x0200
51 #define SQ_TYPE_INT64 0x8400
52 #define SQ_TYPE_BOOL 0x0100
53 #define SQ_TYPE_FLOAT 0x8500
54 #define SQ_TYPE_DATA 0x0700
55 #define SQ_TYPE_CNIDS 0x8700
56 #define SQ_TYPE_UUID 0x0e00
57 #define SQ_TYPE_DATE 0x8600
58 #define SQ_TYPE_TOC 0x8800
60 #define SQ_CPX_TYPE_ARRAY 0x0a00
61 #define SQ_CPX_TYPE_STRING 0x0c00
62 #define SQ_CPX_TYPE_UTF16_STRING 0x1c00
63 #define SQ_CPX_TYPE_DICT 0x0d00
64 #define SQ_CPX_TYPE_CNIDS 0x1a00
65 #define SQ_CPX_TYPE_FILEMETA 0x1b00
67 struct sl_tag {
68 int type;
69 int count;
70 size_t length;
71 size_t size;
74 static ssize_t sl_pack_loop(DALLOC_CTX *query, char *buf,
75 ssize_t offset, size_t bufsize,
76 char *toc_buf, int *toc_idx, int *count);
77 static ssize_t sl_unpack_loop(DALLOC_CTX *query, const char *buf,
78 ssize_t offset, size_t bufsize,
79 int count, ssize_t toc_offset,
80 int encoding);
81 static ssize_t sl_pack(DALLOC_CTX *query, char *buf, size_t bufsize);
83 /******************************************************************************
84 * Wrapper functions for the *VAL macros with bound checking
85 ******************************************************************************/
87 static ssize_t sl_push_uint64_val(char *buf,
88 ssize_t offset,
89 size_t max_offset,
90 uint64_t val)
92 if (offset + 8 > max_offset) {
93 DEBUG(1, ("%s: offset: %zd, max_offset: %zu\n",
94 __func__, offset, max_offset));
95 return -1;
98 SBVAL(buf, offset, val);
99 return offset + 8;
102 static ssize_t sl_pull_uint64_val(const char *buf,
103 ssize_t offset,
104 size_t bufsize,
105 uint encoding,
106 uint64_t *presult)
108 uint64_t val;
110 if (offset + 8 > bufsize) {
111 DEBUG(1,("%s: buffer overflow\n", __func__));
112 return -1;
115 if (encoding == SL_ENC_LITTLE_ENDIAN) {
116 val = BVAL(buf, offset);
117 } else {
118 val = RBVAL(buf, offset);
121 *presult = val;
123 return offset + 8;
127 * Returns the UTF-16 string encoding, by checking the 2-byte byte order mark.
128 * If there is no byte order mark, -1 is returned.
130 static int spotlight_get_utf16_string_encoding(const char *buf, ssize_t offset,
131 size_t query_length, int encoding)
133 int utf16_encoding;
135 /* Assumed encoding in absence of a bom is little endian */
136 utf16_encoding = SL_ENC_LITTLE_ENDIAN;
138 if (query_length >= 2) {
139 uint8_t le_bom[] = {0xff, 0xfe};
140 uint8_t be_bom[] = {0xfe, 0xff};
141 if (memcmp(le_bom, buf + offset, sizeof(uint16_t)) == 0) {
142 utf16_encoding = SL_ENC_LITTLE_ENDIAN | SL_ENC_UTF_16;
143 } else if (memcmp(be_bom, buf + offset, sizeof(uint16_t)) == 0) {
144 utf16_encoding = SL_ENC_BIG_ENDIAN | SL_ENC_UTF_16;
148 return utf16_encoding;
151 /******************************************************************************
152 * marshalling functions
153 ******************************************************************************/
155 static inline uint64_t sl_pack_tag(uint16_t type, uint16_t size_or_count, uint32_t val)
157 uint64_t tag = ((uint64_t)val << 32) | ((uint64_t)type << 16) | size_or_count;
158 return tag;
161 static ssize_t sl_pack_float(double d, char *buf, ssize_t offset, size_t bufsize)
163 union {
164 double d;
165 uint64_t w;
166 } ieee_fp_union;
168 ieee_fp_union.d = d;
170 offset = sl_push_uint64_val(buf, offset, bufsize, sl_pack_tag(SQ_TYPE_FLOAT, 2, 1));
171 if (offset == -1) {
172 return -1;
174 offset = sl_push_uint64_val(buf, offset, bufsize, ieee_fp_union.w);
175 if (offset == -1) {
176 return -1;
179 return offset;
182 static ssize_t sl_pack_uint64(uint64_t u, char *buf, ssize_t offset, size_t bufsize)
184 uint64_t tag;
186 tag = sl_pack_tag(SQ_TYPE_INT64, 2, 1);
187 offset = sl_push_uint64_val(buf, offset, bufsize, tag);
188 if (offset == -1) {
189 return -1;
191 offset = sl_push_uint64_val(buf, offset, bufsize, u);
192 if (offset == -1) {
193 return -1;
196 return offset;
199 static ssize_t sl_pack_uint64_array(uint64_t *u, char *buf, ssize_t offset, size_t bufsize, int *toc_count)
201 int count, i;
202 uint64_t tag;
204 count = talloc_array_length(u);
206 tag = sl_pack_tag(SQ_TYPE_INT64, count + 1, count);
207 offset = sl_push_uint64_val(buf, offset, bufsize, tag);
208 if (offset == -1) {
209 return -1;
212 for (i = 0; i < count; i++) {
213 offset = sl_push_uint64_val(buf, offset, bufsize, u[i]);
214 if (offset == -1) {
215 return -1;
219 if (count > 1) {
220 *toc_count += (count - 1);
223 return offset;
226 static ssize_t sl_pack_bool(sl_bool_t val, char *buf, ssize_t offset, size_t bufsize)
228 uint64_t tag;
230 tag = sl_pack_tag(SQ_TYPE_BOOL, 1, val ? 1 : 0);
231 offset = sl_push_uint64_val(buf, offset, bufsize, tag);
232 if (offset == -1) {
233 return -1;
236 return offset;
239 static ssize_t sl_pack_nil(char *buf, ssize_t offset, size_t bufsize)
241 uint64_t tag;
243 tag = sl_pack_tag(SQ_TYPE_NULL, 1, 1);
244 offset = sl_push_uint64_val(buf, offset, bufsize, tag);
245 if (offset == -1) {
246 return -1;
249 return offset;
252 static ssize_t sl_pack_date(sl_time_t t, char *buf, ssize_t offset, size_t bufsize)
254 uint64_t data;
255 uint64_t tag;
256 union {
257 double d;
258 uint64_t w;
259 } ieee_fp_union;
261 tag = sl_pack_tag(SQ_TYPE_DATE, 2, 1);
262 offset = sl_push_uint64_val(buf, offset, bufsize, tag);
263 if (offset == -1) {
264 return -1;
267 ieee_fp_union.d = (double)(t.tv_sec - SPOTLIGHT_TIME_DELTA);
268 ieee_fp_union.d += (double)t.tv_usec / 1000000;
270 data = ieee_fp_union.w;
271 offset = sl_push_uint64_val(buf, offset, bufsize, data);
272 if (offset == -1) {
273 return -1;
276 return offset;
279 static ssize_t sl_pack_uuid(sl_uuid_t *uuid, char *buf, ssize_t offset, size_t bufsize)
281 uint64_t tag;
283 tag = sl_pack_tag(SQ_TYPE_UUID, 3, 1);
284 offset = sl_push_uint64_val(buf, offset, bufsize, tag);
285 if (offset == -1) {
286 return -1;
289 if (offset + 16 > bufsize) {
290 return -1;
292 memcpy(buf + offset, uuid, 16);
294 return offset + 16;
297 static ssize_t sl_pack_CNID(sl_cnids_t *cnids, char *buf, ssize_t offset,
298 size_t bufsize, char *toc_buf, int *toc_idx)
300 ssize_t result;
301 int len, i;
302 int cnid_count = dalloc_size(cnids->ca_cnids);
303 uint64_t tag;
304 uint64_t id;
305 void *p;
307 tag = sl_pack_tag(SQ_CPX_TYPE_CNIDS, offset / 8, 0);
308 result = sl_push_uint64_val(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, tag);
309 if (result == -1) {
310 return -1;
313 tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1);
314 offset = sl_push_uint64_val(buf, offset, bufsize, tag);
315 if (offset == -1) {
316 return -1;
319 *toc_idx += 1;
321 len = cnid_count + 1;
322 if (cnid_count > 0) {
323 len ++;
326 /* unknown meaning, but always 8 */
327 tag = sl_pack_tag(SQ_TYPE_CNIDS, len, 8 );
328 offset = sl_push_uint64_val(buf, offset, bufsize, tag);
329 if (offset == -1) {
330 return -1;
333 if (cnid_count > 0) {
334 tag = sl_pack_tag(cnids->ca_unkn1, cnid_count, cnids->ca_context);
335 offset = sl_push_uint64_val(buf, offset, bufsize, tag);
336 if (offset == -1) {
337 return -1;
340 for (i = 0; i < cnid_count; i++) {
341 p = dalloc_get_object(cnids->ca_cnids, i);
342 if (p == NULL) {
343 return -1;
345 memcpy(&id, p, sizeof(uint64_t));
346 offset = sl_push_uint64_val(buf, offset, bufsize, id);
347 if (offset == -1) {
348 return -1;
353 return offset;
356 static ssize_t sl_pack_array(sl_array_t *array, char *buf, ssize_t offset,
357 size_t bufsize, char *toc_buf, int *toc_idx)
359 ssize_t result;
360 int count = dalloc_size(array);
361 int octets = offset / 8;
362 uint64_t tag;
363 int toc_idx_save = *toc_idx;
365 tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1);
366 offset = sl_push_uint64_val(buf, offset, bufsize, tag);
367 if (offset == -1) {
368 return -1;
371 *toc_idx += 1;
373 offset = sl_pack_loop(array, buf, offset, bufsize - offset, toc_buf, toc_idx, &count);
375 tag = sl_pack_tag(SQ_CPX_TYPE_ARRAY, octets, count);
376 result = sl_push_uint64_val(toc_buf, toc_idx_save * 8, MAX_SLQ_TOC, tag);
377 if (result == -1) {
378 return -1;
381 return offset;
384 static ssize_t sl_pack_dict(sl_array_t *dict, char *buf, ssize_t offset,
385 size_t bufsize, char *toc_buf, int *toc_idx, int *count)
387 ssize_t result;
388 uint64_t tag;
390 tag = sl_pack_tag(SQ_CPX_TYPE_DICT, offset / 8,
391 dalloc_size(dict));
392 result = sl_push_uint64_val(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, tag);
393 if (result == -1) {
394 return -1;
397 tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1);
398 offset = sl_push_uint64_val(buf, offset, bufsize, tag);
399 if (offset == -1) {
400 return -1;
403 *toc_idx += 1;
405 offset = sl_pack_loop(dict, buf, offset, bufsize - offset, toc_buf, toc_idx, count);
407 return offset;
410 static ssize_t sl_pack_filemeta(sl_filemeta_t *fm, char *buf, ssize_t offset,
411 size_t bufsize, char *toc_buf, int *toc_idx)
413 ssize_t result;
414 ssize_t fmlen;
415 ssize_t saveoff = offset;
416 uint64_t tag;
418 tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1);
419 offset = sl_push_uint64_val(buf, offset, bufsize, tag);
420 if (offset == -1) {
421 return -1;
424 offset += 8;
426 fmlen = sl_pack(fm, buf + offset, bufsize - offset);
427 if (fmlen == -1) {
428 return -1;
432 * Check for empty filemeta array, if it's only 40 bytes, it's
433 * only the header but no content
435 if (fmlen > 40) {
436 offset += fmlen;
437 } else {
438 fmlen = 0;
441 /* unknown meaning, but always 8 */
442 tag = sl_pack_tag(SQ_TYPE_DATA, (fmlen / 8) + 1, 8);
443 result = sl_push_uint64_val(buf, saveoff + 8, bufsize, tag);
444 if (result == -1) {
445 return -1;
448 tag = sl_pack_tag(SQ_CPX_TYPE_FILEMETA, saveoff / 8, fmlen / 8);
449 result = sl_push_uint64_val(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, tag);
450 if (result == -1) {
451 return -1;
454 *toc_idx += 1;
456 return offset;
459 static ssize_t sl_pack_string(char *s, char *buf, ssize_t offset, size_t bufsize,
460 char *toc_buf, int *toc_idx)
462 ssize_t result;
463 size_t len, octets, used_in_last_octet;
464 uint64_t tag;
466 len = strlen(s);
467 if (len > MAX_SL_STRLEN) {
468 return -1;
470 octets = (len + 7) / 8;
471 used_in_last_octet = len % 8;
472 if (used_in_last_octet == 0) {
473 used_in_last_octet = 8;
476 tag = sl_pack_tag(SQ_CPX_TYPE_STRING, offset / 8, used_in_last_octet);
477 result = sl_push_uint64_val(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, tag);
478 if (result == -1) {
479 return -1;
482 tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1);
483 offset = sl_push_uint64_val(buf, offset, bufsize, tag);
484 if (offset == -1) {
485 return -1;
488 *toc_idx += 1;
490 tag = sl_pack_tag(SQ_TYPE_DATA, octets + 1, used_in_last_octet);
491 offset = sl_push_uint64_val(buf, offset, bufsize, tag);
492 if (offset == -1) {
493 return -1;
496 if (offset + (octets * 8) > bufsize) {
497 return -1;
500 memset(buf + offset, 0, octets * 8);
501 memcpy(buf + offset, s, len);
502 offset += octets * 8;
504 return offset;
507 static ssize_t sl_pack_string_as_utf16(char *s, char *buf, ssize_t offset,
508 size_t bufsize, char *toc_buf, int *toc_idx)
510 ssize_t result;
511 int utf16_plus_bom_len, octets, used_in_last_octet;
512 char *utf16string = NULL;
513 char bom[] = { 0xff, 0xfe };
514 size_t slen, utf16len;
515 uint64_t tag;
516 bool ok;
518 slen = strlen(s);
519 if (slen > MAX_SL_STRLEN) {
520 return -1;
523 ok = convert_string_talloc(talloc_tos(),
524 CH_UTF8,
525 CH_UTF16LE,
527 slen,
528 &utf16string,
529 &utf16len);
530 if (!ok) {
531 return -1;
534 utf16_plus_bom_len = utf16len + 2;
535 octets = (utf16_plus_bom_len + 7) / 8;
536 used_in_last_octet = utf16_plus_bom_len % 8;
537 if (used_in_last_octet == 0) {
538 used_in_last_octet = 8;
541 tag = sl_pack_tag(SQ_CPX_TYPE_UTF16_STRING, offset / 8, used_in_last_octet);
542 result = sl_push_uint64_val(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, tag);
543 if (result == -1) {
544 offset = -1;
545 goto done;
548 tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1);
549 offset = sl_push_uint64_val(buf, offset, bufsize, tag);
550 if (offset == -1) {
551 goto done;
554 *toc_idx += 1;
556 tag = sl_pack_tag(SQ_TYPE_DATA, octets + 1, used_in_last_octet);
557 offset = sl_push_uint64_val(buf, offset, bufsize, tag);
558 if (offset == -1) {
559 goto done;
562 if (offset + (octets * 8) > bufsize) {
563 offset = -1;
564 goto done;
567 memset(buf + offset, 0, octets * 8);
568 memcpy(buf + offset, &bom, sizeof(bom));
569 memcpy(buf + offset + 2, utf16string, utf16len);
570 offset += octets * 8;
572 done:
573 TALLOC_FREE(utf16string);
574 return offset;
577 static ssize_t sl_pack_loop(DALLOC_CTX *query, char *buf, ssize_t offset,
578 size_t bufsize, char *toc_buf, int *toc_idx, int *count)
580 const char *type;
581 int n;
582 uint64_t i;
583 sl_bool_t bl;
584 double d;
585 sl_time_t t;
586 void *p;
588 for (n = 0; n < dalloc_size(query); n++) {
590 type = dalloc_get_name(query, n);
591 if (type == NULL) {
592 return -1;
594 p = dalloc_get_object(query, n);
595 if (p == NULL) {
596 return -1;
599 if (strcmp(type, "sl_array_t") == 0) {
600 offset = sl_pack_array(p, buf, offset, bufsize,
601 toc_buf, toc_idx);
602 } else if (strcmp(type, "sl_dict_t") == 0) {
603 offset = sl_pack_dict(p, buf, offset, bufsize,
604 toc_buf, toc_idx, count);
605 } else if (strcmp(type, "sl_filemeta_t") == 0) {
606 offset = sl_pack_filemeta(p, buf, offset, bufsize,
607 toc_buf, toc_idx);
608 } else if (strcmp(type, "uint64_t") == 0) {
609 memcpy(&i, p, sizeof(uint64_t));
610 offset = sl_pack_uint64(i, buf, offset, bufsize);
611 } else if (strcmp(type, "uint64_t *") == 0) {
612 offset = sl_pack_uint64_array(p, buf, offset,
613 bufsize, count);
614 } else if (strcmp(type, "char *") == 0) {
615 offset = sl_pack_string(p, buf, offset, bufsize,
616 toc_buf, toc_idx);
617 } else if (strcmp(type, "smb_ucs2_t *") == 0) {
618 offset = sl_pack_string_as_utf16(p, buf, offset, bufsize,
619 toc_buf, toc_idx);
620 } else if (strcmp(type, "sl_bool_t") == 0) {
621 memcpy(&bl, p, sizeof(sl_bool_t));
622 offset = sl_pack_bool(bl, buf, offset, bufsize);
623 } else if (strcmp(type, "double") == 0) {
624 memcpy(&d, p, sizeof(double));
625 offset = sl_pack_float(d, buf, offset, bufsize);
626 } else if (strcmp(type, "sl_nil_t") == 0) {
627 offset = sl_pack_nil(buf, offset, bufsize);
628 } else if (strcmp(type, "sl_time_t") == 0) {
629 memcpy(&t, p, sizeof(sl_time_t));
630 offset = sl_pack_date(t, buf, offset, bufsize);
631 } else if (strcmp(type, "sl_uuid_t") == 0) {
632 offset = sl_pack_uuid(p, buf, offset, bufsize);
633 } else if (strcmp(type, "sl_cnids_t") == 0) {
634 offset = sl_pack_CNID(p, buf, offset,
635 bufsize, toc_buf, toc_idx);
636 } else {
637 DEBUG(1, ("unknown type: %s\n", type));
638 return -1;
640 if (offset == -1) {
641 DEBUG(1, ("error packing type: %s\n", type));
642 return -1;
646 return offset;
649 /******************************************************************************
650 * unmarshalling functions
651 ******************************************************************************/
653 static ssize_t sl_unpack_tag(const char *buf,
654 ssize_t offset,
655 size_t bufsize,
656 uint encoding,
657 struct sl_tag *tag)
659 uint64_t val;
661 if (offset + 8 > bufsize) {
662 DEBUG(1,("%s: buffer overflow\n", __func__));
663 return -1;
666 if (encoding == SL_ENC_LITTLE_ENDIAN) {
667 val = BVAL(buf, offset);
668 } else {
669 val = RBVAL(buf, offset);
672 tag->size = (val & 0xffff) * 8;
673 tag->type = (val & 0xffff0000) >> 16;
674 tag->count = val >> 32;
675 tag->length = tag->count * 8;
677 if (tag->size > MAX_MDSCMD_SIZE) {
678 DEBUG(1,("%s: size limit %zu\n", __func__, tag->size));
679 return -1;
682 if (tag->length > MAX_MDSCMD_SIZE) {
683 DEBUG(1,("%s: length limit %zu\n", __func__, tag->length));
684 return -1;
687 if (tag->count > MAX_SLQ_COUNT) {
688 DEBUG(1,("%s: count limit %d\n", __func__, tag->count));
689 return -1;
692 return offset + 8;
695 static int sl_unpack_ints(DALLOC_CTX *query,
696 const char *buf,
697 ssize_t offset,
698 size_t bufsize,
699 int encoding)
701 int i, result;
702 struct sl_tag tag;
703 uint64_t query_data64;
705 offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag);
706 if (offset == -1) {
707 return -1;
710 for (i = 0; i < tag.count; i++) {
711 offset = sl_pull_uint64_val(buf, offset, bufsize, encoding, &query_data64);
712 if (offset == -1) {
713 return -1;
715 result = dalloc_add_copy(query, &query_data64, uint64_t);
716 if (result != 0) {
717 return -1;
721 return tag.count;
724 static int sl_unpack_date(DALLOC_CTX *query,
725 const char *buf,
726 ssize_t offset,
727 size_t bufsize,
728 int encoding)
730 int i, result;
731 struct sl_tag tag;
732 uint64_t query_data64;
733 union {
734 double d;
735 uint64_t w;
736 } ieee_fp_union;
737 double fraction;
738 sl_time_t t;
740 offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag);
741 if (offset == -1) {
742 return -1;
745 for (i = 0; i < tag.count; i++) {
746 offset = sl_pull_uint64_val(buf, offset, bufsize, encoding, &query_data64);
747 if (offset == -1) {
748 return -1;
750 ieee_fp_union.w = query_data64;
751 fraction = ieee_fp_union.d - (uint64_t)ieee_fp_union.d;
753 t = (sl_time_t) {
754 .tv_sec = ieee_fp_union.d + SPOTLIGHT_TIME_DELTA,
755 .tv_usec = fraction * 1000000
758 result = dalloc_add_copy(query, &t, sl_time_t);
759 if (result != 0) {
760 return -1;
764 return tag.count;
767 static int sl_unpack_uuid(DALLOC_CTX *query,
768 const char *buf,
769 ssize_t offset,
770 size_t bufsize,
771 int encoding)
773 int i, result;
774 sl_uuid_t uuid;
775 struct sl_tag tag;
777 offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag);
778 if (offset == -1) {
779 return -1;
782 for (i = 0; i < tag.count; i++) {
783 if (offset + 16 > bufsize) {
784 DEBUG(1,("%s: buffer overflow\n", __func__));
785 return -1;
787 memcpy(uuid.sl_uuid, buf + offset, 16);
788 result = dalloc_add_copy(query, &uuid, sl_uuid_t);
789 if (result != 0) {
790 return -1;
792 offset += 16;
795 return tag.count;
798 static int sl_unpack_floats(DALLOC_CTX *query,
799 const char *buf,
800 ssize_t offset,
801 size_t bufsize,
802 int encoding)
804 int i, result;
805 union {
806 double d;
807 uint32_t w[2];
808 } ieee_fp_union;
809 struct sl_tag tag;
811 offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag);
812 if (offset == -1) {
813 return -1;
816 for (i = 0; i < tag.count; i++) {
817 if (offset + 8 > bufsize) {
818 DEBUG(1,("%s: buffer overflow\n", __func__));
819 return -1;
821 if (encoding == SL_ENC_LITTLE_ENDIAN) {
822 #ifdef WORDS_BIGENDIAN
823 ieee_fp_union.w[0] = IVAL(buf, offset + 4);
824 ieee_fp_union.w[1] = IVAL(buf, offset);
825 #else
826 ieee_fp_union.w[0] = IVAL(buf, offset);
827 ieee_fp_union.w[1] = IVAL(buf, offset + 4);
828 #endif
829 } else {
830 #ifdef WORDS_BIGENDIAN
831 ieee_fp_union.w[0] = RIVAL(buf, offset);
832 ieee_fp_union.w[1] = RIVAL(buf, offset + 4);
833 #else
834 ieee_fp_union.w[0] = RIVAL(buf, offset + 4);
835 ieee_fp_union.w[1] = RIVAL(buf, offset);
836 #endif
838 result = dalloc_add_copy(query, &ieee_fp_union.d, double);
839 if (result != 0) {
840 return -1;
842 offset += 8;
845 return tag.count;
848 static int sl_unpack_CNID(DALLOC_CTX *query,
849 const char *buf,
850 ssize_t offset,
851 size_t bufsize,
852 int length,
853 int encoding)
855 int i, count, result;
856 uint64_t query_data64;
857 sl_cnids_t *cnids;
859 cnids = talloc_zero(query, sl_cnids_t);
860 if (cnids == NULL) {
861 return -1;
863 cnids->ca_cnids = dalloc_new(cnids);
864 if (cnids->ca_cnids == NULL) {
865 return -1;
868 if (length < 8) {
869 return -1;
871 if (length == 8) {
873 * That's permitted, length=8 is an empty CNID array.
875 result = dalloc_add(query, cnids, sl_cnids_t);
876 if (result != 0) {
877 return -1;
879 return 0;
882 offset = sl_pull_uint64_val(buf, offset, bufsize, encoding, &query_data64);
883 if (offset == -1) {
884 return -1;
888 * Note: ca_unkn1 and ca_context could be taken from the tag
889 * type and count members, but the fields are packed
890 * differently in this context, so we can't use
891 * sl_unpack_tag().
893 count = query_data64 & 0xffff;;
894 cnids->ca_unkn1 = (query_data64 & 0xffff0000) >> 16;
895 cnids->ca_context = query_data64 >> 32;
897 for (i = 0; i < count; i++) {
898 offset = sl_pull_uint64_val(buf, offset, bufsize, encoding, &query_data64);
899 if (offset == -1) {
900 return -1;
903 result = dalloc_add_copy(cnids->ca_cnids, &query_data64, uint64_t);
904 if (result != 0) {
905 return -1;
909 result = dalloc_add(query, cnids, sl_cnids_t);
910 if (result != 0) {
911 return -1;
914 return 0;
917 static ssize_t sl_unpack_cpx(DALLOC_CTX *query,
918 const char *buf,
919 ssize_t offset,
920 size_t bufsize,
921 int cpx_query_type,
922 int cpx_query_count,
923 ssize_t toc_offset,
924 int encoding)
926 int result;
927 ssize_t roffset = offset;
928 int unicode_encoding;
929 bool mark_exists;
930 char *p;
931 size_t slen, tmp_len;
932 sl_array_t *sl_array;
933 sl_dict_t *sl_dict;
934 sl_filemeta_t *sl_fm;
935 bool ok;
936 struct sl_tag tag;
938 switch (cpx_query_type) {
939 case SQ_CPX_TYPE_ARRAY:
940 sl_array = dalloc_zero(query, sl_array_t);
941 if (sl_array == NULL) {
942 return -1;
944 roffset = sl_unpack_loop(sl_array, buf, offset, bufsize,
945 cpx_query_count, toc_offset, encoding);
946 if (roffset == -1) {
947 return -1;
949 result = dalloc_add(query, sl_array, sl_array_t);
950 if (result != 0) {
951 return -1;
953 break;
955 case SQ_CPX_TYPE_DICT:
956 sl_dict = dalloc_zero(query, sl_dict_t);
957 if (sl_dict == NULL) {
958 return -1;
960 roffset = sl_unpack_loop(sl_dict, buf, offset, bufsize,
961 cpx_query_count, toc_offset, encoding);
962 if (roffset == -1) {
963 return -1;
965 result = dalloc_add(query, sl_dict, sl_dict_t);
966 if (result != 0) {
967 return -1;
969 break;
971 case SQ_CPX_TYPE_STRING:
972 case SQ_CPX_TYPE_UTF16_STRING:
973 offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag);
974 if (offset == -1) {
975 return -1;
978 if (tag.size < 16) {
979 DEBUG(1,("%s: string buffer too small\n", __func__));
980 return -1;
982 slen = tag.size - 16 + tag.count;
983 if (slen > MAX_MDSCMD_SIZE) {
984 return -1;
987 if (offset + slen > bufsize) {
988 DEBUG(1,("%s: buffer overflow\n", __func__));
989 return -1;
992 if (cpx_query_type == SQ_CPX_TYPE_STRING) {
993 p = talloc_strndup(query, buf + offset, slen);
994 if (p == NULL) {
995 return -1;
997 } else {
998 unicode_encoding = spotlight_get_utf16_string_encoding(
999 buf, offset, slen, encoding);
1000 mark_exists = (unicode_encoding & SL_ENC_UTF_16) ? true : false;
1001 if (unicode_encoding & SL_ENC_BIG_ENDIAN) {
1002 DEBUG(1, ("Unsupported big endian UTF16 string\n"));
1003 return -1;
1005 slen -= mark_exists ? 2 : 0;
1006 ok = convert_string_talloc(
1007 query,
1008 CH_UTF16LE,
1009 CH_UTF8,
1010 buf + offset + (mark_exists ? 2 : 0),
1011 slen,
1013 &tmp_len);
1014 if (!ok) {
1015 return -1;
1019 result = dalloc_stradd(query, p);
1020 if (result != 0) {
1021 return -1;
1023 roffset += tag.size;
1024 break;
1026 case SQ_CPX_TYPE_FILEMETA:
1027 offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag);
1028 if (offset == -1) {
1029 return -1;
1031 if (tag.size < 8) {
1032 DBG_WARNING("size too mall: %zu\n", tag.size);
1033 return -1;
1036 sl_fm = dalloc_zero(query, sl_filemeta_t);
1037 if (sl_fm == NULL) {
1038 return -1;
1041 if (tag.size >= 16) {
1042 result = sl_unpack(sl_fm,
1043 buf + offset,
1044 bufsize - offset );
1045 if (result == -1) {
1046 return -1;
1049 result = dalloc_add(query, sl_fm, sl_filemeta_t);
1050 if (result != 0) {
1051 return -1;
1053 roffset += tag.size;
1054 break;
1056 case SQ_CPX_TYPE_CNIDS:
1057 offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag);
1058 if (offset == -1) {
1059 return -1;
1062 result = sl_unpack_CNID(query, buf, offset, bufsize,
1063 tag.size, encoding);
1064 if (result == -1) {
1065 return -1;
1067 roffset += tag.size;
1068 break;
1070 default:
1071 DEBUG(1, ("unknown complex query type: %u\n", cpx_query_type));
1072 return -1;
1075 return roffset;
1078 static ssize_t sl_unpack_loop(DALLOC_CTX *query,
1079 const char *buf,
1080 ssize_t offset,
1081 size_t bufsize,
1082 int count,
1083 ssize_t toc_offset,
1084 int encoding)
1086 int i, toc_index, subcount;
1087 uint64_t result;
1089 while (count > 0) {
1090 struct sl_tag tag;
1092 if (offset >= toc_offset) {
1093 return -1;
1096 result = sl_unpack_tag(buf, offset, bufsize, encoding, &tag);
1097 if (result == -1) {
1098 return -1;
1101 switch (tag.type) {
1102 case SQ_TYPE_COMPLEX: {
1103 struct sl_tag cpx_tag;
1105 if (tag.count < 1) {
1106 DEBUG(1,("%s: invalid tag.count: %d\n",
1107 __func__, tag.count));
1108 return -1;
1110 toc_index = tag.count - 1;
1111 if (toc_index > MAX_SLQ_TOCIDX) {
1112 DEBUG(1,("%s: toc_index too large: %d\n",
1113 __func__, toc_index));
1114 return -1;
1116 result = sl_unpack_tag(buf, toc_offset + (toc_index * 8),
1117 bufsize, encoding, &cpx_tag);
1118 if (result == -1) {
1119 return -1;
1122 offset = sl_unpack_cpx(query, buf, offset + 8, bufsize, cpx_tag.type,
1123 cpx_tag.count, toc_offset, encoding);
1124 if (offset == -1) {
1125 return -1;
1128 * tag.size is not the size here, so we need
1129 * to use the offset returned from sl_unpack_cpx()
1130 * instead of offset += tag.size;
1132 count--;
1133 break;
1136 case SQ_TYPE_NULL: {
1137 sl_nil_t nil = 0;
1139 subcount = tag.count;
1140 if (subcount < 1 || subcount > count) {
1141 return -1;
1143 for (i = 0; i < subcount; i++) {
1144 result = dalloc_add_copy(query, &nil, sl_nil_t);
1145 if (result != 0) {
1146 return -1;
1149 offset += tag.size;
1150 count -= subcount;
1151 break;
1154 case SQ_TYPE_BOOL: {
1155 sl_bool_t b = (tag.count != 0);
1157 result = dalloc_add_copy(query, &b, sl_bool_t);
1158 if (result != 0) {
1159 return -1;
1161 offset += tag.size;
1162 count--;
1163 break;
1166 case SQ_TYPE_INT64:
1167 subcount = sl_unpack_ints(query, buf, offset, bufsize, encoding);
1168 if (subcount < 1 || subcount > count) {
1169 return -1;
1171 offset += tag.size;
1172 count -= subcount;
1173 break;
1175 case SQ_TYPE_UUID:
1176 subcount = sl_unpack_uuid(query, buf, offset, bufsize, encoding);
1177 if (subcount < 1 || subcount > count) {
1178 return -1;
1180 offset += tag.size;
1181 count -= subcount;
1182 break;
1184 case SQ_TYPE_FLOAT:
1185 subcount = sl_unpack_floats(query, buf, offset, bufsize, encoding);
1186 if (subcount < 1 || subcount > count) {
1187 return -1;
1189 offset += tag.size;
1190 count -= subcount;
1191 break;
1193 case SQ_TYPE_DATE:
1194 subcount = sl_unpack_date(query, buf, offset, bufsize, encoding);
1195 if (subcount < 1 || subcount > count) {
1196 return -1;
1198 offset += tag.size;
1199 count -= subcount;
1200 break;
1202 default:
1203 DEBUG(1, ("unknown query type: %d\n", tag.type));
1204 return -1;
1208 return offset;
1211 static ssize_t sl_pack(DALLOC_CTX *query, char *buf, size_t bufsize)
1213 ssize_t result;
1214 char *toc_buf;
1215 int toc_index = 0;
1216 int toc_count = 0;
1217 ssize_t offset, len;
1218 uint64_t hdr;
1219 uint32_t total_octets;
1220 uint32_t data_octets;
1221 uint64_t tag;
1223 memset(buf, 0, bufsize);
1225 toc_buf = talloc_zero_size(query, MAX_SLQ_TOC + 8);
1226 if (toc_buf == NULL) {
1227 return -1;
1230 offset = sl_pack_loop(query, buf, 16, bufsize, toc_buf + 8, &toc_index, &toc_count);
1231 if (offset == -1 || offset < 16) {
1232 DEBUG(10,("%s: sl_pack_loop error\n", __func__));
1233 return -1;
1235 len = offset - 16;
1238 * Marshalling overview:
1240 * 16 bytes at the start of buf:
1242 * 8 bytes byte order mark
1243 * 4 bytes total octets
1244 * 4 bytes table of content octets
1246 * x bytes total octets * 8 from sl_pack_loop
1247 * x bytes ToC octets * 8 from toc_buf
1250 /* Byte-order mark - we are using little endian only for now */
1251 memcpy(buf, "432130dm", strlen("432130dm"));
1254 * The data buffer and ToC buffer sizes are enocoded in number
1255 * of octets (size / 8), plus one, because the octet encoding
1256 * the sizes is included.
1258 data_octets = (len / 8) + 1;
1259 total_octets = data_octets + toc_index + 1;
1261 hdr = total_octets;
1262 hdr |= ((uint64_t)data_octets << 32);
1264 /* HDR */
1265 result = sl_push_uint64_val(buf, 8, bufsize, hdr);
1266 if (result == -1) {
1267 return -1;
1271 * ToC tag with number of ToC entries plus one, the ToC tag
1272 * header.
1274 tag = sl_pack_tag(SQ_TYPE_TOC, toc_index + 1, 0);
1275 result = sl_push_uint64_val(toc_buf, 0, MAX_SLQ_TOC, tag);
1276 if (result == -1) {
1277 return -1;
1280 if ((16 + len + ((toc_index + 1 ) * 8)) > bufsize) {
1281 DEBUG(1, ("%s: exceeding size limit %zu\n", __func__, bufsize));
1282 return -1;
1285 memcpy(buf + 16 + len, toc_buf, (toc_index + 1 ) * 8);
1286 len += 16 + (toc_index + 1 ) * 8;
1288 return len;
1291 /******************************************************************************
1292 * Global functions for packing und unpacking
1293 ******************************************************************************/
1295 NTSTATUS sl_pack_alloc(TALLOC_CTX *mem_ctx,
1296 DALLOC_CTX *d,
1297 struct mdssvc_blob *b,
1298 size_t max_fragment_size)
1300 ssize_t len;
1302 b->spotlight_blob = talloc_zero_array(mem_ctx,
1303 uint8_t,
1304 max_fragment_size);
1305 if (b->spotlight_blob == NULL) {
1306 return NT_STATUS_NO_MEMORY;
1309 len = sl_pack(d, (char *)b->spotlight_blob, max_fragment_size);
1310 if (len == -1) {
1311 return NT_STATUS_DATA_ERROR;
1314 b->length = len;
1315 b->size = len;
1316 return NT_STATUS_OK;
1319 bool sl_unpack(DALLOC_CTX *query, const char *buf, size_t bufsize)
1321 ssize_t result;
1322 ssize_t offset = 0;
1323 int encoding;
1324 uint64_t hdr;
1325 uint32_t total_octets;
1326 uint64_t total_bytes;
1327 uint32_t data_octets;
1328 uint64_t data_bytes;
1329 uint64_t toc_offset;
1330 struct sl_tag toc_tag;
1332 if (bufsize > MAX_MDSCMD_SIZE) {
1333 return false;
1336 if (bufsize < 8) {
1337 return false;
1339 if (strncmp(buf + offset, "md031234", 8) == 0) {
1340 encoding = SL_ENC_BIG_ENDIAN;
1341 } else {
1342 encoding = SL_ENC_LITTLE_ENDIAN;
1344 offset += 8;
1346 offset = sl_pull_uint64_val(buf, offset, bufsize, encoding, &hdr);
1347 if (offset == -1) {
1348 return false;
1351 total_octets = hdr & UINT32_MAX;
1352 data_octets = hdr >> 32;
1355 * Both fields contain the number of octets of the
1356 * corresponding buffer plus the tag octet. We adjust the
1357 * values to match just the number of octets in the buffers.
1359 if (total_octets < 1) {
1360 return false;
1362 if (data_octets < 1) {
1363 return false;
1365 total_octets--;
1366 data_octets--;
1367 data_bytes = ((uint64_t)data_octets) * 8;
1368 total_bytes = ((uint64_t)total_octets) * 8;
1370 if (data_bytes >= total_bytes) {
1371 DEBUG(1,("%s: data_bytes: %" PRIu64 ", total_bytes: %" PRIu64 "\n",
1372 __func__, data_bytes, total_bytes));
1373 return false;
1376 if (total_bytes > (bufsize - offset)) {
1377 return false;
1380 toc_offset = data_bytes;
1382 toc_offset = sl_unpack_tag(buf + offset, toc_offset,
1383 bufsize - offset, encoding, &toc_tag);
1384 if (toc_offset == -1) {
1385 return false;
1388 if (toc_tag.type != SQ_TYPE_TOC) {
1389 DEBUG(1,("%s: unknown tag type %d\n", __func__, toc_tag.type));
1390 return false;
1394 * Check toc_tag.size even though we don't use it when unmarshalling
1396 if (toc_tag.size > MAX_SLQ_TOC) {
1397 DEBUG(1,("%s: bad size %zu\n", __func__, toc_tag.size));
1398 return false;
1400 if (toc_tag.size > (total_bytes - data_bytes)) {
1401 DEBUG(1,("%s: bad size %zu\n", __func__, toc_tag.size));
1402 return false;
1405 if (toc_tag.count != 0) {
1406 DEBUG(1,("%s: bad count %u\n", __func__, toc_tag.count));
1407 return false;
1411 * We already consumed 16 bytes from the buffer (BOM and size
1412 * tag), so we start at buf + offset.
1414 result = sl_unpack_loop(query, buf + offset, 0, bufsize - offset,
1415 1, toc_offset, encoding);
1416 if (result == -1) {
1417 DEBUG(1,("%s: sl_unpack_loop failed\n", __func__));
1418 return false;
1421 return true;