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 if (!ndr_read_uint32(context, s, &v) || v != 0x20000)
222BOOL ndr_write_pickle(NdrContext* context,
wStream* s)
224 WINPR_ASSERT(context);
227 if (!ndr_write_uint32(context, s, 0x20000))
233BOOL ndr_read_constructed(NdrContext* context,
wStream* s,
wStream* target)
235 WINPR_ASSERT(context);
240 if (!ndr_read_uint32(context, s, &len))
244 if (!ndr_skip_bytes(context, s, 4))
248 if (!Stream_CheckAndLogRequiredLength(TAG, s, len))
251 Stream_StaticInit(target, Stream_PointerAs(s, BYTE), len);
256BOOL ndr_start_constructed(NdrContext* context,
wStream* s)
258 WINPR_ASSERT(context);
260 if (!Stream_EnsureRemainingCapacity(s, 8))
263 if (context->constructLevel == NDR_MAX_CONSTRUCTS)
266 context->constructLevel++;
267 context->constructs[context->constructLevel] = Stream_GetPosition(s);
273BOOL ndr_end_constructed(NdrContext* context,
wStream* s)
275 WINPR_ASSERT(context);
276 WINPR_ASSERT(context->constructs);
277 WINPR_ASSERT(context->constructLevel >= 0);
279 size_t offset = context->constructs[context->constructLevel];
282 Stream_StaticInit(&staticS, Stream_Buffer(s) + offset, 4);
285 const size_t len = Stream_GetPosition(s) - (offset + 8);
286 if (len > UINT32_MAX)
288 if (!ndr_write_uint32(context, &staticS, (UINT32)len))
294static size_t ndr_hintsCount(
NdrMessageType msgType,
const void* hints)
296 WINPR_ASSERT(msgType);
298 switch (msgType->arity)
300 case NDR_ARITY_SIMPLE:
302 case NDR_ARITY_ARRAYOF:
305 case NDR_ARITY_VARYING_ARRAYOF:
309 WINPR_ASSERT(0 &&
"unknown arity");
314BOOL ndr_read_uint8(NdrContext* context,
wStream* s, BYTE* v)
316 WINPR_ASSERT(context);
318 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
321 Stream_Read_UINT8(s, *v);
323 ndr_context_bytes_read(context, 1);
327BOOL ndr_read_uint8_(NdrContext* context,
wStream* s,
const void* hints,
void* v)
330 return ndr_read_uint8(context, s, (BYTE*)v);
333BOOL ndr_write_uint8(NdrContext* context,
wStream* s, BYTE v)
335 if (!Stream_EnsureRemainingCapacity(s, 1))
338 Stream_Write_UINT8(s, v);
339 ndr_context_bytes_written(context, 1);
343BOOL ndr_write_uint8_(NdrContext* context,
wStream* s,
const void* hints,
const void* v)
345 WINPR_ASSERT(context);
350 return ndr_write_uint8(context, s, *(
const BYTE*)v);
353const static NdrMessageDescr uint8_descr = { NDR_ARITY_SIMPLE, 1, ndr_read_uint8_,
354 ndr_write_uint8_, NULL, NULL };
361#define SIMPLE_TYPE_IMPL(UPPERTYPE, LOWERTYPE) \
362 BOOL ndr_read_##LOWERTYPE(NdrContext* context, wStream* s, UPPERTYPE* v) \
364 WINPR_ASSERT(context); \
366 if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(UPPERTYPE))) \
369 if (!ndr_read_align(context, s, sizeof(UPPERTYPE))) \
372 if (context->bigEndianDrep) \
373 Stream_Read_##UPPERTYPE##_BE(s, *v); \
375 Stream_Read_##UPPERTYPE(s, *v); \
377 ndr_context_bytes_read(context, sizeof(UPPERTYPE)); \
381 BOOL ndr_read_##LOWERTYPE##_(NdrContext* context, wStream* s, const void* hints, void* v) \
383 WINPR_UNUSED(hints); \
384 return ndr_read_##LOWERTYPE(context, s, (UPPERTYPE*)v); \
387 BOOL ndr_write_##LOWERTYPE(NdrContext* context, wStream* s, UPPERTYPE v) \
389 if (!ndr_write_align(context, s, sizeof(UPPERTYPE)) || \
390 !Stream_EnsureRemainingCapacity(s, sizeof(UPPERTYPE))) \
393 if (context->bigEndianDrep) \
394 Stream_Write_##UPPERTYPE##_BE(s, v); \
396 Stream_Write_##UPPERTYPE(s, v); \
398 ndr_context_bytes_written(context, sizeof(UPPERTYPE)); \
402 BOOL ndr_write_##LOWERTYPE##_(NdrContext* context, wStream* s, const void* hints, \
405 WINPR_ASSERT(context); \
408 WINPR_UNUSED(hints); \
410 return ndr_write_##LOWERTYPE(context, s, *(const UPPERTYPE*)v); \
413 const NdrMessageDescr ndr_##LOWERTYPE##_descr_s = { NDR_ARITY_SIMPLE, \
415 ndr_read_##LOWERTYPE##_, \
416 ndr_write_##LOWERTYPE##_, \
420 NdrMessageType ndr_##LOWERTYPE##_descr(void) \
422 return &ndr_##LOWERTYPE##_descr_s; \
425SIMPLE_TYPE_IMPL(UINT32, uint32)
426SIMPLE_TYPE_IMPL(UINT16, uint16)
427SIMPLE_TYPE_IMPL(UINT64, uint64)
429#define ARRAY_OF_TYPE_IMPL(TYPE, UPPERTYPE) \
430 BOOL ndr_read_##TYPE##Array(NdrContext* context, wStream* s, const void* hints, void* v) \
432 WINPR_ASSERT(context); \
434 WINPR_ASSERT(hints); \
435 return ndr_read_uconformant_array(context, s, hints, ndr_##TYPE##_descr(), v); \
438 BOOL ndr_write_##TYPE##Array(NdrContext* context, wStream* s, const void* hints, \
441 WINPR_ASSERT(context); \
443 WINPR_ASSERT(hints); \
444 const NdrArrayHints* ahints = (const NdrArrayHints*)hints; \
445 return ndr_write_uconformant_array(context, s, ahints->count, ndr_##TYPE##_descr(), v); \
447 void ndr_destroy_##TYPE##Array(NdrContext* context, const void* hints, void* obj) \
449 WINPR_ASSERT(context); \
451 WINPR_ASSERT(hints); \
452 const NdrArrayHints* ahints = (const NdrArrayHints*)hints; \
453 NdrMessageType descr = ndr_##TYPE##_descr(); \
454 if (descr->destroyFn) \
456 UPPERTYPE* ptr = (UPPERTYPE*)obj; \
457 for (UINT32 i = 0; i < ahints->count; i++, ptr++) \
458 descr->destroyFn(context, NULL, ptr); \
462 const NdrMessageDescr ndr_##TYPE##Array_descr_s = { \
463 NDR_ARITY_ARRAYOF, sizeof(UPPERTYPE), ndr_read_##TYPE##Array, \
464 ndr_write_##TYPE##Array, ndr_destroy_##TYPE##Array, NULL \
467 NdrMessageType ndr_##TYPE##Array_descr(void) \
469 return &ndr_##TYPE##Array_descr_s; \
472 BOOL ndr_read_##TYPE##VaryingArray(NdrContext* context, wStream* s, const void* hints, \
475 WINPR_ASSERT(context); \
477 WINPR_ASSERT(hints); \
478 return ndr_read_uconformant_varying_array(context, s, (const NdrVaryingArrayHints*)hints, \
479 ndr_##TYPE##_descr(), v); \
481 BOOL ndr_write_##TYPE##VaryingArray(NdrContext* context, wStream* s, const void* hints, \
484 WINPR_ASSERT(context); \
486 WINPR_ASSERT(hints); \
487 return ndr_write_uconformant_varying_array(context, s, (const NdrVaryingArrayHints*)hints, \
488 ndr_##TYPE##_descr(), v); \
491 const NdrMessageDescr ndr_##TYPE##VaryingArray_descr_s = { NDR_ARITY_VARYING_ARRAYOF, \
493 ndr_read_##TYPE##VaryingArray, \
494 ndr_write_##TYPE##VaryingArray, \
498 NdrMessageType ndr_##TYPE##VaryingArray_descr(void) \
500 return &ndr_##TYPE##VaryingArray_descr_s; \
503ARRAY_OF_TYPE_IMPL(uint8, BYTE)
504ARRAY_OF_TYPE_IMPL(uint16, UINT16)
506BOOL ndr_read_wchar(NdrContext* context,
wStream* s, WCHAR* ptr)
508 return ndr_read_uint16(context, s, (UINT16*)ptr);
511BOOL ndr_read_uconformant_varying_array(NdrContext* context,
wStream* s,
515 WINPR_ASSERT(context);
518 WINPR_ASSERT(itemType);
519 WINPR_ASSERT(ptarget);
521 if (itemType->itemSize == 0)
528 if (!ndr_read_uint32(context, s, &maxCount) || !ndr_read_uint32(context, s, &offset) ||
529 !ndr_read_uint32(context, s, &length))
532 if ((1ull * length * itemType->itemSize) > hints->length)
535 if ((1ull * maxCount * itemType->itemSize) > hints->maxLength)
538 BYTE* target = (BYTE*)ptarget;
539 for (UINT32 i = 0; i < length; i++, target += itemType->itemSize)
541 if (!itemType->readFn(context, s, NULL, target))
545 return ndr_read_align(context, s, 4);
548BOOL ndr_write_uconformant_varying_array(NdrContext* context,
wStream* s,
552 WINPR_ASSERT(context);
555 WINPR_ASSERT(itemType);
558 if (itemType->itemSize == 0)
561 if (!ndr_write_uint32(context, s, hints->maxLength) || !ndr_write_uint32(context, s, 0) ||
562 !ndr_write_uint32(context, s, hints->length))
565 const BYTE* src = (
const BYTE*)psrc;
566 for (UINT32 i = 0; i < hints->length; i++, src += itemType->itemSize)
568 if (!itemType->writeFn(context, s, NULL, src))
578 WINPR_ASSERT(context);
580 WINPR_ASSERT(itemType);
581 WINPR_ASSERT(vtarget);
583 if (itemType->itemSize == 0)
587 if (!ndr_read_uint32(context, s, &count))
590 if (itemType->arity == NDR_ARITY_SIMPLE)
592 if (count > hints->count)
597 if ((1ull * count * itemType->itemSize) > hints->count)
601 BYTE* target = (BYTE*)vtarget;
602 for (UINT32 i = 0; i < count; i++, target += itemType->itemSize)
604 if (!itemType->readFn(context, s, NULL, target))
608 return ndr_read_align(context, s, 4);
611BOOL ndr_write_uconformant_array(NdrContext* context,
wStream* s, UINT32 len,
614 WINPR_ASSERT(context);
616 WINPR_ASSERT(itemType);
619 size_t toWrite = len * itemType->itemSize;
620 size_t padding = (4 - (toWrite % 4)) % 4;
621 if (!ndr_write_uint32(context, s, len) || !Stream_EnsureRemainingCapacity(s, toWrite + padding))
624 for (UINT32 i = 0; i < len; i++, ptr += itemType->itemSize)
626 if (!itemType->writeFn(context, s, NULL, ptr))
632 Stream_Zero(s, padding);
633 ndr_context_bytes_written(context, padding);
641 WINPR_ASSERT(context);
644 WINPR_ASSERT(target);
646#define NDR_MAX_STRUCT_DEFERRED 16
648 size_t ndeferred = 0;
650 for (
size_t i = 0; i < descr->nfields; i++)
654 ptr += field->structOffset;
657 if (field->hintsField >= 0)
660 WINPR_ASSERT((
size_t)field->hintsField < descr->nfields);
661 const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
663 hints = (BYTE*)target + hintsField->structOffset;
666 switch (field->pointerType)
668 case NDR_NOT_POINTER:
669 if (!field->typeDescr->readFn(context, s, hints, ptr))
671 WLog_ERR(TAG,
"error when reading %s.%s", descr->name, field->name);
676 case NDR_POINTER_NON_NULL:
679 if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
681 WLog_ERR(TAG,
"too many deferred when calling ndr_read_struct_fromDescr for %s",
686 deferred->name = field->name;
687 deferred->hints = hints;
688 deferred->target = ptr;
689 deferred->msg = field->typeDescr;
690 if (!ndr_read_refpointer(context, s, &deferred->ptrId))
692 WLog_ERR(TAG,
"error when reading %s.%s", descr->name, field->name);
696 if (!deferred->ptrId && field->pointerType == NDR_POINTER_NON_NULL)
698 WLog_ERR(TAG,
"%s.%s can't be null", descr->name, field->name);
705 WLog_ERR(TAG,
"%s.%s unknown pointer type 0x%x", descr->name, field->name,
711 return ndr_push_deferreds(context, deferreds, ndeferred);
717 WINPR_ASSERT(context);
723 size_t ndeferred = 0;
725 for (
size_t i = 0; i < descr->nfields; i++)
728 const BYTE* ptr = (
const BYTE*)src + field->structOffset;
730 const void* hints = NULL;
732 if (field->hintsField >= 0)
735 WINPR_ASSERT((
size_t)field->hintsField < descr->nfields);
736 const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
738 hints = (
const BYTE*)src + hintsField->structOffset;
741 switch (field->pointerType)
744 case NDR_POINTER_NON_NULL:
746 ndr_refid ptrId = NDR_PTR_NULL;
748 ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr,
const void**));
750 if (!ptr && field->pointerType == NDR_POINTER_NON_NULL)
752 WLog_ERR(TAG,
"%s.%s can't be null", descr->name, field->name);
756 if (!ndr_context_allocatePtr(context, ptr, &ptrId, &isNew))
762 if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
765 "too many deferred when calling ndr_read_struct_fromDescr for %s",
770 deferred->name = field->name;
771 deferred->hints = WINPR_CAST_CONST_PTR_AWAY(hints,
void*);
772 deferred->target = WINPR_CAST_CONST_PTR_AWAY(ptr,
void*);
773 deferred->msg = field->typeDescr;
777 if (!ndr_write_uint32(context, s, ptrId))
781 case NDR_NOT_POINTER:
782 if (!field->typeDescr->writeFn(context, s, hints, ptr))
784 WLog_ERR(TAG,
"error when writing %s.%s", descr->name, field->name);
793 return ndr_push_deferreds(context, deferreds, ndeferred);
796void ndr_struct_dump_fromDescr(wLog* logger, UINT32 lvl,
size_t identLevel,
799 char tabArray[30 + 1];
800 size_t ntabs = (identLevel <= 30) ? identLevel : 30;
802 memset(tabArray,
'\t', ntabs);
805 WLog_Print(logger, lvl,
"%s%s", tabArray, descr->name);
806 for (
size_t i = 0; i < descr->nfields; i++)
809 const BYTE* ptr = (
const BYTE*)obj + field->structOffset;
811 switch (field->pointerType)
814 case NDR_POINTER_NON_NULL:
815 ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr,
const void**));
817 case NDR_NOT_POINTER:
820 WLog_ERR(TAG,
"invalid field->pointerType");
824 WLog_Print(logger, lvl,
"%s*%s:", tabArray, field->name);
825 if (field->typeDescr->dumpFn)
826 field->typeDescr->dumpFn(logger, lvl, identLevel + 1, ptr);
828 WLog_Print(logger, lvl,
"%s\t<no dump function>", tabArray);
832void ndr_struct_destroy(NdrContext* context,
const NdrStructDescr* descr,
void* pptr)
834 WINPR_ASSERT(context);
838 for (
size_t i = 0; i < descr->nfields; i++)
841 void* ptr = (BYTE*)pptr + field->structOffset;
844 if (field->hintsField >= 0)
847 WINPR_ASSERT((
size_t)field->hintsField < descr->nfields);
848 const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
850 hints = (BYTE*)pptr + hintsField->structOffset;
853 if (field->pointerType != NDR_NOT_POINTER)
856 if (ptr && field->typeDescr->destroyFn)
857 field->typeDescr->destroyFn(context, hints, ptr);
859 if (field->pointerType != NDR_NOT_POINTER)
864ndr_refid ndr_pointer_refid(
const void* ptr)
866 return (ndr_refid)((ULONG_PTR)ptr);
869BOOL ndr_read_refpointer(NdrContext* context,
wStream* s, ndr_refid* refId)
871 return ndr_read_uint32(context, s, refId);
880static BOOL findValueRefFn(
const void* key,
void* value,
void* parg)
884 FindValueArgs* args = (FindValueArgs*)parg;
885 if (args->needle == value)
887 *args->presult = (ndr_refid)(UINT_PTR)key;
893BOOL ndr_context_allocatePtr(NdrContext* context,
const void* ptr, ndr_refid* prefId, BOOL* pnewPtr)
895 WINPR_ASSERT(context);
897 FindValueArgs findArgs = { ptr, prefId };
898 if (!HashTable_Foreach(context->refPointers, findValueRefFn, &findArgs))
905 *prefId = context->refIdCounter + 4;
906 if (!HashTable_Insert(context->refPointers, (
void*)(UINT_PTR)(*prefId), ptr))
909 context->refIdCounter += 4;
913BOOL ndr_read_pointedMessageEx(NdrContext* context,
wStream* s, ndr_refid ptrId,
916 WINPR_ASSERT(context);
919 WINPR_ASSERT(target);
925 void* ret = HashTable_GetItemValue(context->refPointers, (
void*)(UINT_PTR)ptrId);
928 size_t itemCount = ndr_hintsCount(descr, hints);
931 ret = calloc(itemCount, descr->itemSize);
935 if (!descr->readFn(context, s, hints, ret) ||
936 !HashTable_Insert(context->refPointers, (
void*)(UINT_PTR)ptrId, ret))
938 if (descr->destroyFn)
939 descr->destroyFn(context, hints, ret);
949BOOL ndr_push_deferreds(NdrContext* context,
NdrDeferredEntry* deferreds,
size_t ndeferred)
951 WINPR_ASSERT(context);
952 WINPR_ASSERT(deferreds);
957 if (context->ndeferred + ndeferred > NDR_MAX_DEFERRED)
959 WLog_ERR(TAG,
"too many deferred");
963 for (
size_t i = ndeferred; i > 0; i--, context->ndeferred++)
965 context->deferred[context->ndeferred] = deferreds[i - 1];
970BOOL ndr_treat_deferred_read(NdrContext* context,
wStream* s)
972 WINPR_ASSERT(context);
975 while (context->ndeferred)
978 context->ndeferred--;
980 WLog_VRB(TAG,
"treating read deferred 0x%x for %s", current.ptrId, current.name);
981 if (!ndr_read_pointedMessageEx(context, s, current.ptrId, current.msg, current.hints,
982 (
void**)current.target))
984 WLog_ERR(TAG,
"error parsing deferred %s", current.name);
992BOOL ndr_treat_deferred_write(NdrContext* context,
wStream* s)
994 WINPR_ASSERT(context);
997 while (context->ndeferred)
1000 context->ndeferred--;
1002 WLog_VRB(TAG,
"treating write deferred for %s", current.name);
1003 if (!current.msg->writeFn(context, s, current.hints, current.target))
1005 WLog_ERR(TAG,
"error writing deferred %s", current.name);
1013BOOL ndr_write_data(NdrContext* context,
wStream* s,
const void* data,
size_t sz)
1015 if (!Stream_EnsureRemainingCapacity(s, sz))
1018 Stream_Write(s, data, sz);
1019 ndr_context_bytes_written(context, sz);
hints for a conformant array
descriptor of a field in a structure
hints for a varying conformant array