1660 lines
46 KiB
C++
1660 lines
46 KiB
C++
|
tag// ---------------------------------------------------------------------------------------
|
||
|
// base.cpp
|
||
|
//
|
||
|
// Copyright (C) Microsoft Corporation
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
#include "xnp.h"
|
||
|
#include "xnver.h"
|
||
|
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
// Definitions
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
#ifdef XNET_FEATURE_XBDM_SERVER
|
||
|
#define XNET_TRACE_PREFIX "XBDM"
|
||
|
#elif defined(XNET_FEATURE_ONLINE)
|
||
|
#define XNET_TRACE_PREFIX "XONLINE"
|
||
|
#else
|
||
|
#define XNET_TRACE_PREFIX "XNET"
|
||
|
#endif
|
||
|
|
||
|
DefineTag(Timer, 0);
|
||
|
DefineTag(poolWarn, TAG_ENABLE);
|
||
|
DefineTag(poolDump, 0);
|
||
|
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
// Crypto Constants
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
extern "C" const BYTE g_abOakleyGroup1Mod[CBDHG1] =
|
||
|
{
|
||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||
|
0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34,
|
||
|
0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1,
|
||
|
0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74,
|
||
|
0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22,
|
||
|
0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd,
|
||
|
0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b,
|
||
|
0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37,
|
||
|
0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45,
|
||
|
0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6,
|
||
|
0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x3a, 0x36, 0x20,
|
||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||
|
};
|
||
|
|
||
|
extern "C" const BYTE g_abOakleyGroup1Base[CBDHG1] =
|
||
|
{
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02
|
||
|
};
|
||
|
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
// CXnBase (Params)
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
#undef DEFINE_PARAM_
|
||
|
#define DEFINE_PARAM_(idx, name, def, min, max) typedef int _farf_##name[offsetof(XNetStartupParams, name) == idx];
|
||
|
#undef DEFINE_PARAM
|
||
|
#define DEFINE_PARAM(name, def, min, max)
|
||
|
XNETPARAMDEFS() // If this barfs, XNETPARAMDEFS is out of sync with XNetStartupParams
|
||
|
#undef DEFINE_PARAM_
|
||
|
#define DEFINE_PARAM_(idx, name, def, min, max) def,
|
||
|
#undef DEFINE_PARAM
|
||
|
#define DEFINE_PARAM(name, def, min, max) def,
|
||
|
const XNetParams CXnBase::s_XNetParamsDef = { XNETPARAMDEFS() };
|
||
|
#undef DEFINE_PARAM_
|
||
|
#define DEFINE_PARAM_(idx, name, def, min, max) min,
|
||
|
#undef DEFINE_PARAM
|
||
|
#define DEFINE_PARAM(name, def, min, max) min,
|
||
|
const XNetParams CXnBase::s_XNetParamsMin = { XNETPARAMDEFS() };
|
||
|
#undef DEFINE_PARAM_
|
||
|
#define DEFINE_PARAM_(idx, name, def, min, max) max,
|
||
|
#undef DEFINE_PARAM
|
||
|
#define DEFINE_PARAM(name, def, min, max) max,
|
||
|
const XNetParams CXnBase::s_XNetParamsMax = { XNETPARAMDEFS() };
|
||
|
#undef DEFINE_PARAM
|
||
|
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
// CLeakTracker
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
#ifdef XNET_FEATURE_ASSERT
|
||
|
|
||
|
#define LEAKS_PER_ALLOC 128
|
||
|
|
||
|
DefineTag(LeakWarn, TAG_ENABLE);
|
||
|
|
||
|
void * CXnBase::LeakAdd(CLeakInfo * pli, void * pv, UINT cb, ULONG tag)
|
||
|
{
|
||
|
ICHECK(BASE, USER|UDPC|SDPC);
|
||
|
|
||
|
if (pv && !pli->_fLeakDisable)
|
||
|
{
|
||
|
RaiseToDpc();
|
||
|
|
||
|
CLeak * pLeak = NULL;
|
||
|
|
||
|
if (pli->_cLeak == pli->_cLeakAlloc)
|
||
|
{
|
||
|
pLeak = (CLeak *)HalAlloc((pli->_cLeakAlloc + LEAKS_PER_ALLOC) * sizeof(CLeak), 0);
|
||
|
|
||
|
if (pLeak == NULL)
|
||
|
{
|
||
|
if (pli->_pLeak)
|
||
|
{
|
||
|
HalFree(pli->_pLeak);
|
||
|
pli->_pLeak = NULL;
|
||
|
pli->_cLeak = 0;
|
||
|
}
|
||
|
|
||
|
TraceSz(LeakWarn, "Leak tracker ran out of memory. Tracking disabled.");
|
||
|
pli->_fLeakDisable = TRUE;
|
||
|
return(pv);
|
||
|
}
|
||
|
|
||
|
if (pli->_pLeak)
|
||
|
{
|
||
|
memcpy(pLeak, pli->_pLeak, pli->_cLeakAlloc * sizeof(CLeak));
|
||
|
HalFree(pli->_pLeak);
|
||
|
}
|
||
|
|
||
|
pli->_pLeak = pLeak;
|
||
|
pli->_cLeakAlloc += LEAKS_PER_ALLOC;
|
||
|
}
|
||
|
|
||
|
if (LeakFind(pli, pv, &pLeak))
|
||
|
{
|
||
|
AssertSz(FALSE, "Attempt to leak track the same memory more than once. Tracking disabled.");
|
||
|
HalFree(pli->_pLeak);
|
||
|
pli->_pLeak = NULL;
|
||
|
pli->_cLeak = 0;
|
||
|
pli->_fLeakDisable = TRUE;
|
||
|
return(pv);
|
||
|
}
|
||
|
|
||
|
UINT cbMov = (pli->_cLeak - (pLeak - pli->_pLeak)) * sizeof(CLeak);
|
||
|
|
||
|
if (cbMov > 0)
|
||
|
{
|
||
|
memmove(pLeak + 1, pLeak, cbMov);
|
||
|
}
|
||
|
|
||
|
pLeak->_pv = pv;
|
||
|
pLeak->_cb = cb;
|
||
|
pLeak->_tag = tag;
|
||
|
pli->_cLeak += 1;
|
||
|
}
|
||
|
|
||
|
return(pv);
|
||
|
}
|
||
|
|
||
|
void * CXnBase::LeakDel(CLeakInfo * pli, void * pv)
|
||
|
{
|
||
|
ICHECK(BASE, USER|UDPC|SDPC);
|
||
|
|
||
|
if (pv && !pli->_fLeakDisable)
|
||
|
{
|
||
|
RaiseToDpc();
|
||
|
|
||
|
CLeak * pLeak = NULL;
|
||
|
|
||
|
if (LeakFind(pli, pv, &pLeak))
|
||
|
{
|
||
|
Assert(pLeak->_pv == pv);
|
||
|
|
||
|
int cbMov = (pli->_cLeak - (pLeak - pli->_pLeak) - 1) * sizeof(CLeak);
|
||
|
|
||
|
if (cbMov > 0)
|
||
|
{
|
||
|
memmove(pLeak, pLeak + 1, cbMov);
|
||
|
}
|
||
|
|
||
|
pli->_cLeak -= 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
AssertSz(FALSE, "Leak tracker can't find memory block in its table");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(pv);
|
||
|
}
|
||
|
|
||
|
BOOL CXnBase::LeakFind(CLeakInfo * pli, void * pv, CLeak ** ppLeak)
|
||
|
{
|
||
|
ICHECK(BASE, UDPC|SDPC);
|
||
|
|
||
|
int iLo = 0;
|
||
|
int iHi = (int)pli->_cLeak;
|
||
|
int iMid;
|
||
|
CLeak * pLeak = pli->_pLeak;
|
||
|
BOOL fFound = FALSE;
|
||
|
|
||
|
while (iLo < iHi)
|
||
|
{
|
||
|
iMid = (iLo + iHi) >> 1;
|
||
|
pLeak = pli->_pLeak + iMid;
|
||
|
|
||
|
if (pLeak->_pv == pv)
|
||
|
{
|
||
|
iLo = iMid;
|
||
|
fFound = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
else if (pLeak->_pv < pv)
|
||
|
iLo = iMid + 1;
|
||
|
else
|
||
|
iHi = iMid;
|
||
|
}
|
||
|
|
||
|
*ppLeak = pli->_pLeak + iLo;
|
||
|
|
||
|
return(fFound);
|
||
|
}
|
||
|
|
||
|
void CXnBase::LeakTerm(CLeakInfo * pli)
|
||
|
{
|
||
|
if (pli->_cLeak > 0)
|
||
|
{
|
||
|
CLeak * pLeak = pli->_pLeak;
|
||
|
UINT cLeak = pli->_cLeak;
|
||
|
UINT cbLeak = 0;
|
||
|
|
||
|
for (; cLeak > 0; --cLeak, ++pLeak)
|
||
|
{
|
||
|
cbLeak += pLeak->_cb;
|
||
|
TraceSz3(LeakWarn, "Leaked %5d bytes at %08lX - %s", pLeak->_cb, pLeak->_pv,
|
||
|
pli->_pfnLeakTag(pLeak->_tag));
|
||
|
}
|
||
|
|
||
|
TraceSz1(LeakWarn, "Leaked %5d bytes total", cbLeak);
|
||
|
}
|
||
|
|
||
|
if (pli->_pLeak)
|
||
|
{
|
||
|
HalFree(pli->_pLeak);
|
||
|
pli->_pLeak = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const char * XnLeakTagToString(ULONG tag)
|
||
|
{
|
||
|
switch (tag)
|
||
|
{
|
||
|
#undef XNETPTAG
|
||
|
#define XNETPTAG(_name, _tag) case PTAG_##_name: return(#_name); break;
|
||
|
XNETPTAGLIST()
|
||
|
}
|
||
|
|
||
|
return("?");
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
// CXnBase (Pool Allocator)
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
//
|
||
|
// Pool allocation block header: We intentionally let this have
|
||
|
// the same size as the system pool header. But we've restructured
|
||
|
// the fields in such a way that if you try to free a memory block
|
||
|
// that was allocated out of our private pool to the system pool,
|
||
|
// you'd get a bugcheck.
|
||
|
//
|
||
|
|
||
|
struct PoolEntry
|
||
|
{
|
||
|
BYTE bOver;
|
||
|
// if busy, the amount of overhead in the block
|
||
|
BYTE bBusy;
|
||
|
// Whether the block is allocated or free
|
||
|
|
||
|
WORD tag;
|
||
|
// We're only using 2 bytes for pool tag,
|
||
|
// since all of our pool tags have the form NET*.
|
||
|
|
||
|
WORD blockSize;
|
||
|
WORD previousSize;
|
||
|
// Size of this block and the previous block in 32-bit units
|
||
|
|
||
|
LIST_ENTRY links;
|
||
|
// A free pool entry has two additional pointer fields
|
||
|
// for maintaining a doubly-linked list of free blocks.
|
||
|
};
|
||
|
|
||
|
#define POOL_BLOCK_SHIFT 5
|
||
|
#define POOL_BLOCK_SIZE (1 << POOL_BLOCK_SHIFT)
|
||
|
#define MAX_POOL_SIZE (POOL_BLOCK_SIZE * 0xffff)
|
||
|
#define POOL_HEADER_SIZE offsetof(PoolEntry, links)
|
||
|
|
||
|
#define MarkPoolEntryBusy(_entry) ((_entry)->bBusy = 1)
|
||
|
#define MarkPoolEntryFree(_entry) ((_entry)->bBusy = 0)
|
||
|
#define IsPoolEntryBusy(_entry) ((_entry)->bBusy != 0)
|
||
|
#define IsPoolEntryFree(_entry) ((_entry)->bBusy == 0)
|
||
|
#define TagPoolEntry(_entry, _tag) ((_entry)->tag = (WORD) ((_tag) >> 16))
|
||
|
|
||
|
#define GetPoolEntryHeader(_ptr) \
|
||
|
((PoolEntry*) ((BYTE*) (_ptr) - POOL_HEADER_SIZE))
|
||
|
#define GetPoolEntryNext(_entry) \
|
||
|
((PoolEntry*) ((BYTE*) (_entry) + ((UINT) (_entry)->blockSize << POOL_BLOCK_SHIFT)))
|
||
|
#define GetPoolEntryPrev(_entry) \
|
||
|
((PoolEntry*) ((BYTE*) (_entry) - ((UINT) (_entry)->previousSize << POOL_BLOCK_SHIFT)))
|
||
|
|
||
|
//
|
||
|
// Beginning and ending address of the entire pool
|
||
|
//
|
||
|
|
||
|
#define IsPoolEntryValid(_entry) ((_entry) >= _pvPoolStart && (_entry) < _pvPoolEnd)
|
||
|
|
||
|
#define InsertFreePoolEntry(_entry) { \
|
||
|
LIST_ENTRY* _head; \
|
||
|
_head = &_aleFree[ \
|
||
|
(_entry)->blockSize <= MAX_SMALL_BLOCKS ? \
|
||
|
(_entry)->blockSize - 1 : \
|
||
|
MAX_SMALL_BLOCKS]; \
|
||
|
InsertHeadList(_head, &(_entry)->links); \
|
||
|
}
|
||
|
|
||
|
|
||
|
void * CXnBase::PoolAlloc(size_t size, ULONG tag)
|
||
|
{
|
||
|
ICHECK(BASE, USER|UDPC|SDPC);
|
||
|
|
||
|
#ifdef XNET_FEATURE_VMEM
|
||
|
if (VMemIsEnabled())
|
||
|
{
|
||
|
return(LeakAdd(&_leakinfo, VMemAlloc(size), size, tag));
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
PoolEntry* entry;
|
||
|
|
||
|
Assert(size > 0 && size <= MAX_POOL_SIZE - POOL_HEADER_SIZE);
|
||
|
size_t size_orig = size;
|
||
|
size = (size + (POOL_HEADER_SIZE + POOL_BLOCK_SIZE - 1)) >> POOL_BLOCK_SHIFT;
|
||
|
|
||
|
RaiseToDpc();
|
||
|
|
||
|
// Find a free pool block that's large enough for us.
|
||
|
// Handle small block allocation with the quick lookup.
|
||
|
if (size <= MAX_SMALL_BLOCKS)
|
||
|
{
|
||
|
UINT index;
|
||
|
|
||
|
for (index=size-1; index <= MAX_SMALL_BLOCKS; index++)
|
||
|
{
|
||
|
if (!IsListEmpty(&_aleFree[index]))
|
||
|
{
|
||
|
entry = GetPoolEntryHeader(_aleFree[index].Flink);
|
||
|
goto found;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LIST_ENTRY* head;
|
||
|
LIST_ENTRY* list;
|
||
|
|
||
|
head = &_aleFree[MAX_SMALL_BLOCKS];
|
||
|
list = head->Flink;
|
||
|
while (list != head)
|
||
|
{
|
||
|
entry = GetPoolEntryHeader(list);
|
||
|
if ((WORD) size <= entry->blockSize)
|
||
|
goto found;
|
||
|
list = list->Flink;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TraceSz1(poolWarn, "Pool allocation failed: %d blocks", size);
|
||
|
TraceSz(poolWarn, "Make sure you promptly call recv() to receive incoming data.");
|
||
|
TraceSz(poolWarn, "You may also want to consider setting a larger pool size.");
|
||
|
|
||
|
#if DBG
|
||
|
if (Tag(poolDump))
|
||
|
PoolDump();
|
||
|
#endif
|
||
|
|
||
|
return(NULL);
|
||
|
|
||
|
found:
|
||
|
// Take it out of the free list
|
||
|
RemoveEntryList(&entry->links);
|
||
|
|
||
|
// If we didn't use up the entire free block,
|
||
|
// put the remaining portion back on the free list.
|
||
|
if (entry->blockSize > size) {
|
||
|
UINT leftover = entry->blockSize - size;
|
||
|
PoolEntry* next;
|
||
|
|
||
|
entry->blockSize = (WORD) size;
|
||
|
|
||
|
next = GetPoolEntryNext(entry);
|
||
|
next->blockSize = (WORD) leftover;
|
||
|
next->previousSize = (WORD) size;
|
||
|
next->tag = entry->tag;
|
||
|
MarkPoolEntryFree(next);
|
||
|
InsertFreePoolEntry(next);
|
||
|
|
||
|
next = GetPoolEntryNext(next);
|
||
|
Assert(IsPoolEntryValid(next));
|
||
|
next->previousSize = (WORD) leftover;
|
||
|
}
|
||
|
|
||
|
MarkPoolEntryBusy(entry);
|
||
|
TagPoolEntry(entry, tag);
|
||
|
entry->bOver = (BYTE)((size << POOL_BLOCK_SHIFT) - size_orig);
|
||
|
|
||
|
#ifdef XNET_FEATURE_ASSERT
|
||
|
LeakAdd(&_leakinfo, (BYTE *)entry + POOL_HEADER_SIZE, size_orig, tag);
|
||
|
#endif
|
||
|
|
||
|
return (BYTE*) entry + POOL_HEADER_SIZE;
|
||
|
}
|
||
|
|
||
|
void * CXnBase::PoolAllocZ(size_t size, ULONG tag)
|
||
|
{
|
||
|
ICHECK(BASE, USER|UDPC|SDPC);
|
||
|
|
||
|
void * pv = PoolAlloc(size, tag);
|
||
|
|
||
|
if (pv)
|
||
|
{
|
||
|
memset(pv, 0, size);
|
||
|
}
|
||
|
|
||
|
return(pv);
|
||
|
}
|
||
|
|
||
|
void CXnBase::PoolFree(void * ptr)
|
||
|
{
|
||
|
ICHECK(BASE, USER|UDPC|SDPC);
|
||
|
|
||
|
if (ptr == NULL)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#ifdef XNET_FEATURE_ASSERT
|
||
|
LeakDel(&_leakinfo, ptr);
|
||
|
#endif
|
||
|
|
||
|
#ifdef XNET_FEATURE_VMEM
|
||
|
if (VMemIsEnabled())
|
||
|
{
|
||
|
VMemFree(ptr);
|
||
|
return;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
PoolEntry* entry;
|
||
|
PoolEntry* prev;
|
||
|
PoolEntry* next;
|
||
|
WORD blockSize;
|
||
|
|
||
|
entry = GetPoolEntryHeader(ptr);
|
||
|
Assert(IsPoolEntryValid(entry) && IsPoolEntryBusy(entry));
|
||
|
|
||
|
RaiseToDpc();
|
||
|
|
||
|
MarkPoolEntryFree(entry);
|
||
|
blockSize = entry->blockSize;
|
||
|
|
||
|
//
|
||
|
// Check to see if the block before this one is free
|
||
|
// If so, collapse the two free blocks together
|
||
|
//
|
||
|
prev = GetPoolEntryPrev(entry);
|
||
|
Assert(IsPoolEntryValid(prev));
|
||
|
if (IsPoolEntryFree(prev)) {
|
||
|
RemoveEntryList(&prev->links);
|
||
|
prev->blockSize = (WORD) (prev->blockSize + blockSize);
|
||
|
entry = prev;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check if the block after this one is free
|
||
|
// If so, collapse the two free blocks together
|
||
|
//
|
||
|
next = GetPoolEntryNext(entry);
|
||
|
Assert(IsPoolEntryValid(next) && next->previousSize == blockSize);
|
||
|
if (IsPoolEntryFree(next)) {
|
||
|
RemoveEntryList(&next->links);
|
||
|
entry->blockSize = (WORD) (entry->blockSize + next->blockSize);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we performed any collapsing,
|
||
|
// update the previousSize field of the next block.
|
||
|
//
|
||
|
if (entry->blockSize != blockSize) {
|
||
|
next = GetPoolEntryNext(entry);
|
||
|
Assert(IsPoolEntryValid(next));
|
||
|
next->previousSize = entry->blockSize;
|
||
|
}
|
||
|
|
||
|
InsertFreePoolEntry(entry);
|
||
|
}
|
||
|
|
||
|
#if DBG
|
||
|
|
||
|
void CXnBase::PoolDump()
|
||
|
{
|
||
|
ICHECK(BASE, USER|UDPC|SDPC);
|
||
|
|
||
|
PoolEntry* entry;
|
||
|
PoolEntry* prev;
|
||
|
UINT cbFree = 0;
|
||
|
UINT cbBusy = 0;
|
||
|
UINT cbOver = 0;
|
||
|
UINT cbPool = (BYTE *)_pvPoolEnd - (BYTE *)_pvPoolStart;
|
||
|
|
||
|
TraceSz(poolWarn, "+\n-------------------------------------------------------------------------");
|
||
|
TraceSz2(poolWarn, "Private Pool Dump (%08lX - %08lX)", _pvPoolStart, _pvPoolEnd);
|
||
|
TraceSz(poolWarn, "");
|
||
|
|
||
|
prev = NULL;
|
||
|
entry = (PoolEntry *)_pvPoolStart;
|
||
|
|
||
|
while (entry < _pvPoolEnd)
|
||
|
{
|
||
|
BOOL fIsFree = IsPoolEntryFree(entry);
|
||
|
BOOL fIsOver = (entry == _pvPoolStart) || ((BYTE *)entry + (1 << POOL_BLOCK_SHIFT)) == (BYTE *)_pvPoolEnd;
|
||
|
UINT cb = (entry->blockSize << POOL_BLOCK_SHIFT);
|
||
|
|
||
|
if (fIsFree)
|
||
|
{
|
||
|
cb -= POOL_HEADER_SIZE;
|
||
|
cbFree += cb;
|
||
|
cbOver += POOL_HEADER_SIZE;
|
||
|
TraceSz3(poolWarn, "%08lX f %7d (+%2d) bytes",
|
||
|
(UINT_PTR)entry + POOL_HEADER_SIZE, cb, POOL_HEADER_SIZE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cb -= entry->bOver;
|
||
|
cbBusy += cb;
|
||
|
cbOver += entry->bOver;
|
||
|
|
||
|
TraceSz4(poolWarn, "%08lX %7d (+%2d) bytes %s",
|
||
|
(UINT_PTR)entry + POOL_HEADER_SIZE, cb, entry->bOver,
|
||
|
XnLeakTagToString(('xTEN' & 0xFFFF) | (entry->tag << 16)));
|
||
|
}
|
||
|
|
||
|
if (prev)
|
||
|
{
|
||
|
Assert(prev->blockSize == entry->previousSize);
|
||
|
prev = entry;
|
||
|
}
|
||
|
|
||
|
entry = GetPoolEntryNext(entry);
|
||
|
}
|
||
|
|
||
|
Assert(entry == _pvPoolEnd);
|
||
|
|
||
|
TraceSz(poolWarn, "");
|
||
|
TraceSz1(poolWarn, "%7d bytes used", cbBusy);
|
||
|
TraceSz1(poolWarn, "%7d bytes free", cbFree);
|
||
|
TraceSz1(poolWarn, "%7d bytes overhead", cbOver);
|
||
|
TraceSz1(poolWarn, "%7d bytes entire pool", cbPool);
|
||
|
TraceSz(poolWarn, "+-------------------------------------------------------------------------\n");
|
||
|
|
||
|
Assert(cbBusy + cbFree + cbOver == cbPool);
|
||
|
|
||
|
if (Tag(poolDump) >= TAG_BREAK)
|
||
|
{
|
||
|
DbgBreak();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif // DBG
|
||
|
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
// CXnBase (Rand)
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
void CXnBase::RandInit(XNetInitParams * pxnip)
|
||
|
{
|
||
|
TCHECK(USER);
|
||
|
|
||
|
BYTE abSha1Ctx[XC_SERVICE_SHA_CONTEXT_SIZE];
|
||
|
BYTE abRand[512];
|
||
|
BYTE abHash[XC_SERVICE_DIGEST_SIZE];
|
||
|
UINT cbRand = 0;
|
||
|
|
||
|
// Gather system randomness into the abRand vector
|
||
|
|
||
|
cbRand = HalRandGather(abRand, sizeof(abRand));
|
||
|
Assert(cbRand <= sizeof(abRand));
|
||
|
|
||
|
// Compute a SHA1 digest of all of the gathered random bits
|
||
|
|
||
|
XcSHAInit(abSha1Ctx);
|
||
|
XcSHAUpdate(abSha1Ctx, pxnip->abSeed, sizeof(pxnip->abSeed));
|
||
|
XcSHAUpdate(abSha1Ctx, (BYTE *)&pxnip->liTime, sizeof(pxnip->liTime));
|
||
|
XcSHAUpdate(abSha1Ctx, abRand, cbRand);
|
||
|
XcSHAFinal(abSha1Ctx, abHash);
|
||
|
|
||
|
// Initialize an RC4 machine with the given digest as a key
|
||
|
|
||
|
XcRC4Key(_abRandRc4Struct, sizeof(abHash), abHash);
|
||
|
|
||
|
// Throw away the first 256 bytes of the RC4 machine
|
||
|
|
||
|
XcRC4Crypt(_abRandRc4Struct, 256, abRand);
|
||
|
|
||
|
// Update the seed value with the next 20 bytes of the RC4 machine
|
||
|
|
||
|
XcRC4Crypt(_abRandRc4Struct, sizeof(pxnip->abSeed), pxnip->abSeed);
|
||
|
}
|
||
|
|
||
|
void CXnBase::Rand(BYTE * pb, UINT cb)
|
||
|
{
|
||
|
ICHECK(BASE, USER|UDPC|SDPC);
|
||
|
RaiseToDpc();
|
||
|
XcRC4Crypt(_abRandRc4Struct, cb, pb);
|
||
|
}
|
||
|
|
||
|
ULONG CXnBase::RandLong()
|
||
|
{
|
||
|
ICHECK(BASE, USER|UDPC|SDPC);
|
||
|
ULONG ul;
|
||
|
Rand((BYTE *)&ul, sizeof(ULONG));
|
||
|
return(ul);
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
// CXnBase (Packet)
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
CPacket * CXnBase::PacketAlloc(ULONG tag, UINT uiFlags, UINT cbPayload, UINT cbPkt, PFNPKTFREE pfn)
|
||
|
{
|
||
|
ICHECK(BASE, USER|UDPC|SDPC);
|
||
|
|
||
|
Assert(sizeof(PFNPKTFREE) == sizeof(void *));
|
||
|
|
||
|
CPacket * ppkt;
|
||
|
UINT cbPtr;
|
||
|
UINT cbPad = 0;
|
||
|
UINT cbHdr = 0;
|
||
|
|
||
|
// We shouldn't be creating CPacket classes that aren't four-byte aligned
|
||
|
|
||
|
if (cbPkt == 0)
|
||
|
cbPkt = sizeof(CPacket);
|
||
|
|
||
|
Assert(cbPkt == ROUNDUP4(cbPkt));
|
||
|
|
||
|
// Add in the ethernet header
|
||
|
|
||
|
cbPkt += ROUNDUP4(sizeof(CEnetHdr));
|
||
|
|
||
|
// This offset tells us where the [Payload] begins.
|
||
|
|
||
|
cbPtr = cbPkt;
|
||
|
|
||
|
// Add in the size of all the headers in the packet based on its type.
|
||
|
|
||
|
cbPkt += CPacket::_abPktTypeInfo[((uiFlags & PKTF_TYPE_MASK) >> PKTF_TYPE_SHIFT) * PKTI_MAX + PKTI_SIZE];
|
||
|
|
||
|
if (uiFlags & PKTF_TYPE_ESP)
|
||
|
{
|
||
|
cbHdr = (uiFlags & PKTF_TYPE_UDP) ? sizeof(CUdpHdr) : (uiFlags & PKTF_TYPE_TCP) ? sizeof(CTcpHdr) : 0;
|
||
|
cbPkt -= cbHdr;
|
||
|
cbPad = cbPkt;
|
||
|
|
||
|
if (uiFlags & PKTF_CRYPT)
|
||
|
{
|
||
|
cbPkt += ROUNDUP8(XC_SERVICE_DES_BLOCKLEN + cbHdr + cbPayload + offsetof(CEspTail, _abHash));
|
||
|
cbPad = cbPkt - cbPad - (XC_SERVICE_DES_BLOCKLEN + cbHdr + cbPayload + offsetof(CEspTail, _abHash));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cbPkt += ROUNDUP4(cbPayload + cbHdr + offsetof(CEspTail, _abHash));
|
||
|
cbPad = cbPkt - cbPad - (cbPayload + cbHdr + offsetof(CEspTail, _abHash));
|
||
|
}
|
||
|
|
||
|
cbPkt += sizeof(CEspTail) - offsetof(CEspTail, _abHash);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Add in the size of the user-specified payload
|
||
|
|
||
|
cbPkt += cbPayload;
|
||
|
}
|
||
|
|
||
|
// Allocate the packet and initialize it
|
||
|
|
||
|
if (uiFlags & PKTF_POOLALLOC)
|
||
|
ppkt = (CPacket *)PoolAlloc(cbPkt, tag);
|
||
|
else
|
||
|
ppkt = (CPacket *)SysAlloc(cbPkt, tag);
|
||
|
|
||
|
if (ppkt)
|
||
|
{
|
||
|
ppkt->Init(uiFlags, (BYTE *)ppkt + cbPtr, cbPkt - cbPtr, pfn ? pfn : (PFNPKTFREE)PacketFree);
|
||
|
|
||
|
// If this is an [ESP] packet, we need to store the _bPadLen field now since
|
||
|
// we just went through a lot of trouble to compute it.
|
||
|
|
||
|
if (uiFlags & PKTF_TYPE_ESP)
|
||
|
{
|
||
|
CEspTail * pEspTail = ppkt->GetEspTail();
|
||
|
|
||
|
pEspTail->_bPadLen = (BYTE)cbPad;
|
||
|
|
||
|
// Fill the padding (if any) with the byte series 1, 2, 3, ...
|
||
|
|
||
|
for (BYTE * pb = (BYTE *)pEspTail; cbPad > 0; )
|
||
|
{
|
||
|
*--pb = (BYTE)cbPad--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
IFDBG(ppkt->SetAllocated();)
|
||
|
|
||
|
ppkt->Validate();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TraceSz1(Warning, "Out of memory allocating a CPacket of size %ld bytes", cbPkt);
|
||
|
}
|
||
|
|
||
|
return(ppkt);
|
||
|
}
|
||
|
|
||
|
void CXnBase::PacketFree(CPacket * ppkt)
|
||
|
{
|
||
|
ICHECK(BASE, USER|UDPC|SDPC);
|
||
|
Assert(!ppkt->TestFlags(PKTF_RECV_LOOPBACK));
|
||
|
|
||
|
#if DBG
|
||
|
Assert(ppkt->IsAllocated());
|
||
|
#endif
|
||
|
|
||
|
if (ppkt->TestFlags(PKTF_POOLALLOC))
|
||
|
PoolFree(ppkt);
|
||
|
else
|
||
|
SysFree(ppkt);
|
||
|
}
|
||
|
|
||
|
void * CPacket::GetHdr(int iHdr)
|
||
|
{
|
||
|
Assert(IsIp());
|
||
|
Assert(PKTF_IHL_SHIFT == 2);
|
||
|
Assert(iHdr == PKTI_ESP || iHdr == PKTI_UDP_TCP);
|
||
|
Assert(PKTI_UDP_TCP == 1);
|
||
|
return( (BYTE *)_pv
|
||
|
+ (_wFlags & PKTF_IHL_MASK)
|
||
|
+ _abPktTypeInfo[(GetType() >> PKTF_TYPE_SHIFT) * PKTI_MAX + iHdr]
|
||
|
+ (((!!(_wFlags & PKTF_CRYPT)) & iHdr) << 3));
|
||
|
}
|
||
|
|
||
|
#if DBG
|
||
|
|
||
|
const char * const CPacket::_aszPktTypeName[] =
|
||
|
{
|
||
|
"[ENET]^[IP][Payload]",
|
||
|
"[ENET]^[IP][ESP][Payload][ESPT]",
|
||
|
"[ENET]^[IP][UDP][Payload]",
|
||
|
"[ENET]^[IP][ESP][UDP][Payload][ESPT]",
|
||
|
"[ENET]^[IP][TCP][Payload]",
|
||
|
"[ENET]^[IP][ESP][TCP][Payload][ESPT]",
|
||
|
"[ENET]^[Payload]",
|
||
|
"[ENET]^[IP][Payload]",
|
||
|
"[ENET]^[IP][ESP][IV][Payload][ESPT]",
|
||
|
"[ENET]^[IP][UDP][Payload]",
|
||
|
"[ENET]^[IP][ESP][IV][UDP][Payload][ESPT]",
|
||
|
"[ENET]^[IP][TCP][Payload]",
|
||
|
"[ENET]^[IP][ESP][IV][TCP][Payload][ESPT]",
|
||
|
"[ENET]^[Payload]",
|
||
|
};
|
||
|
|
||
|
void CPacket::DbgSetSpy()
|
||
|
{
|
||
|
int iType = (int)((_wFlags & PKTF_TYPE_MASK) >> PKTF_TYPE_SHIFT);
|
||
|
|
||
|
_pchSpy = _aszPktTypeName[iType + ((_wFlags & PKTF_CRYPT) ? 7 : 0)];
|
||
|
|
||
|
memset(&_spy, 0, sizeof(CSpy));
|
||
|
|
||
|
_spy._pEnetHdr = (CEnetHdr *)((BYTE *)_pv - sizeof(CEnetHdr));
|
||
|
|
||
|
if ((_wFlags & PKTF_TYPE_MASK) != PKTF_TYPE_ENET)
|
||
|
{
|
||
|
_spy._pIpHdr = (CIpHdr *)_pv;
|
||
|
|
||
|
if (_wFlags & PKTF_TYPE_ESP)
|
||
|
{
|
||
|
_spy._pEspHdr = GetEspHdr();
|
||
|
_spy._pEspTail = GetEspTail();
|
||
|
}
|
||
|
|
||
|
if (_wFlags & PKTF_TYPE_UDP)
|
||
|
{
|
||
|
_spy._pUdpHdr = GetUdpHdr();
|
||
|
}
|
||
|
|
||
|
if (_wFlags & PKTF_TYPE_TCP)
|
||
|
{
|
||
|
_spy._pTcpHdr = GetTcpHdr();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CPacket::Validate()
|
||
|
{
|
||
|
if (IsEsp())
|
||
|
{
|
||
|
CEspTail * pEspTail = GetEspTail();
|
||
|
UINT cbPad = pEspTail->_bPadLen;
|
||
|
BYTE * pbPad = (BYTE *)pEspTail;
|
||
|
|
||
|
// Verify that the padding is the series of bytes 1, 2, 3, ...
|
||
|
|
||
|
while (cbPad > 0 && *--pbPad == (BYTE)cbPad)
|
||
|
cbPad--;
|
||
|
|
||
|
AssertSz1(cbPad == 0, "Packet padding has been overwritten (ppkt=%08lX)", this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
const BYTE CPacket::_abPktTypeInfo[] =
|
||
|
{
|
||
|
// [ENET]^[IP][Payload]
|
||
|
|
||
|
0, // [ESP]
|
||
|
0, // [UDP]/[TCP]
|
||
|
sizeof(CIpHdr), // Size
|
||
|
|
||
|
// [ENET]^[IP][ESP][Payload][ESPT]
|
||
|
|
||
|
sizeof(CIpHdr), // [ESP]
|
||
|
0, // [UDP]/[TCP]
|
||
|
sizeof(CIpHdr) + sizeof(CEspHdr), // Size
|
||
|
|
||
|
// [ENET]^[IP][UDP][Payload]
|
||
|
|
||
|
0, // [ESP]
|
||
|
sizeof(CIpHdr), // [UDP]/[TCP]
|
||
|
sizeof(CIpHdr) + sizeof(CUdpHdr), // Size
|
||
|
|
||
|
// [ENET]^[IP][ESP][UDP][Payload][ESPT]
|
||
|
|
||
|
sizeof(CIpHdr), // [ESP]
|
||
|
sizeof(CIpHdr) + sizeof(CEspHdr), // [UDP]/[TCP]
|
||
|
sizeof(CIpHdr) + sizeof(CEspHdr) + sizeof(CUdpHdr), // Size
|
||
|
|
||
|
// [ENET]^[IP][TCP][Payload]
|
||
|
|
||
|
0, // [ESP]
|
||
|
sizeof(CIpHdr), // [UDP]/[TCP]
|
||
|
sizeof(CIpHdr) + sizeof(CTcpHdr), // Size
|
||
|
|
||
|
// [ENET]^[IP][ESP][TCP][Payload][ESPT]
|
||
|
|
||
|
sizeof(CIpHdr), // [ESP]
|
||
|
sizeof(CIpHdr) + sizeof(CEspHdr), // [UDP]/[TCP]
|
||
|
sizeof(CIpHdr) + sizeof(CEspHdr) + sizeof(CTcpHdr), // Size
|
||
|
|
||
|
// [ENET]^[Payload]
|
||
|
0, // [ESP]
|
||
|
0, // [UDP]/[TCP]
|
||
|
0 // Size
|
||
|
|
||
|
};
|
||
|
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
// CPacketQueue
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
void CPacketQueue::InsertHead(CPacket * ppkt)
|
||
|
{
|
||
|
if ((ppkt->_ppktNext = _ppktHead) == NULL)
|
||
|
_ppktTail = ppkt;
|
||
|
_ppktHead = ppkt;
|
||
|
}
|
||
|
|
||
|
void CPacketQueue::InsertTail(CPacket * ppkt)
|
||
|
{
|
||
|
if (_ppktTail)
|
||
|
_ppktTail->_ppktNext = ppkt;
|
||
|
else
|
||
|
_ppktHead = ppkt;
|
||
|
|
||
|
_ppktTail = ppkt;
|
||
|
ppkt->_ppktNext = NULL;
|
||
|
}
|
||
|
|
||
|
void CPacketQueue::InsertHead(CPacketQueue * ppq)
|
||
|
{
|
||
|
Assert(!ppq->IsEmpty());
|
||
|
if (_ppktTail == NULL)
|
||
|
_ppktTail = ppq->_ppktTail;
|
||
|
ppq->_ppktTail->_ppktNext = _ppktHead;
|
||
|
_ppktHead = ppq->_ppktHead;
|
||
|
ppq->Init();
|
||
|
}
|
||
|
|
||
|
CPacket * CPacketQueue::RemoveHead()
|
||
|
{
|
||
|
Assert(!IsEmpty());
|
||
|
|
||
|
CPacket * ppkt = _ppktHead;
|
||
|
|
||
|
if ((_ppktHead = ppkt->_ppktNext) == NULL)
|
||
|
_ppktTail = NULL;
|
||
|
else
|
||
|
ppkt->_ppktNext = NULL;
|
||
|
|
||
|
return(ppkt);
|
||
|
}
|
||
|
|
||
|
void CPacketQueue::Complete(CXnBase * pXnBase)
|
||
|
{
|
||
|
while (!IsEmpty())
|
||
|
{
|
||
|
RemoveHead()->Complete(pXnBase);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CPacketQueue::Discard(CXnBase * pXnBase)
|
||
|
{
|
||
|
while (!IsEmpty())
|
||
|
{
|
||
|
pXnBase->PacketFree(RemoveHead());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CPacketQueue::Dequeue(CPacket * ppktDequeue)
|
||
|
{
|
||
|
CPacket ** pppkt = &_ppktHead;
|
||
|
CPacket * ppktPrev = NULL;
|
||
|
CPacket * ppkt;
|
||
|
|
||
|
for (; (ppkt = *pppkt) != NULL; ppktPrev = ppkt, pppkt = &ppkt->_ppktNext)
|
||
|
{
|
||
|
if (ppkt == ppktDequeue)
|
||
|
{
|
||
|
*pppkt = ppkt->_ppktNext;
|
||
|
|
||
|
if (_ppktHead == NULL)
|
||
|
_ppktTail = NULL;
|
||
|
else if (_ppktTail == ppkt)
|
||
|
_ppktTail = ppktPrev;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
AssertSz1(0, "CPacketQueue::Dequeue(%08lX) - Packet not found in queue", ppktDequeue);
|
||
|
}
|
||
|
|
||
|
UINT CPacketQueue::Count()
|
||
|
{
|
||
|
CPacket * ppkt;
|
||
|
UINT c;
|
||
|
|
||
|
for (c = 0, ppkt = _ppktHead; ppkt; ppkt = ppkt->_ppktNext)
|
||
|
{
|
||
|
c += 1;
|
||
|
}
|
||
|
|
||
|
return(c);
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
// CIpAddr
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
CIpAddr CIpAddr::DefaultMask() const
|
||
|
{
|
||
|
if (IsClassA()) return(IPADDR_CLASSA_NETMASK);
|
||
|
if (IsClassB()) return(IPADDR_CLASSB_NETMASK);
|
||
|
if (IsClassC()) return(IPADDR_CLASSC_NETMASK);
|
||
|
if (IsClassD()) return(IPADDR_CLASSD_NETMASK);
|
||
|
|
||
|
TraceSz1(Warning, "CIpAddr:DefaultMask(): Invalid host IP address: %s", Str());
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
BOOL CIpAddr::IsValidUnicast() const
|
||
|
{
|
||
|
return(_dw != 0 && !IsSecure() && !IsBroadcast() && !IsMulticast() && !IsLoopback());
|
||
|
}
|
||
|
|
||
|
BOOL CIpAddr::IsValidAddr() const
|
||
|
{
|
||
|
return( ((BYTE)(_ab[0] - 1)) < 254 // 1 ... 254
|
||
|
&& _ab[1] <= 254 // 0 ... 254
|
||
|
&& _ab[2] <= 254 // 0 ... 254
|
||
|
&& ((BYTE)(_ab[3] - 1)) < 254); // 1 ... 254
|
||
|
}
|
||
|
|
||
|
#if DBG || defined(XNET_FEATURE_ASSERT) || defined(XNET_FEATURE_TRACE)
|
||
|
|
||
|
char * CIpAddr::Str() const
|
||
|
{
|
||
|
#define IPADDR_STR_BUFFS 32
|
||
|
#define IPADDR_BUF_SIZE 16
|
||
|
static char g_chBufIpAddr[IPADDR_STR_BUFFS * IPADDR_BUF_SIZE];
|
||
|
static LONG g_lBufIndexIpAddr = 0;
|
||
|
char * pch = &g_chBufIpAddr[(InterlockedIncrement(&g_lBufIndexIpAddr) % IPADDR_STR_BUFFS) * IPADDR_BUF_SIZE];
|
||
|
XnInAddrToString(*(IN_ADDR *)&_dw, pch, IPADDR_BUF_SIZE);
|
||
|
return(pch);
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
// HexStr
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
#if DBG || defined(XNET_FEATURE_ASSERT) || defined(XNET_FEATURE_TRACE)
|
||
|
|
||
|
char * HexStr(const void * pv, size_t cb)
|
||
|
{
|
||
|
#define HEXSTR_BUFFS 8
|
||
|
#define HEXSTR_CBMAX 128
|
||
|
#define HEXSTR_BUF_SIZE (HEXSTR_CBMAX * 2 + 1)
|
||
|
static char g_chHexBuf[HEXSTR_BUFFS * HEXSTR_BUF_SIZE];
|
||
|
static LONG g_lHexBufIndex = 0;
|
||
|
char * pch = &g_chHexBuf[(InterlockedIncrement(&g_lHexBufIndex) % HEXSTR_BUFFS) * HEXSTR_BUF_SIZE];
|
||
|
char * pchDst = pch;
|
||
|
BYTE * pb = (BYTE *)pv;
|
||
|
UINT ui;
|
||
|
|
||
|
if (cb > HEXSTR_CBMAX)
|
||
|
cb = HEXSTR_CBMAX;
|
||
|
|
||
|
for (; cb > 0; --cb, ++pb)
|
||
|
{
|
||
|
ui = (*pb) >> 4;
|
||
|
*pchDst++ = ui < 10 ? '0' + ui : 'A' + (ui - 10);
|
||
|
ui = (*pb) & 0x0F;
|
||
|
*pchDst++ = ui < 10 ? '0' + ui : 'A' + (ui - 10);
|
||
|
}
|
||
|
|
||
|
*pchDst = 0;
|
||
|
|
||
|
return(pch);
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
// XnInAddrToString
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
void XnInAddrToString(const IN_ADDR ina, char * pchBuf, INT cchBuf)
|
||
|
{
|
||
|
char ach[16];
|
||
|
BYTE * pbSrc = (BYTE *)&ina;
|
||
|
BYTE * pbEnd = pbSrc + 4;
|
||
|
char * pbDst = (cchBuf < sizeof(ach)) ? ach : pchBuf;
|
||
|
UINT b;
|
||
|
|
||
|
while (pbSrc < pbEnd)
|
||
|
{
|
||
|
b = *pbSrc++;
|
||
|
|
||
|
if (b >= 10)
|
||
|
{
|
||
|
if (b >= 100) { *pbDst++ = '0' + (b / 100); b %= 100; }
|
||
|
*pbDst++ = '0' + (b / 10); b %= 10;
|
||
|
}
|
||
|
|
||
|
*pbDst++ = '0' + b;
|
||
|
*pbDst++ = '.';
|
||
|
}
|
||
|
|
||
|
pbDst[-1] = 0;
|
||
|
|
||
|
if (cchBuf < sizeof(ach) && cchBuf > 0)
|
||
|
{
|
||
|
memcpy(pchBuf, ach, cchBuf);
|
||
|
pchBuf[cchBuf - 1] = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
// XnAddrStr
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
#if DBG || defined(XNET_FEATURE_ASSERT) || defined(XNET_FEATURE_TRACE)
|
||
|
|
||
|
char * XnAddrStr(const XNADDR * pxnaddr)
|
||
|
{
|
||
|
#define XNADDR_STR_BUFFS 4
|
||
|
#define XNADDR_BUF_SIZE 128
|
||
|
static char g_chXnAddrBuf[XNADDR_STR_BUFFS * XNADDR_BUF_SIZE];
|
||
|
static LONG g_lXnAddrBufIndex = 0;
|
||
|
char * pch = &g_chXnAddrBuf[(InterlockedIncrement(&g_lXnAddrBufIndex) % XNADDR_STR_BUFFS) * XNADDR_BUF_SIZE];
|
||
|
|
||
|
memset(pch, 0, XNADDR_BUF_SIZE);
|
||
|
|
||
|
_snprintf(pch, XNADDR_BUF_SIZE - strlen(pch) - 2, "{ %s ", ((CEnetAddr *)pxnaddr->abEnet)->Str());
|
||
|
|
||
|
if (pxnaddr->ina.s_addr != 0)
|
||
|
{
|
||
|
_snprintf(&pch[strlen(pch)], XNADDR_BUF_SIZE - strlen(pch) - 2, "%s ",
|
||
|
CIpAddr(pxnaddr->ina.s_addr).Str());
|
||
|
|
||
|
if (pxnaddr->inaOnline.s_addr != 0)
|
||
|
{
|
||
|
_snprintf(&pch[strlen(pch)], XNADDR_BUF_SIZE - strlen(pch) - 2, "NAT %s:%d SG %s ",
|
||
|
CIpAddr(pxnaddr->inaOnline.s_addr).Str(), NTOHS(pxnaddr->wPortOnline),
|
||
|
HexStr(pxnaddr->abOnline, sizeof(pxnaddr->abOnline)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_snprintf(&pch[strlen(pch)], XNADDR_BUF_SIZE - strlen(pch) - 2, "}");
|
||
|
|
||
|
return(pch);
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
// CEnetAddr
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
#if DBG || defined(XNET_FEATURE_ASSERT) || defined(XNET_FEATURE_TRACE)
|
||
|
|
||
|
char * CEnetAddr::Str() const
|
||
|
{
|
||
|
#define ENETADDR_STR_BUFFS 32
|
||
|
#define ENETADDR_BUF_SIZE 18
|
||
|
static char g_chBuf[ENETADDR_STR_BUFFS * ENETADDR_BUF_SIZE];
|
||
|
static LONG g_lBufIndex = 0;
|
||
|
char * pch = &g_chBuf[(InterlockedIncrement(&g_lBufIndex) % ENETADDR_STR_BUFFS) * ENETADDR_BUF_SIZE];
|
||
|
_snprintf(pch, ENETADDR_BUF_SIZE, "%02X-%02X-%02X-%02X-%02X-%02X", _ab[0], _ab[1], _ab[2], _ab[3], _ab[4], _ab[5]);
|
||
|
return(pch);
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
// CXnBase (Timer)
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
void CXnBase::TimerSet(CTimer * pt, DWORD dwTick)
|
||
|
{
|
||
|
ICHECK(BASE, UDPC|SDPC);
|
||
|
|
||
|
Assert(sizeof(PFNTIMER) == sizeof(void *));
|
||
|
AssertSz(dwTick >= _dwTick, "Attempt to set timer in the past");
|
||
|
|
||
|
TraceSz2(Timer, "TimerSet %08lX dwTick=%08lX", pt, dwTick);
|
||
|
|
||
|
if (pt->_le.Flink != NULL)
|
||
|
{
|
||
|
TraceSz1(Timer, "TimerDequeue %08lX", pt);
|
||
|
AssertListEntry(&_leTimers, &pt->_le);
|
||
|
RemoveEntryList(&pt->_le);
|
||
|
pt->_le.Flink = NULL;
|
||
|
}
|
||
|
|
||
|
AssertList(&_leTimers);
|
||
|
|
||
|
pt->_dwTick = dwTick;
|
||
|
|
||
|
if (dwTick < 0xFFFFFFFF)
|
||
|
{
|
||
|
if (IsListEmpty(&_leTimers) || dwTick <= ((CTimer *)_leTimers.Flink)->_dwTick)
|
||
|
{
|
||
|
TraceSz1(Timer, "TimerEnqueue %08lX (Head)", pt);
|
||
|
InsertHeadList(&_leTimers, &pt->_le)
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CTimer * ptPrev = (CTimer *)_leTimers.Blink;
|
||
|
|
||
|
while (dwTick < ptPrev->_dwTick)
|
||
|
{
|
||
|
ptPrev = (CTimer *)ptPrev->_le.Blink;
|
||
|
Assert(ptPrev != (CTimer *)&_leTimers);
|
||
|
}
|
||
|
|
||
|
pt->_le.Flink = ptPrev->_le.Flink;
|
||
|
pt->_le.Blink = &ptPrev->_le;
|
||
|
ptPrev->_le.Flink->Blink = &pt->_le;
|
||
|
ptPrev->_le.Flink = &pt->_le;
|
||
|
|
||
|
TraceSz2(Timer, "TimerEnqueue %08lX (after %08lX)", pt, ptPrev);
|
||
|
}
|
||
|
|
||
|
AssertListEntry(&_leTimers, &pt->_le);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DWORD CXnBase::TimerSetRelative(CTimer * pt, DWORD dwTicksFromNow)
|
||
|
{
|
||
|
ICHECK(BASE, UDPC|SDPC);
|
||
|
|
||
|
DWORD dwTick = _dwTick + dwTicksFromNow;
|
||
|
|
||
|
if (dwTick < pt->_dwTick)
|
||
|
{
|
||
|
TimerSet(pt, dwTick);
|
||
|
}
|
||
|
|
||
|
return(dwTick);
|
||
|
}
|
||
|
|
||
|
void CXnBase::TimerDpc(PKDPC pkdpc, void * pvContext, void * pvParam1, void * pvParam2)
|
||
|
{
|
||
|
((CXnBase *)pvContext)->TimerPush();
|
||
|
}
|
||
|
|
||
|
void CXnBase::TimerPush()
|
||
|
{
|
||
|
ICHECK(BASE, SDPC);
|
||
|
|
||
|
_dwTickKe = KeQueryTickCount();
|
||
|
DWORD dwTick = ++_dwTick;
|
||
|
|
||
|
while (1)
|
||
|
{
|
||
|
AssertList(&_leTimers);
|
||
|
|
||
|
CTimer * pt = (CTimer *)_leTimers.Flink;
|
||
|
|
||
|
if (pt == (CTimer *)&_leTimers)
|
||
|
break;
|
||
|
|
||
|
if (dwTick < pt->_dwTick)
|
||
|
break;
|
||
|
|
||
|
TraceSz2(Timer, "TimerPush %08lX dwTick=%08lX", pt, dwTick);
|
||
|
|
||
|
RemoveEntryList(&pt->_le);
|
||
|
|
||
|
pt->_le.Flink = NULL;
|
||
|
pt->_dwTick = TIMER_INFINITE;
|
||
|
|
||
|
(this->*(pt->_pfn))(pt);
|
||
|
}
|
||
|
|
||
|
SecRegProbe();
|
||
|
|
||
|
NicTimer();
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
// CXnBase
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
NTSTATUS CXnBase::BaseInit(XNetInitParams * pxnip)
|
||
|
{
|
||
|
TCHECK(USER);
|
||
|
|
||
|
NTSTATUS status = HalInit(pxnip);
|
||
|
if (!NT_SUCCESS(status))
|
||
|
return(status);
|
||
|
|
||
|
SetInitFlag(INITF_BASE);
|
||
|
|
||
|
memcpy(&cfgSizeOfStruct, &s_XNetParamsDef.cfgSizeOfStruct, sizeof(XNetParams));
|
||
|
|
||
|
if (pxnip->pxnp != NULL)
|
||
|
{
|
||
|
if ( pxnip->pxnp->cfgSizeOfStruct != sizeof(XNetStartupParams)
|
||
|
&& pxnip->pxnp->cfgSizeOfStruct != sizeof(XNetParams))
|
||
|
{
|
||
|
TraceSz(Warning, "Ignoring parameters because cfgSizeOfStruct is invalid");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
BYTE * pbDef = (BYTE *)&s_XNetParamsDef.cfgSizeOfStruct;
|
||
|
BYTE * pbMin = (BYTE *)&s_XNetParamsMin.cfgSizeOfStruct;
|
||
|
BYTE * pbMax = (BYTE *)&s_XNetParamsMax.cfgSizeOfStruct;
|
||
|
BYTE * pbSrc = (BYTE *)&pxnip->pxnp->cfgSizeOfStruct;
|
||
|
BYTE * pbDst = (BYTE *)&cfgSizeOfStruct;
|
||
|
int c = pxnip->pxnp->cfgSizeOfStruct;
|
||
|
|
||
|
for (; --c >= 0; ++pbDef, ++pbMin, ++pbMax, ++pbSrc, ++pbDst)
|
||
|
{
|
||
|
if (*pbSrc == 0)
|
||
|
*pbDst = *pbDef;
|
||
|
else if (*pbSrc < *pbMin)
|
||
|
*pbDst = *pbMin;
|
||
|
else if (*pbSrc > *pbMax)
|
||
|
*pbDst = *pbMax;
|
||
|
else
|
||
|
*pbDst = *pbSrc;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef XNET_FEATURE_XBDM_SERVER
|
||
|
cfgFlags |= XNET_STARTUP_BYPASS_SECURITY;
|
||
|
#endif
|
||
|
|
||
|
RandInit(pxnip);
|
||
|
|
||
|
InitializeListHead(&_leTimers);
|
||
|
KeInitializeDpc(&_dpcTimer, TimerDpc, this);
|
||
|
KeInitializeTimer(&_timerTimer);
|
||
|
|
||
|
// Start the current timer tick at one day to allow timer routines to compute times
|
||
|
// in the past and not have to worry about going negative.
|
||
|
|
||
|
_dwTick = 24 * 60 * 60 * TICKS_PER_SECOND;
|
||
|
_dwTickKe = KeQueryTickCount();
|
||
|
|
||
|
#ifdef XNET_FEATURE_ASSERT
|
||
|
_leakinfo._pfnLeakTag = XnLeakTagToString;
|
||
|
#endif
|
||
|
|
||
|
SetInitFlag(INITF_BASE_1);
|
||
|
|
||
|
UINT cbPool = cfgPrivatePoolSizeInPages * 4096;
|
||
|
PoolEntry * ppe;
|
||
|
UINT n;
|
||
|
|
||
|
ppe = (PoolEntry *)SysAlloc(cbPool, PTAG_Pool);
|
||
|
|
||
|
if (ppe == NULL)
|
||
|
{
|
||
|
return(NETERR_MEMORY);
|
||
|
}
|
||
|
|
||
|
_pvPoolStart = ppe;
|
||
|
_pvPoolEnd = ((BYTE *)ppe + cbPool);
|
||
|
|
||
|
SetInitFlag(INITF_BASE_2);
|
||
|
|
||
|
// Initialize the free lists with a single big block
|
||
|
|
||
|
for (n = 0; n <= MAX_SMALL_BLOCKS; n++)
|
||
|
{
|
||
|
InitializeListHead(&_aleFree[n]);
|
||
|
}
|
||
|
|
||
|
// NOTE: we reserved the very first and the very last
|
||
|
// pool blocks. This saves us from a couple of extra checks
|
||
|
// during alloc and free.
|
||
|
|
||
|
MarkPoolEntryBusy(ppe);
|
||
|
TagPoolEntry(ppe, PTAG_PoolStart);
|
||
|
ppe->bOver = (1 << POOL_BLOCK_SHIFT);
|
||
|
ppe->previousSize = 0;
|
||
|
ppe->blockSize = 1;
|
||
|
|
||
|
ppe = GetPoolEntryNext(ppe);
|
||
|
n = (cbPool >> POOL_BLOCK_SHIFT) - 2;
|
||
|
MarkPoolEntryFree(ppe);
|
||
|
TagPoolEntry(ppe, 0);
|
||
|
ppe->previousSize = 1;
|
||
|
ppe->blockSize = (WORD)n;
|
||
|
InsertFreePoolEntry(ppe);
|
||
|
|
||
|
ppe = GetPoolEntryNext(ppe);
|
||
|
MarkPoolEntryBusy(ppe);
|
||
|
TagPoolEntry(ppe, PTAG_PoolEnd);
|
||
|
ppe->bOver = (1 << POOL_BLOCK_SHIFT);
|
||
|
ppe->previousSize = (WORD)n;
|
||
|
ppe->blockSize = 1;
|
||
|
|
||
|
return(NETERR_OK);
|
||
|
}
|
||
|
|
||
|
void CXnBase::BaseStart()
|
||
|
{
|
||
|
ICHECK(BASE, USER);
|
||
|
|
||
|
HalStart();
|
||
|
|
||
|
Assert(!TestInitFlag(INITF_BASE_STOP));
|
||
|
|
||
|
LARGE_INTEGER liDue;
|
||
|
liDue.QuadPart = -10000000 / TICKS_PER_SECOND;
|
||
|
KeSetTimerEx(&_timerTimer, liDue, 1000 / TICKS_PER_SECOND, &_dpcTimer);
|
||
|
}
|
||
|
|
||
|
void CXnBase::BaseStop()
|
||
|
{
|
||
|
TCHECK(UDPC|SDPC);
|
||
|
|
||
|
if (!TestInitFlag(INITF_BASE_STOP))
|
||
|
{
|
||
|
if (TestInitFlag(INITF_BASE_1))
|
||
|
{
|
||
|
KeCancelTimer(&_timerTimer);
|
||
|
KeRemoveQueueDpc(&_dpcTimer);
|
||
|
}
|
||
|
|
||
|
SetInitFlag(INITF_BASE_STOP);
|
||
|
}
|
||
|
|
||
|
HalStop();
|
||
|
}
|
||
|
|
||
|
void CXnBase::BaseTerm()
|
||
|
{
|
||
|
TCHECK(UDPC);
|
||
|
|
||
|
BaseStop();
|
||
|
|
||
|
Assert(IsListEmpty(&_leTimers));
|
||
|
|
||
|
if (TestInitFlag(INITF_BASE_2))
|
||
|
{
|
||
|
SysFree(_pvPoolStart);
|
||
|
_pvPoolStart = NULL;
|
||
|
_pvPoolEnd = NULL;
|
||
|
}
|
||
|
|
||
|
#ifdef XNET_FEATURE_ASSERT
|
||
|
if (_leakinfo._cLeak > 0)
|
||
|
{
|
||
|
LeakTerm(&_leakinfo);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
SetInitFlag(INITF_BASE_TERM);
|
||
|
|
||
|
HalTerm();
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
// Assert
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
#ifdef XNET_FEATURE_ASSERT
|
||
|
|
||
|
char * __cdecl DbgAssertFmt(char const * pszFmt, ...)
|
||
|
{
|
||
|
#define ASSERT_ENTRY_SIZE 512
|
||
|
#define ASSERT_ENTRY_COUNT 8
|
||
|
static char s_achAssert[ASSERT_ENTRY_SIZE * ASSERT_ENTRY_COUNT];
|
||
|
static LONG s_lAssert = 0;
|
||
|
char * pchBuf = &s_achAssert[(InterlockedIncrement(&s_lAssert) % ASSERT_ENTRY_COUNT) * ASSERT_ENTRY_SIZE];
|
||
|
pchBuf[0] = 0;
|
||
|
va_list va;
|
||
|
va_start(va, pszFmt);
|
||
|
_vsnprintf(pchBuf, ASSERT_ENTRY_SIZE - 1, pszFmt, va);
|
||
|
va_end(va);
|
||
|
pchBuf[ASSERT_ENTRY_SIZE - 1] = 0;
|
||
|
return(pchBuf);
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
// Trace
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
#ifdef XNET_FEATURE_TRACE
|
||
|
|
||
|
void __cdecl DbgTrace(const char * pszTag, const char * pszFmt, ...)
|
||
|
{
|
||
|
char ach[256];
|
||
|
char * pchBuf = ach;
|
||
|
int cchBuf = sizeof(ach);
|
||
|
char * psz = (char *)pszFmt;
|
||
|
va_list va;
|
||
|
|
||
|
memset(ach, 0, sizeof(ach));
|
||
|
|
||
|
if (*psz == '+')
|
||
|
++psz;
|
||
|
else
|
||
|
{
|
||
|
if (*psz == '\n')
|
||
|
{
|
||
|
*pchBuf++ = *psz++;
|
||
|
cchBuf -= 1;
|
||
|
}
|
||
|
#ifdef XNET_FEATURE_XBOX
|
||
|
_snprintf(pchBuf, cchBuf - 2, "[" XNET_TRACE_PREFIX "] %s: ", pszTag);
|
||
|
#else
|
||
|
_snprintf(pchBuf, cchBuf - 3, "[" XNET_TRACE_PREFIX ".%03X] %s: ", GetCurrentThreadId(), pszTag);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
va_start(va, pszFmt);
|
||
|
int cch = strlen(pchBuf);
|
||
|
_vsnprintf(&pchBuf[cch], cchBuf - cch - 2, psz, va);
|
||
|
va_end(va);
|
||
|
|
||
|
cch = strlen(pchBuf);
|
||
|
|
||
|
if (cch > 0 && *pszFmt && pszFmt[strlen(pszFmt) - 1] == '+')
|
||
|
pchBuf[cch - 1] = 0;
|
||
|
else
|
||
|
strcpy(pchBuf + cch, "\n");
|
||
|
|
||
|
#ifdef XNET_FEATURE_XBDM_SERVER
|
||
|
DbgPrintToKd(ach);
|
||
|
#else
|
||
|
DbgPrint("%s", ach);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
// DbgVerifyList
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
#ifdef XNET_FEATURE_ASSERT
|
||
|
|
||
|
const char * DbgVerifyList(LIST_ENTRY * ple, LIST_ENTRY * pleRequire)
|
||
|
{
|
||
|
LIST_ENTRY * plePrev;
|
||
|
LIST_ENTRY * pleThis;
|
||
|
BOOL fFound = FALSE;
|
||
|
UINT cEnt = 0;
|
||
|
|
||
|
if (ple->Flink == NULL || ple->Blink == NULL)
|
||
|
return("List Flink is NULL");
|
||
|
else if (ple->Blink == NULL)
|
||
|
return("List Blink is NULL");
|
||
|
|
||
|
if (ple->Flink == ple || ple->Blink == ple)
|
||
|
{
|
||
|
if (ple->Flink != ple->Blink)
|
||
|
{
|
||
|
return("List head corrupt");
|
||
|
}
|
||
|
|
||
|
return((!pleRequire) ? NULL : "Required entry not found");
|
||
|
}
|
||
|
|
||
|
plePrev = ple;
|
||
|
pleThis = ple->Flink;
|
||
|
|
||
|
while (1)
|
||
|
{
|
||
|
if (pleThis == NULL)
|
||
|
{
|
||
|
return("List entry has an Flink that points to NULL");
|
||
|
}
|
||
|
|
||
|
if (plePrev != pleThis->Blink)
|
||
|
{
|
||
|
return("List entry has a Blink that doesn't point to previous entry");
|
||
|
return(FALSE);
|
||
|
}
|
||
|
|
||
|
if (pleThis == ple)
|
||
|
break;
|
||
|
|
||
|
if (pleThis == pleRequire)
|
||
|
{
|
||
|
if (fFound)
|
||
|
{
|
||
|
return("List has required entry twice. Cycle detected.");
|
||
|
}
|
||
|
|
||
|
fFound = TRUE;
|
||
|
}
|
||
|
|
||
|
plePrev = pleThis;
|
||
|
pleThis = plePrev->Flink;
|
||
|
|
||
|
if (++cEnt > 100000)
|
||
|
{
|
||
|
return("List has cycle");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return((!pleRequire || fFound) ? NULL : "Required entry not found");
|
||
|
}
|
||
|
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
// DataToString
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
#if defined(XNET_FEATURE_TRACE) || defined(XNET_FEATURE_ASSERT)
|
||
|
|
||
|
char * DataToString(BYTE * pb, UINT cb)
|
||
|
{
|
||
|
#define DTS_ENTRY_SIZE 32
|
||
|
#define DTS_ENTRY_COUNT 8
|
||
|
static char s_achDts[DTS_ENTRY_SIZE * DTS_ENTRY_COUNT];
|
||
|
static LONG s_lDts = 0;
|
||
|
char * pbBuf = &s_achDts[(InterlockedIncrement(&s_lDts) % DTS_ENTRY_COUNT) * DTS_ENTRY_SIZE];
|
||
|
char * pbDst = pbBuf;
|
||
|
|
||
|
if (cb > DTS_ENTRY_SIZE - 1)
|
||
|
cb = DTS_ENTRY_SIZE - 1;
|
||
|
|
||
|
for (; cb > 0; ++pbDst, ++pb, --cb)
|
||
|
{
|
||
|
if (*pb >= 31 && *pb < 127)
|
||
|
*pbDst = *pb;
|
||
|
else
|
||
|
*pbDst = '.';
|
||
|
}
|
||
|
|
||
|
*pbDst = 0;
|
||
|
|
||
|
return(pbBuf);
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
// Push/Pop Recv Tags
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
#ifdef XNET_FEATURE_TRACE
|
||
|
|
||
|
void CXnBase::PushPktRecvTags(BOOL fBroadcast)
|
||
|
{
|
||
|
ICHECK(BASE, UDPC|SDPC);
|
||
|
|
||
|
Assert(_cbPushPop < sizeof(_abPushPop));
|
||
|
|
||
|
_abPushPop[_cbPushPop++] = !!fBroadcast;
|
||
|
|
||
|
if (fBroadcast && ++_cPushPopBroadcast == 1)
|
||
|
{
|
||
|
_pktRecvPushPop = Tag(pktRecv);
|
||
|
_pktWarnPushPop = Tag(pktWarn);
|
||
|
Tag(pktRecv) = Tag(pktBroadcast);
|
||
|
Tag(pktWarn) = Tag(pktBroadcast);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CXnBase::PopPktRecvTags()
|
||
|
{
|
||
|
ICHECK(BASE, UDPC|SDPC);
|
||
|
|
||
|
Assert(_cbPushPop > 0);
|
||
|
|
||
|
if (_abPushPop[--_cbPushPop])
|
||
|
{
|
||
|
Assert(_cPushPopBroadcast > 0);
|
||
|
|
||
|
if (--_cPushPopBroadcast == 0)
|
||
|
{
|
||
|
Tag(pktRecv) = _pktRecvPushPop;
|
||
|
Tag(pktWarn) = _pktWarnPushPop;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
// ---------------------------------------------------------------------------------------
|
||
|
|
||
|
#endif
|