d5b41bb8a0
* Add macro WINPR_REINTERPRET_CAST to cast (checked) from type A to B * Fix cast warnings
995 lines
32 KiB
C
995 lines
32 KiB
C
/**
|
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
|
* Authentication redirection virtual channel
|
|
*
|
|
* Copyright 2024 David Fort <contact@hardening-consulting.com>
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#include <winpr/assert.h>
|
|
#include <winpr/collections.h>
|
|
#include <winpr/wlog.h>
|
|
|
|
#include <freerdp/log.h>
|
|
|
|
#include <rdpear-common/ndr.h>
|
|
|
|
#define TAG FREERDP_TAG("ndr")
|
|
|
|
#define NDR_MAX_CONSTRUCTS 16
|
|
#define NDR_MAX_DEFERRED 50
|
|
|
|
struct NdrContext_s
|
|
{
|
|
BYTE version;
|
|
BOOL bigEndianDrep;
|
|
size_t alignBytes;
|
|
|
|
int currentLevel;
|
|
size_t indentLevels[16];
|
|
|
|
int constructLevel;
|
|
size_t constructs[NDR_MAX_CONSTRUCTS];
|
|
|
|
wHashTable* refPointers;
|
|
size_t ndeferred;
|
|
NdrDeferredEntry deferred[NDR_MAX_DEFERRED];
|
|
|
|
UINT32 refIdCounter;
|
|
};
|
|
|
|
NdrContext* ndr_context_new(BOOL bigEndianDrep, BYTE version)
|
|
{
|
|
NdrContext* ret = calloc(1, sizeof(*ret));
|
|
if (!ret)
|
|
return NULL;
|
|
|
|
ret->version = version;
|
|
ret->bigEndianDrep = bigEndianDrep;
|
|
ret->alignBytes = 4;
|
|
ret->refPointers = HashTable_New(FALSE);
|
|
if (!ret->refPointers)
|
|
{
|
|
free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
ndr_context_reset(ret);
|
|
return ret;
|
|
}
|
|
|
|
void ndr_context_reset(NdrContext* context)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
|
|
context->currentLevel = 0;
|
|
context->constructLevel = -1;
|
|
memset(context->indentLevels, 0, sizeof(context->indentLevels));
|
|
|
|
if (context->refPointers)
|
|
HashTable_Clear(context->refPointers);
|
|
context->ndeferred = 0;
|
|
context->refIdCounter = 0x20000;
|
|
}
|
|
|
|
NdrContext* ndr_context_copy(const NdrContext* src)
|
|
{
|
|
WINPR_ASSERT(src);
|
|
|
|
NdrContext* ret = calloc(1, sizeof(*ret));
|
|
if (!ret)
|
|
return NULL;
|
|
|
|
*ret = *src;
|
|
|
|
ret->refPointers = HashTable_New(FALSE);
|
|
if (!ret->refPointers)
|
|
{
|
|
free(ret);
|
|
return NULL;
|
|
}
|
|
|
|
ndr_context_reset(ret);
|
|
return ret;
|
|
}
|
|
|
|
void ndr_context_free(NdrContext* context)
|
|
{
|
|
if (context)
|
|
{
|
|
HashTable_Free(context->refPointers);
|
|
free(context);
|
|
}
|
|
}
|
|
|
|
static void ndr_context_bytes_read(NdrContext* context, size_t len)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
context->indentLevels[context->currentLevel] += len;
|
|
}
|
|
|
|
static void ndr_context_bytes_written(NdrContext* context, size_t len)
|
|
{
|
|
ndr_context_bytes_read(context, len);
|
|
}
|
|
|
|
NdrContext* ndr_read_header(wStream* s)
|
|
{
|
|
if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
|
|
return NULL;
|
|
|
|
BYTE version = Stream_Get_UINT8(s);
|
|
BYTE drep = Stream_Get_UINT8(s);
|
|
UINT16 headerLen = Stream_Get_UINT16(s);
|
|
|
|
if (headerLen < 4 || !Stream_CheckAndLogRequiredLength(TAG, s, headerLen - 4))
|
|
return NULL;
|
|
|
|
/* skip filler */
|
|
Stream_Seek(s, headerLen - 4);
|
|
|
|
return ndr_context_new((drep != 0x10), version);
|
|
}
|
|
|
|
BOOL ndr_write_header(NdrContext* context, wStream* s)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
|
|
if (!Stream_EnsureRemainingCapacity(s, 8))
|
|
return FALSE;
|
|
|
|
Stream_Write_UINT8(s, context->version);
|
|
Stream_Write_UINT8(s, context->bigEndianDrep ? 0x00 : 0x10);
|
|
Stream_Write_UINT16(s, 0x8); /* header len */
|
|
|
|
BYTE filler[] = { 0xcc, 0xcc, 0xcc, 0xcc };
|
|
Stream_Write(s, filler, sizeof(filler));
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ndr_skip_bytes(NdrContext* context, wStream* s, size_t nbytes)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
|
|
if (!Stream_CheckAndLogRequiredLength(TAG, s, nbytes))
|
|
return FALSE;
|
|
|
|
context->indentLevels[context->currentLevel] += nbytes;
|
|
Stream_Seek(s, nbytes);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ndr_read_align(NdrContext* context, wStream* s, size_t sz)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
|
|
size_t rest = context->indentLevels[context->currentLevel] % sz;
|
|
if (rest)
|
|
{
|
|
size_t padding = (sz - rest);
|
|
if (!Stream_CheckAndLogRequiredLength(TAG, s, padding))
|
|
return FALSE;
|
|
|
|
Stream_Seek(s, padding);
|
|
context->indentLevels[context->currentLevel] += padding;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ndr_write_align(NdrContext* context, wStream* s, size_t sz)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
|
|
size_t rest = context->indentLevels[context->currentLevel] % sz;
|
|
if (rest)
|
|
{
|
|
size_t padding = (sz - rest);
|
|
|
|
if (!Stream_EnsureRemainingCapacity(s, padding))
|
|
return FALSE;
|
|
|
|
Stream_Zero(s, padding);
|
|
context->indentLevels[context->currentLevel] += padding;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ndr_read_pickle(NdrContext* context, wStream* s)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
|
|
UINT32 v = 0;
|
|
|
|
/* NDR format label */
|
|
if (!ndr_read_uint32(context, s, &v) || v != 0x20000)
|
|
return FALSE;
|
|
|
|
return ndr_read_uint32(context, s, &v); // padding
|
|
}
|
|
|
|
BOOL ndr_write_pickle(NdrContext* context, wStream* s)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
|
|
/* NDR format label */
|
|
if (!ndr_write_uint32(context, s, 0x20000))
|
|
return FALSE;
|
|
|
|
ndr_write_uint32(context, s, 0); /* padding */
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ndr_read_constructed(NdrContext* context, wStream* s, wStream* target)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
|
|
UINT32 len = 0;
|
|
|
|
/* len */
|
|
if (!ndr_read_uint32(context, s, &len))
|
|
return FALSE;
|
|
|
|
/* padding */
|
|
if (!ndr_skip_bytes(context, s, 4))
|
|
return FALSE;
|
|
|
|
/* payload */
|
|
if (!Stream_CheckAndLogRequiredLength(TAG, s, len))
|
|
return FALSE;
|
|
|
|
Stream_StaticInit(target, Stream_PointerAs(s, BYTE), len);
|
|
Stream_Seek(s, len);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ndr_start_constructed(NdrContext* context, wStream* s)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
|
|
if (!Stream_EnsureCapacity(s, 8))
|
|
return FALSE;
|
|
|
|
if (context->constructLevel == NDR_MAX_CONSTRUCTS)
|
|
return FALSE;
|
|
|
|
context->constructLevel++;
|
|
context->constructs[context->constructLevel] = Stream_GetPosition(s);
|
|
|
|
Stream_Zero(s, 8);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ndr_end_constructed(NdrContext* context, wStream* s)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
WINPR_ASSERT(context->constructs);
|
|
WINPR_ASSERT(context->constructLevel >= 0);
|
|
|
|
size_t offset = context->constructs[context->constructLevel];
|
|
|
|
wStream staticS = { 0 };
|
|
Stream_StaticInit(&staticS, Stream_Buffer(s) + offset, 4);
|
|
|
|
/* len */
|
|
const size_t len = Stream_GetPosition(s) - (offset + 8);
|
|
if (len > UINT32_MAX)
|
|
return FALSE;
|
|
if (!ndr_write_uint32(context, &staticS, (UINT32)len))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static size_t ndr_hintsCount(NdrMessageType msgType, const void* hints)
|
|
{
|
|
WINPR_ASSERT(msgType);
|
|
|
|
switch (msgType->arity)
|
|
{
|
|
case NDR_ARITY_SIMPLE:
|
|
return 1;
|
|
case NDR_ARITY_ARRAYOF:
|
|
WINPR_ASSERT(hints);
|
|
return ((const NdrArrayHints*)hints)->count;
|
|
case NDR_ARITY_VARYING_ARRAYOF:
|
|
WINPR_ASSERT(hints);
|
|
return ((const NdrVaryingArrayHints*)hints)->maxLength;
|
|
default:
|
|
WINPR_ASSERT(0 && "unknown arity");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
BOOL ndr_read_uint8(NdrContext* context, wStream* s, BYTE* v)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
|
|
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
|
|
return FALSE;
|
|
|
|
Stream_Read_UINT8(s, *v);
|
|
|
|
ndr_context_bytes_read(context, 1);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ndr_read_uint8_(NdrContext* context, wStream* s, const void* hints, void* v)
|
|
{
|
|
WINPR_UNUSED(hints);
|
|
return ndr_read_uint8(context, s, (BYTE*)v);
|
|
}
|
|
|
|
BOOL ndr_write_uint8(NdrContext* context, wStream* s, BYTE v)
|
|
{
|
|
if (!Stream_EnsureRemainingCapacity(s, 1))
|
|
return FALSE;
|
|
|
|
Stream_Write_UINT8(s, v);
|
|
ndr_context_bytes_written(context, 1);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ndr_write_uint8_(NdrContext* context, wStream* s, const void* hints, const void* v)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
WINPR_ASSERT(s);
|
|
WINPR_ASSERT(v);
|
|
WINPR_UNUSED(hints);
|
|
|
|
return ndr_write_uint8(context, s, *(const BYTE*)v);
|
|
}
|
|
|
|
const static NdrMessageDescr uint8_descr = { NDR_ARITY_SIMPLE, 1, ndr_read_uint8_,
|
|
ndr_write_uint8_, NULL, NULL };
|
|
|
|
NdrMessageType ndr_uint8_descr(void)
|
|
{
|
|
return &uint8_descr;
|
|
}
|
|
|
|
#define SIMPLE_TYPE_IMPL(UPPERTYPE, LOWERTYPE) \
|
|
BOOL ndr_read_##LOWERTYPE(NdrContext* context, wStream* s, UPPERTYPE* v) \
|
|
{ \
|
|
WINPR_ASSERT(context); \
|
|
\
|
|
if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(UPPERTYPE))) \
|
|
return FALSE; \
|
|
\
|
|
if (!ndr_read_align(context, s, sizeof(UPPERTYPE))) \
|
|
return FALSE; \
|
|
\
|
|
if (context->bigEndianDrep) \
|
|
Stream_Read_##UPPERTYPE##_BE(s, *v); \
|
|
else \
|
|
Stream_Read_##UPPERTYPE(s, *v); \
|
|
\
|
|
ndr_context_bytes_read(context, sizeof(UPPERTYPE)); \
|
|
return TRUE; \
|
|
} \
|
|
\
|
|
BOOL ndr_read_##LOWERTYPE##_(NdrContext* context, wStream* s, const void* hints, void* v) \
|
|
{ \
|
|
WINPR_UNUSED(hints); \
|
|
return ndr_read_##LOWERTYPE(context, s, (UPPERTYPE*)v); \
|
|
} \
|
|
\
|
|
BOOL ndr_write_##LOWERTYPE(NdrContext* context, wStream* s, UPPERTYPE v) \
|
|
{ \
|
|
if (!ndr_write_align(context, s, sizeof(UPPERTYPE)) || \
|
|
!Stream_EnsureRemainingCapacity(s, sizeof(UPPERTYPE))) \
|
|
return FALSE; \
|
|
\
|
|
if (context->bigEndianDrep) \
|
|
Stream_Write_##UPPERTYPE##_BE(s, v); \
|
|
else \
|
|
Stream_Write_##UPPERTYPE(s, v); \
|
|
\
|
|
ndr_context_bytes_written(context, sizeof(UPPERTYPE)); \
|
|
return TRUE; \
|
|
} \
|
|
\
|
|
BOOL ndr_write_##LOWERTYPE##_(NdrContext* context, wStream* s, const void* hints, \
|
|
const void* v) \
|
|
{ \
|
|
WINPR_ASSERT(context); \
|
|
WINPR_ASSERT(s); \
|
|
WINPR_ASSERT(v); \
|
|
WINPR_UNUSED(hints); \
|
|
\
|
|
return ndr_write_##LOWERTYPE(context, s, *(const UPPERTYPE*)v); \
|
|
} \
|
|
\
|
|
const NdrMessageDescr ndr_##LOWERTYPE##_descr_s = { NDR_ARITY_SIMPLE, \
|
|
sizeof(UPPERTYPE), \
|
|
ndr_read_##LOWERTYPE##_, \
|
|
ndr_write_##LOWERTYPE##_, \
|
|
NULL, \
|
|
NULL }; \
|
|
\
|
|
NdrMessageType ndr_##LOWERTYPE##_descr(void) \
|
|
{ \
|
|
return &ndr_##LOWERTYPE##_descr_s; \
|
|
}
|
|
|
|
SIMPLE_TYPE_IMPL(UINT32, uint32)
|
|
SIMPLE_TYPE_IMPL(UINT16, uint16)
|
|
SIMPLE_TYPE_IMPL(UINT64, uint64)
|
|
|
|
#define ARRAY_OF_TYPE_IMPL(TYPE, UPPERTYPE) \
|
|
BOOL ndr_read_##TYPE##Array(NdrContext* context, wStream* s, const void* hints, void* v) \
|
|
{ \
|
|
WINPR_ASSERT(context); \
|
|
WINPR_ASSERT(s); \
|
|
WINPR_ASSERT(hints); \
|
|
return ndr_read_uconformant_array(context, s, hints, ndr_##TYPE##_descr(), v); \
|
|
} \
|
|
\
|
|
BOOL ndr_write_##TYPE##Array(NdrContext* context, wStream* s, const void* hints, \
|
|
const void* v) \
|
|
{ \
|
|
WINPR_ASSERT(context); \
|
|
WINPR_ASSERT(s); \
|
|
WINPR_ASSERT(hints); \
|
|
const NdrArrayHints* ahints = (const NdrArrayHints*)hints; \
|
|
return ndr_write_uconformant_array(context, s, ahints->count, ndr_##TYPE##_descr(), v); \
|
|
} \
|
|
void ndr_destroy_##TYPE##Array(NdrContext* context, const void* hints, void* obj) \
|
|
{ \
|
|
WINPR_ASSERT(context); \
|
|
WINPR_ASSERT(obj); \
|
|
WINPR_ASSERT(hints); \
|
|
const NdrArrayHints* ahints = (const NdrArrayHints*)hints; \
|
|
NdrMessageType descr = ndr_##TYPE##_descr(); \
|
|
if (descr->destroyFn) \
|
|
{ \
|
|
UPPERTYPE* ptr = (UPPERTYPE*)obj; \
|
|
for (UINT32 i = 0; i < ahints->count; i++, ptr++) \
|
|
descr->destroyFn(context, NULL, ptr); \
|
|
} \
|
|
} \
|
|
\
|
|
const NdrMessageDescr ndr_##TYPE##Array_descr_s = { \
|
|
NDR_ARITY_ARRAYOF, sizeof(UPPERTYPE), ndr_read_##TYPE##Array, \
|
|
ndr_write_##TYPE##Array, ndr_destroy_##TYPE##Array, NULL \
|
|
}; \
|
|
\
|
|
NdrMessageType ndr_##TYPE##Array_descr(void) \
|
|
{ \
|
|
return &ndr_##TYPE##Array_descr_s; \
|
|
} \
|
|
\
|
|
BOOL ndr_read_##TYPE##VaryingArray(NdrContext* context, wStream* s, const void* hints, \
|
|
void* v) \
|
|
{ \
|
|
WINPR_ASSERT(context); \
|
|
WINPR_ASSERT(s); \
|
|
WINPR_ASSERT(hints); \
|
|
return ndr_read_uconformant_varying_array(context, s, (const NdrVaryingArrayHints*)hints, \
|
|
ndr_##TYPE##_descr(), v); \
|
|
} \
|
|
BOOL ndr_write_##TYPE##VaryingArray(NdrContext* context, wStream* s, const void* hints, \
|
|
const void* v) \
|
|
{ \
|
|
WINPR_ASSERT(context); \
|
|
WINPR_ASSERT(s); \
|
|
WINPR_ASSERT(hints); \
|
|
return ndr_write_uconformant_varying_array(context, s, (const NdrVaryingArrayHints*)hints, \
|
|
ndr_##TYPE##_descr(), v); \
|
|
} \
|
|
\
|
|
const NdrMessageDescr ndr_##TYPE##VaryingArray_descr_s = { NDR_ARITY_VARYING_ARRAYOF, \
|
|
sizeof(UPPERTYPE), \
|
|
ndr_read_##TYPE##VaryingArray, \
|
|
ndr_write_##TYPE##VaryingArray, \
|
|
NULL, \
|
|
NULL }; \
|
|
\
|
|
NdrMessageType ndr_##TYPE##VaryingArray_descr(void) \
|
|
{ \
|
|
return &ndr_##TYPE##VaryingArray_descr_s; \
|
|
}
|
|
|
|
ARRAY_OF_TYPE_IMPL(uint8, BYTE)
|
|
ARRAY_OF_TYPE_IMPL(uint16, UINT16)
|
|
|
|
BOOL ndr_read_wchar(NdrContext* context, wStream* s, WCHAR* ptr)
|
|
{
|
|
return ndr_read_uint16(context, s, (UINT16*)ptr);
|
|
}
|
|
|
|
BOOL ndr_read_uconformant_varying_array(NdrContext* context, wStream* s,
|
|
const NdrVaryingArrayHints* hints, NdrMessageType itemType,
|
|
void* ptarget)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
WINPR_ASSERT(s);
|
|
WINPR_ASSERT(hints);
|
|
WINPR_ASSERT(itemType);
|
|
WINPR_ASSERT(ptarget);
|
|
|
|
UINT32 maxCount = 0;
|
|
UINT32 offset = 0;
|
|
UINT32 length = 0;
|
|
|
|
if (!ndr_read_uint32(context, s, &maxCount) || !ndr_read_uint32(context, s, &offset) ||
|
|
!ndr_read_uint32(context, s, &length))
|
|
return FALSE;
|
|
|
|
if ((length * itemType->itemSize) < hints->length)
|
|
return FALSE;
|
|
|
|
if ((maxCount * itemType->itemSize) < hints->maxLength)
|
|
return FALSE;
|
|
|
|
BYTE* target = (BYTE*)ptarget;
|
|
for (UINT32 i = 0; i < length; i++, target += itemType->itemSize)
|
|
{
|
|
if (!itemType->readFn(context, s, NULL, target))
|
|
return FALSE;
|
|
}
|
|
|
|
return ndr_read_align(context, s, 4);
|
|
}
|
|
|
|
BOOL ndr_write_uconformant_varying_array(NdrContext* context, wStream* s,
|
|
const NdrVaryingArrayHints* hints, NdrMessageType itemType,
|
|
const void* psrc)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
WINPR_ASSERT(s);
|
|
WINPR_ASSERT(hints);
|
|
WINPR_ASSERT(itemType);
|
|
WINPR_ASSERT(psrc);
|
|
|
|
if (!ndr_write_uint32(context, s, hints->maxLength) || !ndr_write_uint32(context, s, 0) ||
|
|
!ndr_write_uint32(context, s, hints->length))
|
|
return FALSE;
|
|
|
|
const BYTE* src = (const BYTE*)psrc;
|
|
for (UINT32 i = 0; i < hints->length; i++, src += itemType->itemSize)
|
|
{
|
|
if (!itemType->writeFn(context, s, NULL, src))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ndr_read_uconformant_array(NdrContext* context, wStream* s, const NdrArrayHints* hints,
|
|
NdrMessageType itemType, void* vtarget)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
WINPR_ASSERT(s);
|
|
WINPR_ASSERT(itemType);
|
|
WINPR_ASSERT(vtarget);
|
|
|
|
UINT32 count = 0;
|
|
|
|
if (!ndr_read_uint32(context, s, &count))
|
|
return FALSE;
|
|
|
|
if ((count * itemType->itemSize < hints->count))
|
|
return FALSE;
|
|
|
|
BYTE* target = (BYTE*)vtarget;
|
|
for (UINT32 i = 0; i < count; i++, target += itemType->itemSize)
|
|
{
|
|
if (!itemType->readFn(context, s, NULL, target))
|
|
return FALSE;
|
|
}
|
|
|
|
return ndr_read_align(context, s, /*context->alignBytes*/ 4);
|
|
}
|
|
|
|
BOOL ndr_write_uconformant_array(NdrContext* context, wStream* s, UINT32 len,
|
|
NdrMessageType itemType, const BYTE* ptr)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
WINPR_ASSERT(s);
|
|
WINPR_ASSERT(itemType);
|
|
WINPR_ASSERT(ptr);
|
|
|
|
size_t toWrite = len * itemType->itemSize;
|
|
size_t padding = (4 - (toWrite % 4)) % 4;
|
|
if (!ndr_write_uint32(context, s, len) || !Stream_EnsureRemainingCapacity(s, toWrite + padding))
|
|
return FALSE;
|
|
|
|
for (UINT32 i = 0; i < len; i++, ptr += itemType->itemSize)
|
|
{
|
|
if (!itemType->writeFn(context, s, NULL, ptr))
|
|
return FALSE;
|
|
}
|
|
|
|
if (padding)
|
|
{
|
|
Stream_Zero(s, padding);
|
|
ndr_context_bytes_written(context, padding);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ndr_struct_read_fromDescr(NdrContext* context, wStream* s, const NdrStructDescr* descr,
|
|
void* target)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
WINPR_ASSERT(s);
|
|
WINPR_ASSERT(descr);
|
|
WINPR_ASSERT(target);
|
|
|
|
#define NDR_MAX_STRUCT_DEFERRED 16
|
|
NdrDeferredEntry deferreds[NDR_MAX_STRUCT_DEFERRED] = { 0 };
|
|
size_t ndeferred = 0;
|
|
|
|
for (size_t i = 0; i < descr->nfields; i++)
|
|
{
|
|
const NdrFieldStruct* field = &descr->fields[i];
|
|
BYTE* ptr = target;
|
|
ptr += field->structOffset;
|
|
void* hints = NULL;
|
|
|
|
if (field->hintsField >= 0)
|
|
{
|
|
/* computes the address of the hints field if any */
|
|
WINPR_ASSERT((size_t)field->hintsField < descr->nfields);
|
|
const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
|
|
|
|
hints = (BYTE*)target + hintsField->structOffset;
|
|
}
|
|
|
|
switch (field->pointerType)
|
|
{
|
|
case NDR_NOT_POINTER:
|
|
if (!field->typeDescr->readFn(context, s, hints, ptr))
|
|
{
|
|
WLog_ERR(TAG, "error when reading %s.%s", descr->name, field->name);
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case NDR_POINTER:
|
|
case NDR_POINTER_NON_NULL:
|
|
{
|
|
NdrDeferredEntry* deferred = &deferreds[ndeferred];
|
|
if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
|
|
{
|
|
WLog_ERR(TAG, "too many deferred when calling ndr_read_struct_fromDescr for %s",
|
|
descr->name);
|
|
return FALSE;
|
|
}
|
|
|
|
deferred->name = field->name;
|
|
deferred->hints = hints;
|
|
deferred->target = ptr;
|
|
deferred->msg = field->typeDescr;
|
|
if (!ndr_read_refpointer(context, s, &deferred->ptrId))
|
|
{
|
|
WLog_ERR(TAG, "error when reading %s.%s", descr->name, field->name);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!deferred->ptrId && field->pointerType == NDR_POINTER_NON_NULL)
|
|
{
|
|
WLog_ERR(TAG, "%s.%s can't be null", descr->name, field->name);
|
|
return FALSE;
|
|
}
|
|
ndeferred++;
|
|
break;
|
|
}
|
|
default:
|
|
WLog_ERR(TAG, "%s.%s unknown pointer type 0x%x", descr->name, field->name,
|
|
field->pointerType);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return ndr_push_deferreds(context, deferreds, ndeferred);
|
|
}
|
|
|
|
BOOL ndr_struct_write_fromDescr(NdrContext* context, wStream* s, const NdrStructDescr* descr,
|
|
const void* src)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
WINPR_ASSERT(s);
|
|
WINPR_ASSERT(descr);
|
|
WINPR_ASSERT(src);
|
|
|
|
NdrDeferredEntry deferreds[NDR_MAX_STRUCT_DEFERRED] = { 0 };
|
|
size_t ndeferred = 0;
|
|
|
|
for (size_t i = 0; i < descr->nfields; i++)
|
|
{
|
|
const NdrFieldStruct* field = &descr->fields[i];
|
|
const BYTE* ptr = (const BYTE*)src + field->structOffset;
|
|
|
|
const void* hints = NULL;
|
|
|
|
if (field->hintsField >= 0)
|
|
{
|
|
/* computes the address of the hints field if any */
|
|
WINPR_ASSERT((size_t)field->hintsField < descr->nfields);
|
|
const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
|
|
|
|
hints = (const BYTE*)src + hintsField->structOffset;
|
|
}
|
|
|
|
switch (field->pointerType)
|
|
{
|
|
case NDR_POINTER:
|
|
case NDR_POINTER_NON_NULL:
|
|
{
|
|
ndr_refid ptrId = NDR_PTR_NULL;
|
|
BOOL isNew = 0;
|
|
ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr, const void**));
|
|
|
|
if (!ptr && field->pointerType == NDR_POINTER_NON_NULL)
|
|
{
|
|
WLog_ERR(TAG, "%s.%s can't be null", descr->name, field->name);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!ndr_context_allocatePtr(context, ptr, &ptrId, &isNew))
|
|
return FALSE;
|
|
|
|
if (isNew)
|
|
{
|
|
NdrDeferredEntry* deferred = &deferreds[ndeferred];
|
|
if (ndeferred >= NDR_MAX_STRUCT_DEFERRED)
|
|
{
|
|
WLog_ERR(TAG,
|
|
"too many deferred when calling ndr_read_struct_fromDescr for %s",
|
|
descr->name);
|
|
return FALSE;
|
|
}
|
|
|
|
deferred->name = field->name;
|
|
deferred->hints = WINPR_CAST_CONST_PTR_AWAY(hints, void*);
|
|
deferred->target = WINPR_CAST_CONST_PTR_AWAY(ptr, void*);
|
|
deferred->msg = field->typeDescr;
|
|
ndeferred++;
|
|
}
|
|
|
|
if (!ndr_write_uint32(context, s, ptrId))
|
|
return FALSE;
|
|
break;
|
|
}
|
|
case NDR_NOT_POINTER:
|
|
if (!field->typeDescr->writeFn(context, s, hints, ptr))
|
|
{
|
|
WLog_ERR(TAG, "error when writing %s.%s", descr->name, field->name);
|
|
return FALSE;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ndr_push_deferreds(context, deferreds, ndeferred);
|
|
}
|
|
|
|
void ndr_struct_dump_fromDescr(wLog* logger, UINT32 lvl, size_t identLevel,
|
|
const NdrStructDescr* descr, const void* obj)
|
|
{
|
|
char tabArray[30 + 1];
|
|
size_t ntabs = (identLevel <= 30) ? identLevel : 30;
|
|
|
|
memset(tabArray, '\t', ntabs);
|
|
tabArray[ntabs] = 0;
|
|
|
|
WLog_Print(logger, lvl, "%s%s", tabArray, descr->name);
|
|
for (size_t i = 0; i < descr->nfields; i++)
|
|
{
|
|
const NdrFieldStruct* field = &descr->fields[i];
|
|
const BYTE* ptr = (const BYTE*)obj + field->structOffset;
|
|
|
|
switch (field->pointerType)
|
|
{
|
|
case NDR_POINTER:
|
|
case NDR_POINTER_NON_NULL:
|
|
ptr = *(WINPR_CAST_CONST_PTR_AWAY(ptr, const void**));
|
|
break;
|
|
case NDR_NOT_POINTER:
|
|
break;
|
|
default:
|
|
WLog_ERR(TAG, "invalid field->pointerType");
|
|
break;
|
|
}
|
|
|
|
WLog_Print(logger, lvl, "%s*%s:", tabArray, field->name);
|
|
if (field->typeDescr->dumpFn)
|
|
field->typeDescr->dumpFn(logger, lvl, identLevel + 1, ptr);
|
|
else
|
|
WLog_Print(logger, lvl, "%s\t<no dump function>", tabArray);
|
|
}
|
|
}
|
|
|
|
void ndr_struct_destroy(NdrContext* context, const NdrStructDescr* descr, void* pptr)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
WINPR_ASSERT(descr);
|
|
WINPR_ASSERT(pptr);
|
|
|
|
for (size_t i = 0; i < descr->nfields; i++)
|
|
{
|
|
const NdrFieldStruct* field = &descr->fields[i];
|
|
void* ptr = (BYTE*)pptr + field->structOffset;
|
|
void* hints = NULL;
|
|
|
|
if (field->hintsField >= 0)
|
|
{
|
|
/* computes the address of the hints field if any */
|
|
WINPR_ASSERT((size_t)field->hintsField < descr->nfields);
|
|
const NdrFieldStruct* hintsField = &descr->fields[field->hintsField];
|
|
|
|
hints = (BYTE*)pptr + hintsField->structOffset;
|
|
}
|
|
|
|
if (field->pointerType != NDR_NOT_POINTER)
|
|
ptr = *(void**)ptr;
|
|
|
|
if (ptr && field->typeDescr->destroyFn)
|
|
field->typeDescr->destroyFn(context, hints, ptr);
|
|
|
|
if (field->pointerType != NDR_NOT_POINTER)
|
|
free(ptr);
|
|
}
|
|
}
|
|
|
|
ndr_refid ndr_pointer_refid(const void* ptr)
|
|
{
|
|
return (ndr_refid)((ULONG_PTR)ptr);
|
|
}
|
|
|
|
BOOL ndr_read_refpointer(NdrContext* context, wStream* s, ndr_refid* refId)
|
|
{
|
|
return ndr_read_uint32(context, s, refId);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
const void* needle;
|
|
ndr_refid* presult;
|
|
} FindValueArgs;
|
|
|
|
static BOOL findValueRefFn(const void* key, void* value, void* parg)
|
|
{
|
|
WINPR_ASSERT(parg);
|
|
|
|
FindValueArgs* args = (FindValueArgs*)parg;
|
|
if (args->needle == value)
|
|
{
|
|
*args->presult = (ndr_refid)(UINT_PTR)key;
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ndr_context_allocatePtr(NdrContext* context, const void* ptr, ndr_refid* prefId, BOOL* pnewPtr)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
|
|
FindValueArgs findArgs = { ptr, prefId };
|
|
if (!HashTable_Foreach(context->refPointers, findValueRefFn, &findArgs))
|
|
{
|
|
*pnewPtr = FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
*pnewPtr = TRUE;
|
|
*prefId = context->refIdCounter + 4;
|
|
if (!HashTable_Insert(context->refPointers, (void*)(UINT_PTR)(*prefId), ptr))
|
|
return FALSE;
|
|
|
|
context->refIdCounter += 4;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ndr_read_pointedMessageEx(NdrContext* context, wStream* s, ndr_refid ptrId,
|
|
NdrMessageType descr, void* hints, void** target)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
WINPR_ASSERT(s);
|
|
WINPR_ASSERT(descr);
|
|
WINPR_ASSERT(target);
|
|
|
|
*target = NULL;
|
|
if (!ptrId)
|
|
return TRUE;
|
|
|
|
void* ret = HashTable_GetItemValue(context->refPointers, (void*)(UINT_PTR)ptrId);
|
|
if (!ret)
|
|
{
|
|
size_t itemCount = ndr_hintsCount(descr, hints);
|
|
ret = calloc(itemCount, descr->itemSize);
|
|
if (!ret)
|
|
return FALSE;
|
|
|
|
if (!descr->readFn(context, s, hints, ret) ||
|
|
!HashTable_Insert(context->refPointers, (void*)(UINT_PTR)ptrId, ret))
|
|
{
|
|
if (descr->destroyFn)
|
|
descr->destroyFn(context, hints, ret);
|
|
free(ret);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
*target = ret;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ndr_push_deferreds(NdrContext* context, NdrDeferredEntry* deferreds, size_t ndeferred)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
WINPR_ASSERT(deferreds);
|
|
|
|
if (!ndeferred)
|
|
return TRUE;
|
|
|
|
if (context->ndeferred + ndeferred > NDR_MAX_DEFERRED)
|
|
{
|
|
WLog_ERR(TAG, "too many deferred");
|
|
return FALSE;
|
|
}
|
|
|
|
for (size_t i = ndeferred; i > 0; i--, context->ndeferred++)
|
|
{
|
|
context->deferred[context->ndeferred] = deferreds[i - 1];
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ndr_treat_deferred_read(NdrContext* context, wStream* s)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
WINPR_ASSERT(s);
|
|
|
|
while (context->ndeferred)
|
|
{
|
|
NdrDeferredEntry current = context->deferred[context->ndeferred - 1];
|
|
context->ndeferred--;
|
|
|
|
WLog_VRB(TAG, "treating read deferred 0x%x for %s", current.ptrId, current.name);
|
|
if (!ndr_read_pointedMessageEx(context, s, current.ptrId, current.msg, current.hints,
|
|
(void**)current.target))
|
|
{
|
|
WLog_ERR(TAG, "error parsing deferred %s", current.name);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ndr_treat_deferred_write(NdrContext* context, wStream* s)
|
|
{
|
|
WINPR_ASSERT(context);
|
|
WINPR_ASSERT(s);
|
|
|
|
while (context->ndeferred)
|
|
{
|
|
NdrDeferredEntry current = context->deferred[context->ndeferred - 1];
|
|
context->ndeferred--;
|
|
|
|
WLog_VRB(TAG, "treating write deferred for %s", current.name);
|
|
if (!current.msg->writeFn(context, s, current.hints, current.target))
|
|
{
|
|
WLog_ERR(TAG, "error writing deferred %s", current.name);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|