diff options
Diffstat (limited to 'source3/rpc_server/mdssvc/marshalling.c')
-rw-r--r-- | source3/rpc_server/mdssvc/marshalling.c | 1422 |
1 files changed, 1422 insertions, 0 deletions
diff --git a/source3/rpc_server/mdssvc/marshalling.c b/source3/rpc_server/mdssvc/marshalling.c new file mode 100644 index 0000000..b3e16d9 --- /dev/null +++ b/source3/rpc_server/mdssvc/marshalling.c @@ -0,0 +1,1422 @@ +/* + Unix SMB/CIFS implementation. + Main metadata server / Spotlight routines + + Copyright (C) Ralph Boehme 2012-2014 + + 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 "dalloc.h" +#include "marshalling.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_RPC_SRV + +/* + * This is used to talloc an array that will hold the table of + * contents of a marshalled Spotlight RPC (S-RPC) reply. Each ToC + * entry is 8 bytes, so we allocate space for 1024 entries which + * should be sufficient for even the largest S-RPC replies. + * + * The total buffersize for S-RPC packets is typically limited to 64k, + * so we can only store so many elements there anyway. + */ +#define MAX_SLQ_TOC 1024*8 +#define MAX_SLQ_TOCIDX 1024 +#define MAX_SLQ_COUNT 4096 +#define MAX_SL_STRLEN 1024 + +/****************************************************************************** + * RPC data marshalling and unmarshalling + ******************************************************************************/ + +/* Spotlight epoch is 1.1.2001 00:00 UTC */ +#define SPOTLIGHT_TIME_DELTA 978307200 /* Diff from UNIX epoch to Spotlight epoch */ + +#define SQ_TYPE_NULL 0x0000 +#define SQ_TYPE_COMPLEX 0x0200 +#define SQ_TYPE_INT64 0x8400 +#define SQ_TYPE_BOOL 0x0100 +#define SQ_TYPE_FLOAT 0x8500 +#define SQ_TYPE_DATA 0x0700 +#define SQ_TYPE_CNIDS 0x8700 +#define SQ_TYPE_UUID 0x0e00 +#define SQ_TYPE_DATE 0x8600 +#define SQ_TYPE_TOC 0x8800 + +#define SQ_CPX_TYPE_ARRAY 0x0a00 +#define SQ_CPX_TYPE_STRING 0x0c00 +#define SQ_CPX_TYPE_UTF16_STRING 0x1c00 +#define SQ_CPX_TYPE_DICT 0x0d00 +#define SQ_CPX_TYPE_CNIDS 0x1a00 +#define SQ_CPX_TYPE_FILEMETA 0x1b00 + +struct sl_tag { + int type; + int count; + size_t length; + size_t size; +}; + +static ssize_t sl_pack_loop(DALLOC_CTX *query, char *buf, + ssize_t offset, size_t bufsize, + char *toc_buf, int *toc_idx, int *count); +static ssize_t sl_unpack_loop(DALLOC_CTX *query, const char *buf, + ssize_t offset, size_t bufsize, + int count, ssize_t toc_offset, + int encoding); +static ssize_t sl_pack(DALLOC_CTX *query, char *buf, size_t bufsize); + +/****************************************************************************** + * Wrapper functions for the *VAL macros with bound checking + ******************************************************************************/ + +static ssize_t sl_push_uint64_val(char *buf, + ssize_t offset, + size_t max_offset, + uint64_t val) +{ + if (offset + 8 > max_offset) { + DEBUG(1, ("%s: offset: %zd, max_offset: %zu", + __func__, offset, max_offset)); + return -1; + } + + SBVAL(buf, offset, val); + return offset + 8; +} + +static ssize_t sl_pull_uint64_val(const char *buf, + ssize_t offset, + size_t bufsize, + uint encoding, + uint64_t *presult) +{ + uint64_t val; + + if (offset + 8 > bufsize) { + DEBUG(1,("%s: buffer overflow\n", __func__)); + return -1; + } + + if (encoding == SL_ENC_LITTLE_ENDIAN) { + val = BVAL(buf, offset); + } else { + val = RBVAL(buf, offset); + } + + *presult = val; + + return offset + 8; +} + +/* + * Returns the UTF-16 string encoding, by checking the 2-byte byte order mark. + * If there is no byte order mark, -1 is returned. + */ +static int spotlight_get_utf16_string_encoding(const char *buf, ssize_t offset, + size_t query_length, int encoding) +{ + int utf16_encoding; + + /* Assumed encoding in absence of a bom is little endian */ + utf16_encoding = SL_ENC_LITTLE_ENDIAN; + + if (query_length >= 2) { + uint8_t le_bom[] = {0xff, 0xfe}; + uint8_t be_bom[] = {0xfe, 0xff}; + if (memcmp(le_bom, buf + offset, sizeof(uint16_t)) == 0) { + utf16_encoding = SL_ENC_LITTLE_ENDIAN | SL_ENC_UTF_16; + } else if (memcmp(be_bom, buf + offset, sizeof(uint16_t)) == 0) { + utf16_encoding = SL_ENC_BIG_ENDIAN | SL_ENC_UTF_16; + } + } + + return utf16_encoding; +} + +/****************************************************************************** + * marshalling functions + ******************************************************************************/ + +static inline uint64_t sl_pack_tag(uint16_t type, uint16_t size_or_count, uint32_t val) +{ + uint64_t tag = ((uint64_t)val << 32) | ((uint64_t)type << 16) | size_or_count; + return tag; +} + +static ssize_t sl_pack_float(double d, char *buf, ssize_t offset, size_t bufsize) +{ + union { + double d; + uint64_t w; + } ieee_fp_union; + + ieee_fp_union.d = d; + + offset = sl_push_uint64_val(buf, offset, bufsize, sl_pack_tag(SQ_TYPE_FLOAT, 2, 1)); + if (offset == -1) { + return -1; + } + offset = sl_push_uint64_val(buf, offset, bufsize, ieee_fp_union.w); + if (offset == -1) { + return -1; + } + + return offset; +} + +static ssize_t sl_pack_uint64(uint64_t u, char *buf, ssize_t offset, size_t bufsize) +{ + uint64_t tag; + + tag = sl_pack_tag(SQ_TYPE_INT64, 2, 1); + offset = sl_push_uint64_val(buf, offset, bufsize, tag); + if (offset == -1) { + return -1; + } + offset = sl_push_uint64_val(buf, offset, bufsize, u); + if (offset == -1) { + return -1; + } + + return offset; +} + +static ssize_t sl_pack_uint64_array(uint64_t *u, char *buf, ssize_t offset, size_t bufsize, int *toc_count) +{ + int count, i; + uint64_t tag; + + count = talloc_array_length(u); + + tag = sl_pack_tag(SQ_TYPE_INT64, count + 1, count); + offset = sl_push_uint64_val(buf, offset, bufsize, tag); + if (offset == -1) { + return -1; + } + + for (i = 0; i < count; i++) { + offset = sl_push_uint64_val(buf, offset, bufsize, u[i]); + if (offset == -1) { + return -1; + } + } + + if (count > 1) { + *toc_count += (count - 1); + } + + return offset; +} + +static ssize_t sl_pack_bool(sl_bool_t val, char *buf, ssize_t offset, size_t bufsize) +{ + uint64_t tag; + + tag = sl_pack_tag(SQ_TYPE_BOOL, 1, val ? 1 : 0); + offset = sl_push_uint64_val(buf, offset, bufsize, tag); + if (offset == -1) { + return -1; + } + + return offset; +} + +static ssize_t sl_pack_nil(char *buf, ssize_t offset, size_t bufsize) +{ + uint64_t tag; + + tag = sl_pack_tag(SQ_TYPE_NULL, 1, 1); + offset = sl_push_uint64_val(buf, offset, bufsize, tag); + if (offset == -1) { + return -1; + } + + return offset; +} + +static ssize_t sl_pack_date(sl_time_t t, char *buf, ssize_t offset, size_t bufsize) +{ + uint64_t data; + uint64_t tag; + union { + double d; + uint64_t w; + } ieee_fp_union; + + tag = sl_pack_tag(SQ_TYPE_DATE, 2, 1); + offset = sl_push_uint64_val(buf, offset, bufsize, tag); + if (offset == -1) { + return -1; + } + + ieee_fp_union.d = (double)(t.tv_sec - SPOTLIGHT_TIME_DELTA); + ieee_fp_union.d += (double)t.tv_usec / 1000000; + + data = ieee_fp_union.w; + offset = sl_push_uint64_val(buf, offset, bufsize, data); + if (offset == -1) { + return -1; + } + + return offset; +} + +static ssize_t sl_pack_uuid(sl_uuid_t *uuid, char *buf, ssize_t offset, size_t bufsize) +{ + uint64_t tag; + + tag = sl_pack_tag(SQ_TYPE_UUID, 3, 1); + offset = sl_push_uint64_val(buf, offset, bufsize, tag); + if (offset == -1) { + return -1; + } + + if (offset + 16 > bufsize) { + return -1; + } + memcpy(buf + offset, uuid, 16); + + return offset + 16; +} + +static ssize_t sl_pack_CNID(sl_cnids_t *cnids, char *buf, ssize_t offset, + size_t bufsize, char *toc_buf, int *toc_idx) +{ + ssize_t result; + int len, i; + int cnid_count = dalloc_size(cnids->ca_cnids); + uint64_t tag; + uint64_t id; + void *p; + + tag = sl_pack_tag(SQ_CPX_TYPE_CNIDS, offset / 8, 0); + result = sl_push_uint64_val(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, tag); + if (result == -1) { + return -1; + } + + tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1); + offset = sl_push_uint64_val(buf, offset, bufsize, tag); + if (offset == -1) { + return -1; + } + + *toc_idx += 1; + + len = cnid_count + 1; + if (cnid_count > 0) { + len ++; + } + + /* unknown meaning, but always 8 */ + tag = sl_pack_tag(SQ_TYPE_CNIDS, len, 8 ); + offset = sl_push_uint64_val(buf, offset, bufsize, tag); + if (offset == -1) { + return -1; + } + + if (cnid_count > 0) { + tag = sl_pack_tag(cnids->ca_unkn1, cnid_count, cnids->ca_context); + offset = sl_push_uint64_val(buf, offset, bufsize, tag); + if (offset == -1) { + return -1; + } + + for (i = 0; i < cnid_count; i++) { + p = dalloc_get_object(cnids->ca_cnids, i); + if (p == NULL) { + return -1; + } + memcpy(&id, p, sizeof(uint64_t)); + offset = sl_push_uint64_val(buf, offset, bufsize, id); + if (offset == -1) { + return -1; + } + } + } + + return offset; +} + +static ssize_t sl_pack_array(sl_array_t *array, char *buf, ssize_t offset, + size_t bufsize, char *toc_buf, int *toc_idx) +{ + ssize_t result; + int count = dalloc_size(array); + int octets = offset / 8; + uint64_t tag; + int toc_idx_save = *toc_idx; + + tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1); + offset = sl_push_uint64_val(buf, offset, bufsize, tag); + if (offset == -1) { + return -1; + } + + *toc_idx += 1; + + offset = sl_pack_loop(array, buf, offset, bufsize - offset, toc_buf, toc_idx, &count); + + tag = sl_pack_tag(SQ_CPX_TYPE_ARRAY, octets, count); + result = sl_push_uint64_val(toc_buf, toc_idx_save * 8, MAX_SLQ_TOC, tag); + if (result == -1) { + return -1; + } + + return offset; +} + +static ssize_t sl_pack_dict(sl_array_t *dict, char *buf, ssize_t offset, + size_t bufsize, char *toc_buf, int *toc_idx, int *count) +{ + ssize_t result; + uint64_t tag; + + tag = sl_pack_tag(SQ_CPX_TYPE_DICT, offset / 8, + dalloc_size(dict)); + result = sl_push_uint64_val(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, tag); + if (result == -1) { + return -1; + } + + tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1); + offset = sl_push_uint64_val(buf, offset, bufsize, tag); + if (offset == -1) { + return -1; + } + + *toc_idx += 1; + + offset = sl_pack_loop(dict, buf, offset, bufsize - offset, toc_buf, toc_idx, count); + + return offset; +} + +static ssize_t sl_pack_filemeta(sl_filemeta_t *fm, char *buf, ssize_t offset, + size_t bufsize, char *toc_buf, int *toc_idx) +{ + ssize_t result; + ssize_t fmlen; + ssize_t saveoff = offset; + uint64_t tag; + + tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1); + offset = sl_push_uint64_val(buf, offset, bufsize, tag); + if (offset == -1) { + return -1; + } + + offset += 8; + + fmlen = sl_pack(fm, buf + offset, bufsize - offset); + if (fmlen == -1) { + return -1; + } + + /* + * Check for empty filemeta array, if it's only 40 bytes, it's + * only the header but no content + */ + if (fmlen > 40) { + offset += fmlen; + } else { + fmlen = 0; + } + + /* unknown meaning, but always 8 */ + tag = sl_pack_tag(SQ_TYPE_DATA, (fmlen / 8) + 1, 8); + result = sl_push_uint64_val(buf, saveoff + 8, bufsize, tag); + if (result == -1) { + return -1; + } + + tag = sl_pack_tag(SQ_CPX_TYPE_FILEMETA, saveoff / 8, fmlen / 8); + result = sl_push_uint64_val(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, tag); + if (result == -1) { + return -1; + } + + *toc_idx += 1; + + return offset; +} + +static ssize_t sl_pack_string(char *s, char *buf, ssize_t offset, size_t bufsize, + char *toc_buf, int *toc_idx) +{ + ssize_t result; + size_t len, octets, used_in_last_octet; + uint64_t tag; + + len = strlen(s); + if (len > MAX_SL_STRLEN) { + return -1; + } + octets = (len + 7) / 8; + used_in_last_octet = len % 8; + if (used_in_last_octet == 0) { + used_in_last_octet = 8; + } + + tag = sl_pack_tag(SQ_CPX_TYPE_STRING, offset / 8, used_in_last_octet); + result = sl_push_uint64_val(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, tag); + if (result == -1) { + return -1; + } + + tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1); + offset = sl_push_uint64_val(buf, offset, bufsize, tag); + if (offset == -1) { + return -1; + } + + *toc_idx += 1; + + tag = sl_pack_tag(SQ_TYPE_DATA, octets + 1, used_in_last_octet); + offset = sl_push_uint64_val(buf, offset, bufsize, tag); + if (offset == -1) { + return -1; + } + + if (offset + (octets * 8) > bufsize) { + return -1; + } + + memset(buf + offset, 0, octets * 8); + memcpy(buf + offset, s, len); + offset += octets * 8; + + return offset; +} + +static ssize_t sl_pack_string_as_utf16(char *s, char *buf, ssize_t offset, + size_t bufsize, char *toc_buf, int *toc_idx) +{ + ssize_t result; + int utf16_plus_bom_len, octets, used_in_last_octet; + char *utf16string = NULL; + char bom[] = { 0xff, 0xfe }; + size_t slen, utf16len; + uint64_t tag; + bool ok; + + slen = strlen(s); + if (slen > MAX_SL_STRLEN) { + return -1; + } + + ok = convert_string_talloc(talloc_tos(), + CH_UTF8, + CH_UTF16LE, + s, + slen, + &utf16string, + &utf16len); + if (!ok) { + return -1; + } + + utf16_plus_bom_len = utf16len + 2; + octets = (utf16_plus_bom_len + 7) / 8; + used_in_last_octet = utf16_plus_bom_len % 8; + if (used_in_last_octet == 0) { + used_in_last_octet = 8; + } + + tag = sl_pack_tag(SQ_CPX_TYPE_UTF16_STRING, offset / 8, used_in_last_octet); + result = sl_push_uint64_val(toc_buf, *toc_idx * 8, MAX_SLQ_TOC, tag); + if (result == -1) { + offset = -1; + goto done; + } + + tag = sl_pack_tag(SQ_TYPE_COMPLEX, 1, *toc_idx + 1); + offset = sl_push_uint64_val(buf, offset, bufsize, tag); + if (offset == -1) { + goto done; + } + + *toc_idx += 1; + + tag = sl_pack_tag(SQ_TYPE_DATA, octets + 1, used_in_last_octet); + offset = sl_push_uint64_val(buf, offset, bufsize, tag); + if (offset == -1) { + goto done; + } + + if (offset + (octets * 8) > bufsize) { + offset = -1; + goto done; + } + + memset(buf + offset, 0, octets * 8); + memcpy(buf + offset, &bom, sizeof(bom)); + memcpy(buf + offset + 2, utf16string, utf16len); + offset += octets * 8; + +done: + TALLOC_FREE(utf16string); + return offset; +} + +static ssize_t sl_pack_loop(DALLOC_CTX *query, char *buf, ssize_t offset, + size_t bufsize, char *toc_buf, int *toc_idx, int *count) +{ + const char *type; + int n; + uint64_t i; + sl_bool_t bl; + double d; + sl_time_t t; + void *p; + + for (n = 0; n < dalloc_size(query); n++) { + + type = dalloc_get_name(query, n); + if (type == NULL) { + return -1; + } + p = dalloc_get_object(query, n); + if (p == NULL) { + return -1; + } + + if (strcmp(type, "sl_array_t") == 0) { + offset = sl_pack_array(p, buf, offset, bufsize, + toc_buf, toc_idx); + } else if (strcmp(type, "sl_dict_t") == 0) { + offset = sl_pack_dict(p, buf, offset, bufsize, + toc_buf, toc_idx, count); + } else if (strcmp(type, "sl_filemeta_t") == 0) { + offset = sl_pack_filemeta(p, buf, offset, bufsize, + toc_buf, toc_idx); + } else if (strcmp(type, "uint64_t") == 0) { + memcpy(&i, p, sizeof(uint64_t)); + offset = sl_pack_uint64(i, buf, offset, bufsize); + } else if (strcmp(type, "uint64_t *") == 0) { + offset = sl_pack_uint64_array(p, buf, offset, + bufsize, count); + } else if (strcmp(type, "char *") == 0) { + offset = sl_pack_string(p, buf, offset, bufsize, + toc_buf, toc_idx); + } else if (strcmp(type, "smb_ucs2_t *") == 0) { + offset = sl_pack_string_as_utf16(p, buf, offset, bufsize, + toc_buf, toc_idx); + } else if (strcmp(type, "sl_bool_t") == 0) { + memcpy(&bl, p, sizeof(sl_bool_t)); + offset = sl_pack_bool(bl, buf, offset, bufsize); + } else if (strcmp(type, "double") == 0) { + memcpy(&d, p, sizeof(double)); + offset = sl_pack_float(d, buf, offset, bufsize); + } else if (strcmp(type, "sl_nil_t") == 0) { + offset = sl_pack_nil(buf, offset, bufsize); + } else if (strcmp(type, "sl_time_t") == 0) { + memcpy(&t, p, sizeof(sl_time_t)); + offset = sl_pack_date(t, buf, offset, bufsize); + } else if (strcmp(type, "sl_uuid_t") == 0) { + offset = sl_pack_uuid(p, buf, offset, bufsize); + } else if (strcmp(type, "sl_cnids_t") == 0) { + offset = sl_pack_CNID(p, buf, offset, + bufsize, toc_buf, toc_idx); + } else { + DEBUG(1, ("unknown type: %s", type)); + return -1; + } + if (offset == -1) { + DEBUG(1, ("error packing type: %s\n", type)); + return -1; + } + } + + return offset; +} + +/****************************************************************************** + * unmarshalling functions + ******************************************************************************/ + +static ssize_t sl_unpack_tag(const char *buf, + ssize_t offset, + size_t bufsize, + uint encoding, + struct sl_tag *tag) +{ + uint64_t val; + + if (offset + 8 > bufsize) { + DEBUG(1,("%s: buffer overflow\n", __func__)); + return -1; + } + + if (encoding == SL_ENC_LITTLE_ENDIAN) { + val = BVAL(buf, offset); + } else { + val = RBVAL(buf, offset); + } + + tag->size = (val & 0xffff) * 8; + tag->type = (val & 0xffff0000) >> 16; + tag->count = val >> 32; + tag->length = tag->count * 8; + + if (tag->size > MAX_SL_FRAGMENT_SIZE) { + DEBUG(1,("%s: size limit %zu\n", __func__, tag->size)); + return -1; + } + + if (tag->length > MAX_SL_FRAGMENT_SIZE) { + DEBUG(1,("%s: length limit %zu\n", __func__, tag->length)); + return -1; + } + + if (tag->count > MAX_SLQ_COUNT) { + DEBUG(1,("%s: count limit %d\n", __func__, tag->count)); + return -1; + } + + return offset + 8; +} + +static int sl_unpack_ints(DALLOC_CTX *query, + const char *buf, + ssize_t offset, + size_t bufsize, + int encoding) +{ + int i, result; + struct sl_tag tag; + uint64_t query_data64; + + offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag); + if (offset == -1) { + return -1; + } + + for (i = 0; i < tag.count; i++) { + offset = sl_pull_uint64_val(buf, offset, bufsize, encoding, &query_data64); + if (offset == -1) { + return -1; + } + result = dalloc_add_copy(query, &query_data64, uint64_t); + if (result != 0) { + return -1; + } + } + + return tag.count; +} + +static int sl_unpack_date(DALLOC_CTX *query, + const char *buf, + ssize_t offset, + size_t bufsize, + int encoding) +{ + int i, result; + struct sl_tag tag; + uint64_t query_data64; + union { + double d; + uint64_t w; + } ieee_fp_union; + double fraction; + sl_time_t t; + + offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag); + if (offset == -1) { + return -1; + } + + for (i = 0; i < tag.count; i++) { + offset = sl_pull_uint64_val(buf, offset, bufsize, encoding, &query_data64); + if (offset == -1) { + return -1; + } + ieee_fp_union.w = query_data64; + fraction = ieee_fp_union.d - (uint64_t)ieee_fp_union.d; + + t = (sl_time_t) { + .tv_sec = ieee_fp_union.d + SPOTLIGHT_TIME_DELTA, + .tv_usec = fraction * 1000000 + }; + + result = dalloc_add_copy(query, &t, sl_time_t); + if (result != 0) { + return -1; + } + } + + return tag.count; +} + +static int sl_unpack_uuid(DALLOC_CTX *query, + const char *buf, + ssize_t offset, + size_t bufsize, + int encoding) +{ + int i, result; + sl_uuid_t uuid; + struct sl_tag tag; + + offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag); + if (offset == -1) { + return -1; + } + + for (i = 0; i < tag.count; i++) { + if (offset + 16 > bufsize) { + DEBUG(1,("%s: buffer overflow\n", __func__)); + return -1; + } + memcpy(uuid.sl_uuid, buf + offset, 16); + result = dalloc_add_copy(query, &uuid, sl_uuid_t); + if (result != 0) { + return -1; + } + offset += 16; + } + + return tag.count; +} + +static int sl_unpack_floats(DALLOC_CTX *query, + const char *buf, + ssize_t offset, + size_t bufsize, + int encoding) +{ + int i, result; + union { + double d; + uint32_t w[2]; + } ieee_fp_union; + struct sl_tag tag; + + offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag); + if (offset == -1) { + return -1; + } + + for (i = 0; i < tag.count; i++) { + if (offset + 8 > bufsize) { + DEBUG(1,("%s: buffer overflow\n", __func__)); + return -1; + } + if (encoding == SL_ENC_LITTLE_ENDIAN) { +#ifdef WORDS_BIGENDIAN + ieee_fp_union.w[0] = IVAL(buf, offset + 4); + ieee_fp_union.w[1] = IVAL(buf, offset); +#else + ieee_fp_union.w[0] = IVAL(buf, offset); + ieee_fp_union.w[1] = IVAL(buf, offset + 4); +#endif + } else { +#ifdef WORDS_BIGENDIAN + ieee_fp_union.w[0] = RIVAL(buf, offset); + ieee_fp_union.w[1] = RIVAL(buf, offset + 4); +#else + ieee_fp_union.w[0] = RIVAL(buf, offset + 4); + ieee_fp_union.w[1] = RIVAL(buf, offset); +#endif + } + result = dalloc_add_copy(query, &ieee_fp_union.d, double); + if (result != 0) { + return -1; + } + offset += 8; + } + + return tag.count; +} + +static int sl_unpack_CNID(DALLOC_CTX *query, + const char *buf, + ssize_t offset, + size_t bufsize, + int length, + int encoding) +{ + int i, count, result; + uint64_t query_data64; + sl_cnids_t *cnids; + + cnids = talloc_zero(query, sl_cnids_t); + if (cnids == NULL) { + return -1; + } + cnids->ca_cnids = dalloc_new(cnids); + if (cnids->ca_cnids == NULL) { + return -1; + } + + if (length < 8) { + return -1; + } + if (length == 8) { + /* + * That's permitted, length=8 is an empty CNID array. + */ + result = dalloc_add(query, cnids, sl_cnids_t); + if (result != 0) { + return -1; + } + return 0; + } + + offset = sl_pull_uint64_val(buf, offset, bufsize, encoding, &query_data64); + if (offset == -1) { + return -1; + } + + /* + * Note: ca_unkn1 and ca_context could be taken from the tag + * type and count members, but the fields are packed + * differently in this context, so we can't use + * sl_unpack_tag(). + */ + count = query_data64 & 0xffff;; + cnids->ca_unkn1 = (query_data64 & 0xffff0000) >> 16; + cnids->ca_context = query_data64 >> 32; + + for (i = 0; i < count; i++) { + offset = sl_pull_uint64_val(buf, offset, bufsize, encoding, &query_data64); + if (offset == -1) { + return -1; + } + + result = dalloc_add_copy(cnids->ca_cnids, &query_data64, uint64_t); + if (result != 0) { + return -1; + } + } + + result = dalloc_add(query, cnids, sl_cnids_t); + if (result != 0) { + return -1; + } + + return 0; +} + +static ssize_t sl_unpack_cpx(DALLOC_CTX *query, + const char *buf, + ssize_t offset, + size_t bufsize, + int cpx_query_type, + int cpx_query_count, + ssize_t toc_offset, + int encoding) +{ + int result; + ssize_t roffset = offset; + int unicode_encoding; + bool mark_exists; + char *p; + size_t slen, tmp_len; + sl_array_t *sl_array; + sl_dict_t *sl_dict; + sl_filemeta_t *sl_fm; + bool ok; + struct sl_tag tag; + + switch (cpx_query_type) { + case SQ_CPX_TYPE_ARRAY: + sl_array = dalloc_zero(query, sl_array_t); + if (sl_array == NULL) { + return -1; + } + roffset = sl_unpack_loop(sl_array, buf, offset, bufsize, + cpx_query_count, toc_offset, encoding); + if (roffset == -1) { + return -1; + } + result = dalloc_add(query, sl_array, sl_array_t); + if (result != 0) { + return -1; + } + break; + + case SQ_CPX_TYPE_DICT: + sl_dict = dalloc_zero(query, sl_dict_t); + if (sl_dict == NULL) { + return -1; + } + roffset = sl_unpack_loop(sl_dict, buf, offset, bufsize, + cpx_query_count, toc_offset, encoding); + if (roffset == -1) { + return -1; + } + result = dalloc_add(query, sl_dict, sl_dict_t); + if (result != 0) { + return -1; + } + break; + + case SQ_CPX_TYPE_STRING: + case SQ_CPX_TYPE_UTF16_STRING: + offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag); + if (offset == -1) { + return -1; + } + + if (tag.size < 16) { + DEBUG(1,("%s: string buffer too small\n", __func__)); + return -1; + } + slen = tag.size - 16 + tag.count; + if (slen > MAX_SL_FRAGMENT_SIZE) { + return -1; + } + + if (offset + slen > bufsize) { + DEBUG(1,("%s: buffer overflow\n", __func__)); + return -1; + } + + if (cpx_query_type == SQ_CPX_TYPE_STRING) { + p = talloc_strndup(query, buf + offset, slen); + if (p == NULL) { + return -1; + } + } else { + unicode_encoding = spotlight_get_utf16_string_encoding( + buf, offset, slen, encoding); + mark_exists = (unicode_encoding & SL_ENC_UTF_16) ? true : false; + if (unicode_encoding & SL_ENC_BIG_ENDIAN) { + DEBUG(1, ("Unsupported big endian UTF16 string")); + return -1; + } + slen -= mark_exists ? 2 : 0; + ok = convert_string_talloc( + query, + CH_UTF16LE, + CH_UTF8, + buf + offset + (mark_exists ? 2 : 0), + slen, + &p, + &tmp_len); + if (!ok) { + return -1; + } + } + + result = dalloc_stradd(query, p); + if (result != 0) { + return -1; + } + roffset += tag.size; + break; + + case SQ_CPX_TYPE_FILEMETA: + offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag); + if (offset == -1) { + return -1; + } + if (tag.size < 8) { + DBG_WARNING("size too mall: %zu\n", tag.size); + return -1; + } + + sl_fm = dalloc_zero(query, sl_filemeta_t); + if (sl_fm == NULL) { + return -1; + } + + if (tag.size >= 16) { + result = sl_unpack(sl_fm, + buf + offset, + bufsize - offset ); + if (result == -1) { + return -1; + } + } + result = dalloc_add(query, sl_fm, sl_filemeta_t); + if (result != 0) { + return -1; + } + roffset += tag.size; + break; + + case SQ_CPX_TYPE_CNIDS: + offset = sl_unpack_tag(buf, offset, bufsize, encoding, &tag); + if (offset == -1) { + return -1; + } + + result = sl_unpack_CNID(query, buf, offset, bufsize, + tag.size, encoding); + if (result == -1) { + return -1; + } + roffset += tag.size; + break; + + default: + DEBUG(1, ("unknown complex query type: %u", cpx_query_type)); + return -1; + } + + return roffset; +} + +static ssize_t sl_unpack_loop(DALLOC_CTX *query, + const char *buf, + ssize_t offset, + size_t bufsize, + int count, + ssize_t toc_offset, + int encoding) +{ + int i, toc_index, subcount; + uint64_t result; + + while (count > 0) { + struct sl_tag tag; + + if (offset >= toc_offset) { + return -1; + } + + result = sl_unpack_tag(buf, offset, bufsize, encoding, &tag); + if (result == -1) { + return -1; + } + + switch (tag.type) { + case SQ_TYPE_COMPLEX: { + struct sl_tag cpx_tag; + + if (tag.count < 1) { + DEBUG(1,("%s: invalid tag.count: %d\n", + __func__, tag.count)); + return -1; + } + toc_index = tag.count - 1; + if (toc_index > MAX_SLQ_TOCIDX) { + DEBUG(1,("%s: toc_index too large: %d\n", + __func__, toc_index)); + return -1; + } + result = sl_unpack_tag(buf, toc_offset + (toc_index * 8), + bufsize, encoding, &cpx_tag); + if (result == -1) { + return -1; + } + + offset = sl_unpack_cpx(query, buf, offset + 8, bufsize, cpx_tag.type, + cpx_tag.count, toc_offset, encoding); + if (offset == -1) { + return -1; + } + /* + * tag.size is not the size here, so we need + * to use the offset returned from sl_unpack_cpx() + * instead of offset += tag.size; + */ + count--; + break; + } + + case SQ_TYPE_NULL: { + sl_nil_t nil = 0; + + subcount = tag.count; + if (subcount < 1 || subcount > count) { + return -1; + } + for (i = 0; i < subcount; i++) { + result = dalloc_add_copy(query, &nil, sl_nil_t); + if (result != 0) { + return -1; + } + } + offset += tag.size; + count -= subcount; + break; + } + + case SQ_TYPE_BOOL: { + sl_bool_t b = (tag.count != 0); + + result = dalloc_add_copy(query, &b, sl_bool_t); + if (result != 0) { + return -1; + } + offset += tag.size; + count--; + break; + } + + case SQ_TYPE_INT64: + subcount = sl_unpack_ints(query, buf, offset, bufsize, encoding); + if (subcount < 1 || subcount > count) { + return -1; + } + offset += tag.size; + count -= subcount; + break; + + case SQ_TYPE_UUID: + subcount = sl_unpack_uuid(query, buf, offset, bufsize, encoding); + if (subcount < 1 || subcount > count) { + return -1; + } + offset += tag.size; + count -= subcount; + break; + + case SQ_TYPE_FLOAT: + subcount = sl_unpack_floats(query, buf, offset, bufsize, encoding); + if (subcount < 1 || subcount > count) { + return -1; + } + offset += tag.size; + count -= subcount; + break; + + case SQ_TYPE_DATE: + subcount = sl_unpack_date(query, buf, offset, bufsize, encoding); + if (subcount < 1 || subcount > count) { + return -1; + } + offset += tag.size; + count -= subcount; + break; + + default: + DEBUG(1, ("unknown query type: %d\n", tag.type)); + return -1; + } + } + + return offset; +} + +static ssize_t sl_pack(DALLOC_CTX *query, char *buf, size_t bufsize) +{ + ssize_t result; + char *toc_buf; + int toc_index = 0; + int toc_count = 0; + ssize_t offset, len; + uint64_t hdr; + uint32_t total_octets; + uint32_t data_octets; + uint64_t tag; + + memset(buf, 0, bufsize); + + toc_buf = talloc_zero_size(query, MAX_SLQ_TOC + 8); + if (toc_buf == NULL) { + return -1; + } + + offset = sl_pack_loop(query, buf, 16, bufsize, toc_buf + 8, &toc_index, &toc_count); + if (offset == -1 || offset < 16) { + DEBUG(10,("%s: sl_pack_loop error\n", __func__)); + return -1; + } + len = offset - 16; + + /* + * Marshalling overview: + * + * 16 bytes at the start of buf: + * + * 8 bytes byte order mark + * 4 bytes total octets + * 4 bytes table of content octets + * + * x bytes total octets * 8 from sl_pack_loop + * x bytes ToC octets * 8 from toc_buf + */ + + /* Byte-order mark - we are using little endian only for now */ + memcpy(buf, "432130dm", strlen("432130dm")); + + /* + * The data buffer and ToC buffer sizes are enocoded in number + * of octets (size / 8), plus one, because the octet encoding + * the sizes is included. + */ + data_octets = (len / 8) + 1; + total_octets = data_octets + toc_index + 1; + + hdr = total_octets; + hdr |= ((uint64_t)data_octets << 32); + + /* HDR */ + result = sl_push_uint64_val(buf, 8, bufsize, hdr); + if (result == -1) { + return -1; + } + + /* + * ToC tag with number of ToC entries plus one, the ToC tag + * header. + */ + tag = sl_pack_tag(SQ_TYPE_TOC, toc_index + 1, 0); + result = sl_push_uint64_val(toc_buf, 0, MAX_SLQ_TOC, tag); + if (result == -1) { + return -1; + } + + if ((16 + len + ((toc_index + 1 ) * 8)) > bufsize) { + DEBUG(1, ("%s: exceeding size limit %zu", __func__, bufsize)); + return -1; + } + + memcpy(buf + 16 + len, toc_buf, (toc_index + 1 ) * 8); + len += 16 + (toc_index + 1 ) * 8; + + return len; +} + +/****************************************************************************** + * Global functions for packing und unpacking + ******************************************************************************/ + +NTSTATUS sl_pack_alloc(TALLOC_CTX *mem_ctx, + DALLOC_CTX *d, + struct mdssvc_blob *b, + size_t max_fragment_size) +{ + ssize_t len; + + b->spotlight_blob = talloc_zero_array(mem_ctx, + uint8_t, + max_fragment_size); + if (b->spotlight_blob == NULL) { + return NT_STATUS_NO_MEMORY; + } + + len = sl_pack(d, (char *)b->spotlight_blob, max_fragment_size); + if (len == -1) { + return NT_STATUS_DATA_ERROR; + } + + b->length = len; + b->size = len; + return NT_STATUS_OK; +} + +bool sl_unpack(DALLOC_CTX *query, const char *buf, size_t bufsize) +{ + ssize_t result; + ssize_t offset = 0; + int encoding; + uint64_t hdr; + uint32_t total_octets; + uint64_t total_bytes; + uint32_t data_octets; + uint64_t data_bytes; + uint64_t toc_offset; + struct sl_tag toc_tag; + + if (bufsize > MAX_SL_FRAGMENT_SIZE) { + return false; + } + + if (bufsize < 8) { + return false; + } + if (strncmp(buf + offset, "md031234", 8) == 0) { + encoding = SL_ENC_BIG_ENDIAN; + } else { + encoding = SL_ENC_LITTLE_ENDIAN; + } + offset += 8; + + offset = sl_pull_uint64_val(buf, offset, bufsize, encoding, &hdr); + if (offset == -1) { + return false; + } + + total_octets = hdr & UINT32_MAX; + data_octets = hdr >> 32; + + /* + * Both fields contain the number of octets of the + * corresponding buffer plus the tag octet. We adjust the + * values to match just the number of octets in the buffers. + */ + if (total_octets < 1) { + return false; + } + if (data_octets < 1) { + return false; + } + total_octets--; + data_octets--; + data_bytes = ((uint64_t)data_octets) * 8; + total_bytes = ((uint64_t)total_octets) * 8; + + if (data_bytes >= total_bytes) { + DEBUG(1,("%s: data_bytes: %" PRIu64 ", total_bytes: %" PRIu64 "\n", + __func__, data_bytes, total_bytes)); + return false; + } + + if (total_bytes > (bufsize - offset)) { + return false; + } + + toc_offset = data_bytes; + + toc_offset = sl_unpack_tag(buf + offset, toc_offset, + bufsize - offset, encoding, &toc_tag); + if (toc_offset == -1) { + return false; + } + + if (toc_tag.type != SQ_TYPE_TOC) { + DEBUG(1,("%s: unknown tag type %d\n", __func__, toc_tag.type)); + return false; + } + + /* + * Check toc_tag.size even though we don't use it when unmarshalling + */ + if (toc_tag.size > MAX_SLQ_TOC) { + DEBUG(1,("%s: bad size %zu\n", __func__, toc_tag.size)); + return false; + } + if (toc_tag.size > (total_bytes - data_bytes)) { + DEBUG(1,("%s: bad size %zu\n", __func__, toc_tag.size)); + return false; + } + + if (toc_tag.count != 0) { + DEBUG(1,("%s: bad count %u\n", __func__, toc_tag.count)); + return false; + } + + /* + * We already consumed 16 bytes from the buffer (BOM and size + * tag), so we start at buf + offset. + */ + result = sl_unpack_loop(query, buf + offset, 0, bufsize - offset, + 1, toc_offset, encoding); + if (result == -1) { + DEBUG(1,("%s: sl_unpack_loop failed\n", __func__)); + return false; + } + + return true; +} |