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;
51NdrContext* 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);
71void 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;
85NdrContext* 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);
106void ndr_context_free(NdrContext* context)
110 HashTable_Free(context->refPointers);
115static void ndr_context_bytes_read(NdrContext* context,
size_t len)
117 WINPR_ASSERT(context);
118 context->indentLevels[context->currentLevel] += len;
121static void ndr_context_bytes_written(NdrContext* context,
size_t len)
123 ndr_context_bytes_read(context, len);
126NdrContext* 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);
144BOOL 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));
160BOOL 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);
172BOOL 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;
190BOOL 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;
209BOOL ndr_read_pickle(NdrContext* context,
wStream* s)
211 WINPR_ASSERT(context);
216 return !(!ndr_read_uint32(context, s, &v) || v != 0x20000);
219BOOL ndr_write_pickle(NdrContext* context,
wStream* s)
221 WINPR_ASSERT(context);
224 return ndr_write_uint32(context, s, 0x20000);
227BOOL ndr_read_constructed(NdrContext* context,
wStream* s,
wStream* target)
229 WINPR_ASSERT(context);
234 if (!ndr_read_uint32(context, s, &len))
238 if (!ndr_skip_bytes(context, s, 4))
242 if (!Stream_CheckAndLogRequiredLength(TAG, s, len))
245 Stream_StaticInit(target, Stream_PointerAs(s, BYTE), len);
250BOOL ndr_start_constructed(NdrContext* context,
wStream* s)
252 WINPR_ASSERT(context);
254 if (!Stream_EnsureRemainingCapacity(s, 8))
257 if (context->constructLevel == NDR_MAX_CONSTRUCTS)
260 context->constructLevel++;
261 context->constructs[context->constructLevel] = Stream_GetPosition(s);
267BOOL ndr_end_constructed(NdrContext* context,
wStream* s)
269 WINPR_ASSERT(context);
270 WINPR_ASSERT(context->constructs);
271 WINPR_ASSERT(context->constructLevel >= 0);
273 size_t offset = context->constructs[context->constructLevel];
275 wStream staticS = WINPR_C_ARRAY_INIT;
276 Stream_StaticInit(&staticS, Stream_Buffer(s) + offset, 4);
279 const size_t len = Stream_GetPosition(s) - (offset + 8);
280 if (len > UINT32_MAX)
282 if (!ndr_write_uint32(context, &staticS, (UINT32)len))
288static size_t ndr_hintsCount(
NdrMessageType msgType,
const void* hints)
290 WINPR_ASSERT(msgType);
292 switch (msgType->arity)
294 case NDR_ARITY_SIMPLE:
296 case NDR_ARITY_ARRAYOF:
299 case NDR_ARITY_VARYING_ARRAYOF:
303 WINPR_ASSERT(0 &&
"unknown arity");
308BOOL ndr_read_uint8(NdrContext* context,
wStream* s, BYTE* v)
310 WINPR_ASSERT(context);
312 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
315 Stream_Read_UINT8(s, *v);
317 ndr_context_bytes_read(context, 1);
321BOOL ndr_read_uint8_(NdrContext* context,
wStream* s,
const void* hints,
void* v)
324 return ndr_read_uint8(context, s, (BYTE*)v);
327BOOL ndr_write_uint8(NdrContext* context,
wStream* s, BYTE v)
329 if (!Stream_EnsureRemainingCapacity(s, 1))
332 Stream_Write_UINT8(s, v);
333 ndr_context_bytes_written(context, 1);
337BOOL ndr_write_uint8_(NdrContext* context,
wStream* s,
const void* hints,
const void* v)
339 WINPR_ASSERT(context);
344 return ndr_write_uint8(context, s, *(
const BYTE*)v);
347const static NdrMessageDescr uint8_descr = { NDR_ARITY_SIMPLE, 1, ndr_read_uint8_,
348 ndr_write_uint8_,
nullptr,
nullptr };
355#define SIMPLE_TYPE_IMPL(UPPERTYPE, LOWERTYPE) \
356 BOOL ndr_read_##LOWERTYPE(NdrContext* context, wStream* s, UPPERTYPE* v) \
358 WINPR_ASSERT(context); \
359 if (!ndr_read_align(context, s, sizeof(UPPERTYPE))) \
361 if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(UPPERTYPE))) \
364 if (context->bigEndianDrep) \
365 Stream_Read_##UPPERTYPE##_BE(s, *v); \
367 Stream_Read_##UPPERTYPE(s, *v); \
369 ndr_context_bytes_read(context, sizeof(UPPERTYPE)); \
373 BOOL ndr_read_##LOWERTYPE##_(NdrContext* context, wStream* s, const void* hints, void* v) \
375 WINPR_UNUSED(hints); \
376 return ndr_read_##LOWERTYPE(context, s, (UPPERTYPE*)v); \
379 BOOL ndr_write_##LOWERTYPE(NdrContext* context, wStream* s, UPPERTYPE v) \
381 if (!ndr_write_align(context, s, sizeof(UPPERTYPE)) || \
382 !Stream_EnsureRemainingCapacity(s, sizeof(UPPERTYPE))) \
385 if (context->bigEndianDrep) \
386 Stream_Write_##UPPERTYPE##_BE(s, v); \
388 Stream_Write_##UPPERTYPE(s, v); \
390 ndr_context_bytes_written(context, sizeof(UPPERTYPE)); \
394 BOOL ndr_write_##LOWERTYPE##_(NdrContext* context, wStream* s, const void* hints, \
397 WINPR_ASSERT(context); \
400 WINPR_UNUSED(hints); \
402 return ndr_write_##LOWERTYPE(context, s, *(const UPPERTYPE*)v); \
405 const NdrMessageDescr ndr_##LOWERTYPE##_descr_s = { \
406 NDR_ARITY_SIMPLE, sizeof(UPPERTYPE), ndr_read_##LOWERTYPE##_, \
407 ndr_write_##LOWERTYPE##_, nullptr, nullptr \
410 NdrMessageType ndr_##LOWERTYPE##_descr(void) \
412 return &ndr_##LOWERTYPE##_descr_s; \
415SIMPLE_TYPE_IMPL(UINT32, uint32)
416SIMPLE_TYPE_IMPL(UINT16, uint16)
417SIMPLE_TYPE_IMPL(UINT64, uint64)
419#define ARRAY_OF_TYPE_IMPL(TYPE, UPPERTYPE) \
420 BOOL ndr_read_##TYPE##Array(NdrContext* context, wStream* s, const void* hints, void* v) \
422 WINPR_ASSERT(context); \
424 WINPR_ASSERT(hints); \
425 return ndr_read_uconformant_array(context, s, hints, ndr_##TYPE##_descr(), v); \
428 BOOL ndr_write_##TYPE##Array(NdrContext* context, wStream* s, const void* hints, \
431 WINPR_ASSERT(context); \
433 WINPR_ASSERT(hints); \
434 const NdrArrayHints* ahints = (const NdrArrayHints*)hints; \
435 return ndr_write_uconformant_array(context, s, ahints->count, ndr_##TYPE##_descr(), v); \
437 void ndr_destroy_##TYPE##Array(NdrContext* context, const void* hints, void* obj) \
439 WINPR_ASSERT(context); \
441 WINPR_ASSERT(hints); \
442 const NdrArrayHints* ahints = (const NdrArrayHints*)hints; \
443 NdrMessageType descr = ndr_##TYPE##_descr(); \
444 if (descr->destroyFn) \
446 UPPERTYPE* ptr = (UPPERTYPE*)obj; \
447 for (UINT32 i = 0; i < ahints->count; i++, ptr++) \
448 descr->destroyFn(context, nullptr, ptr); \
452 const NdrMessageDescr ndr_##TYPE##Array_descr_s = { \
453 NDR_ARITY_ARRAYOF, sizeof(UPPERTYPE), ndr_read_##TYPE##Array, \
454 ndr_write_##TYPE##Array, ndr_destroy_##TYPE##Array, nullptr \
457 NdrMessageType ndr_##TYPE##Array_descr(void) \
459 return &ndr_##TYPE##Array_descr_s; \
462 BOOL ndr_read_##TYPE##VaryingArray(NdrContext* context, wStream* s, const void* hints, \
465 WINPR_ASSERT(context); \
467 WINPR_ASSERT(hints); \
468 return ndr_read_uconformant_varying_array(context, s, (const NdrVaryingArrayHints*)hints, \
469 ndr_##TYPE##_descr(), v); \
471 BOOL ndr_write_##TYPE##VaryingArray(NdrContext* context, wStream* s, const void* hints, \
474 WINPR_ASSERT(context); \
476 WINPR_ASSERT(hints); \
477 return ndr_write_uconformant_varying_array(context, s, (const NdrVaryingArrayHints*)hints, \
478 ndr_##TYPE##_descr(), v); \
481 const NdrMessageDescr ndr_##TYPE##VaryingArray_descr_s = { \
482 NDR_ARITY_VARYING_ARRAYOF, sizeof(UPPERTYPE), ndr_read_##TYPE##VaryingArray, \
483 ndr_write_##TYPE##VaryingArray, nullptr, nullptr \
486 NdrMessageType ndr_##TYPE##VaryingArray_descr(void) \
488 return &ndr_##TYPE##VaryingArray_descr_s; \
491ARRAY_OF_TYPE_IMPL(uint8, BYTE)
492ARRAY_OF_TYPE_IMPL(uint16, UINT16)
494BOOL ndr_read_wchar(NdrContext* context,
wStream* s, WCHAR* ptr)
496 return ndr_read_uint16(context, s, (UINT16*)ptr);
499BOOL ndr_read_uconformant_varying_array(NdrContext* context,
wStream* s,
503 WINPR_ASSERT(context);
506 WINPR_ASSERT(itemType);
507 WINPR_ASSERT(ptarget);
509 if (itemType->itemSize == 0)
516 if (!ndr_read_uint32(context, s, &maxCount) || !ndr_read_uint32(context, s, &offset) ||
517 !ndr_read_uint32(context, s, &length))
520 if ((1ull * length * itemType->itemSize) > hints->length)
523 if ((1ull * maxCount * itemType->itemSize) > hints->maxLength)
526 BYTE* target = (BYTE*)ptarget;
527 for (UINT32 i = 0; i < length; i++, target += itemType->itemSize)
529 if (!itemType->readFn(context, s,
nullptr, target))
533 return ndr_read_align(context, s, 4);
536BOOL ndr_write_uconformant_varying_array(NdrContext* context,
wStream* s,
540 WINPR_ASSERT(context);
543 WINPR_ASSERT(itemType);
546 if (itemType->itemSize == 0)
549 if (!ndr_write_uint32(context, s, hints->maxLength) || !ndr_write_uint32(context, s, 0) ||
550 !ndr_write_uint32(context, s, hints->length))
553 const BYTE* src = (
const BYTE*)psrc;
554 for (UINT32 i = 0; i < hints->length; i++, src += itemType->itemSize)
556 if (!itemType->writeFn(context, s,
nullptr, src))
566 WINPR_ASSERT(context);
568 WINPR_ASSERT(itemType);
569 WINPR_ASSERT(vtarget);
571 if (itemType->itemSize == 0)
575 if (!ndr_read_uint32(context, s, &count))
578 if (itemType->arity == NDR_ARITY_SIMPLE)
580 if (count > hints->count)
585 if ((1ull * count * itemType->itemSize) > hints->count)
589 BYTE* target = (BYTE*)vtarget;
590 for (UINT32 i = 0; i < count; i++, target += itemType->itemSize)
592 if (!itemType->readFn(context, s,
nullptr, target))
596 return ndr_read_align(context, s, 4);
599BOOL ndr_write_uconformant_array(NdrContext* context,
wStream* s, UINT32 len,
602 WINPR_ASSERT(context);
604 WINPR_ASSERT(itemType);
607 size_t toWrite = len * itemType->itemSize;
608 size_t padding = (4 - (toWrite % 4)) % 4;
609 if (!ndr_write_uint32(context, s, len) || !Stream_EnsureRemainingCapacity(s, toWrite + padding))
612 for (UINT32 i = 0; i < len; i++, ptr += itemType->itemSize)
614 if (!itemType->writeFn(context, s,
nullptr, ptr))
620 Stream_Zero(s, padding);
621 ndr_context_bytes_written(context, padding);
629 WINPR_ASSERT(context);
632 WINPR_ASSERT(target);
634#define NDR_MAX_STRUCT_DEFERRED 16
636 size_t ndeferred = 0;
638 for (
size_t i = 0; i < descr->nfields; i++)
642 ptr += field->structOffset;
643 void* hints =
nullptr;
645 if (field->hintsField >= 0)
648 WINPR_ASSERT((
size_t)field->hintsField < descr->nfields);
649 const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
651 hints = (BYTE*)target + hintsField->structOffset;
654 switch (field->pointerType)
656 case NDR_NOT_POINTER:
657 if (!field->typeDescr->readFn(context, s, hints, ptr))
659 WLog_ERR(TAG,
"error when reading %s.%s", descr->name, field->name);
664 case NDR_POINTER_NON_NULL:
667 if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
669 WLog_ERR(TAG,
"too many deferred when calling ndr_read_struct_fromDescr for %s",
674 deferred->name = field->name;
675 deferred->hints = hints;
676 deferred->target = ptr;
677 deferred->msg = field->typeDescr;
678 if (!ndr_read_refpointer(context, s, &deferred->ptrId))
680 WLog_ERR(TAG,
"error when reading %s.%s", descr->name, field->name);
684 if (!deferred->ptrId && field->pointerType == NDR_POINTER_NON_NULL)
686 WLog_ERR(TAG,
"%s.%s can't be null", descr->name, field->name);
693 WLog_ERR(TAG,
"%s.%s unknown pointer type 0x%x", descr->name, field->name,
699 return ndr_push_deferreds(context, deferreds, ndeferred);
705 WINPR_ASSERT(context);
711 size_t ndeferred = 0;
713 for (
size_t i = 0; i < descr->nfields; i++)
716 const BYTE* ptr = (
const BYTE*)src + field->structOffset;
718 const void* hints =
nullptr;
720 if (field->hintsField >= 0)
723 WINPR_ASSERT((
size_t)field->hintsField < descr->nfields);
724 const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
726 hints = (
const BYTE*)src + hintsField->structOffset;
729 switch (field->pointerType)
732 case NDR_POINTER_NON_NULL:
734 ndr_refid ptrId = NDR_PTR_NULL;
736 ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr,
const void**));
738 if (!ptr && field->pointerType == NDR_POINTER_NON_NULL)
740 WLog_ERR(TAG,
"%s.%s can't be null", descr->name, field->name);
744 if (!ndr_context_allocatePtr(context, ptr, &ptrId, &isNew))
750 if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
753 "too many deferred when calling ndr_read_struct_fromDescr for %s",
758 deferred->name = field->name;
759 deferred->hints = WINPR_CAST_CONST_PTR_AWAY(hints,
void*);
760 deferred->target = WINPR_CAST_CONST_PTR_AWAY(ptr,
void*);
761 deferred->msg = field->typeDescr;
765 if (!ndr_write_uint32(context, s, ptrId))
769 case NDR_NOT_POINTER:
770 if (!field->typeDescr->writeFn(context, s, hints, ptr))
772 WLog_ERR(TAG,
"error when writing %s.%s", descr->name, field->name);
781 return ndr_push_deferreds(context, deferreds, ndeferred);
784void ndr_struct_dump_fromDescr(wLog* logger, UINT32 lvl,
size_t identLevel,
787 char tabArray[30 + 1];
788 size_t ntabs = (identLevel <= 30) ? identLevel : 30;
790 memset(tabArray,
'\t', ntabs);
793 WLog_Print(logger, lvl,
"%s%s", tabArray, descr->name);
794 for (
size_t i = 0; i < descr->nfields; i++)
797 const BYTE* ptr = (
const BYTE*)obj + field->structOffset;
799 switch (field->pointerType)
802 case NDR_POINTER_NON_NULL:
803 ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr,
const void**));
805 case NDR_NOT_POINTER:
808 WLog_ERR(TAG,
"invalid field->pointerType");
812 WLog_Print(logger, lvl,
"%s*%s:", tabArray, field->name);
813 if (field->typeDescr->dumpFn)
814 field->typeDescr->dumpFn(logger, lvl, identLevel + 1, ptr);
816 WLog_Print(logger, lvl,
"%s\t<no dump function>", tabArray);
820void ndr_struct_destroy(NdrContext* context,
const NdrStructDescr* descr,
void* pptr)
822 WINPR_ASSERT(context);
826 for (
size_t i = 0; i < descr->nfields; i++)
829 void* ptr = (BYTE*)pptr + field->structOffset;
830 void* hints =
nullptr;
832 if (field->hintsField >= 0)
835 WINPR_ASSERT((
size_t)field->hintsField < descr->nfields);
836 const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
838 hints = (BYTE*)pptr + hintsField->structOffset;
841 if (field->pointerType != NDR_NOT_POINTER)
844 if (ptr && field->typeDescr->destroyFn)
845 field->typeDescr->destroyFn(context, hints, ptr);
847 if (field->pointerType != NDR_NOT_POINTER)
852ndr_refid ndr_pointer_refid(
const void* ptr)
854 return (ndr_refid)((ULONG_PTR)ptr);
857BOOL ndr_read_refpointer(NdrContext* context,
wStream* s, ndr_refid* refId)
859 return ndr_read_uint32(context, s, refId);
868static BOOL findValueRefFn(
const void* key,
void* value,
void* parg)
872 FindValueArgs* args = (FindValueArgs*)parg;
873 if (args->needle == value)
875 *args->presult = (ndr_refid)(UINT_PTR)key;
881BOOL ndr_context_allocatePtr(NdrContext* context,
const void* ptr, ndr_refid* prefId, BOOL* pnewPtr)
883 WINPR_ASSERT(context);
885 FindValueArgs findArgs = { ptr, prefId };
886 if (!HashTable_Foreach(context->refPointers, findValueRefFn, &findArgs))
893 *prefId = context->refIdCounter + 4;
894 if (!HashTable_Insert(context->refPointers, (
void*)(UINT_PTR)(*prefId), ptr))
897 context->refIdCounter += 4;
901BOOL ndr_read_pointedMessageEx(NdrContext* context,
wStream* s, ndr_refid ptrId,
904 WINPR_ASSERT(context);
907 WINPR_ASSERT(target);
913 void* ret = HashTable_GetItemValue(context->refPointers, (
void*)(UINT_PTR)ptrId);
916 size_t itemCount = ndr_hintsCount(descr, hints);
919 ret = calloc(itemCount, descr->itemSize);
923 if (!descr->readFn(context, s, hints, ret) ||
924 !HashTable_Insert(context->refPointers, (
void*)(UINT_PTR)ptrId, ret))
926 if (descr->destroyFn)
927 descr->destroyFn(context, hints, ret);
934 WLog_ERR(TAG,
"aliased pointer aren't supported for now");
942BOOL ndr_push_deferreds(NdrContext* context,
NdrDeferredEntry* deferreds,
size_t ndeferred)
944 WINPR_ASSERT(context);
945 WINPR_ASSERT(deferreds);
950 if (context->ndeferred + ndeferred > NDR_MAX_DEFERRED)
952 WLog_ERR(TAG,
"too many deferred");
956 for (
size_t i = ndeferred; i > 0; i--, context->ndeferred++)
958 context->deferred[context->ndeferred] = deferreds[i - 1];
963BOOL ndr_treat_deferred_read(NdrContext* context,
wStream* s)
965 WINPR_ASSERT(context);
968 while (context->ndeferred)
971 context->ndeferred--;
973 WLog_VRB(TAG,
"treating read deferred 0x%x for %s", current.ptrId, current.name);
974 if (!ndr_read_pointedMessageEx(context, s, current.ptrId, current.msg, current.hints,
975 (
void**)current.target))
977 WLog_ERR(TAG,
"error parsing deferred %s", current.name);
985BOOL ndr_treat_deferred_write(NdrContext* context,
wStream* s)
987 WINPR_ASSERT(context);
990 while (context->ndeferred)
993 context->ndeferred--;
995 WLog_VRB(TAG,
"treating write deferred for %s", current.name);
996 if (!current.msg->writeFn(context, s, current.hints, current.target))
998 WLog_ERR(TAG,
"error writing deferred %s", current.name);
1006BOOL ndr_write_data(NdrContext* context,
wStream* s,
const void* data,
size_t sz)
1008 if (!Stream_EnsureRemainingCapacity(s, sz))
1011 Stream_Write(s, data, sz);
1012 ndr_context_bytes_written(context, sz);
hints for a conformant array
descriptor of a field in a structure
hints for a varying conformant array