Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1026 lines
23 KiB

/*++
Microsoft Windows NT RPC Name Service
Copyright (c) 1995 Microsoft Corporation
Module Name:
brodcast.cxx
Abstract:
This file contains the code for all mailslot activity relating to
binding handle queries-- both outgoing and incoming ones.
Things to improve in future revisions:
1. CBindingVector::msriList
2. formQueryPacket
Author:
Satish Thatte (SatishT) 08/15/95 Created all the code below except where
otherwise indicated.
--*/
#include <api.hxx>
#include <var.hxx>
#include <locquery.h>
/* NOTE: Compatibility Issue: see below for meaning of END_FLAG_SIZE */
const int END_FLAG_SIZE = 4;
/**************** Utilities for Broadcast **********************/
/* a little macro exclusively for use in UnmarshallBroadcastReplyItem */
#define unBufferIt(source) \
lBufferSize -= advance; \
if (lBufferSize < 0) { \
StatusCode = NSI_S_UNMARSHALL_UNSUCCESSFUL; \
return; \
} \
if (source) memcpy((char*) source, pcBuffer, advance); \
pcBuffer += advance;
void
UnmarshallBroadcastReplyItem(
TSSLEntryList *psslCache,
STRING_T szEntryDomain,
char * &pcBuffer,
long& lBufferSize,
ULONG& StatusCode
)
/*++
Routine Description:
Unmarshall an item from the broadcast reply buffer. If a well-formed item
is found, the locator's cache and the temporary given cache (first argument)
is updated and the buffer size is decreased accordingly. A reply packet
contains the name of the replying locator's domain, which is used as the
domain of the entry unless otherwise specified in the name itself.
If the unmarshalling is unsuccessful, this is indicated in the status code returned.
Arguments:
psslCache - temporary cache for remote handle
szEntryDomain - domain of replying locator
pcBuffer - reply buffer
lBufferSize - reduced buffer size returned here
StatusCode - status of unmarshalling attempt returned here
Returned Status:
NSI_S_OK, NSI_OUT_OF_MEMORY, NSI_S_UNMARSHALL_UNSUCCESSFUL,
NSI_S_UNSUPPORTED_BUFFER_TYPE
Remarks:
The following function has "return"s inside a __try block. Since this whole code
is slated for removal after a couple of updates, I have left them in. They could
be easily replaced with "goto"s if necessary.
--*/
{
StatusCode = NSI_S_OK;
long advance;
RPC_SYNTAX_IDENTIFIER Interface;
RPC_SYNTAX_IDENTIFIER XferSyntax;
STRING_T binding;
STRING_T entryName = NULL;
advance = sizeof(fixed_part_of_reply);
fixed_part_of_reply fpr;
unBufferIt(&fpr);
if (fpr.type != MailslotServerEntryType) {
StatusCode = NSI_S_UNSUPPORTED_BUFFER_TYPE;
return;
}
Interface = fpr.Interface;
XferSyntax = fpr.XferSyntax;
advance = fpr.EntryNameLength * sizeof(WCHAR);
entryName = (WCHAR*) pcBuffer;
if (fpr.EntryNameLength != (wcslen(entryName) + 1)) {
StatusCode = NSI_S_UNMARSHALL_UNSUCCESSFUL;
return;
}
unBufferIt(NULL);
advance = sizeof(UNSIGNED32);
UNSIGNED32 objVectorSize;
unBufferIt(&objVectorSize);
GUID * guidVector = new GUID[objVectorSize];
NSI_UUID_VECTOR_T * pUuidVector = (NSI_UUID_VECTOR_T *)
new char [
sizeof(UNSIGNED32) +
sizeof(NSI_UUID_P_T) * objVectorSize
];
pUuidVector->count = objVectorSize;
advance = sizeof(void*); // this is a useless pointer part
lBufferSize -= advance; // therefore we don't unmarshall it
pcBuffer += advance; // we just account for its size
CStringW *pswEntry = NULL, *pswDomain = NULL;
CEntryName * penTempName = NULL;
__try{
advance = sizeof(GUID);
for (ULONG i = 0; i < objVectorSize; i++) {
pUuidVector->uuid[i] = &(guidVector[i]);
unBufferIt(&(guidVector[i]));
}
advance = fpr.BindingLength * sizeof(WCHAR);
binding = (WCHAR*) pcBuffer;
if (fpr.BindingLength != (wcslen(binding) + 1)) {
StatusCode = NSI_S_UNMARSHALL_UNSUCCESSFUL;
return;
}
unBufferIt(NULL);
/* first update the central cache and, if there is anything new,
also update the temporary cache in psslCache
*/
parseEntryName(entryName,pswDomain,pswEntry);
if (!pswDomain && szEntryDomain) // if name has no domain then use the
// domain of origin in forming name
penTempName = new CEntryName(szEntryDomain,*pswEntry);
else penTempName = new CEntryName(entryName);
myRpcLocator->UpdateCache(
*penTempName,
RPC_C_NS_SYNTAX_DCE,
Interface,
XferSyntax,
binding,
pUuidVector,
psslCache
);
}
__finally {
delete [] pUuidVector;
delete [] guidVector;
delete pswDomain;
delete pswEntry;
delete penTempName;
}
}
BOOL
CBroadcastQueryPacket::subsumes(QueryPacket& NewQuery, ULONG tolerance) {
// too stale?
if (!isCurrent(tolerance)) return FALSE;
CStringW OtherEntry(NewQuery.EntryName);
// incompatible entry name involved?
if (!(swEntryName == OtherEntry) && (swEntryName.length() > 0))
return FALSE;
CGUID OtherObject(NewQuery.Object);
// incompatible object involved?
if (!(OtherObject == Object) && !Object.IsNil()) return FALSE;
CGUIDVersion OtherInterface(NewQuery.Interface);
CGUID myGUID(Interface.myIdAndVersion().SyntaxGUID);
// incompatible interface involved?
if (
!OtherInterface.isMatching(Interface,RPC_C_VERS_COMPATIBLE) &&
!myGUID.IsNil()
)
return FALSE;
// we ran the gauntlet successfully -- stop the broadcast!
return TRUE;
}
void
formQueryPacket(
CEntryName * penEntryName,
CGUIDVersion * pGVInterface,
CGUID * pIDobject,
QueryPacket & NetRequest
)
/*++
Routine Description:
Forms a query packet given the items in it.
Arguments:
penEntryName - (wrapped) entry name
pGVInterface - (wrapped) interface UUID and version
pIDobject - (wrapped) object UUID
NetRequest - The request packet is returned here
Remarks:
BUGBUG:
We used to perform some shenanigans with interface and object settings
in the QueryPacket to allow us to correctly report NSI_S_ENTRY_NOT_FOUND
when appropriate. Specifically, using wildcards for interface and object
if the entry name was non null so as to gather all information about the
specific entry from the net.
In order to interoperate with the old locator, these shenanigans have
been suspended, and we will currently return NSI_S_ENTRY_NOT_FOUND
spuriously when no information is received from the net in response to
a specific rather than a wildcard query, just like the old locator
(so that the old tests pass when they expect NSI_S_ENTRY_NOT_FOUND !!).
The problem with the old locator is that it carefully avoids sending
duplicate binding handles, so if a null interface is given in
the broadcast query, it naturally assumes that we don't care about the
interface, and may omit info about some interfaces unless they have
distinct binding handles associated with them.
--*/
{
memset(&(NetRequest.Object),0,sizeof(NetRequest.Object));
memset(&(NetRequest.Interface),0,sizeof(NetRequest.Interface));
if (penEntryName) // specific entry - we used to leave wildcards for interface and object
wcscpy(NetRequest.EntryName,*penEntryName);
else // otherwise, use real interface and object if given
memset(&(NetRequest.EntryName),0,sizeof(WCHAR)*MAX_ENTRY_NAME_LENGTH);
// NOTE: compatibility removal: the following two lines were conditional
// on the else before the "shenanigans" were eliminated
if (pIDobject) NetRequest.Object = pIDobject->myGUID();
if (pGVInterface) NetRequest.Interface = *pGVInterface;
}
TSSLEntryList *
getBroadcastResults(
ULONG cacheTolerance,
CEntryName * penEntryName,
CGUIDVersion * pGVInterface,
CGUID * pIDobject,
ULONG & StatusCode
)
/*++
Routine Description:
Broadcasts for the requested binding handles.
Arguments:
cacheTolerance - the staleness tolerance associated with this request,
used in deciding whether to broadcast at all
penEntryName - (wrapped) entry name
pGVInterface - (wrapped) interface UUID and version
pIDobject - (wrapped) object UUID
StatusCode - The status of the broadcast attempt is returned here
Returns:
A temporary cache of entries containing the new information.
Remarks:
A broadcast is made only if a subsuming broadcast has not been made within
the staleness limit specified by the parameter cacheTolerance. Only one
broadcast is allowed at a time, since there is only one mailslot for replies.
--*/
{
char * pcBuffer;
ULONG waitCur = INITIAL_MAILSLOT_READ_WAIT; // current wait time for replies
STRING_T szEntryDomain;
READ_MAIL_SLOT *hMailslotForReplies = myRpcLocator->hMailslotForReplies;
long cbRead;
QueryReply NetReply;
if (!hMailslotForReplies) {
StatusCode = NSI_S_ENTRY_NOT_FOUND;
return NULL;
}
QueryPacket NetRequest;
wcscpy(NetRequest.WkstaName, TEXT("\\\\"));
wcscpy(NetRequest.WkstaName + 2, *myRpcLocator->getComputerName());
formQueryPacket(penEntryName,pGVInterface,pIDobject,NetRequest);
// now get this broadcast cleared -- make sure it is not redundant
if (!myRpcLocator->broadcastCleared(NetRequest,cacheTolerance))
{
if (penEntryName)
DBGOUT(BROADCAST, "\nBroadcast request denied for " << *penEntryName << "\n"
<< "Interface = " << &NetRequest.Interface.SyntaxGUID << "\n"
<< "Object = " << &NetRequest.Object << "\n\n");
else DBGOUT(BROADCAST, "\nBroadcast request denied for NULL entry\n"
<< "Interface = " << &NetRequest.Interface.SyntaxGUID << "\n"
<< "Object = " << &NetRequest.Object << "\n\n");
return NULL;
}
else
{
if (penEntryName)
DBGOUT(BROADCAST, "\nI am broadcasting for " << *penEntryName << "\n"
<< "Interface = " << &NetRequest.Interface.SyntaxGUID << "\n"
<< "Object = " << &NetRequest.Object << "\n\n");
else DBGOUT(BROADCAST, "\nI am broadcasting for NULL entry\n"
<< "Interface = " << &NetRequest.Interface.SyntaxGUID << "\n"
<< "Object = " << &NetRequest.Object << "\n\n");
myRpcLocator->markBroadcast(NetRequest);
}
// szDomain should be either NULL or a plain Domain name
STRING_T szDomainParam;
CStringW *pswDomain = penEntryName ? penEntryName->getDomainName() : NULL;
if (pswDomain)
szDomainParam = catenate(TEXT("\\\\"),*pswDomain);
else szDomainParam = TEXT("\\\\*");
TSSLEntryList *psslCache = new TSSLEntryList;
StatusCode = NSI_S_OK;
__try {
csBindingBroadcastGuard.Enter(); // only one broadcast at a time
WRITE_MAIL_SLOT MSquery(szDomainParam, PMAILNAME_S);
MSquery.Write((char *) &NetRequest, sizeof(NetRequest));
// now loop waiting for responses from other RPC servers
while (cbRead = hMailslotForReplies->Read((char *) &NetReply,
sizeof(NetReply),
waitCur
)
)
{
pcBuffer = NetReply.Buffer;
szEntryDomain = (_wcsicmp(
*myRpcLocator->getDomainName(),
NetReply.Domain
)
== 0
)? NULL: NetReply.Domain;
while (!StatusCode) {
UnmarshallBroadcastReplyItem(
psslCache,
szEntryDomain,
pcBuffer,
cbRead,
StatusCode
);
}
StatusCode = NSI_S_OK;
// halve the wait period everytime you get a response from the net
waitCur >>= 1;
}
csBindingBroadcastGuard.Leave();
}
__except(EXCEPTION_EXECUTE_HANDLER) {
csBindingBroadcastGuard.Leave();
StatusCode = GetExceptionCode();
psslCache->wipeOut();
delete psslCache;
psslCache = NULL;
if (pswDomain) delete [] szDomainParam;
return NULL;
}
if (pswDomain) delete [] szDomainParam;
if (psslCache->size() == 0) {
delete psslCache;
psslCache = NULL;
}
return psslCache;
}
/**************** Methods related to MailSlotReplyItem ********************/
/* a little macro exclusively for use in CMailSlotReplyItem::Marshall */
#define bufferIt(source) \
lBufferSize -= advance; \
if (lBufferSize < 0) return 0; \
memcpy(pcBuffer, (char*) source, advance); \
pcBuffer += advance;
DWORD
CMailSlotReplyItem::Marshall(
char * pcBuffer,
long lBufferSize
)
/*++
Method Description:
--*/
{
DWORD dwOriginalBufferSize = lBufferSize;
fixed_part_of_reply fpr;
fpr.type = MailslotServerEntryType;
fpr.Interface = Interface;
fpr.XferSyntax = XferSyntax;
fpr.BindingLength = wcslen(binding)+ 1;
fpr.EntryNameLength = wcslen(entryName) + 1;
long advance;
advance = sizeof(fixed_part_of_reply);
bufferIt(&fpr);
advance = fpr.EntryNameLength * sizeof(WCHAR);
bufferIt(entryName);
advance = sizeof(long);
long objListSize = pObjectList->size();
bufferIt(&objListSize);
advance = sizeof(void*); // this is a useless pointer part
lBufferSize -= advance; // therefore we don't marshall it
pcBuffer += advance; // we just account for its size
TCSafeSkipListIterator<CGUID> objIter(*pObjectList);
advance = sizeof(GUID);
for (CGUID *obj =objIter.next(); obj; obj =objIter.next()) {
GUID rep = obj->myGUID();
bufferIt(&rep);
}
advance = fpr.BindingLength * sizeof(WCHAR);
bufferIt(binding);
return dwOriginalBufferSize - lBufferSize;
}
TMSRILinkList *
CBindingVector::msriList(
CInterface *pIf,
TCSafeSkipList<CGUID>* psslObjList
)
/*++
Method Description:
We combine every relevant object in the server entry with every basic
binding string although that seems ridiculous. This is the way the old
locator expects us to behave as far as I can tell.
--*/
{
CMailSlotReplyItem *pmsrl;
TMSRILinkList *pmsrill = new TMSRILinkList;
TCSafeSkipListIterator<CStringW> bindingIter(*this);
for (CStringW * psw = bindingIter.next(); psw; psw = bindingIter.next())
{
pmsrl = new CMailSlotReplyItem;
pmsrl->binding = *psw;
pmsrl->pObjectList = psslObjList;
pmsrl->Interface = pIf->myIdAndVersion();
pmsrl->XferSyntax = pIf->xferSyntaxIdAndVersion();
pmsrl->entryName = pMyEntry->getCurrentName();
pmsrill->insert(pmsrl);
}
return pmsrill;
}
/********** CBroadcastLookupHandle Methods ****************/
CBroadcastLookupHandle::CBroadcastLookupHandle(
UNSIGNED32 EntryNameSyntax,
STRING_T EntryName,
CGUIDVersion * pGVInterface,
CGUIDVersion * pGVTransferSyntax,
CGUID * pIDobject,
unsigned long ulVectorSize,
unsigned long ulCacheAge
) :
CRemoteLookupHandle(
EntryNameSyntax,
EntryName,
pGVInterface,
pGVTransferSyntax,
pIDobject,
ulVectorSize,
ulCacheAge
)
{
}
void
CBroadcastLookupHandle::initialize()
{
StatusCode = NSI_S_OK;
psslNewCache = getBroadcastResults(
ulCacheMax,
penEntryName,
pgvInterface,
pidObject,
StatusCode
);
if (!psslNewCache) plhFetched = NULL;
else {
TSSLEntryListIterator *pCacheIter = new TSSLEntryListIterator(*psslNewCache);
plhFetched = new CGroupLookupHandle(
pCacheIter,
pgvInterface,
pgvTransferSyntax,
pidObject,
ulVS,
ulCacheMax
);
}
if (penEntryName && !plhFetched) // we looked for a specific entry
StatusCode = NSI_S_ENTRY_NO_NEW_INFO; // by name but found nothing new,
// possibly because the broadcast
// was redundant and was not made
ulCreationTime = CurrentTime();
fNotInitialized = FALSE;
}
/****************** CBroadcastObjectInqHandle Methods *******************/
CBroadcastObjectInqHandle::CBroadcastObjectInqHandle(
STRING_T szEntryName,
ULONG ulCacheAge
)
: CRemoteObjectInqHandle(szEntryName,ulCacheAge)
{
}
void
CBroadcastObjectInqHandle::initialize()
{
StatusCode = NSI_S_OK;
TSSLEntryList *psslNewCache = getBroadcastResults(
ulCacheMax,
penEntryName,
NULL,
NULL,
StatusCode
);
if (!psslNewCache || (psslNewCache->size() == 0)) {
pUuidVector = NULL;
return;
}
CEntry *pEntry = psslNewCache->pop();
psslNewCache->wipeOut();
delete psslNewCache;
pUuidVector = getVector(pEntry->objectInquiry(ulCacheMax));
delete pEntry;
ulCreationTime = CurrentTime();
fNotInitialized = FALSE;
}
/************ Methods for CServerMailSlotReplyHandle *************/
CServerMailSlotReplyHandle::CServerMailSlotReplyHandle(
TMSRILinkList * pmsrill
)
{
pmsriIterator = new TMSRILinkListIterator(*pmsrill);
delete pmsrill; // see CServerLookupHandle::CServerLookupHandle above
}
CServerMailSlotReplyHandle::~CServerMailSlotReplyHandle()
{
for ( // run down the remaining items in the handle
CMailSlotReplyItem* pmsri = pmsriIterator->next();
pmsri;
pmsri = pmsriIterator->next()
)
{
delete pmsri;
}
delete pmsriIterator;
}
/************ Methods for CIndexMailSlotReplyHandle *************/
void
CIndexMailSlotReplyHandle::advanceCurrentHandle()
{
delete pCurrentHandle;
pCurrentHandle = NULL;
CEntry *pCurEntry;
while (!pEIterator->finished()) {
pCurEntry = pEIterator->next();
if (pCurEntry->isCacheType()) continue;
pCurrentHandle = pCurEntry->MailSlotLookup(
pGVInterface,
pIDobject
);
if (pCurrentHandle && !pCurrentHandle->finished()) break;
}
}
CIndexMailSlotReplyHandle::CIndexMailSlotReplyHandle(
CGUIDVersion * pGVInf,
CGUID * pIDobj,
TEntryIterator * pEI
)
{
pEIterator = pEI;
pGVInterface = pGVInf;
pIDobject = pIDobj;
pCurrentHandle = NULL;
advanceCurrentHandle();
}
CMailSlotReplyItem *
CIndexMailSlotReplyHandle::next()
{
if (pCurrentHandle && pCurrentHandle->finished())
advanceCurrentHandle();
if (!pCurrentHandle) return NULL; // no more entries
else return pCurrentHandle->next();
}
int
CIndexMailSlotReplyHandle::finished()
{
if (pCurrentHandle && pCurrentHandle->finished())
advanceCurrentHandle();
if (!pCurrentHandle) return TRUE; // no more entries
else return FALSE;
}
/************ MailSlotLookup Methods *************/
CMailSlotReplyHandle *
CServerEntry::MailSlotLookup(
CGUIDVersion * pGVInterface,
CGUID * pIDobject
)
/*++
Method Description:
--*/
{
TCSafeSkipListIterator<CInterface> IfIter(InterfaceList);
TMSRILinkList *pmsrill = new TMSRILinkList;
for (CInterface *pIf = IfIter.next(); pIf != NULL; pIf = IfIter.next())
if (pIf->isCompatibleWith(pGVInterface,NULL)
)
{
TMSRILinkList *pmsrillTemp
= pIf->pBVhandles->msriList(pIf,&ObjectList);
pmsrill->catenate(*pmsrillTemp);
delete pmsrillTemp;
}
return new CServerMailSlotReplyHandle(
pmsrill
);
}
CMailSlotReplyHandle *
CFullServerEntry::MailSlotLookup(
CGUIDVersion * pGVInterface,
CGUID * pIDobject
)
{
if (pLocalEntry) return pLocalEntry->MailSlotLookup(
pGVInterface,
pIDobject
);
else return NULL;
}
/************** Thread definition for replying to broadcasts ***************/
#define cleanup() \
delete plhQuery; \
delete pGVinterface; \
delete pIDobject; \
delete pEntryName;
void
QueryProcess(void*)
/*++
Routine Description:
This thread creates a mailslot which listens for requests for
RPC servers of a given interface GID. It then uses LookUp to
build a response list. It then replies via a mailslot to the
requesting machine.
--*/
{
QueryReply NetReply;
QueryPacket NetQuery;
DWORD dwMailSize, dwBufferUsed;
// create both a server (s) side mailslot
READ_MAIL_SLOT *hMailslotForQueries;
__try{
hMailslotForQueries = new READ_MAIL_SLOT(PMAILNAME_S, sizeof(QueryPacket));
}
__except(EXCEPTION_EXECUTE_HANDLER) {
ExitThread(NSI_S_MAILSLOT_ERROR);
}
wcscpy(NetReply.Domain, *myRpcLocator->getDomainName());
while (1) {
CMailSlotReplyHandle * plhQuery = NULL;
CGUIDVersion * pGVinterface = NULL;
CGUID * pIDobject = NULL;
CEntry * pEntry = NULL;
RPC_STATUS status = NSI_S_OK;
dwMailSize = hMailslotForQueries->Read(
(char *) &NetQuery,
sizeof(QueryPacket),
MAILSLOT_WAIT_FOREVER
);
if (dwMailSize != sizeof(QueryPacket))
continue; // strange query, ignore it
// ignore messages to self, sending a request on a mailslot
// by a NetLookUp request will be delivered to the local slot too.
// The pointer arithmetic skips over the initial "\\" in the name
if (myRpcLocator->IsSelf(NetQuery.WkstaName+2)) continue;
DBGOUT(BROADCAST, "\nReceived broadcast request from " << NetQuery.WkstaName << "\n\n");
// OK looks like a genuine query -- if we are in a workgroup, rememeber
// the broadcaster as a potential master
if (myRpcLocator->IsInWorkgroup()) myRpcLocator->addMaster((NetQuery.WkstaName)+2);
pGVinterface = UuidIsNil(&NetQuery.Interface.SyntaxGUID,&status) ? NULL :
new CGUIDVersion(NetQuery.Interface);
pIDobject = UuidIsNil(&NetQuery.Object,&status) ? NULL :
new CGUID(NetQuery.Object);
// Variable used to make sure the entry name is well formed and local
CEntryName *pEntryName = NULL;
// This global reader block is needed because a lot of the info in the
// MSRIs is "borrowed" from regular entries, i.e., not copied. Thus, an
// unexport in a separate thread can have unfortunate consequences.
CriticalReader me(rwEntryGuard);
if (NetQuery.EntryName[0] == 0) { // use default entry
plhQuery = new CIndexMailSlotReplyHandle(
pGVinterface,
pIDobject,
myRpcLocator->IndexLookup(pGVinterface)
);
if (!plhQuery) {
cleanup(); // ignore the query
continue; // if nothing to report
}
}
else {
__try {
pEntryName = new CEntryName(NetQuery.EntryName);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
cleanup(); // ignore the query
continue; // if there is any problem
}
if (!pEntryName->isLocal()) {
cleanup(); // ignore the query
continue; // if entry not in our domain
}
pEntry = myRpcLocator->findEntry(pEntryName);
if (!pEntry) {
cleanup(); // ignore the query
continue; // if entry not found
}
else plhQuery = pEntry->MailSlotLookup(
pGVinterface,
pIDobject
);
if (!plhQuery) {
cleanup(); // ignore the query
continue; // if nothing to report
}
}
WRITE_MAIL_SLOT MSReply(NetQuery.WkstaName, PMAILNAME_C);
char *pcBuffer = NetReply.Buffer;
/* we must take care of the possibility that an item is fetched but
cannot be marshalled because the buffer is full -- we have to
retry marshalling it in the next cycle
*/
CMailSlotReplyItem * pmsriNext = NULL;
pmsriNext = plhQuery->next();
while (pmsriNext) {
int BufferNotFull = TRUE;
dwBufferUsed = 0;
while (BufferNotFull && pmsriNext) {
/* NOTE: Compatibility Issue: The +END_FLAG_SIZE below reserves space for
the 4-byte 0 flag required by the old locator at the end of the buffer.
*/
DWORD dwBytesWritten =
pmsriNext->Marshall(
pcBuffer+dwBufferUsed,
NET_REPLY_BUFFER_SIZE-(dwBufferUsed+END_FLAG_SIZE)
);
if (dwBytesWritten) {
dwBufferUsed += dwBytesWritten;
delete pmsriNext;
pmsriNext = plhQuery->next(); // only if marshalling was successful
}
else BufferNotFull = FALSE;
}
/* NOTE: Compatibility Issue:
The old locator does not use the count of bytes read to decide when
to stop unmarshalling a mailslot reply -- instead it uses the 0 flag
after the last marshalled entry for that purpose, so for compatibility
reasons we must do the needful.
*/
memset(pcBuffer+dwBufferUsed,0,END_FLAG_SIZE);
MSReply.Write(
(char*) &NetReply,
dwBufferUsed + MAX_DOMAIN_NAME_LENGTH * sizeof(WCHAR) + END_FLAG_SIZE
);
}
cleanup();
}
}