19 #include <winpr/assert.h>
20 #include <winpr/collections.h>
21 #include <winpr/wlog.h>
23 #include <freerdp/log.h>
25 #include <rdpear-common/ndr.h>
27 #define TAG FREERDP_TAG("ndr")
29 #define NDR_MAX_CONSTRUCTS 16
30 #define NDR_MAX_DEFERRED 50
39 size_t indentLevels[16];
42 size_t constructs[NDR_MAX_CONSTRUCTS];
44 wHashTable* refPointers;
51 NdrContext* ndr_context_new(BOOL bigEndianDrep, BYTE version)
53 NdrContext* ret = calloc(1,
sizeof(*ret));
57 ret->version = version;
58 ret->bigEndianDrep = bigEndianDrep;
60 ret->refPointers = HashTable_New(FALSE);
61 if (!ret->refPointers)
67 ndr_context_reset(ret);
71 void ndr_context_reset(NdrContext* context)
73 WINPR_ASSERT(context);
75 context->currentLevel = 0;
76 context->constructLevel = -1;
77 memset(context->indentLevels, 0,
sizeof(context->indentLevels));
79 if (context->refPointers)
80 HashTable_Clear(context->refPointers);
81 context->ndeferred = 0;
82 context->refIdCounter = 0x20000;
85 NdrContext* ndr_context_copy(
const NdrContext* src)
89 NdrContext* ret = calloc(1,
sizeof(*ret));
95 ret->refPointers = HashTable_New(FALSE);
96 if (!ret->refPointers)
102 ndr_context_reset(ret);
106 void ndr_context_free(NdrContext* context)
110 HashTable_Free(context->refPointers);
115 static void ndr_context_bytes_read(NdrContext* context,
size_t len)
117 WINPR_ASSERT(context);
118 context->indentLevels[context->currentLevel] += len;
121 static void ndr_context_bytes_written(NdrContext* context,
size_t len)
123 ndr_context_bytes_read(context, len);
126 NdrContext* ndr_read_header(
wStream* s)
128 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
131 BYTE version = Stream_Get_UINT8(s);
132 BYTE drep = Stream_Get_UINT8(s);
133 UINT16 headerLen = Stream_Get_UINT16(s);
135 if (headerLen < 4 || !Stream_CheckAndLogRequiredLength(TAG, s, headerLen - 4))
139 Stream_Seek(s, headerLen - 4);
141 return ndr_context_new((drep != 0x10), version);
144 BOOL ndr_write_header(NdrContext* context,
wStream* s)
146 WINPR_ASSERT(context);
148 if (!Stream_EnsureRemainingCapacity(s, 8))
151 Stream_Write_UINT8(s, context->version);
152 Stream_Write_UINT8(s, context->bigEndianDrep ? 0x00 : 0x10);
153 Stream_Write_UINT16(s, 0x8);
155 BYTE filler[] = { 0xcc, 0xcc, 0xcc, 0xcc };
156 Stream_Write(s, filler,
sizeof(filler));
160 BOOL ndr_skip_bytes(NdrContext* context,
wStream* s,
size_t nbytes)
162 WINPR_ASSERT(context);
164 if (!Stream_CheckAndLogRequiredLength(TAG, s, nbytes))
167 context->indentLevels[context->currentLevel] += nbytes;
168 Stream_Seek(s, nbytes);
172 BOOL ndr_read_align(NdrContext* context,
wStream* s,
size_t sz)
174 WINPR_ASSERT(context);
176 size_t rest = context->indentLevels[context->currentLevel] % sz;
179 size_t padding = (sz - rest);
180 if (!Stream_CheckAndLogRequiredLength(TAG, s, padding))
183 Stream_Seek(s, padding);
184 context->indentLevels[context->currentLevel] += padding;
190 BOOL ndr_write_align(NdrContext* context,
wStream* s,
size_t sz)
192 WINPR_ASSERT(context);
194 size_t rest = context->indentLevels[context->currentLevel] % sz;
197 size_t padding = (sz - rest);
199 if (!Stream_EnsureRemainingCapacity(s, padding))
202 Stream_Zero(s, padding);
203 context->indentLevels[context->currentLevel] += padding;
209 BOOL ndr_read_pickle(NdrContext* context,
wStream* s)
211 WINPR_ASSERT(context);
216 if (!ndr_read_uint32(context, s, &v) || v != 0x20000)
219 return ndr_read_uint32(context, s, &v);
222 BOOL ndr_write_pickle(NdrContext* context,
wStream* s)
224 WINPR_ASSERT(context);
227 if (!ndr_write_uint32(context, s, 0x20000))
230 ndr_write_uint32(context, s, 0);
234 BOOL ndr_read_constructed(NdrContext* context,
wStream* s,
wStream* target)
236 WINPR_ASSERT(context);
241 if (!ndr_read_uint32(context, s, &len))
245 if (!ndr_skip_bytes(context, s, 4))
249 if (!Stream_CheckAndLogRequiredLength(TAG, s, len))
252 Stream_StaticInit(target, Stream_PointerAs(s, BYTE), len);
257 BOOL ndr_start_constructed(NdrContext* context,
wStream* s)
259 WINPR_ASSERT(context);
261 if (!Stream_EnsureCapacity(s, 8))
264 if (context->constructLevel == NDR_MAX_CONSTRUCTS)
267 context->constructLevel++;
268 context->constructs[context->constructLevel] = Stream_GetPosition(s);
274 BOOL ndr_end_constructed(NdrContext* context,
wStream* s)
276 WINPR_ASSERT(context);
277 WINPR_ASSERT(context->constructs);
278 WINPR_ASSERT(context->constructLevel >= 0);
280 size_t offset = context->constructs[context->constructLevel];
283 Stream_StaticInit(&staticS, Stream_Buffer(s) + offset, 4);
286 const size_t len = Stream_GetPosition(s) - (offset + 8);
287 if (len > UINT32_MAX)
289 if (!ndr_write_uint32(context, &staticS, (UINT32)len))
295 static size_t ndr_hintsCount(
NdrMessageType msgType,
const void* hints)
297 WINPR_ASSERT(msgType);
299 switch (msgType->arity)
301 case NDR_ARITY_SIMPLE:
303 case NDR_ARITY_ARRAYOF:
306 case NDR_ARITY_VARYING_ARRAYOF:
310 WINPR_ASSERT(0 &&
"unknown arity");
315 BOOL ndr_read_uint8(NdrContext* context,
wStream* s, BYTE* v)
317 WINPR_ASSERT(context);
319 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
322 Stream_Read_UINT8(s, *v);
324 ndr_context_bytes_read(context, 1);
328 BOOL ndr_read_uint8_(NdrContext* context,
wStream* s,
const void* hints,
void* v)
331 return ndr_read_uint8(context, s, (BYTE*)v);
334 BOOL ndr_write_uint8(NdrContext* context,
wStream* s, BYTE v)
336 if (!Stream_EnsureRemainingCapacity(s, 1))
339 Stream_Write_UINT8(s, v);
340 ndr_context_bytes_written(context, 1);
344 BOOL ndr_write_uint8_(NdrContext* context,
wStream* s,
const void* hints,
const void* v)
346 WINPR_ASSERT(context);
351 return ndr_write_uint8(context, s, *(
const BYTE*)v);
354 const static NdrMessageDescr uint8_descr = { NDR_ARITY_SIMPLE, 1, ndr_read_uint8_,
355 ndr_write_uint8_, NULL, NULL };
362 #define SIMPLE_TYPE_IMPL(UPPERTYPE, LOWERTYPE) \
363 BOOL ndr_read_##LOWERTYPE(NdrContext* context, wStream* s, UPPERTYPE* v) \
365 WINPR_ASSERT(context); \
367 if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(UPPERTYPE))) \
370 if (!ndr_read_align(context, s, sizeof(UPPERTYPE))) \
373 if (context->bigEndianDrep) \
374 Stream_Read_##UPPERTYPE##_BE(s, *v); \
376 Stream_Read_##UPPERTYPE(s, *v); \
378 ndr_context_bytes_read(context, sizeof(UPPERTYPE)); \
382 BOOL ndr_read_##LOWERTYPE##_(NdrContext* context, wStream* s, const void* hints, void* v) \
384 WINPR_UNUSED(hints); \
385 return ndr_read_##LOWERTYPE(context, s, (UPPERTYPE*)v); \
388 BOOL ndr_write_##LOWERTYPE(NdrContext* context, wStream* s, UPPERTYPE v) \
390 if (!ndr_write_align(context, s, sizeof(UPPERTYPE)) || \
391 !Stream_EnsureRemainingCapacity(s, sizeof(UPPERTYPE))) \
394 if (context->bigEndianDrep) \
395 Stream_Write_##UPPERTYPE##_BE(s, v); \
397 Stream_Write_##UPPERTYPE(s, v); \
399 ndr_context_bytes_written(context, sizeof(UPPERTYPE)); \
403 BOOL ndr_write_##LOWERTYPE##_(NdrContext* context, wStream* s, const void* hints, \
406 WINPR_ASSERT(context); \
409 WINPR_UNUSED(hints); \
411 return ndr_write_##LOWERTYPE(context, s, *(const UPPERTYPE*)v); \
414 const NdrMessageDescr ndr_##LOWERTYPE##_descr_s = { NDR_ARITY_SIMPLE, \
416 ndr_read_##LOWERTYPE##_, \
417 ndr_write_##LOWERTYPE##_, \
421 NdrMessageType ndr_##LOWERTYPE##_descr(void) \
423 return &ndr_##LOWERTYPE##_descr_s; \
426 SIMPLE_TYPE_IMPL(UINT32, uint32)
427 SIMPLE_TYPE_IMPL(UINT16, uint16)
428 SIMPLE_TYPE_IMPL(UINT64, uint64)
430 #define ARRAY_OF_TYPE_IMPL(TYPE, UPPERTYPE) \
431 BOOL ndr_read_##TYPE##Array(NdrContext* context, wStream* s, const void* hints, void* v) \
433 WINPR_ASSERT(context); \
435 WINPR_ASSERT(hints); \
436 return ndr_read_uconformant_array(context, s, hints, ndr_##TYPE##_descr(), v); \
439 BOOL ndr_write_##TYPE##Array(NdrContext* context, wStream* s, const void* hints, \
442 WINPR_ASSERT(context); \
444 WINPR_ASSERT(hints); \
445 const NdrArrayHints* ahints = (const NdrArrayHints*)hints; \
446 return ndr_write_uconformant_array(context, s, ahints->count, ndr_##TYPE##_descr(), v); \
448 void ndr_destroy_##TYPE##Array(NdrContext* context, const void* hints, void* obj) \
450 WINPR_ASSERT(context); \
452 WINPR_ASSERT(hints); \
453 const NdrArrayHints* ahints = (const NdrArrayHints*)hints; \
454 NdrMessageType descr = ndr_##TYPE##_descr(); \
455 if (descr->destroyFn) \
457 UPPERTYPE* ptr = (UPPERTYPE*)obj; \
458 for (UINT32 i = 0; i < ahints->count; i++, ptr++) \
459 descr->destroyFn(context, NULL, ptr); \
463 const NdrMessageDescr ndr_##TYPE##Array_descr_s = { \
464 NDR_ARITY_ARRAYOF, sizeof(UPPERTYPE), ndr_read_##TYPE##Array, \
465 ndr_write_##TYPE##Array, ndr_destroy_##TYPE##Array, NULL \
468 NdrMessageType ndr_##TYPE##Array_descr(void) \
470 return &ndr_##TYPE##Array_descr_s; \
473 BOOL ndr_read_##TYPE##VaryingArray(NdrContext* context, wStream* s, const void* hints, \
476 WINPR_ASSERT(context); \
478 WINPR_ASSERT(hints); \
479 return ndr_read_uconformant_varying_array(context, s, (const NdrVaryingArrayHints*)hints, \
480 ndr_##TYPE##_descr(), v); \
482 BOOL ndr_write_##TYPE##VaryingArray(NdrContext* context, wStream* s, const void* hints, \
485 WINPR_ASSERT(context); \
487 WINPR_ASSERT(hints); \
488 return ndr_write_uconformant_varying_array(context, s, (const NdrVaryingArrayHints*)hints, \
489 ndr_##TYPE##_descr(), v); \
492 const NdrMessageDescr ndr_##TYPE##VaryingArray_descr_s = { NDR_ARITY_VARYING_ARRAYOF, \
494 ndr_read_##TYPE##VaryingArray, \
495 ndr_write_##TYPE##VaryingArray, \
499 NdrMessageType ndr_##TYPE##VaryingArray_descr(void) \
501 return &ndr_##TYPE##VaryingArray_descr_s; \
504 ARRAY_OF_TYPE_IMPL(uint8, BYTE)
505 ARRAY_OF_TYPE_IMPL(uint16, UINT16)
507 BOOL ndr_read_wchar(NdrContext* context,
wStream* s, WCHAR* ptr)
509 return ndr_read_uint16(context, s, (UINT16*)ptr);
512 BOOL ndr_read_uconformant_varying_array(NdrContext* context,
wStream* s,
516 WINPR_ASSERT(context);
519 WINPR_ASSERT(itemType);
520 WINPR_ASSERT(ptarget);
526 if (!ndr_read_uint32(context, s, &maxCount) || !ndr_read_uint32(context, s, &offset) ||
527 !ndr_read_uint32(context, s, &length))
530 if ((length * itemType->itemSize) < hints->length)
533 if ((maxCount * itemType->itemSize) < hints->maxLength)
536 BYTE* target = (BYTE*)ptarget;
537 for (UINT32 i = 0; i < length; i++, target += itemType->itemSize)
539 if (!itemType->readFn(context, s, NULL, target))
543 return ndr_read_align(context, s, 4);
546 BOOL ndr_write_uconformant_varying_array(NdrContext* context,
wStream* s,
550 WINPR_ASSERT(context);
553 WINPR_ASSERT(itemType);
556 if (!ndr_write_uint32(context, s, hints->maxLength) || !ndr_write_uint32(context, s, 0) ||
557 !ndr_write_uint32(context, s, hints->length))
560 const BYTE* src = (
const BYTE*)psrc;
561 for (UINT32 i = 0; i < hints->length; i++, src += itemType->itemSize)
563 if (!itemType->writeFn(context, s, NULL, src))
573 WINPR_ASSERT(context);
575 WINPR_ASSERT(itemType);
576 WINPR_ASSERT(vtarget);
580 if (!ndr_read_uint32(context, s, &count))
583 if ((count * itemType->itemSize < hints->count))
586 BYTE* target = (BYTE*)vtarget;
587 for (UINT32 i = 0; i < count; i++, target += itemType->itemSize)
589 if (!itemType->readFn(context, s, NULL, target))
593 return ndr_read_align(context, s, 4);
596 BOOL ndr_write_uconformant_array(NdrContext* context,
wStream* s, UINT32 len,
599 WINPR_ASSERT(context);
601 WINPR_ASSERT(itemType);
604 size_t toWrite = len * itemType->itemSize;
605 size_t padding = (4 - (toWrite % 4)) % 4;
606 if (!ndr_write_uint32(context, s, len) || !Stream_EnsureRemainingCapacity(s, toWrite + padding))
609 for (UINT32 i = 0; i < len; i++, ptr += itemType->itemSize)
611 if (!itemType->writeFn(context, s, NULL, ptr))
617 Stream_Zero(s, padding);
618 ndr_context_bytes_written(context, padding);
626 WINPR_ASSERT(context);
629 WINPR_ASSERT(target);
631 #define NDR_MAX_STRUCT_DEFERRED 16
633 size_t ndeferred = 0;
635 for (
size_t i = 0; i < descr->nfields; i++)
639 ptr += field->structOffset;
642 if (field->hintsField >= 0)
645 WINPR_ASSERT((
size_t)field->hintsField < descr->nfields);
646 const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
648 hints = (BYTE*)target + hintsField->structOffset;
651 switch (field->pointerType)
653 case NDR_NOT_POINTER:
654 if (!field->typeDescr->readFn(context, s, hints, ptr))
656 WLog_ERR(TAG,
"error when reading %s.%s", descr->name, field->name);
661 case NDR_POINTER_NON_NULL:
664 if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
666 WLog_ERR(TAG,
"too many deferred when calling ndr_read_struct_fromDescr for %s",
671 deferred->name = field->name;
672 deferred->hints = hints;
673 deferred->target = ptr;
674 deferred->msg = field->typeDescr;
675 if (!ndr_read_refpointer(context, s, &deferred->ptrId))
677 WLog_ERR(TAG,
"error when reading %s.%s", descr->name, field->name);
681 if (!deferred->ptrId && field->pointerType == NDR_POINTER_NON_NULL)
683 WLog_ERR(TAG,
"%s.%s can't be null", descr->name, field->name);
690 WLog_ERR(TAG,
"%s.%s unknown pointer type 0x%x", descr->name, field->name,
696 return ndr_push_deferreds(context, deferreds, ndeferred);
702 WINPR_ASSERT(context);
708 size_t ndeferred = 0;
710 for (
size_t i = 0; i < descr->nfields; i++)
713 const BYTE* ptr = (
const BYTE*)src + field->structOffset;
715 const void* hints = NULL;
717 if (field->hintsField >= 0)
720 WINPR_ASSERT((
size_t)field->hintsField < descr->nfields);
721 const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
723 hints = (
const BYTE*)src + hintsField->structOffset;
726 switch (field->pointerType)
729 case NDR_POINTER_NON_NULL:
731 ndr_refid ptrId = NDR_PTR_NULL;
733 ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr,
const void**));
735 if (!ptr && field->pointerType == NDR_POINTER_NON_NULL)
737 WLog_ERR(TAG,
"%s.%s can't be null", descr->name, field->name);
741 if (!ndr_context_allocatePtr(context, ptr, &ptrId, &isNew))
747 if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
750 "too many deferred when calling ndr_read_struct_fromDescr for %s",
755 deferred->name = field->name;
756 deferred->hints = WINPR_CAST_CONST_PTR_AWAY(hints,
void*);
757 deferred->target = WINPR_CAST_CONST_PTR_AWAY(ptr,
void*);
758 deferred->msg = field->typeDescr;
762 if (!ndr_write_uint32(context, s, ptrId))
766 case NDR_NOT_POINTER:
767 if (!field->typeDescr->writeFn(context, s, hints, ptr))
769 WLog_ERR(TAG,
"error when writing %s.%s", descr->name, field->name);
778 return ndr_push_deferreds(context, deferreds, ndeferred);
781 void ndr_struct_dump_fromDescr(wLog* logger, UINT32 lvl,
size_t identLevel,
784 char tabArray[30 + 1];
785 size_t ntabs = (identLevel <= 30) ? identLevel : 30;
787 memset(tabArray,
'\t', ntabs);
790 WLog_Print(logger, lvl,
"%s%s", tabArray, descr->name);
791 for (
size_t i = 0; i < descr->nfields; i++)
794 const BYTE* ptr = (
const BYTE*)obj + field->structOffset;
796 switch (field->pointerType)
799 case NDR_POINTER_NON_NULL:
800 ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr,
const void**));
802 case NDR_NOT_POINTER:
805 WLog_ERR(TAG,
"invalid field->pointerType");
809 WLog_Print(logger, lvl,
"%s*%s:", tabArray, field->name);
810 if (field->typeDescr->dumpFn)
811 field->typeDescr->dumpFn(logger, lvl, identLevel + 1, ptr);
813 WLog_Print(logger, lvl,
"%s\t<no dump function>", tabArray);
817 void ndr_struct_destroy(NdrContext* context,
const NdrStructDescr* descr,
void* pptr)
819 WINPR_ASSERT(context);
823 for (
size_t i = 0; i < descr->nfields; i++)
826 void* ptr = (BYTE*)pptr + field->structOffset;
829 if (field->hintsField >= 0)
832 WINPR_ASSERT((
size_t)field->hintsField < descr->nfields);
833 const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
835 hints = (BYTE*)pptr + hintsField->structOffset;
838 if (field->pointerType != NDR_NOT_POINTER)
841 if (ptr && field->typeDescr->destroyFn)
842 field->typeDescr->destroyFn(context, hints, ptr);
844 if (field->pointerType != NDR_NOT_POINTER)
849 ndr_refid ndr_pointer_refid(
const void* ptr)
851 return (ndr_refid)((ULONG_PTR)ptr);
854 BOOL ndr_read_refpointer(NdrContext* context,
wStream* s, ndr_refid* refId)
856 return ndr_read_uint32(context, s, refId);
865 static BOOL findValueRefFn(
const void* key,
void* value,
void* parg)
869 FindValueArgs* args = (FindValueArgs*)parg;
870 if (args->needle == value)
872 *args->presult = (ndr_refid)(UINT_PTR)key;
878 BOOL ndr_context_allocatePtr(NdrContext* context,
const void* ptr, ndr_refid* prefId, BOOL* pnewPtr)
880 WINPR_ASSERT(context);
882 FindValueArgs findArgs = { ptr, prefId };
883 if (!HashTable_Foreach(context->refPointers, findValueRefFn, &findArgs))
890 *prefId = context->refIdCounter + 4;
891 if (!HashTable_Insert(context->refPointers, (
void*)(UINT_PTR)(*prefId), ptr))
894 context->refIdCounter += 4;
898 BOOL ndr_read_pointedMessageEx(NdrContext* context,
wStream* s, ndr_refid ptrId,
901 WINPR_ASSERT(context);
904 WINPR_ASSERT(target);
910 void* ret = HashTable_GetItemValue(context->refPointers, (
void*)(UINT_PTR)ptrId);
913 size_t itemCount = ndr_hintsCount(descr, hints);
914 ret = calloc(itemCount, descr->itemSize);
918 if (!descr->readFn(context, s, hints, ret) ||
919 !HashTable_Insert(context->refPointers, (
void*)(UINT_PTR)ptrId, ret))
921 if (descr->destroyFn)
922 descr->destroyFn(context, hints, ret);
932 BOOL ndr_push_deferreds(NdrContext* context,
NdrDeferredEntry* deferreds,
size_t ndeferred)
934 WINPR_ASSERT(context);
935 WINPR_ASSERT(deferreds);
940 if (context->ndeferred + ndeferred > NDR_MAX_DEFERRED)
942 WLog_ERR(TAG,
"too many deferred");
946 for (
size_t i = ndeferred; i > 0; i--, context->ndeferred++)
948 context->deferred[context->ndeferred] = deferreds[i - 1];
953 BOOL ndr_treat_deferred_read(NdrContext* context,
wStream* s)
955 WINPR_ASSERT(context);
958 while (context->ndeferred)
961 context->ndeferred--;
963 WLog_VRB(TAG,
"treating read deferred 0x%x for %s", current.ptrId, current.name);
964 if (!ndr_read_pointedMessageEx(context, s, current.ptrId, current.msg, current.hints,
965 (
void**)current.target))
967 WLog_ERR(TAG,
"error parsing deferred %s", current.name);
975 BOOL ndr_treat_deferred_write(NdrContext* context,
wStream* s)
977 WINPR_ASSERT(context);
980 while (context->ndeferred)
983 context->ndeferred--;
985 WLog_VRB(TAG,
"treating write deferred for %s", current.name);
986 if (!current.msg->writeFn(context, s, current.hints, current.target))
988 WLog_ERR(TAG,
"error writing deferred %s", current.name);
hints for a conformant array
descriptor of a field in a structure
hints for a varying conformant array