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);
525 if (!ndr_read_uint32(context, s, &maxCount) || !ndr_read_uint32(context, s, &offset) ||
526 !ndr_read_uint32(context, s, &length))
529 if ((length * itemType->itemSize) < hints->length)
532 if ((maxCount * itemType->itemSize) < hints->maxLength)
535 BYTE* target = (BYTE*)ptarget;
536 for (UINT32 i = 0; i < length; i++, target += itemType->itemSize)
538 if (!itemType->readFn(context, s, NULL, target))
542 return ndr_read_align(context, s, 4);
545BOOL ndr_write_uconformant_varying_array(NdrContext* context,
wStream* s,
549 WINPR_ASSERT(context);
552 WINPR_ASSERT(itemType);
555 if (!ndr_write_uint32(context, s, hints->maxLength) || !ndr_write_uint32(context, s, 0) ||
556 !ndr_write_uint32(context, s, hints->length))
559 const BYTE* src = (
const BYTE*)psrc;
560 for (UINT32 i = 0; i < hints->length; i++, src += itemType->itemSize)
562 if (!itemType->writeFn(context, s, NULL, src))
572 WINPR_ASSERT(context);
574 WINPR_ASSERT(itemType);
575 WINPR_ASSERT(vtarget);
579 if (!ndr_read_uint32(context, s, &count))
582 if ((count * itemType->itemSize < hints->count))
585 BYTE* target = (BYTE*)vtarget;
586 for (UINT32 i = 0; i < count; i++, target += itemType->itemSize)
588 if (!itemType->readFn(context, s, NULL, target))
592 return ndr_read_align(context, s, 4);
595BOOL ndr_write_uconformant_array(NdrContext* context,
wStream* s, UINT32 len,
598 WINPR_ASSERT(context);
600 WINPR_ASSERT(itemType);
603 size_t toWrite = len * itemType->itemSize;
604 size_t padding = (4 - (toWrite % 4)) % 4;
605 if (!ndr_write_uint32(context, s, len) || !Stream_EnsureRemainingCapacity(s, toWrite + padding))
608 for (UINT32 i = 0; i < len; i++, ptr += itemType->itemSize)
610 if (!itemType->writeFn(context, s, NULL, ptr))
616 Stream_Zero(s, padding);
617 ndr_context_bytes_written(context, padding);
625 WINPR_ASSERT(context);
628 WINPR_ASSERT(target);
630#define NDR_MAX_STRUCT_DEFERRED 16
632 size_t ndeferred = 0;
634 for (
size_t i = 0; i < descr->nfields; i++)
638 ptr += field->structOffset;
641 if (field->hintsField >= 0)
644 WINPR_ASSERT((
size_t)field->hintsField < descr->nfields);
645 const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
647 hints = (BYTE*)target + hintsField->structOffset;
650 switch (field->pointerType)
652 case NDR_NOT_POINTER:
653 if (!field->typeDescr->readFn(context, s, hints, ptr))
655 WLog_ERR(TAG,
"error when reading %s.%s", descr->name, field->name);
660 case NDR_POINTER_NON_NULL:
663 if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
665 WLog_ERR(TAG,
"too many deferred when calling ndr_read_struct_fromDescr for %s",
670 deferred->name = field->name;
671 deferred->hints = hints;
672 deferred->target = ptr;
673 deferred->msg = field->typeDescr;
674 if (!ndr_read_refpointer(context, s, &deferred->ptrId))
676 WLog_ERR(TAG,
"error when reading %s.%s", descr->name, field->name);
680 if (!deferred->ptrId && field->pointerType == NDR_POINTER_NON_NULL)
682 WLog_ERR(TAG,
"%s.%s can't be null", descr->name, field->name);
689 WLog_ERR(TAG,
"%s.%s unknown pointer type 0x%x", descr->name, field->name,
695 return ndr_push_deferreds(context, deferreds, ndeferred);
701 WINPR_ASSERT(context);
707 size_t ndeferred = 0;
709 for (
size_t i = 0; i < descr->nfields; i++)
712 const BYTE* ptr = (
const BYTE*)src + field->structOffset;
714 const void* hints = NULL;
716 if (field->hintsField >= 0)
719 WINPR_ASSERT((
size_t)field->hintsField < descr->nfields);
720 const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
722 hints = (
const BYTE*)src + hintsField->structOffset;
725 switch (field->pointerType)
728 case NDR_POINTER_NON_NULL:
730 ndr_refid ptrId = NDR_PTR_NULL;
732 ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr,
const void**));
734 if (!ptr && field->pointerType == NDR_POINTER_NON_NULL)
736 WLog_ERR(TAG,
"%s.%s can't be null", descr->name, field->name);
740 if (!ndr_context_allocatePtr(context, ptr, &ptrId, &isNew))
746 if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
749 "too many deferred when calling ndr_read_struct_fromDescr for %s",
754 deferred->name = field->name;
755 deferred->hints = WINPR_CAST_CONST_PTR_AWAY(hints,
void*);
756 deferred->target = WINPR_CAST_CONST_PTR_AWAY(ptr,
void*);
757 deferred->msg = field->typeDescr;
761 if (!ndr_write_uint32(context, s, ptrId))
765 case NDR_NOT_POINTER:
766 if (!field->typeDescr->writeFn(context, s, hints, ptr))
768 WLog_ERR(TAG,
"error when writing %s.%s", descr->name, field->name);
777 return ndr_push_deferreds(context, deferreds, ndeferred);
780void ndr_struct_dump_fromDescr(wLog* logger, UINT32 lvl,
size_t identLevel,
783 char tabArray[30 + 1];
784 size_t ntabs = (identLevel <= 30) ? identLevel : 30;
786 memset(tabArray,
'\t', ntabs);
789 WLog_Print(logger, lvl,
"%s%s", tabArray, descr->name);
790 for (
size_t i = 0; i < descr->nfields; i++)
793 const BYTE* ptr = (
const BYTE*)obj + field->structOffset;
795 switch (field->pointerType)
798 case NDR_POINTER_NON_NULL:
799 ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr,
const void**));
801 case NDR_NOT_POINTER:
804 WLog_ERR(TAG,
"invalid field->pointerType");
808 WLog_Print(logger, lvl,
"%s*%s:", tabArray, field->name);
809 if (field->typeDescr->dumpFn)
810 field->typeDescr->dumpFn(logger, lvl, identLevel + 1, ptr);
812 WLog_Print(logger, lvl,
"%s\t<no dump function>", tabArray);
816void ndr_struct_destroy(NdrContext* context,
const NdrStructDescr* descr,
void* pptr)
818 WINPR_ASSERT(context);
822 for (
size_t i = 0; i < descr->nfields; i++)
825 void* ptr = (BYTE*)pptr + field->structOffset;
828 if (field->hintsField >= 0)
831 WINPR_ASSERT((
size_t)field->hintsField < descr->nfields);
832 const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
834 hints = (BYTE*)pptr + hintsField->structOffset;
837 if (field->pointerType != NDR_NOT_POINTER)
840 if (ptr && field->typeDescr->destroyFn)
841 field->typeDescr->destroyFn(context, hints, ptr);
843 if (field->pointerType != NDR_NOT_POINTER)
848ndr_refid ndr_pointer_refid(
const void* ptr)
850 return (ndr_refid)((ULONG_PTR)ptr);
853BOOL ndr_read_refpointer(NdrContext* context,
wStream* s, ndr_refid* refId)
855 return ndr_read_uint32(context, s, refId);
864static BOOL findValueRefFn(
const void* key,
void* value,
void* parg)
868 FindValueArgs* args = (FindValueArgs*)parg;
869 if (args->needle == value)
871 *args->presult = (ndr_refid)(UINT_PTR)key;
877BOOL ndr_context_allocatePtr(NdrContext* context,
const void* ptr, ndr_refid* prefId, BOOL* pnewPtr)
879 WINPR_ASSERT(context);
881 FindValueArgs findArgs = { ptr, prefId };
882 if (!HashTable_Foreach(context->refPointers, findValueRefFn, &findArgs))
889 *prefId = context->refIdCounter + 4;
890 if (!HashTable_Insert(context->refPointers, (
void*)(UINT_PTR)(*prefId), ptr))
893 context->refIdCounter += 4;
897BOOL ndr_read_pointedMessageEx(NdrContext* context,
wStream* s, ndr_refid ptrId,
900 WINPR_ASSERT(context);
903 WINPR_ASSERT(target);
909 void* ret = HashTable_GetItemValue(context->refPointers, (
void*)(UINT_PTR)ptrId);
912 size_t itemCount = ndr_hintsCount(descr, hints);
913 ret = calloc(itemCount, descr->itemSize);
917 if (!descr->readFn(context, s, hints, ret) ||
918 !HashTable_Insert(context->refPointers, (
void*)(UINT_PTR)ptrId, ret))
920 if (descr->destroyFn)
921 descr->destroyFn(context, hints, ret);
931BOOL ndr_push_deferreds(NdrContext* context,
NdrDeferredEntry* deferreds,
size_t ndeferred)
933 WINPR_ASSERT(context);
934 WINPR_ASSERT(deferreds);
939 if (context->ndeferred + ndeferred > NDR_MAX_DEFERRED)
941 WLog_ERR(TAG,
"too many deferred");
945 for (
size_t i = ndeferred; i > 0; i--, context->ndeferred++)
947 context->deferred[context->ndeferred] = deferreds[i - 1];
952BOOL ndr_treat_deferred_read(NdrContext* context,
wStream* s)
954 WINPR_ASSERT(context);
957 while (context->ndeferred)
960 context->ndeferred--;
962 WLog_VRB(TAG,
"treating read deferred 0x%x for %s", current.ptrId, current.name);
963 if (!ndr_read_pointedMessageEx(context, s, current.ptrId, current.msg, current.hints,
964 (
void**)current.target))
966 WLog_ERR(TAG,
"error parsing deferred %s", current.name);
974BOOL ndr_treat_deferred_write(NdrContext* context,
wStream* s)
976 WINPR_ASSERT(context);
979 while (context->ndeferred)
982 context->ndeferred--;
984 WLog_VRB(TAG,
"treating write deferred for %s", current.name);
985 if (!current.msg->writeFn(context, s, current.hints, current.target))
987 WLog_ERR(TAG,
"error writing deferred %s", current.name);
995BOOL ndr_write_data(NdrContext* context,
wStream* s,
const void* data,
size_t sz)
997 if (!Stream_EnsureRemainingCapacity(s, sz))
1000 Stream_Write(s, data, sz);
1001 ndr_context_bytes_written(context, sz);
hints for a conformant array
descriptor of a field in a structure
hints for a varying conformant array