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); \
360 if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(UPPERTYPE))) \
363 if (!ndr_read_align(context, s, sizeof(UPPERTYPE))) \
366 if (context->bigEndianDrep) \
367 Stream_Read_##UPPERTYPE##_BE(s, *v); \
369 Stream_Read_##UPPERTYPE(s, *v); \
371 ndr_context_bytes_read(context, sizeof(UPPERTYPE)); \
375 BOOL ndr_read_##LOWERTYPE##_(NdrContext* context, wStream* s, const void* hints, void* v) \
377 WINPR_UNUSED(hints); \
378 return ndr_read_##LOWERTYPE(context, s, (UPPERTYPE*)v); \
381 BOOL ndr_write_##LOWERTYPE(NdrContext* context, wStream* s, UPPERTYPE v) \
383 if (!ndr_write_align(context, s, sizeof(UPPERTYPE)) || \
384 !Stream_EnsureRemainingCapacity(s, sizeof(UPPERTYPE))) \
387 if (context->bigEndianDrep) \
388 Stream_Write_##UPPERTYPE##_BE(s, v); \
390 Stream_Write_##UPPERTYPE(s, v); \
392 ndr_context_bytes_written(context, sizeof(UPPERTYPE)); \
396 BOOL ndr_write_##LOWERTYPE##_(NdrContext* context, wStream* s, const void* hints, \
399 WINPR_ASSERT(context); \
402 WINPR_UNUSED(hints); \
404 return ndr_write_##LOWERTYPE(context, s, *(const UPPERTYPE*)v); \
407 const NdrMessageDescr ndr_##LOWERTYPE##_descr_s = { \
408 NDR_ARITY_SIMPLE, sizeof(UPPERTYPE), ndr_read_##LOWERTYPE##_, \
409 ndr_write_##LOWERTYPE##_, nullptr, nullptr \
412 NdrMessageType ndr_##LOWERTYPE##_descr(void) \
414 return &ndr_##LOWERTYPE##_descr_s; \
417SIMPLE_TYPE_IMPL(UINT32, uint32)
418SIMPLE_TYPE_IMPL(UINT16, uint16)
419SIMPLE_TYPE_IMPL(UINT64, uint64)
421#define ARRAY_OF_TYPE_IMPL(TYPE, UPPERTYPE) \
422 BOOL ndr_read_##TYPE##Array(NdrContext* context, wStream* s, const void* hints, void* v) \
424 WINPR_ASSERT(context); \
426 WINPR_ASSERT(hints); \
427 return ndr_read_uconformant_array(context, s, hints, ndr_##TYPE##_descr(), v); \
430 BOOL ndr_write_##TYPE##Array(NdrContext* context, wStream* s, const void* hints, \
433 WINPR_ASSERT(context); \
435 WINPR_ASSERT(hints); \
436 const NdrArrayHints* ahints = (const NdrArrayHints*)hints; \
437 return ndr_write_uconformant_array(context, s, ahints->count, ndr_##TYPE##_descr(), v); \
439 void ndr_destroy_##TYPE##Array(NdrContext* context, const void* hints, void* obj) \
441 WINPR_ASSERT(context); \
443 WINPR_ASSERT(hints); \
444 const NdrArrayHints* ahints = (const NdrArrayHints*)hints; \
445 NdrMessageType descr = ndr_##TYPE##_descr(); \
446 if (descr->destroyFn) \
448 UPPERTYPE* ptr = (UPPERTYPE*)obj; \
449 for (UINT32 i = 0; i < ahints->count; i++, ptr++) \
450 descr->destroyFn(context, nullptr, ptr); \
454 const NdrMessageDescr ndr_##TYPE##Array_descr_s = { \
455 NDR_ARITY_ARRAYOF, sizeof(UPPERTYPE), ndr_read_##TYPE##Array, \
456 ndr_write_##TYPE##Array, ndr_destroy_##TYPE##Array, nullptr \
459 NdrMessageType ndr_##TYPE##Array_descr(void) \
461 return &ndr_##TYPE##Array_descr_s; \
464 BOOL ndr_read_##TYPE##VaryingArray(NdrContext* context, wStream* s, const void* hints, \
467 WINPR_ASSERT(context); \
469 WINPR_ASSERT(hints); \
470 return ndr_read_uconformant_varying_array(context, s, (const NdrVaryingArrayHints*)hints, \
471 ndr_##TYPE##_descr(), v); \
473 BOOL ndr_write_##TYPE##VaryingArray(NdrContext* context, wStream* s, const void* hints, \
476 WINPR_ASSERT(context); \
478 WINPR_ASSERT(hints); \
479 return ndr_write_uconformant_varying_array(context, s, (const NdrVaryingArrayHints*)hints, \
480 ndr_##TYPE##_descr(), v); \
483 const NdrMessageDescr ndr_##TYPE##VaryingArray_descr_s = { \
484 NDR_ARITY_VARYING_ARRAYOF, sizeof(UPPERTYPE), ndr_read_##TYPE##VaryingArray, \
485 ndr_write_##TYPE##VaryingArray, nullptr, nullptr \
488 NdrMessageType ndr_##TYPE##VaryingArray_descr(void) \
490 return &ndr_##TYPE##VaryingArray_descr_s; \
493ARRAY_OF_TYPE_IMPL(uint8, BYTE)
494ARRAY_OF_TYPE_IMPL(uint16, UINT16)
496BOOL ndr_read_wchar(NdrContext* context,
wStream* s, WCHAR* ptr)
498 return ndr_read_uint16(context, s, (UINT16*)ptr);
501BOOL ndr_read_uconformant_varying_array(NdrContext* context,
wStream* s,
505 WINPR_ASSERT(context);
508 WINPR_ASSERT(itemType);
509 WINPR_ASSERT(ptarget);
511 if (itemType->itemSize == 0)
518 if (!ndr_read_uint32(context, s, &maxCount) || !ndr_read_uint32(context, s, &offset) ||
519 !ndr_read_uint32(context, s, &length))
522 if ((1ull * length * itemType->itemSize) > hints->length)
525 if ((1ull * maxCount * itemType->itemSize) > hints->maxLength)
528 BYTE* target = (BYTE*)ptarget;
529 for (UINT32 i = 0; i < length; i++, target += itemType->itemSize)
531 if (!itemType->readFn(context, s,
nullptr, target))
535 return ndr_read_align(context, s, 4);
538BOOL ndr_write_uconformant_varying_array(NdrContext* context,
wStream* s,
542 WINPR_ASSERT(context);
545 WINPR_ASSERT(itemType);
548 if (itemType->itemSize == 0)
551 if (!ndr_write_uint32(context, s, hints->maxLength) || !ndr_write_uint32(context, s, 0) ||
552 !ndr_write_uint32(context, s, hints->length))
555 const BYTE* src = (
const BYTE*)psrc;
556 for (UINT32 i = 0; i < hints->length; i++, src += itemType->itemSize)
558 if (!itemType->writeFn(context, s,
nullptr, src))
568 WINPR_ASSERT(context);
570 WINPR_ASSERT(itemType);
571 WINPR_ASSERT(vtarget);
573 if (itemType->itemSize == 0)
577 if (!ndr_read_uint32(context, s, &count))
580 if (itemType->arity == NDR_ARITY_SIMPLE)
582 if (count > hints->count)
587 if ((1ull * count * itemType->itemSize) > hints->count)
591 BYTE* target = (BYTE*)vtarget;
592 for (UINT32 i = 0; i < count; i++, target += itemType->itemSize)
594 if (!itemType->readFn(context, s,
nullptr, target))
598 return ndr_read_align(context, s, 4);
601BOOL ndr_write_uconformant_array(NdrContext* context,
wStream* s, UINT32 len,
604 WINPR_ASSERT(context);
606 WINPR_ASSERT(itemType);
609 size_t toWrite = len * itemType->itemSize;
610 size_t padding = (4 - (toWrite % 4)) % 4;
611 if (!ndr_write_uint32(context, s, len) || !Stream_EnsureRemainingCapacity(s, toWrite + padding))
614 for (UINT32 i = 0; i < len; i++, ptr += itemType->itemSize)
616 if (!itemType->writeFn(context, s,
nullptr, ptr))
622 Stream_Zero(s, padding);
623 ndr_context_bytes_written(context, padding);
631 WINPR_ASSERT(context);
634 WINPR_ASSERT(target);
636#define NDR_MAX_STRUCT_DEFERRED 16
638 size_t ndeferred = 0;
640 for (
size_t i = 0; i < descr->nfields; i++)
644 ptr += field->structOffset;
645 void* hints =
nullptr;
647 if (field->hintsField >= 0)
650 WINPR_ASSERT((
size_t)field->hintsField < descr->nfields);
651 const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
653 hints = (BYTE*)target + hintsField->structOffset;
656 switch (field->pointerType)
658 case NDR_NOT_POINTER:
659 if (!field->typeDescr->readFn(context, s, hints, ptr))
661 WLog_ERR(TAG,
"error when reading %s.%s", descr->name, field->name);
666 case NDR_POINTER_NON_NULL:
669 if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
671 WLog_ERR(TAG,
"too many deferred when calling ndr_read_struct_fromDescr for %s",
676 deferred->name = field->name;
677 deferred->hints = hints;
678 deferred->target = ptr;
679 deferred->msg = field->typeDescr;
680 if (!ndr_read_refpointer(context, s, &deferred->ptrId))
682 WLog_ERR(TAG,
"error when reading %s.%s", descr->name, field->name);
686 if (!deferred->ptrId && field->pointerType == NDR_POINTER_NON_NULL)
688 WLog_ERR(TAG,
"%s.%s can't be null", descr->name, field->name);
695 WLog_ERR(TAG,
"%s.%s unknown pointer type 0x%x", descr->name, field->name,
701 return ndr_push_deferreds(context, deferreds, ndeferred);
707 WINPR_ASSERT(context);
713 size_t ndeferred = 0;
715 for (
size_t i = 0; i < descr->nfields; i++)
718 const BYTE* ptr = (
const BYTE*)src + field->structOffset;
720 const void* hints =
nullptr;
722 if (field->hintsField >= 0)
725 WINPR_ASSERT((
size_t)field->hintsField < descr->nfields);
726 const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
728 hints = (
const BYTE*)src + hintsField->structOffset;
731 switch (field->pointerType)
734 case NDR_POINTER_NON_NULL:
736 ndr_refid ptrId = NDR_PTR_NULL;
738 ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr,
const void**));
740 if (!ptr && field->pointerType == NDR_POINTER_NON_NULL)
742 WLog_ERR(TAG,
"%s.%s can't be null", descr->name, field->name);
746 if (!ndr_context_allocatePtr(context, ptr, &ptrId, &isNew))
752 if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
755 "too many deferred when calling ndr_read_struct_fromDescr for %s",
760 deferred->name = field->name;
761 deferred->hints = WINPR_CAST_CONST_PTR_AWAY(hints,
void*);
762 deferred->target = WINPR_CAST_CONST_PTR_AWAY(ptr,
void*);
763 deferred->msg = field->typeDescr;
767 if (!ndr_write_uint32(context, s, ptrId))
771 case NDR_NOT_POINTER:
772 if (!field->typeDescr->writeFn(context, s, hints, ptr))
774 WLog_ERR(TAG,
"error when writing %s.%s", descr->name, field->name);
783 return ndr_push_deferreds(context, deferreds, ndeferred);
786void ndr_struct_dump_fromDescr(wLog* logger, UINT32 lvl,
size_t identLevel,
789 char tabArray[30 + 1];
790 size_t ntabs = (identLevel <= 30) ? identLevel : 30;
792 memset(tabArray,
'\t', ntabs);
795 WLog_Print(logger, lvl,
"%s%s", tabArray, descr->name);
796 for (
size_t i = 0; i < descr->nfields; i++)
799 const BYTE* ptr = (
const BYTE*)obj + field->structOffset;
801 switch (field->pointerType)
804 case NDR_POINTER_NON_NULL:
805 ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr,
const void**));
807 case NDR_NOT_POINTER:
810 WLog_ERR(TAG,
"invalid field->pointerType");
814 WLog_Print(logger, lvl,
"%s*%s:", tabArray, field->name);
815 if (field->typeDescr->dumpFn)
816 field->typeDescr->dumpFn(logger, lvl, identLevel + 1, ptr);
818 WLog_Print(logger, lvl,
"%s\t<no dump function>", tabArray);
822void ndr_struct_destroy(NdrContext* context,
const NdrStructDescr* descr,
void* pptr)
824 WINPR_ASSERT(context);
828 for (
size_t i = 0; i < descr->nfields; i++)
831 void* ptr = (BYTE*)pptr + field->structOffset;
832 void* hints =
nullptr;
834 if (field->hintsField >= 0)
837 WINPR_ASSERT((
size_t)field->hintsField < descr->nfields);
838 const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
840 hints = (BYTE*)pptr + hintsField->structOffset;
843 if (field->pointerType != NDR_NOT_POINTER)
846 if (ptr && field->typeDescr->destroyFn)
847 field->typeDescr->destroyFn(context, hints, ptr);
849 if (field->pointerType != NDR_NOT_POINTER)
854ndr_refid ndr_pointer_refid(
const void* ptr)
856 return (ndr_refid)((ULONG_PTR)ptr);
859BOOL ndr_read_refpointer(NdrContext* context,
wStream* s, ndr_refid* refId)
861 return ndr_read_uint32(context, s, refId);
870static BOOL findValueRefFn(
const void* key,
void* value,
void* parg)
874 FindValueArgs* args = (FindValueArgs*)parg;
875 if (args->needle == value)
877 *args->presult = (ndr_refid)(UINT_PTR)key;
883BOOL ndr_context_allocatePtr(NdrContext* context,
const void* ptr, ndr_refid* prefId, BOOL* pnewPtr)
885 WINPR_ASSERT(context);
887 FindValueArgs findArgs = { ptr, prefId };
888 if (!HashTable_Foreach(context->refPointers, findValueRefFn, &findArgs))
895 *prefId = context->refIdCounter + 4;
896 if (!HashTable_Insert(context->refPointers, (
void*)(UINT_PTR)(*prefId), ptr))
899 context->refIdCounter += 4;
903BOOL ndr_read_pointedMessageEx(NdrContext* context,
wStream* s, ndr_refid ptrId,
906 WINPR_ASSERT(context);
909 WINPR_ASSERT(target);
915 void* ret = HashTable_GetItemValue(context->refPointers, (
void*)(UINT_PTR)ptrId);
918 size_t itemCount = ndr_hintsCount(descr, hints);
921 ret = calloc(itemCount, descr->itemSize);
925 if (!descr->readFn(context, s, hints, ret) ||
926 !HashTable_Insert(context->refPointers, (
void*)(UINT_PTR)ptrId, ret))
928 if (descr->destroyFn)
929 descr->destroyFn(context, hints, ret);
939BOOL ndr_push_deferreds(NdrContext* context,
NdrDeferredEntry* deferreds,
size_t ndeferred)
941 WINPR_ASSERT(context);
942 WINPR_ASSERT(deferreds);
947 if (context->ndeferred + ndeferred > NDR_MAX_DEFERRED)
949 WLog_ERR(TAG,
"too many deferred");
953 for (
size_t i = ndeferred; i > 0; i--, context->ndeferred++)
955 context->deferred[context->ndeferred] = deferreds[i - 1];
960BOOL ndr_treat_deferred_read(NdrContext* context,
wStream* s)
962 WINPR_ASSERT(context);
965 while (context->ndeferred)
968 context->ndeferred--;
970 WLog_VRB(TAG,
"treating read deferred 0x%x for %s", current.ptrId, current.name);
971 if (!ndr_read_pointedMessageEx(context, s, current.ptrId, current.msg, current.hints,
972 (
void**)current.target))
974 WLog_ERR(TAG,
"error parsing deferred %s", current.name);
982BOOL ndr_treat_deferred_write(NdrContext* context,
wStream* s)
984 WINPR_ASSERT(context);
987 while (context->ndeferred)
990 context->ndeferred--;
992 WLog_VRB(TAG,
"treating write deferred for %s", current.name);
993 if (!current.msg->writeFn(context, s, current.hints, current.target))
995 WLog_ERR(TAG,
"error writing deferred %s", current.name);
1003BOOL ndr_write_data(NdrContext* context,
wStream* s,
const void* data,
size_t sz)
1005 if (!Stream_EnsureRemainingCapacity(s, sz))
1008 Stream_Write(s, data, sz);
1009 ndr_context_bytes_written(context, sz);
hints for a conformant array
descriptor of a field in a structure
hints for a varying conformant array