21#include <winpr/assert.h>
22#include <winpr/collections.h>
23#include <winpr/interlocked.h>
24#include <winpr/wlog.h>
25#include <winpr/string.h>
30#define TAG WINPR_TAG("ndr")
32#define NDR_MAX_DEFERRED 128
55struct WinPrNdrEncoder_s
65 NdrDeferreds deferreds;
68struct WinPrNdrDecoder_s
75 wHashTable* referents;
76 NdrDeferreds deferreds;
79#define NDR_REFERENT_MAGIC 0x46455244
91 volatile LONG refCount;
111static NdrReferent* ndr_referent(
void* ptr)
116 NdrReferent* ref = (NdrReferent*)((uintptr_t)ptr - offsetof(NdrReferent, payload));
117 WINPR_ASSERT(ref->magic == NDR_REFERENT_MAGIC);
121static void ndr_deferreds_enter(NdrDeferreds* d)
130 WINPR_ASSERT(d->depth);
133 if (d->nblock == NDR_MAX_DEFERRED)
135 WLog_ERR(TAG,
"too many deferred pointers when adding %s", entry->name);
139 d->block[d->nblock++] = *entry;
143static BOOL ndr_deferreds_leave(NdrDeferreds* d, BOOL success)
146 WINPR_ASSERT(d->depth);
152 const size_t nblock = d->nblock;
157 if (d->nstack + nblock > NDR_MAX_DEFERRED)
159 WLog_ERR(TAG,
"too many deferred pointers");
163 for (
size_t i = nblock; i > 0; i--)
164 d->stack[d->nstack++] = d->block[i - 1];
177 *entry = d->stack[d->nstack];
190 const BYTE* hints = (
const BYTE*)obj + hintsField->
structOffset;
191 return WINPR_CAST_CONST_PTR_AWAY(hints,
void*);
196 WinPrNdrEncoder* ret = calloc(1,
sizeof(*ret));
200 ret->version = version;
201 ret->bigEndianDrep = bigEndianDrep;
202 ret->refIdCounter = 0x20000;
220 WINPR_ASSERT(context);
221 context->offset += len;
226 WINPR_ASSERT(context);
228 if (!Stream_EnsureRemainingCapacity(s, 8))
231 Stream_Write_UINT8(s, context->version);
232 Stream_Write_UINT8(s, context->bigEndianDrep ? 0x00 : 0x10);
233 Stream_Write_UINT16(s, 0x8);
235 BYTE filler[] = { 0xcc, 0xcc, 0xcc, 0xcc };
236 Stream_Write(s, filler,
sizeof(filler));
242 WINPR_ASSERT(context);
244 size_t rest = context->offset % sz;
247 size_t padding = (sz - rest);
249 if (!Stream_EnsureRemainingCapacity(s, padding))
252 Stream_Zero(s, padding);
253 context->offset += padding;
261 WINPR_ASSERT(context);
264 if (context->inObject)
266 WLog_ERR(TAG,
"an object is already being serialized");
270 if (!Stream_EnsureRemainingCapacity(s, 8))
274 context->objectStart = Stream_GetPosition(s);
277 context->inObject = TRUE;
284 WINPR_ASSERT(context);
287 if (!context->inObject)
289 WLog_ERR(TAG,
"no object being serialized");
293 const size_t start = context->objectStart + 8;
294 WINPR_ASSERT(Stream_GetPosition(s) >= start);
297 const size_t padding = (8 - ((Stream_GetPosition(s) - start) % 8)) % 8;
298 if (!Stream_EnsureRemainingCapacity(s, padding))
300 Stream_Zero(s, padding);
301 context->offset += padding;
303 const size_t len = Stream_GetPosition(s) - start;
304 if (len > UINT32_MAX)
307 BYTE* header = Stream_Buffer(s) + context->objectStart;
308 if (context->bigEndianDrep)
309 winpr_Data_Write_UINT32_BE(header, (UINT32)len);
311 winpr_Data_Write_UINT32(header, (UINT32)len);
313 context->inObject = FALSE;
319 WINPR_ASSERT(context);
322 context->refIdCounter += 4;
323 return winpr_ndr_encoder_write_uint32(context, s, refId);
328 WINPR_ASSERT(msgType);
330 switch (msgType->arity)
343 WINPR_ASSERT(0 &&
"unknown arity");
354 WLog_ERR(TAG,
"%s.%s: fixed array without hints or size", descr->
name, field->
name);
358 const UINT64 needed =
362 WLog_ERR(TAG,
"%s.%s: %" PRIu64
" bytes don't fit in a fixed array of %" PRIuz
" bytes",
380 v = *(
const UINT8*)ptr;
385 memcpy(&v16, ptr,
sizeof(v16));
392 memcpy(&v32, ptr,
sizeof(v32));
397 memcpy(&v, ptr,
sizeof(v));
400 WLog_ERR(TAG,
"%s.%s: range on a field of %" PRIuz
" bytes", descr->
name, field->
name,
407 WLog_ERR(TAG,
"%s.%s: %" PRIu64
" is out of range(0, %" PRIu32
")", descr->
name,
421BOOL winpr_ndr_encoder_write_uint8(WinPrNdrEncoder* context,
wStream* s, BYTE v)
423 if (!Stream_EnsureRemainingCapacity(s, 1))
426 Stream_Write_UINT8(s, v);
431BOOL winpr_ndr_encoder_write_uint8_(WinPrNdrEncoder* context,
wStream* s,
const void* hints,
434 WINPR_ASSERT(context);
439 return winpr_ndr_encoder_write_uint8(context, s, *(
const BYTE*)v);
444 winpr_ndr_encoder_write_uint8_,
nullptr,
nullptr
449 return &winpr_ndr_uint8_descr_s;
452BOOL winpr_ndr_encoder_write_int8(WinPrNdrEncoder* context,
wStream* s, INT8 v)
454 return winpr_ndr_encoder_write_uint8(context, s, (BYTE)v);
457BOOL winpr_ndr_encoder_write_int8_(WinPrNdrEncoder* context,
wStream* s,
const void* hints,
460 WINPR_ASSERT(context);
465 return winpr_ndr_encoder_write_int8(context, s, *(
const INT8*)v);
470 winpr_ndr_encoder_write_int8_,
nullptr,
nullptr
475 return &winpr_ndr_int8_descr_s;
478#define SIMPLE_TYPE_IMPL(PREFIX, UPPERTYPE, LOWERTYPE) \
479 BOOL PREFIX##_decoder_read_##LOWERTYPE(WinPrNdrDecoder* context, wStream* s, UPPERTYPE* v) \
481 WINPR_ASSERT(context); \
483 if (!winpr_ndr_decoder_read_align(context, s, sizeof(UPPERTYPE))) \
485 if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(UPPERTYPE))) \
488 if (context->bigEndianDrep) \
489 Stream_Read_##UPPERTYPE##_BE(s, *v); \
491 Stream_Read_##UPPERTYPE(s, *v); \
493 winpr_ndr_decoder_bytes_read(context, sizeof(UPPERTYPE)); \
497 BOOL PREFIX##_decoder_read_##LOWERTYPE##_(WinPrNdrDecoder* context, wStream* s, \
498 const void* hints, void* v) \
500 WINPR_UNUSED(hints); \
501 return PREFIX##_decoder_read_##LOWERTYPE(context, s, (UPPERTYPE*)v); \
504 BOOL PREFIX##_encoder_write_##LOWERTYPE(WinPrNdrEncoder* context, wStream* s, UPPERTYPE v) \
506 if (!winpr_ndr_encoder_write_align(context, s, sizeof(UPPERTYPE)) || \
507 !Stream_EnsureRemainingCapacity(s, sizeof(UPPERTYPE))) \
510 if (context->bigEndianDrep) \
511 Stream_Write_##UPPERTYPE##_BE(s, v); \
513 Stream_Write_##UPPERTYPE(s, v); \
515 winpr_ndr_encoder_bytes_written(context, sizeof(UPPERTYPE)); \
519 BOOL PREFIX##_encoder_write_##LOWERTYPE##_(WinPrNdrEncoder* context, wStream* s, \
520 const void* hints, const void* v) \
522 WINPR_ASSERT(context); \
525 WINPR_UNUSED(hints); \
527 return PREFIX##_encoder_write_##LOWERTYPE(context, s, *(const UPPERTYPE*)v); \
530 const WinPrNdrMessageDescr PREFIX##_##LOWERTYPE##_descr_s = { \
531 WINPR_NDR_ARITY_SIMPLE, \
533 PREFIX##_decoder_read_##LOWERTYPE##_, \
534 PREFIX##_encoder_write_##LOWERTYPE##_, \
539 WinPrNdrMessageType PREFIX##_##LOWERTYPE##_descr(void) \
541 return &PREFIX##_##LOWERTYPE##_descr_s; \
544SIMPLE_TYPE_IMPL(winpr_ndr, UINT32, uint32)
545SIMPLE_TYPE_IMPL(winpr_ndr, INT32, int32)
546SIMPLE_TYPE_IMPL(winpr_ndr, UINT16, uint16)
547SIMPLE_TYPE_IMPL(winpr_ndr, INT16, int16)
548SIMPLE_TYPE_IMPL(winpr_ndr, UINT64, uint64)
549SIMPLE_TYPE_IMPL(winpr_ndr, INT64, int64)
551#define ARRAY_OF_TYPE_IMPL(TYPE, UPPERTYPE) \
552 BOOL winpr_ndr_decoder_read_##TYPE##Array(WinPrNdrDecoder* context, wStream* s, \
553 const void* hints, void* v) \
555 WINPR_ASSERT(context); \
557 return winpr_ndr_decoder_read_uconformant_array(context, s, hints, \
558 winpr_ndr_##TYPE##_descr(), v); \
561 BOOL winpr_ndr_encoder_write_##TYPE##Array(WinPrNdrEncoder* context, wStream* s, \
562 const void* hints, const void* v) \
564 WINPR_ASSERT(context); \
566 const WinPrNdrArrayHints* ahints = (const WinPrNdrArrayHints*)hints; \
567 return winpr_ndr_encoder_write_uconformant_array(context, s, ahints->count, \
568 winpr_ndr_##TYPE##_descr(), v); \
570 void winpr_ndr_destroy_##TYPE##Array(const void* hints, void* obj) \
573 WINPR_ASSERT(hints); \
574 const WinPrNdrArrayHints* ahints = (const WinPrNdrArrayHints*)hints; \
575 WinPrNdrMessageType descr = winpr_ndr_##TYPE##_descr(); \
576 if (descr->destroyFn) \
578 UPPERTYPE* ptr = (UPPERTYPE*)obj; \
579 for (UINT32 i = 0; i < ahints->count; i++, ptr++) \
580 descr->destroyFn(nullptr, ptr); \
584 const WinPrNdrMessageDescr winpr_ndr_##TYPE##Array_descr_s = { \
585 WINPR_NDR_ARITY_ARRAYOF, \
587 winpr_ndr_decoder_read_##TYPE##Array, \
588 winpr_ndr_encoder_write_##TYPE##Array, \
589 winpr_ndr_destroy_##TYPE##Array, \
593 WinPrNdrMessageType winpr_ndr_##TYPE##Array_descr(void) \
595 return &winpr_ndr_##TYPE##Array_descr_s; \
598 BOOL winpr_ndr_decoder_read_##TYPE##VaryingArray(WinPrNdrDecoder* context, wStream* s, \
599 const void* hints, void* v) \
601 WINPR_ASSERT(context); \
603 return winpr_ndr_decoder_read_uconformant_varying_array( \
604 context, s, (const WinPrNdrVaryingArrayHints*)hints, winpr_ndr_##TYPE##_descr(), v); \
606 BOOL winpr_ndr_encoder_write_##TYPE##VaryingArray(WinPrNdrEncoder* context, wStream* s, \
607 const void* hints, const void* v) \
609 WINPR_ASSERT(context); \
611 return winpr_ndr_encoder_write_uconformant_varying_array( \
612 context, s, (const WinPrNdrVaryingArrayHints*)hints, winpr_ndr_##TYPE##_descr(), v); \
615 const WinPrNdrMessageDescr winpr_ndr_##TYPE##VaryingArray_descr_s = { \
616 WINPR_NDR_ARITY_VARYING_ARRAYOF, \
618 winpr_ndr_decoder_read_##TYPE##VaryingArray, \
619 winpr_ndr_encoder_write_##TYPE##VaryingArray, \
624 WinPrNdrMessageType winpr_ndr_##TYPE##VaryingArray_descr(void) \
626 return &winpr_ndr_##TYPE##VaryingArray_descr_s; \
636 if (!winpr_ndr_decoder_read_uint16(context, s, &v))
639 winpr_Data_Write_UINT16(ptr, v);
646 return winpr_ndr_encoder_write_uint16(context, s, winpr_Data_Get_UINT16(ptr));
649static BOOL ndr_decoder_read_wchar_(WinPrNdrDecoder* context,
wStream* s,
const void* hints,
656static BOOL ndr_encoder_write_wchar_(WinPrNdrEncoder* context,
wStream* s,
const void* hints,
665 ndr_encoder_write_wchar_,
nullptr,
nullptr
673ARRAY_OF_TYPE_IMPL(uint8, BYTE)
674ARRAY_OF_TYPE_IMPL(uint16, UINT16)
675ARRAY_OF_TYPE_IMPL(wchar, WCHAR)
677static BOOL winpr_ndr_decoder_read_string_conformant(WinPrNdrDecoder* context,
wStream* s,
678 const void* hints,
void* v)
680 WINPR_ASSERT(context);
686 if (!winpr_ndr_decoder_read_uint32(context, s, &sz))
689 if (!Stream_CheckAndLogRequiredLength(TAG, s, sz))
696 Stream_Read(s, ret, sz);
700 char** pstr = (
char**)v;
705static BOOL winpr_ndr_encoder_write_string_conformant(WinPrNdrEncoder* context,
wStream* s,
706 const void* hints,
const void* v)
708 WINPR_ASSERT(context);
713 const char* str = *(
const char*
const*)v;
714 const size_t l = str ? strlen(str) + 1 : 0;
718 if (!winpr_ndr_encoder_write_uint32(context, s, (UINT32)l) ||
719 !Stream_EnsureRemainingCapacity(s, l))
721 Stream_Write(s, str, l);
729 winpr_ndr_decoder_read_string_conformant,
730 winpr_ndr_encoder_write_string_conformant,
741static BOOL ndr_read_string_varying_header(WinPrNdrDecoder* context,
wStream* s,
size_t charSize,
747 if (!winpr_ndr_decoder_read_uint32(context, s, &maxCount) ||
748 !winpr_ndr_decoder_read_uint32(context, s, &offset) ||
749 !winpr_ndr_decoder_read_uint32(context, s, &length))
754 WLog_ERR(TAG,
"offset != 0 not supported yet");
758 if (length > maxCount)
760 WLog_ERR(TAG,
"varying string length %" PRIu32
" > maximum count %" PRIu32, length,
765 if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, length, charSize))
772static BOOL winpr_ndr_decoder_read_string_varying(WinPrNdrDecoder* context,
wStream* s,
773 const void* hints,
void* v)
775 WINPR_ASSERT(context);
781 if (!ndr_read_string_varying_header(context, s,
sizeof(
char), &length))
789 Stream_Read(s, ret, length);
793 char** pstr = (
char**)v;
798static BOOL winpr_ndr_encoder_write_string_varying(WinPrNdrEncoder* context,
wStream* s,
799 const void* hints,
const void* v)
801 WINPR_ASSERT(context);
806 const char* str = *(
const char*
const*)v;
807 const size_t l = str ? strlen(str) + 1 : 0;
811 if (!winpr_ndr_encoder_write_uint32(context, s, (UINT32)l) ||
812 !winpr_ndr_encoder_write_uint32(context, s, 0) ||
813 !winpr_ndr_encoder_write_uint32(context, s, (UINT32)l) ||
814 !Stream_EnsureRemainingCapacity(s, l))
817 Stream_Write(s, str, l);
825 winpr_ndr_decoder_read_string_varying,
826 winpr_ndr_encoder_write_string_varying,
836static BOOL ndr_decoder_read_wstring_varying(WinPrNdrDecoder* context,
wStream* s,
837 const void* hints,
void* v)
839 WINPR_ASSERT(context);
845 if (!ndr_read_string_varying_header(context, s,
sizeof(WCHAR), &length))
849 (1ull * length + 1) *
sizeof(WCHAR));
853 for (UINT32 i = 0; i < length; i++)
862 WCHAR** pstr = (WCHAR**)v;
867static BOOL ndr_encoder_write_wstring_varying(WinPrNdrEncoder* context,
wStream* s,
868 const void* hints,
const void* v)
870 WINPR_ASSERT(context);
875 const WCHAR* str = *(
const WCHAR*
const*)v;
876 const size_t l = str ? _wcslen(str) + 1 : 0;
880 if (!winpr_ndr_encoder_write_uint32(context, s, (UINT32)l) ||
881 !winpr_ndr_encoder_write_uint32(context, s, 0) ||
882 !winpr_ndr_encoder_write_uint32(context, s, (UINT32)l))
885 for (
size_t i = 0; i < l; i++)
895 ndr_encoder_write_wstring_varying,
nullptr ,
nullptr
904static BOOL ndr_encoder_write_items(WinPrNdrEncoder* context,
wStream* s, UINT32 count,
909 ndr_deferreds_enter(&context->deferreds);
910 for (UINT32 i = 0; ok && (i < count); i++, src += itemType->
itemSize)
911 ok = itemType->
writeFn(context, s,
nullptr, src);
913 return ndr_deferreds_leave(&context->deferreds, ok);
921 WINPR_ASSERT(context);
924 WINPR_ASSERT(itemType);
927 if (!winpr_ndr_encoder_write_uint32(context, s, hints->
maxLength) ||
928 !winpr_ndr_encoder_write_uint32(context, s, 0) ||
929 !winpr_ndr_encoder_write_uint32(context, s, hints->
length))
932 return ndr_encoder_write_items(context, s, hints->
length, itemType, psrc);
938 WINPR_ASSERT(context);
940 WINPR_ASSERT(itemType);
943 if (!winpr_ndr_encoder_write_uint32(context, s, len))
946 return ndr_encoder_write_items(context, s, len, itemType, ptr);
949static BOOL ndr_encoder_write_field(WinPrNdrEncoder* encoder,
wStream* s,
953 const BYTE* ptr = (
const BYTE*)src + field->
structOffset;
954 void* hints = ndr_field_hints(descr, field, src);
962 ok = ndr_encoder_write_items(encoder, s, (UINT32)ndr_inline_count(field),
965 ok = ndr_check_range(descr, field, ptr) &&
970 WLog_ERR(TAG,
"error when writing %s.%s", descr->
name, field->
name);
983 WLog_ERR(TAG,
"%s.%s unknown pointer type 0x%x", descr->
name, field->
name,
989 const void* target = ptr;
992 if (!ndr_check_fixed_array(descr, field, hints))
996 if (ndr_hintsCount(field->
typeDescr, hints) == 0)
1001 ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr,
const void**));
1007 WLog_ERR(TAG,
"%s.%s can't be null", descr->
name, field->
name);
1016 WLog_ERR(TAG,
"error allocating pointer for %s.%s", descr->
name, field->
name);
1023 hints, WINPR_CAST_CONST_PTR_AWAY(target,
void*),
1025 if (!ndr_deferreds_add(&encoder->deferreds, &deferred))
1029 return winpr_ndr_encoder_write_uint32(encoder, s, ptrId);
1035 WINPR_ASSERT(context);
1037 WINPR_ASSERT(descr);
1044 ndr_deferreds_enter(&context->deferreds);
1045 for (
size_t i = 0; ok && (i < descr->
nfields); i++)
1046 ok = ndr_encoder_write_field(context, s, descr, &descr->
fields[i], src);
1048 return ndr_deferreds_leave(&context->deferreds, ok);
1059 WINPR_ASSERT(context);
1060 WINPR_ASSERT(prefId);
1061 WINPR_ASSERT(pnewPtr);
1066 if (context->refIdCounter > UINT32_MAX - 4)
1070 *prefId = context->refIdCounter;
1071 context->refIdCounter += 4;
1077 WINPR_ASSERT(context);
1081 while (ndr_deferreds_pop(&context->deferreds, ¤t))
1083 WLog_VRB(TAG,
"treating write deferred for %s", current.
name);
1086 WLog_ERR(TAG,
"error writing deferred %s", current.
name);
1096 if (!Stream_EnsureRemainingCapacity(s, sz))
1099 Stream_Write(s, data, sz);
1108 WinPrNdrDecoder* ret = calloc(1,
sizeof(*ret));
1112 ret->version = version;
1113 ret->bigEndianDrep = bigEndianDrep;
1114 ret->referents = HashTable_New(FALSE);
1115 if (!ret->referents)
1126 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
1129 BYTE version = Stream_Get_UINT8(s);
1130 BYTE drep = Stream_Get_UINT8(s);
1131 UINT16 headerLen = Stream_Get_UINT16(s);
1135 WLog_ERR(TAG,
"unsupported type serialization version %" PRIu8, version);
1139 if (headerLen < 4 || !Stream_CheckAndLogRequiredLength(TAG, s, headerLen - 4))
1143 Stream_Seek(s, headerLen - 4);
1153 HashTable_Free(context->referents);
1160 WINPR_ASSERT(context);
1161 context->offset += len;
1166 WINPR_ASSERT(context);
1168 if (!Stream_CheckAndLogRequiredLength(TAG, s, nbytes))
1171 context->offset += nbytes;
1172 Stream_Seek(s, nbytes);
1178 WINPR_ASSERT(context);
1180 size_t rest = context->offset % sz;
1183 size_t padding = (sz - rest);
1184 if (!Stream_CheckAndLogRequiredLength(TAG, s, padding))
1187 Stream_Seek(s, padding);
1188 context->offset += padding;
1196 return winpr_ndr_decoder_read_uint32(context, s, refId);
1201 WINPR_ASSERT(context);
1203 WINPR_ASSERT(
object);
1205 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
1209 if (context->bigEndianDrep)
1210 Stream_Read_UINT32_BE(s, len);
1212 Stream_Read_UINT32(s, len);
1215 if (!Stream_CheckAndLogRequiredLength(TAG, s, len))
1218 Stream_StaticInit(
object, Stream_PointerAs(s, BYTE), len);
1219 Stream_Seek(s, len);
1222 context->offset = 0;
1226BOOL winpr_ndr_decoder_read_uint8(WinPrNdrDecoder* context,
wStream* s, BYTE* v)
1228 WINPR_ASSERT(context);
1230 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
1233 Stream_Read_UINT8(s, *v);
1239BOOL winpr_ndr_decoder_read_uint8_(WinPrNdrDecoder* context,
wStream* s,
const void* hints,
void* v)
1241 WINPR_UNUSED(hints);
1242 return winpr_ndr_decoder_read_uint8(context, s, (BYTE*)v);
1245BOOL winpr_ndr_decoder_read_int8(WinPrNdrDecoder* context,
wStream* s, INT8* v)
1250 if (!winpr_ndr_decoder_read_uint8(context, s, &b))
1256BOOL winpr_ndr_decoder_read_int8_(WinPrNdrDecoder* context,
wStream* s,
const void* hints,
void* v)
1258 WINPR_UNUSED(hints);
1259 return winpr_ndr_decoder_read_int8(context, s, (INT8*)v);
1263static BOOL ndr_decoder_read_items(WinPrNdrDecoder* context,
wStream* s, UINT32 count,
1268 ndr_deferreds_enter(&context->deferreds);
1269 for (UINT32 i = 0; ok && (i < count); i++, target += itemType->
itemSize)
1270 ok = itemType->
readFn(context, s,
nullptr, target);
1272 return ndr_deferreds_leave(&context->deferreds, ok);
1280static BOOL ndr_check_array_bound(
const char* what, UINT32 count,
WinPrNdrMessageType itemType,
1287 WLog_ERR(TAG,
"%s: conformance %" PRIu32
" doesn't match the expected count %" PRIu32,
1288 what, count, limit);
1294 if ((1ull * count * itemType->
itemSize) > limit)
1296 WLog_ERR(TAG,
"%s: %" PRIu32
" items don't fit in %" PRIu32, what, count, limit);
1303static BOOL ndr_read_varying_array_header(WinPrNdrDecoder* context,
wStream* s,
1311 UINT32 maxCount = 0;
1315 if (!winpr_ndr_decoder_read_uint32(context, s, &maxCount) ||
1316 !winpr_ndr_decoder_read_uint32(context, s, &offset) ||
1317 !winpr_ndr_decoder_read_uint32(context, s, &length))
1320 if (length > maxCount)
1322 WLog_ERR(TAG,
"varying array length %" PRIu32
" > maximum count %" PRIu32, length,
1336 *pmaxCount = maxCount;
1345 WINPR_ASSERT(context);
1347 WINPR_ASSERT(itemType);
1348 WINPR_ASSERT(target);
1353 UINT32 maxCount = 0;
1355 if (!ndr_read_varying_array_header(context, s, hints, itemType, &maxCount, &length))
1358 return ndr_decoder_read_items(context, s, length, itemType, target);
1365 WINPR_ASSERT(context);
1367 WINPR_ASSERT(itemType);
1368 WINPR_ASSERT(vtarget);
1377 if (!winpr_ndr_decoder_read_uint32(context, s, &count))
1380 if (!ndr_check_array_bound(
"conformant array", count, itemType, hints->
count))
1383 return ndr_decoder_read_items(context, s, count, itemType, vtarget);
1387static BOOL ndr_referent_size(
const char* name,
WinPrNdrMessageType descr,
const void* hints,
1392 WLog_ERR(TAG,
"%s: the size of a self allocated type isn't known", name);
1398 WLog_ERR(TAG,
"%s: no hints for an array", name);
1405 const size_t count = ndr_hintsCount(descr, hints);
1406 if (count > SIZE_MAX / descr->
itemSize)
1410 *psize = (count ? count : 1) * descr->
itemSize;
1414static void ndr_referent_copy_hints(NdrReferent* ref,
const void* hints)
1416 ref->hasHints = FALSE;
1417 if (!ref->typeDescr || !hints)
1420 switch (ref->typeDescr->arity)
1424 ref->hasHints = TRUE;
1428 ref->hasHints = TRUE;
1435static NdrReferent* ndr_referent_new(
WinPrNdrMessageType descr,
const void* hints,
size_t size)
1437 const size_t headerSize = offsetof(NdrReferent, payload);
1438 const size_t payloadSize =
sizeof(((NdrReferent*)
nullptr)->payload);
1439 if (size > SIZE_MAX - headerSize - payloadSize)
1442 NdrReferent* ref = calloc(1, headerSize + ((size > payloadSize) ? size : payloadSize));
1446 ref->magic = NDR_REFERENT_MAGIC;
1448 ref->typeDescr = descr;
1450 ndr_referent_copy_hints(ref, hints);
1456 NdrReferent* ref = ndr_referent_new(descr, hints, size);
1457 WINPR_PRAGMA_DIAG_PUSH
1458 WINPR_DO_COVERITY_PRAGMA(
1459 coverity compliance block
1460 deviate
'RESOURCE_LEAK' 'returning a constant offset pointer, winpr_ndr_pointer_release does the inverse')
1461 return ref ? ref->payload.data :
nullptr;
1462 WINPR_PRAGMA_DIAG_POP
1470 return ndr_referent(WINPR_CAST_CONST_PTR_AWAY(ptr,
void*))->size;
1478 NdrReferent* ref = ndr_referent(ptr);
1479 InterlockedIncrement(&ref->refCount);
1488 NdrReferent* ref = ndr_referent(ptr);
1489 if (InterlockedDecrement(&ref->refCount) > 0)
1492 if (ref->typeDescr && ref->typeDescr->destroyFn)
1493 ref->typeDescr->destroyFn(ref->hasHints ? &ref->hints :
nullptr, ref->payload.data);
1499static BOOL ndr_decoder_register(WinPrNdrDecoder* decoder, NdrReferent* ref,
winpr_ndr_refid refId,
1504 return HashTable_Insert(decoder->referents, (
void*)(UINT_PTR)refId, ref);
1508static BOOL ndr_referent_check(
const char* name,
const NdrReferent* ref,
WinPrNdrMessageType descr,
1509 const void* hints,
size_t size)
1513 WLog_ERR(TAG,
"%s: referent 0x%" PRIx32
" is the referent of a unique pointer", name,
1518 if ((ref->typeDescr != descr) || (ref->size != size))
1521 "%s: referent 0x%" PRIx32
" is aliased with another type or size (%" PRIuz
1522 " vs %" PRIuz
" bytes)",
1523 name, ref->refId, size, ref->size);
1528 switch (descr->arity)
1531 same = memcmp(&ref->hints.array, hints,
sizeof(ref->hints.array)) == 0;
1534 same = memcmp(&ref->hints.varying, hints,
sizeof(ref->hints.varying)) == 0;
1541 WLog_ERR(TAG,
"%s: referent 0x%" PRIx32
" is aliased with other hints", name, ref->refId);
1546static BOOL ndr_decoder_read_full_pointer(WinPrNdrDecoder* decoder,
wStream* s,
const char* name,
1549 WINPR_ASSERT(decoder);
1550 WINPR_ASSERT(descr);
1551 WINPR_ASSERT(target);
1562 if (!ndr_referent_size(name, descr, hints, &size))
1565 NdrReferent* ref = HashTable_GetItemValue(decoder->referents, (
void*)(UINT_PTR)refId);
1568 if (!ndr_referent_check(name, ref, descr, hints, size))
1577 !Stream_CheckAndLogRequiredLength(TAG, s, ndr_hintsCount(descr, hints)))
1580 ref = ndr_referent_new(descr, hints, size);
1584 if (!ndr_decoder_register(decoder, ref, refId, TRUE))
1591 *target = ref->payload.data;
1593 return ndr_deferreds_add(&decoder->deferreds, &deferred);
1599 WINPR_ASSERT(context);
1601 ndr_deferreds_enter(&context->deferreds);
1602 const BOOL ok = ndr_decoder_read_full_pointer(context, s, name, descr, hints, target);
1603 return ndr_deferreds_leave(&context->deferreds, ok);
1606static BOOL ndr_decoder_read_field(WinPrNdrDecoder* decoder,
wStream* s,
1611 void* hints = ndr_field_hints(descr, field, target);
1619 ok = ndr_decoder_read_items(decoder, s, (UINT32)ndr_inline_count(field),
1623 ndr_check_range(descr, field, ptr);
1627 WLog_ERR(TAG,
"error when reading %s.%s", descr->name, field->
name);
1636 void* referent =
nullptr;
1637 const BOOL ok = ndr_decoder_read_full_pointer(decoder, s, field->
name, field->
typeDescr,
1641 memcpy(ptr, (
const void*)&referent,
sizeof(referent));
1644 WLog_ERR(TAG,
"error when reading %s.%s", descr->name, field->
name);
1656 WLog_ERR(TAG,
"%s.%s unknown pointer type 0x%x", descr->name, field->
name,
1667 WLog_ERR(TAG,
"error when reading %s.%s", descr->name, field->
name);
1671 if (!deferred.
ptrId)
1675 WLog_ERR(TAG,
"%s.%s can't be null", descr->name, field->
name);
1681 (!hints || (ndr_hintsCount(field->
typeDescr, hints) != 0)))
1683 WLog_ERR(TAG,
"%s.%s is null but the array is not empty", descr->name, field->
name);
1689 if (!deferred.
allocate && !ndr_check_fixed_array(descr, field, hints))
1692 return ndr_deferreds_add(&decoder->deferreds, &deferred);
1698 WINPR_ASSERT(context);
1700 WINPR_ASSERT(descr);
1701 WINPR_ASSERT(target);
1707 ndr_deferreds_enter(&context->deferreds);
1708 for (
size_t i = 0; ok && (i < descr->
nfields); i++)
1709 ok = ndr_decoder_read_field(context, s, descr, &descr->
fields[i], target);
1711 return ndr_deferreds_leave(&context->deferreds, ok);
1716 WINPR_ASSERT(context);
1720 while (ndr_deferreds_pop(&context->deferreds, ¤t))
1722 WLog_VRB(TAG,
"treating read deferred 0x%x for %s", current.
ptrId, current.
name);
1733 WLog_ERR(TAG,
"error parsing deferred %s", current.
name);
1743 void* hints,
void** target)
1745 WINPR_ASSERT(context);
1747 WINPR_ASSERT(descr);
1748 WINPR_ASSERT(target);
1755 if (HashTable_Contains(context->referents, (
void*)(UINT_PTR)ptrId))
1757 WLog_ERR(TAG,
"unique pointer 0x%" PRIx32
" is aliased", ptrId);
1761 void* ret =
nullptr;
1765 if (!descr->
readFn(context, s, hints, (
void*)&ret))
1773 if (!ndr_referent_size(
"unique pointer", descr, hints, &size))
1779 !Stream_CheckAndLogRequiredLength(TAG, s, ndr_hintsCount(descr, hints)))
1786 if (!descr->
readFn(context, s, hints, ret))
1793 if (!ndr_decoder_register(context, ndr_referent(ret), ptrId, FALSE))
1806 WINPR_ASSERT(context);
1807 WINPR_ASSERT(deferreds);
1810 ndr_deferreds_enter(&context->deferreds);
1811 for (
size_t i = 0; ok && (i < ndeferred); i++)
1812 ok = ndr_deferreds_add(&context->deferreds, &deferreds[i]);
1814 return ndr_deferreds_leave(&context->deferreds, ok);
1825 WINPR_ASSERT(descr);
1832 WINPR_ASSERT(switchField);
1834 if (ndr_union_switch_size(descr) == 2)
1835 return *(
const UINT16*)switchField;
1836 return *(
const UINT32*)switchField;
1843 WINPR_ASSERT(descr);
1845 for (
size_t i = 0; i < descr->
narms; i++)
1858 WINPR_ASSERT(armStruct);
1863 WLog_ERR(TAG,
"%s: no arm for the discriminant 0x%08" PRIx32, descr->
name, switchValue);
1875static BOOL ndr_encoder_write_discriminant(WinPrNdrEncoder* context,
wStream* s,
1878 if (ndr_union_switch_size(descr) == 4)
1879 return winpr_ndr_encoder_write_uint32(context, s, switchValue);
1881 if (switchValue > UINT16_MAX)
1883 WLog_ERR(TAG,
"%s: discriminant 0x%08" PRIx32
" doesn't fit in 16 bits", descr->
name,
1887 return winpr_ndr_encoder_write_uint16(context, s, (UINT16)switchValue);
1890static BOOL ndr_decoder_read_discriminant(WinPrNdrDecoder* context,
wStream* s,
1893 if (ndr_union_switch_size(descr) == 4)
1894 return winpr_ndr_decoder_read_uint32(context, s, discriminant);
1897 if (!winpr_ndr_decoder_read_uint16(context, s, &v))
1907 WINPR_ASSERT(context);
1911 const WinPrNdrUnionArm* arm = ndr_union_arm(descr, switchValue, &armStruct);
1912 if (!arm || !ndr_encoder_write_discriminant(context, s, descr, switchValue))
1916 ndr_deferreds_enter(&context->deferreds);
1918 ok = ndr_encoder_write_field(context, s, &armStruct, &arm->
field, src);
1919 return ndr_deferreds_leave(&context->deferreds, ok);
1925 WINPR_ASSERT(context);
1926 WINPR_ASSERT(target);
1928 UINT32 discriminant = 0;
1929 if (!ndr_decoder_read_discriminant(context, s, descr, &discriminant))
1932 if (discriminant != switchValue)
1935 "%s: discriminant 0x%08" PRIx32
" doesn't match the switch value 0x%08" PRIx32,
1936 descr->
name, discriminant, switchValue);
1941 const WinPrNdrUnionArm* arm = ndr_union_arm(descr, switchValue, &armStruct);
1946 ndr_deferreds_enter(&context->deferreds);
1948 ok = ndr_decoder_read_field(context, s, &armStruct, &arm->
field, target);
1949 return ndr_deferreds_leave(&context->deferreds, ok);
1954 WINPR_ASSERT(descr);
1970 char tabArray[30 + 1];
1971 size_t ntabs = (identLevel <= 30) ? identLevel : 30;
1973 memset(tabArray,
'\t', ntabs);
1974 tabArray[ntabs] = 0;
1976 WLog_Print(logger, lvl,
"%s%s", tabArray, descr->
name);
1977 for (
size_t i = 0; i < descr->
nfields; i++)
1980 const BYTE* ptr = (
const BYTE*)obj + field->
structOffset;
1987 ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr,
const void**));
1993 WLog_ERR(TAG,
"invalid field->pointerType");
1997 WLog_Print(logger, lvl,
"%s*%s:", tabArray, field->
name);
2001 WLog_Print(logger, lvl,
"%s\t<no dump function>", tabArray);
2007 WINPR_ASSERT(descr);
2010 for (
size_t i = 0; i < descr->
nfields; i++)
2014 void* hints =
nullptr;
2032 void** pptrField = (
void**)ptr;
2034 *pptrField =
nullptr;
NDR encoder and decoder ([C706] chapter 14, [MS-RPCE] 2.2.5 and 2.2.6)
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_encoder_allocatePtr(WinPrNdrEncoder *context, const void *ptr, winpr_ndr_refid *prefId, BOOL *pnewPtr)
allocates the referent id of an embedded pointer
WinPrNdrFieldStruct field
WINPR_API void winpr_ndr_decoder_free(WinPrNdrDecoder *context)
frees a decoder
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_encoder_end_object(WinPrNdrEncoder *context, wStream *s)
ends the serialized object started with winpr_ndr_encoder_start_object()
UINT32 winpr_ndr_refid
a referent id, the wire value of a pointer (WINPR_NDR_PTR_NULL for a null pointer)
@ WINPR_NDR_POINTER_NON_NULL
WinPrNdrPointerType pointerType
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_encoder_write_wchar(WinPrNdrEncoder *context, wStream *s, const WCHAR *ptr)
writes a WCHAR stored in UTF-16LE, aligned on 2 bytes
WINPR_API void * winpr_ndr_pointer_ref(void *ptr)
takes a reference on a referent
WINPR_ATTR_NODISCARD WINPR_API WinPrNdrMessageType winpr_ndr_string_varying_descr(void)
the message type of winpr_ndr_string_varying_descr_s
WINPR_ATTR_NODISCARD WINPR_API winpr_ndr_refid winpr_ndr_pointer_refid(const void *ptr)
a referent id built from the value of a pointer
WINPR_API void winpr_ndr_struct_dump_fromDescr(wLog *logger, UINT32 lvl, size_t identLevel, const WinPrNdrStructDescr *descr, const void *obj)
dumps a structure in the logs from its descriptor
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_encoder_write_header(WinPrNdrEncoder *context, wStream *s)
writes the common type header ([MS-RPCE] 2.2.6.1): version, endianness, header length and filler
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_decoder_read_uconformant_array(WinPrNdrDecoder *context, wStream *s, const WinPrNdrArrayHints *hints, WinPrNdrMessageType itemType, void *vtarget)
reads a conformant array: its count and the items
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_decoder_skip_bytes(WinPrNdrDecoder *context, wStream *s, size_t nbytes)
skips bytes
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_decoder_read_pointedMessageEx(WinPrNdrDecoder *context, wStream *s, winpr_ndr_refid ptrId, WinPrNdrMessageType descr, void *hints, void **target)
reads the referent of a unique pointer
const WinPrNdrFieldStruct * fields
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_decoder_read_fromDescr(WinPrNdrDecoder *context, wStream *s, const WinPrNdrStructDescr *descr, void *target)
reads a structure from its descriptor
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_encoder_write_data(WinPrNdrEncoder *context, wStream *s, const void *data, size_t sz)
writes raw bytes, without alignment
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_decoder_read_uconformant_varying_array(WinPrNdrDecoder *context, wStream *s, const WinPrNdrVaryingArrayHints *hints, WinPrNdrMessageType itemType, void *ptarget)
reads a conformant varying array: its maximum count, offset, actual count and the items
WINPR_ATTR_NODISCARD WINPR_API WinPrNdrMessageType winpr_ndr_wstring_varying_descr(void)
the message type of winpr_ndr_wstring_varying_descr_s
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_encoder_treat_deferreds(WinPrNdrEncoder *context, wStream *s)
writes the pending deferred referents
WINPR_API WinPrNdrDecoder * winpr_ndr_decoder_new(BOOL bigEndianDrep, BYTE version)
creates a decoder
WINPR_API WinPrNdrEncoder * winpr_ndr_encoder_new(BOOL bigEndianDrep, BYTE version)
creates an encoder
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_decoder_read_union(WinPrNdrDecoder *context, wStream *s, const WinPrNdrUnionDescr *descr, UINT32 switchValue, void *target)
reads a union, its discriminant must be switchValue
WINPR_ATTR_NODISCARD WINPR_API WinPrNdrMessageType winpr_ndr_string_conformant_descr(void)
the message type of winpr_ndr_string_conformant_descr_s
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_encoder_write_toplevel_refid(WinPrNdrEncoder *context, wStream *s)
writes a new referent id for a non-null top-level pointer
WINPR_NDR_DESTROY_FN destroyFn
WINPR_ATTR_NODISCARD WINPR_API size_t winpr_ndr_pointer_size(const void *ptr)
the size of a referent, as allocated by winpr_ndr_pointer_new()
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_decoder_read_wchar(WinPrNdrDecoder *context, wStream *s, WCHAR *ptr)
reads a WCHAR, aligned on 2 bytes
@ WINPR_NDR_ARITY_VARYING_ARRAYOF
@ WINPR_NDR_ARITY_ARRAYOF
@ WINPR_NDR_ARITY_SELF_ALLOCATED
WINPR_API const WinPrNdrMessageDescr winpr_ndr_string_varying_descr_s
[string] char*: a conformant varying string of char, read in a char* allocated with winpr_ndr_pointer...
WINPR_API void winpr_ndr_encoder_free(WinPrNdrEncoder *context)
frees an encoder
WINPR_API const WinPrNdrMessageDescr winpr_ndr_wstring_varying_descr_s
[string] wchar_t*: a conformant varying string of WCHAR, read in a WCHAR* allocated with winpr_ndr_po...
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_decoder_read_refpointer(WinPrNdrDecoder *context, wStream *s, UINT32 *refId)
reads a referent id
WINPR_API void winpr_ndr_decoder_bytes_read(WinPrNdrDecoder *context, size_t len)
accounts for bytes read directly from the stream, for the alignments
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_decoder_read_align(WinPrNdrDecoder *context, wStream *s, size_t sz)
skips the padding to align the current object offset
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_encoder_write_uconformant_varying_array(WinPrNdrEncoder *context, wStream *s, const WinPrNdrVaryingArrayHints *hints, WinPrNdrMessageType itemType, const void *src)
writes a conformant varying array: its maximum count, offset 0, actual count and the items
WINPR_API WinPrNdrEncoder * winpr_ndr_encoder_from_decoder(const WinPrNdrDecoder *src)
creates an encoder with the same endianness and version as a decoder, to answer the message it decode...
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_encoder_write_union(WinPrNdrEncoder *context, wStream *s, const WinPrNdrUnionDescr *descr, UINT32 switchValue, const void *src)
writes the discriminant and the arm of a union
WINPR_API void * winpr_ndr_pointer_new(WinPrNdrMessageType descr, const void *hints, size_t size)
allocates a referent
WINPR_API void winpr_ndr_encoder_bytes_written(WinPrNdrEncoder *context, size_t len)
accounts for bytes written directly in the stream, for the alignments
WINPR_API void winpr_ndr_pointer_release(void *ptr)
releases a referent
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_decoder_read_object(WinPrNdrDecoder *context, wStream *s, wStream *object)
reads the private header of a serialized object ([MS-RPCE] 2.2.6 type serialization version 1)
const WinPrNdrUnionArm * arms
const WinPrNdrUnionArm * defaultArm
WINPR_API WinPrNdrDecoder * winpr_ndr_decoder_new_fromStream(wStream *s)
reads the common type header ([MS-RPCE] 2.2.6.1) and creates a decoder for its version and endianness
WINPR_ATTR_NODISCARD WINPR_API WinPrNdrMessageType winpr_ndr_wchar_descr(void)
the message type of winpr_ndr_wchar_descr_s
WINPR_ATTR_NODISCARD WINPR_API UINT32 winpr_ndr_union_switch_value(const WinPrNdrUnionDescr *descr, const void *switchField)
the value of the [switch_is()] field of a union
WINPR_API const WinPrNdrMessageDescr winpr_ndr_wchar_descr_s
a WCHAR, stored in UTF-16LE in memory
WINPR_NDR_WRITER_FN writeFn
WINPR_API void winpr_ndr_struct_destroy(const WinPrNdrStructDescr *descr, void *pptr)
releases the content of a structure from its descriptor
WINPR_NDR_READER_FN readFn
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_encoder_write_fromDescr(WinPrNdrEncoder *context, wStream *s, const WinPrNdrStructDescr *descr, const void *src)
writes a structure from its descriptor
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_decoder_push_deferreds(WinPrNdrDecoder *context, WinPrNdrDeferredEntry *deferreds, size_t ndeferred)
adds deferred pointers, read by the next winpr_ndr_decoder_treat_deferreds()
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_encoder_write_uconformant_array(WinPrNdrEncoder *context, wStream *s, UINT32 len, WinPrNdrMessageType itemType, const BYTE *ptr)
writes a conformant array: its count and the items
WINPR_API const WinPrNdrMessageDescr winpr_ndr_string_conformant_descr_s
conformant string of char, read in a char* allocated with winpr_ndr_pointer_new()
WINPR_API void winpr_ndr_union_destroy(const WinPrNdrUnionDescr *descr, UINT32 switchValue, void *obj)
releases the content of the arm switchValue of a union
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_encoder_write_align(WinPrNdrEncoder *context, wStream *s, size_t sz)
writes zero padding to align the current object offset
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_decoder_read_pointer_ex(WinPrNdrDecoder *context, wStream *s, const char *name, WinPrNdrMessageType descr, void *hints, void **target)
reads a full pointer ([ptr])
#define WINPR_NDR_PTR_NULL
the referent id of a null pointer
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_encoder_start_object(WinPrNdrEncoder *context, wStream *s)
starts a serialized object ([MS-RPCE] 2.2.6 type serialization version 1)
WinPrNdrMessageType typeDescr
WINPR_ATTR_NODISCARD WINPR_API BOOL winpr_ndr_decoder_treat_deferreds(WinPrNdrDecoder *context, wStream *s)
reads the pending deferred referents
hints for a conformant array
a deferred pointer, a referent to read after the current construct
descriptor of a field in a structure
an arm of a non-encapsulated union
a non-encapsulated union ([switch_type()])
hints for a conformant varying array