2358 lines
68 KiB
C++
2358 lines
68 KiB
C++
/*++
|
||
|
||
Copyright (C) Microsoft Corporation, 1997 - 1999
|
||
|
||
Module Name:
|
||
|
||
mq.cxx
|
||
|
||
Abstract:
|
||
|
||
This is the code that actually makes Falcon API calls. It is
|
||
used by the Falcon/RPC transport (mqtrans.cxx)
|
||
|
||
Author:
|
||
|
||
Edward Reus (edwardr) 05-Jul-1997
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include <precomp.hxx>
|
||
#include <trans.hxx>
|
||
#include <dgtrans.hxx>
|
||
#include <wswrap.hxx>
|
||
#include <rpc.h>
|
||
#include <rpcdce.h>
|
||
#include <mqmgr.h>
|
||
#include "mqtrans.hxx"
|
||
|
||
static HINSTANCE g_hMqRt = 0;
|
||
FALCON_API *g_pMqApi = 0;
|
||
static CQueueMap *g_pQueueMap = 0;
|
||
|
||
char placeHolder[sizeof(MUTEX)]; // provide placeholder for the mutex
|
||
MUTEX *gp_csContext = (MUTEX *)placeHolder; // the mutex itself
|
||
|
||
#ifdef UNICODE
|
||
#define ptszVal pwszVal
|
||
#define VT_LPTSTR VT_LPWSTR
|
||
#else
|
||
#define ptszVal pszVal
|
||
#define VT_LPTSTR VT_LPSTR
|
||
#endif
|
||
|
||
static PCONTEXT_HANDLE g_pContext = 0;
|
||
|
||
// WARNING: the size and ordering of g_arszMqApis is dependent
|
||
// on the definition of the FALCON_API structure in mqtrans.hxx
|
||
// WARNING: When you are freeing Falcon allocated strings, do
|
||
// *not* use MQFreeMemory. Use MQFreeStringFromProperty. Otherwise
|
||
// you will break Win98 code.
|
||
|
||
const static char *g_arszMqApis[] = {
|
||
"MQLocateBegin",
|
||
"MQLocateNext",
|
||
"MQLocateEnd",
|
||
"MQInstanceToFormatName",
|
||
"MQOpenQueue",
|
||
"MQFreeMemory",
|
||
"MQSendMessage",
|
||
"MQReceiveMessage",
|
||
"MQCloseQueue",
|
||
"MQDeleteQueue",
|
||
"MQPathNameToFormatName",
|
||
"MQCreateQueue",
|
||
"MQGetMachineProperties",
|
||
"MQGetQueueProperties",
|
||
"MQSetQueueProperties",
|
||
NULL };
|
||
|
||
// Error mappings for MQ_MapStatusCode():
|
||
|
||
typedef struct _STATUS_MAPPING
|
||
{
|
||
HRESULT hr;
|
||
RPC_STATUS status;
|
||
} STATUS_MAPPING;
|
||
|
||
const static STATUS_MAPPING g_StatusMap[] =
|
||
{
|
||
{ MQ_OK, RPC_S_OK },
|
||
{ MQ_ERROR_IO_TIMEOUT, RPC_P_TIMEOUT },
|
||
{ MQ_ERROR_INSUFFICIENT_RESOURCES, RPC_S_OUT_OF_MEMORY },
|
||
{ MQ_ERROR_QUEUE_NOT_FOUND, RPC_S_SERVER_UNAVAILABLE },
|
||
{ MQ_ERROR, RPC_S_INTERNAL_ERROR },
|
||
{ MQMSG_CLASS_NACK_BAD_DST_Q, RPC_S_SERVER_UNAVAILABLE },
|
||
{ MQMSG_CLASS_NACK_PURGED, RPC_S_CALL_FAILED_DNE },
|
||
{ MQMSG_CLASS_NACK_REACH_QUEUE_TIMEOUT, RPC_S_CALL_FAILED_DNE },
|
||
{ MQMSG_CLASS_NACK_Q_EXCEED_QUOTA, RPC_S_CALL_FAILED_DNE },
|
||
{ MQMSG_CLASS_NACK_ACCESS_DENIED, RPC_S_CALL_FAILED_DNE },
|
||
{ MQMSG_CLASS_NACK_HOP_COUNT_EXCEEDED, RPC_S_CALL_FAILED_DNE },
|
||
{ MQMSG_CLASS_NACK_BAD_SIGNATURE, RPC_S_CALL_FAILED_DNE },
|
||
{ MQMSG_CLASS_NACK_BAD_ENCRYPTION, RPC_S_CALL_FAILED_DNE },
|
||
{ MQMSG_CLASS_NACK_COULD_NOT_ENCRYPT, RPC_S_CALL_FAILED_DNE },
|
||
{ MQMSG_CLASS_NACK_NOT_TRANSACTIONAL_Q, RPC_S_CALL_FAILED_DNE },
|
||
{ MQMSG_CLASS_NACK_NOT_TRANSACTIONAL_MSG,RPC_S_CALL_FAILED_DNE },
|
||
{ MQMSG_CLASS_NACK_Q_DELETED, RPC_S_CALL_FAILED_DNE },
|
||
{ MQMSG_CLASS_NACK_Q_PURGED, RPC_S_CALL_FAILED_DNE },
|
||
{ MQMSG_CLASS_NACK_RECEIVE_TIMEOUT, RPC_S_CALL_FAILED_DNE },
|
||
{ MQ_ERROR_ILLEGAL_QUEUE_PATHNAME, RPC_S_INVALID_ENDPOINT_FORMAT}
|
||
};
|
||
|
||
|
||
//----------------------------------------------------------------
|
||
// MQ_Initialize()
|
||
//
|
||
// Called by DG_TransportLoad() to initialize the ncadg_mq
|
||
// transport. This function does a dynamic load of the Falcon
|
||
// runtime DLL (MQRT.DLL) and creates a call table to access
|
||
// the Falcon API. If successful, return TRUE, else return
|
||
// FALSE.
|
||
//----------------------------------------------------------------
|
||
BOOL
|
||
MQ_Initialize()
|
||
{
|
||
BOOL fStatus = TRUE;
|
||
RPC_STATUS RpcStatus = RPC_S_OK;
|
||
|
||
// Make sure the function count is correct...
|
||
ASSERT( sizeof(g_arszMqApis)-sizeof(PVOID) == sizeof(FALCON_API) );
|
||
|
||
//
|
||
gp_csContext = new (gp_csContext) MUTEX(&RpcStatus);
|
||
if (RpcStatus != RPC_S_OK)
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
// Get the MSMQ runtime library:
|
||
g_hMqRt = LoadLibrary(TEXT("mqrt.dll"));
|
||
if (!g_hMqRt)
|
||
{
|
||
fStatus = FALSE;
|
||
}
|
||
|
||
// Build up the MSMQ call table that we will use to access
|
||
// the MSMQ C API:
|
||
if (fStatus)
|
||
{
|
||
g_pMqApi = (FALCON_API*)I_RpcAllocate(sizeof(FALCON_API));
|
||
if (!g_pMqApi)
|
||
{
|
||
fStatus = FALSE;
|
||
}
|
||
}
|
||
|
||
if (fStatus)
|
||
{
|
||
FARPROC *ppFn = (FARPROC*)g_pMqApi;
|
||
int i = 0;
|
||
|
||
while (g_arszMqApis[i])
|
||
{
|
||
*ppFn = GetProcAddress(g_hMqRt,g_arszMqApis[i++]);
|
||
if (!*ppFn)
|
||
{
|
||
fStatus = FALSE;
|
||
break;
|
||
}
|
||
|
||
ppFn++;
|
||
}
|
||
}
|
||
|
||
if (fStatus)
|
||
{
|
||
g_pQueueMap = new CQueueMap;
|
||
if (!g_pQueueMap)
|
||
{
|
||
fStatus = FALSE;
|
||
}
|
||
else
|
||
{
|
||
fStatus = g_pQueueMap->Initialize();
|
||
}
|
||
}
|
||
|
||
if (!fStatus)
|
||
{
|
||
gp_csContext->Free();
|
||
|
||
if (g_hMqRt)
|
||
{
|
||
FreeLibrary(g_hMqRt);
|
||
g_hMqRt = 0;
|
||
}
|
||
|
||
if (g_pQueueMap)
|
||
{
|
||
delete g_pQueueMap;
|
||
g_pQueueMap = 0;
|
||
}
|
||
|
||
if (g_pMqApi)
|
||
{
|
||
I_RpcFree(g_pMqApi);
|
||
g_pMqApi = 0;
|
||
}
|
||
}
|
||
|
||
return fStatus;
|
||
}
|
||
|
||
//----------------------------------------------------------------
|
||
// MQ_MapStatusCode()
|
||
//
|
||
// Convert a MQ HRESULT status to a RPC_STATUS code.
|
||
//----------------------------------------------------------------
|
||
RPC_STATUS MQ_MapStatusCode( HRESULT hr, RPC_STATUS defaultStatus )
|
||
{
|
||
int iSize = sizeof(g_StatusMap)/sizeof(STATUS_MAPPING);
|
||
RPC_STATUS status = defaultStatus;
|
||
|
||
for (int i=0; i<iSize; i++)
|
||
{
|
||
if (hr == g_StatusMap[i].hr)
|
||
{
|
||
status = g_StatusMap[i].status;
|
||
break;
|
||
}
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
//----------------------------------------------------------------
|
||
// MQ_InitOptions()
|
||
//
|
||
// Initialize transport specific binding handle options structure.
|
||
//----------------------------------------------------------------
|
||
RPC_STATUS RPC_ENTRY MQ_InitOptions( IN void PAPI *pvTransportOptions )
|
||
{
|
||
RPC_STATUS status = RPC_S_OK;
|
||
MQ_OPTIONS *pOpts = (MQ_OPTIONS*)pvTransportOptions;
|
||
|
||
if (pOpts)
|
||
{
|
||
memset(pOpts,0,sizeof(MQ_OPTIONS));
|
||
pOpts->ulPriority = DEFAULT_PRIORITY;
|
||
pOpts->ulTimeToReachQueue = INFINITE;
|
||
pOpts->ulTimeToReceive = INFINITE;
|
||
}
|
||
else
|
||
{
|
||
status = RPC_S_OUT_OF_MEMORY;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------
|
||
// MQ_SetOption()
|
||
//
|
||
// Set transport specific binding handle options.
|
||
//----------------------------------------------------------------
|
||
RPC_STATUS RPC_ENTRY MQ_SetOption( IN void PAPI *pvTransportOptions,
|
||
IN unsigned long option,
|
||
IN ULONG_PTR optionValue )
|
||
{
|
||
RPC_STATUS status = RPC_S_OK;
|
||
MQ_OPTIONS *pOpts = (MQ_OPTIONS*)pvTransportOptions;
|
||
|
||
switch (option)
|
||
{
|
||
case RPC_C_OPT_MQ_DELIVERY:
|
||
if ( (optionValue == RPC_C_MQ_EXPRESS)
|
||
|| (optionValue == RPC_C_MQ_RECOVERABLE) )
|
||
pOpts->ulDelivery = (unsigned long) optionValue;
|
||
else
|
||
status = RPC_S_INVALID_ARG;
|
||
break;
|
||
|
||
case RPC_C_OPT_MQ_PRIORITY:
|
||
if (optionValue <= MQ_MAX_PRIORITY)
|
||
pOpts->ulPriority = (unsigned long) optionValue;
|
||
else
|
||
pOpts->ulPriority = MQ_MAX_PRIORITY;
|
||
break;
|
||
|
||
case RPC_C_OPT_MQ_JOURNAL:
|
||
if ( (optionValue == RPC_C_MQ_JOURNAL_NONE)
|
||
|| (optionValue == RPC_C_MQ_JOURNAL_ALWAYS)
|
||
|| (optionValue == RPC_C_MQ_JOURNAL_DEADLETTER) )
|
||
pOpts->ulJournaling = (unsigned long) optionValue;
|
||
else
|
||
status = RPC_S_INVALID_ARG;
|
||
break;
|
||
|
||
case RPC_C_OPT_MQ_TIME_TO_REACH_QUEUE:
|
||
pOpts->ulTimeToReachQueue = (unsigned long) optionValue;
|
||
break;
|
||
|
||
case RPC_C_OPT_MQ_TIME_TO_BE_RECEIVED:
|
||
pOpts->ulTimeToReceive = (unsigned long) optionValue;
|
||
break;
|
||
|
||
case RPC_C_OPT_MQ_ACKNOWLEDGE:
|
||
pOpts->fAck = (optionValue != FALSE);
|
||
break;
|
||
|
||
case RPC_C_OPT_MQ_AUTHN_SERVICE:
|
||
if (optionValue == RPC_C_AUTHN_MQ)
|
||
{
|
||
status = RPC_S_OK;
|
||
}
|
||
else if (optionValue == RPC_C_AUTHN_NONE)
|
||
{
|
||
pOpts->fAuthenticate = FALSE;
|
||
pOpts->fEncrypt = FALSE;
|
||
status = RPC_S_OK;
|
||
}
|
||
else
|
||
{
|
||
status = RPC_S_UNKNOWN_AUTHN_SERVICE;
|
||
}
|
||
break;
|
||
|
||
case RPC_C_OPT_MQ_AUTHN_LEVEL:
|
||
if (optionValue > RPC_C_AUTHN_LEVEL_NONE)
|
||
{
|
||
pOpts->fAuthenticate = TRUE;
|
||
pOpts->fEncrypt = (optionValue == RPC_C_AUTHN_LEVEL_PKT_PRIVACY)? TRUE : FALSE;
|
||
}
|
||
else
|
||
{
|
||
pOpts->fAuthenticate = FALSE;
|
||
pOpts->fEncrypt = FALSE;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
status = RPC_S_CANNOT_SUPPORT;
|
||
break;
|
||
}
|
||
|
||
// The following is some code to make sure the RPC_C_xxx
|
||
// constants always have the correct values:
|
||
#if ( (RPC_C_MQ_EXPRESS != MQMSG_DELIVERY_EXPRESS) \
|
||
|| (RPC_C_MQ_RECOVERABLE != MQMSG_DELIVERY_RECOVERABLE) )
|
||
#error "RPC constants wrong"
|
||
#endif
|
||
|
||
#if ( (RPC_C_MQ_JOURNAL_NONE != MQMSG_JOURNAL_NONE) \
|
||
|| (RPC_C_MQ_JOURNAL_ALWAYS != MQMSG_JOURNAL) \
|
||
|| (RPC_C_MQ_JOURNAL_DEADLETTER != MQMSG_DEADLETTER) )
|
||
#error "RPC constants wrong"
|
||
#endif
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------
|
||
// MQ_InqOption()
|
||
//
|
||
// Get transport specific binding handle options.
|
||
//----------------------------------------------------------------
|
||
RPC_STATUS RPC_ENTRY MQ_InqOption( IN void PAPI *pvTransportOptions,
|
||
IN unsigned long option,
|
||
OUT ULONG_PTR *pOptionValue )
|
||
{
|
||
RPC_STATUS status = RPC_S_OK;
|
||
MQ_OPTIONS *pOpts = (MQ_OPTIONS*)pvTransportOptions;
|
||
|
||
switch (option)
|
||
{
|
||
case RPC_C_OPT_MQ_DELIVERY:
|
||
*pOptionValue = pOpts->ulDelivery;
|
||
break;
|
||
|
||
case RPC_C_OPT_MQ_PRIORITY:
|
||
*pOptionValue = pOpts->ulPriority;
|
||
break;
|
||
|
||
case RPC_C_OPT_MQ_JOURNAL:
|
||
*pOptionValue = pOpts->ulJournaling;
|
||
break;
|
||
|
||
case RPC_C_OPT_MQ_TIME_TO_REACH_QUEUE:
|
||
*pOptionValue = pOpts->ulTimeToReachQueue;
|
||
break;
|
||
|
||
case RPC_C_OPT_MQ_TIME_TO_BE_RECEIVED:
|
||
*pOptionValue = pOpts->ulTimeToReceive;
|
||
break;
|
||
|
||
case RPC_C_OPT_MQ_ACKNOWLEDGE:
|
||
*pOptionValue = pOpts->fAck;
|
||
break;
|
||
|
||
default:
|
||
status = RPC_S_INVALID_ARG;
|
||
*pOptionValue = 0;
|
||
break;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
//----------------------------------------------------------------
|
||
// MQ_ImplementOptions()
|
||
//
|
||
// Apply transport specific binding handle options to the
|
||
// specified server.
|
||
//----------------------------------------------------------------
|
||
RPC_STATUS RPC_ENTRY MQ_ImplementOptions(
|
||
IN DG_TRANSPORT_ENDPOINT pvTransEndpoint,
|
||
IN void *pvTransportOptions )
|
||
{
|
||
RPC_STATUS Status = RPC_S_OK;
|
||
HRESULT hr;
|
||
MQ_OPTIONS *pOpts = (MQ_OPTIONS*)pvTransportOptions;
|
||
MQ_DATAGRAM_ENDPOINT *pEndpoint = (MQ_DATAGRAM_ENDPOINT*)pvTransEndpoint;
|
||
|
||
pEndpoint->fAck = pOpts->fAck;
|
||
pEndpoint->ulDelivery = pOpts->ulDelivery;
|
||
pEndpoint->ulPriority = pOpts->ulPriority;
|
||
pEndpoint->ulJournaling = pOpts->ulJournaling;
|
||
pEndpoint->ulTimeToReachQueue = pOpts->ulTimeToReachQueue;
|
||
pEndpoint->ulTimeToReceive = pOpts->ulTimeToReceive;
|
||
pEndpoint->fAuthenticate = pOpts->fAuthenticate;
|
||
pEndpoint->fEncrypt = pOpts->fEncrypt;
|
||
|
||
//
|
||
// If the fAck flag is set, then we want to get an acknowledgement
|
||
// for each call (message) as it gets to the destination (server)
|
||
// queue. So, setup an admin queue to receive Falcon ACK messages.
|
||
//
|
||
if ( (pEndpoint->fAck) && (pEndpoint->hAdminQueue == 0) )
|
||
{
|
||
hr = SetupAdminQueue(pEndpoint);
|
||
Status = MQ_MapStatusCode(hr,RPC_S_INTERNAL_ERROR);
|
||
|
||
if (Status == RPC_S_OK)
|
||
{
|
||
MQ_RegisterQueueToDelete(pEndpoint->wsAdminQFormat,pEndpoint->wsMachine);
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
//----------------------------------------------------------------
|
||
// MQ_BuildAddressVector()
|
||
//
|
||
//----------------------------------------------------------------
|
||
RPC_STATUS MQ_BuildAddressVector( OUT NETWORK_ADDRESS_VECTOR **ppVector )
|
||
{
|
||
DWORD dwSize;
|
||
RPC_CHAR wsMachine[MAX_COMPUTERNAME_LEN];
|
||
NETWORK_ADDRESS_VECTOR *pVector;
|
||
|
||
|
||
dwSize = sizeof(wsMachine)/sizeof(RPC_CHAR);
|
||
GetComputerName((RPC_SCHAR *)wsMachine,&dwSize);
|
||
|
||
*ppVector = 0;
|
||
|
||
pVector = new( sizeof(RPC_CHAR*)
|
||
+ sizeof(RPC_CHAR)*(1+RpcpStringLength(wsMachine)) )
|
||
NETWORK_ADDRESS_VECTOR;
|
||
|
||
if (!pVector)
|
||
{
|
||
return RPC_S_OUT_OF_MEMORY;
|
||
}
|
||
|
||
pVector->Count = 1;
|
||
pVector->NetworkAddresses[0] = (RPC_CHAR*)(&pVector->NetworkAddresses[1]);
|
||
RpcpStringCopy(pVector->NetworkAddresses[0],wsMachine);
|
||
|
||
*ppVector = pVector;
|
||
|
||
return RPC_S_OK;
|
||
}
|
||
|
||
//----------------------------------------------------------------
|
||
// MQ_FillInAddress()
|
||
//
|
||
//----------------------------------------------------------------
|
||
RPC_STATUS MQ_FillInAddress( MQ_ADDRESS *pAddress,
|
||
MQPROPVARIANT *pMsgProps )
|
||
{
|
||
pAddress->fAuthenticated = pMsgProps[I_AUTHENTICATED].bVal;
|
||
pAddress->ulPrivacyLevel = pMsgProps[I_PRIVACY_LEVEL].ulVal;
|
||
|
||
ParseQueuePathName( (RPC_CHAR *)pMsgProps[I_MESSAGE_LABEL].ptszVal,
|
||
pAddress->wsMachine,
|
||
pAddress->wsQName );
|
||
|
||
return RPC_S_OK;
|
||
}
|
||
|
||
#ifdef TRANSPORT_DLL
|
||
//----------------------------------------------------------------
|
||
// MIDL_user_allocate()
|
||
//
|
||
// Used by Mq_RegisterQueueToDelete().
|
||
//----------------------------------------------------------------
|
||
void __RPC_FAR * __RPC_USER MIDL_user_allocate( size_t len )
|
||
{
|
||
return (I_RpcAllocate(len));
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------
|
||
// MIDL_user_free()
|
||
//
|
||
// Used by Mq_RegisterQueueToDelete().
|
||
//----------------------------------------------------------------
|
||
void __RPC_USER MIDL_user_free( void __RPC_FAR *ptr )
|
||
{
|
||
I_RpcFree(ptr);
|
||
}
|
||
#endif
|
||
|
||
|
||
//----------------------------------------------------------------
|
||
// MQ_RegisterQueueToDelete()
|
||
//
|
||
//----------------------------------------------------------------
|
||
RPC_STATUS MQ_RegisterQueueToDelete( RPC_CHAR *pwsQFormat,
|
||
RPC_CHAR *pwsMachine )
|
||
{
|
||
RPC_STATUS Status;
|
||
RPC_CHAR *pwsStringBinding;
|
||
RPC_BINDING_HANDLE hBinding = 0;
|
||
|
||
ASSERT(pwsQFormat);
|
||
|
||
gp_csContext->Request();
|
||
|
||
// This is a long critical section... but only for the first call.
|
||
|
||
if (!g_pContext)
|
||
{
|
||
Status = RpcStringBindingCompose( NULL,
|
||
Q_SVC_PROTSEQ,
|
||
pwsMachine,
|
||
Q_SVC_ENDPOINT,
|
||
NULL,
|
||
&pwsStringBinding );
|
||
if (RPC_S_OK == Status)
|
||
{
|
||
Status = RpcBindingFromStringBinding( pwsStringBinding,
|
||
&hBinding );
|
||
if (RPC_S_OK != Status)
|
||
{
|
||
gp_csContext->Clear();
|
||
return Status;
|
||
}
|
||
|
||
RpcStringFree(&pwsStringBinding);
|
||
|
||
RpcTryExcept
|
||
{
|
||
Status = MqGetContext(hBinding,&g_pContext);
|
||
Status = RpcBindingFree(&hBinding);
|
||
}
|
||
RpcExcept(I_RpcExceptionFilter(RpcExceptionCode()))
|
||
{
|
||
Status = RpcExceptionCode();
|
||
}
|
||
RpcEndExcept
|
||
}
|
||
}
|
||
|
||
gp_csContext->Clear();
|
||
|
||
if (g_pContext)
|
||
{
|
||
Status = MqRegisterQueue(g_pContext,pwsQFormat);
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------
|
||
// ConstructQueuePathName()
|
||
//
|
||
// Return Value: TRUE on success.
|
||
// FALSE on fail (Path Name buffer too small).
|
||
//----------------------------------------------------------------
|
||
BOOL ConstructQueuePathName( IN RPC_CHAR *pwsMachine,
|
||
IN RPC_CHAR *pwsQName,
|
||
OUT RPC_CHAR *pwsPathName,
|
||
IN OUT DWORD *pdwSize )
|
||
{
|
||
BOOL status = TRUE;
|
||
DWORD len = sizeof(RPC_CHAR) * (1 + RpcpStringLength(pwsMachine)
|
||
+ RpcpStringLength(WS_SEPARATOR)
|
||
+ RpcpStringLength(pwsQName) );
|
||
|
||
if (*pdwSize < len)
|
||
{
|
||
status = FALSE;
|
||
}
|
||
else
|
||
{
|
||
RpcpStringCopy(pwsPathName,pwsMachine);
|
||
RpcpStringCat(pwsPathName,WS_SEPARATOR);
|
||
RpcpStringCat(pwsPathName,pwsQName);
|
||
}
|
||
|
||
*pdwSize = len;
|
||
return status;
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------
|
||
// ConstructPrivateQueuePathName()
|
||
//
|
||
// Return Value: TRUE on success.
|
||
// FALSE on fail (Path Name buffer too small).
|
||
//----------------------------------------------------------------
|
||
BOOL ConstructPrivateQueuePathName( IN RPC_CHAR *pwsMachine,
|
||
IN RPC_CHAR *pwsQName,
|
||
OUT RPC_CHAR *pwsPathName,
|
||
IN OUT DWORD *pdwSize )
|
||
{
|
||
BOOL status = TRUE;
|
||
DWORD dwSize = sizeof(RPC_CHAR) * (1 + RpcpStringLength(pwsMachine)
|
||
+ RpcpStringLength(WS_PRIVATE_DOLLAR)
|
||
+ RpcpStringLength(pwsQName) );
|
||
|
||
if (*pdwSize < dwSize)
|
||
{
|
||
status = FALSE;
|
||
}
|
||
else
|
||
{
|
||
RpcpStringCopy(pwsPathName,pwsMachine);
|
||
RpcpStringCat(pwsPathName,WS_PRIVATE_DOLLAR);
|
||
RpcpStringCat(pwsPathName,pwsQName);
|
||
}
|
||
|
||
*pdwSize = dwSize;
|
||
return status;
|
||
}
|
||
|
||
#ifdef USE_PRIVATE_QUEUES
|
||
//----------------------------------------------------------------
|
||
// ConstructPrivateDirectFormat()
|
||
//
|
||
// Return Value: TRUE on success.
|
||
// FALSE on fail (Path Name buffer too small).
|
||
//----------------------------------------------------------------
|
||
BOOL ConstructPrivateDirectFormat( IN RPC_CHAR *pwsMachine,
|
||
IN RPC_CHAR *pwsQName,
|
||
OUT RPC_CHAR *pwsPathName,
|
||
IN OUT DWORD *pdwSize )
|
||
{
|
||
BOOL status = TRUE;
|
||
DWORD dwSize = sizeof(RPC_CHAR) * (1 + RpcpStringLength(WS_DIRECT),
|
||
+ RpcpStringLength(pwsMachine)
|
||
+ RpcpStringLength(WS_PRIVATE_DOLLAR)
|
||
+ RpcpStringLength(pwsQName) );
|
||
|
||
if (*pdwSize < dwSize)
|
||
{
|
||
status = FALSE;
|
||
}
|
||
else
|
||
{
|
||
RpcpStringCopy(pwsPathName,WS_DIRECT);
|
||
RpcpStringCat(pwsPathName,pwsMachine);
|
||
RpcpStringCat(pwsPathName,WS_PRIVATE_DOLLAR);
|
||
RpcpStringCat(pwsPathName,pwsQName);
|
||
}
|
||
|
||
*pdwSize = dwSize;
|
||
return status;
|
||
}
|
||
#endif
|
||
|
||
#if FALSE
|
||
//----------------------------------------------------------------
|
||
// ConstructDirectFormat()
|
||
//
|
||
// Return Value: TRUE on success.
|
||
// FALSE on fail (Path Name buffer too small).
|
||
//----------------------------------------------------------------
|
||
BOOL ConstructDirectFormat( IN RPC_CHAR *pwsMachine,
|
||
IN RPC_CHAR *pwsQName,
|
||
OUT RPC_CHAR *pwsPathName,
|
||
IN OUT DWORD *pdwSize )
|
||
{
|
||
BOOL status = TRUE;
|
||
DWORD dwSize = sizeof(RPC_CHAR) * (1 + RpcpStringLength(WS_DIRECT)
|
||
+ RpcpStringLength(pwsMachine)
|
||
+ RpcpStringLength(WS_SEPARATOR)
|
||
+ RpcpStringLength(pwsQName) );
|
||
|
||
if (*pdwSize < dwSize)
|
||
{
|
||
status = FALSE;
|
||
}
|
||
else
|
||
{
|
||
RpcpStringCopy(pwsPathName,WS_DIRECT);
|
||
RpcpStringCat(pwsPathName,pwsMachine);
|
||
RpcpStringCat(pwsPathName,WS_SEPARATOR);
|
||
RpcpStringCat(pwsPathName,pwsQName);
|
||
}
|
||
|
||
*pdwSize = dwSize;
|
||
return status;
|
||
}
|
||
#endif
|
||
|
||
//----------------------------------------------------------------
|
||
// ParseQueuePathName()
|
||
//
|
||
// For RPC's use of MQ, a queue path name is of the form:
|
||
// "machine_name\queue_name" or "machine\PRIVATE$\queue_name".
|
||
// This routine extracts the machine name and queue name from
|
||
// a given queue path name.
|
||
//
|
||
// Return Value: TRUE on success.
|
||
// FALSE on fail (Can't find the "\" separator).
|
||
//----------------------------------------------------------------
|
||
BOOL ParseQueuePathName(
|
||
IN RPC_CHAR *pwsPathName,
|
||
OUT RPC_CHAR wsMachineName[MAX_COMPUTERNAME_LEN],
|
||
OUT RPC_CHAR wsQueueName[MQ_MAX_Q_NAME_LEN] )
|
||
{
|
||
BOOL status = TRUE;
|
||
RPC_CHAR *pSlash;
|
||
|
||
pSlash = (RPC_CHAR *)RpcpCharacter(pwsPathName,*(WS_SEPARATOR));
|
||
|
||
if (pSlash)
|
||
{
|
||
*pSlash = (RPC_CHAR)0;
|
||
RpcpStringCopy(wsMachineName,pwsPathName);
|
||
*pSlash = *(WS_SEPARATOR);
|
||
}
|
||
else
|
||
status = FALSE;
|
||
|
||
if (status)
|
||
{
|
||
pSlash = (RPC_CHAR *)RpcpCharacter(pwsPathName,*(WS_SEPARATOR));
|
||
|
||
if (pSlash)
|
||
{
|
||
RpcpStringCopy(wsQueueName,++pSlash);
|
||
}
|
||
else
|
||
status = FALSE;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
//--------------------------------------------------------------------
|
||
// LocateQueueViaQName()
|
||
//
|
||
// Try to find a MQ queue of type specified by the Queue UUID (the
|
||
// queue type) with the specified queue name. The first one that is
|
||
// found (if any) is returned.
|
||
//--------------------------------------------------------------------
|
||
HRESULT LocateQueueViaQName( IN OUT MQ_ADDRESS *pAddress )
|
||
{
|
||
HRESULT hr = MQ_OK;
|
||
HANDLE hEnum;
|
||
int iSize;
|
||
DWORD dwSize;
|
||
DWORD cProps;
|
||
DWORD cQueue;
|
||
UUID QUuid;
|
||
QUEUEPROPID aqPropId[MAX_VAR];
|
||
MQPROPVARIANT aPropVar[MAX_VAR];
|
||
MQPROPERTYRESTRICTION aPropRestrict[MAX_VAR];
|
||
MQRESTRICTION restrict;
|
||
MQCOLUMNSET column;
|
||
RPC_CHAR wsMachine[MAX_COMPUTERNAME_LEN];
|
||
RPC_CHAR wsQName[MQ_MAX_Q_NAME_LEN];
|
||
|
||
if (RPC_S_OK != UuidFromString(SVR_QTYPE_UUID_STR,&QUuid))
|
||
{
|
||
return MQ_ERROR;
|
||
}
|
||
|
||
// Set up the restriction properties such that we will
|
||
// only find our queue (of type pQUuid):
|
||
|
||
cProps = 0;
|
||
aPropRestrict[cProps].rel = PREQ;
|
||
aPropRestrict[cProps].prop = PROPID_Q_TYPE;
|
||
aPropRestrict[cProps].prval.vt = VT_CLSID;
|
||
aPropRestrict[cProps].prval.puuid = &QUuid;
|
||
cProps++;
|
||
|
||
ASSERT(cProps < MAX_VAR);
|
||
|
||
restrict.cRes = cProps;
|
||
restrict.paPropRes = aPropRestrict;
|
||
|
||
cProps = 0;
|
||
aqPropId[cProps++] = PROPID_Q_INSTANCE;
|
||
aqPropId[cProps++] = PROPID_Q_PATHNAME;
|
||
|
||
ASSERT(cProps < MAX_VAR);
|
||
|
||
column.cCol = cProps;
|
||
column.aCol = aqPropId;
|
||
|
||
// Ok, do a locate enumeration:
|
||
|
||
hr = MQLocateBegin(NULL,&restrict,&column,NULL,&hEnum);
|
||
if (FAILED(hr))
|
||
{
|
||
return hr;
|
||
}
|
||
|
||
cQueue = cProps;
|
||
while (cQueue > 0)
|
||
{
|
||
hr = MQLocateNext( hEnum, &cQueue, aPropVar );
|
||
if (FAILED(hr))
|
||
{
|
||
MQLocateEnd(hEnum);
|
||
return hr;
|
||
}
|
||
|
||
if (cQueue > 0)
|
||
{
|
||
// Now extract the queue name from the path name:
|
||
if (ParseQueuePathName((RPC_CHAR *)(aPropVar[1].ptszVal),wsMachine,wsQName))
|
||
{
|
||
if (!RpcpStringCompare(pAddress->wsQName,wsQName))
|
||
{
|
||
// We have a match! Ok, get the format name,
|
||
// cleanup then return...
|
||
|
||
// Transform the queue instance UUID into a
|
||
// format name:
|
||
dwSize = sizeof(pAddress->wsQFormat);
|
||
hr = MQInstanceToFormatName( aPropVar[0].puuid, pAddress->wsQFormat, &dwSize);
|
||
if (FAILED(hr))
|
||
{
|
||
break;
|
||
}
|
||
|
||
// Free memory allocated by MQLocateNext():
|
||
MQFreeMemory(aPropVar[0].puuid); // From: PROPID_Q_INSTANCE
|
||
MQFreeStringFromProperty(&aPropVar[1]); // From: PROPID_Q_PATHNAME
|
||
|
||
// Machine name:
|
||
RpcpStringCopy(pAddress->wsMachine,wsMachine);
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Free memory allocated by MQLocateNext():
|
||
MQFreeMemory(aPropVar[0].puuid); // From: PROPID_Q_INSTANCE
|
||
MQFreeStringFromProperty(&aPropVar[1]); // From: PROPID_Q_PATHNAME
|
||
}
|
||
}
|
||
|
||
|
||
MQLocateEnd(hEnum);
|
||
|
||
if (cQueue == 0)
|
||
{
|
||
return MQ_ERROR_QUEUE_NOT_FOUND;
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------
|
||
// CreateQueue()
|
||
//
|
||
// Create a MQ queue of the specified path name.
|
||
//
|
||
// Return Value: MQ HRESULT value.
|
||
//
|
||
//----------------------------------------------------------------
|
||
HRESULT CreateQueue( IN SECURITY_DESCRIPTOR *pSecurityDescriptor,
|
||
IN UUID *pQueueUuid,
|
||
IN RPC_CHAR *pwsPathName,
|
||
IN RPC_CHAR *pwsQueueLabel,
|
||
IN ULONG ulQueueFlags,
|
||
OUT RPC_CHAR *pwsFormat,
|
||
IN OUT DWORD *pdwFormatSize )
|
||
{
|
||
HRESULT hr;
|
||
DWORD cProps;
|
||
DWORD dwSize;
|
||
MQQUEUEPROPS qProps;
|
||
MQPROPVARIANT aPropVar[MAX_VAR];
|
||
QUEUEPROPID aqPropId[MAX_VAR];
|
||
|
||
|
||
//
|
||
// Setup properties to create a queue on this machine:
|
||
//
|
||
cProps = 0;
|
||
|
||
// Set the PathName:
|
||
aqPropId[cProps] = PROPID_Q_PATHNAME;
|
||
aPropVar[cProps].vt = VT_LPTSTR;
|
||
aPropVar[cProps].ptszVal = (RPC_SCHAR *)pwsPathName;
|
||
cProps++;
|
||
|
||
// Set the type of the queue (this is a UUID).
|
||
// This can be used to locate RPC specific queues.
|
||
if (pQueueUuid)
|
||
{
|
||
aqPropId[cProps] = PROPID_Q_TYPE;
|
||
aPropVar[cProps].vt = VT_CLSID;
|
||
aPropVar[cProps].puuid = pQueueUuid;
|
||
cProps++;
|
||
}
|
||
|
||
// Do we want to force authentication of messages
|
||
// on the queue?
|
||
if (ulQueueFlags & RPC_C_MQ_AUTHN_LEVEL_PKT_INTEGRITY)
|
||
{
|
||
aqPropId[cProps] = PROPID_Q_AUTHENTICATE;
|
||
aPropVar[cProps].vt = VT_UI1;
|
||
aPropVar[cProps].bVal = TRUE;
|
||
cProps++;
|
||
}
|
||
|
||
// Do we want to force encrypted messages?
|
||
if (ulQueueFlags & RPC_C_MQ_AUTHN_LEVEL_PKT_PRIVACY)
|
||
{
|
||
aqPropId[cProps] = PROPID_Q_PRIV_LEVEL;
|
||
aPropVar[cProps].vt = VT_UI4;
|
||
aPropVar[cProps].ulVal = MQ_PRIV_LEVEL_BODY;
|
||
cProps++;
|
||
}
|
||
|
||
// Put a description to the queue.
|
||
// Useful for administration purposes (through the MSMQ admin tools).
|
||
aqPropId[cProps] = PROPID_Q_LABEL;
|
||
aPropVar[cProps].vt = VT_LPTSTR;
|
||
aPropVar[cProps].ptszVal = (RPC_SCHAR *)pwsQueueLabel;
|
||
cProps++;
|
||
|
||
ASSERT(cProps < MAX_VAR);
|
||
|
||
// Assemble the QUEUEPROPS structure:
|
||
qProps.cProp = cProps;
|
||
qProps.aPropID = aqPropId;
|
||
qProps.aPropVar = aPropVar;
|
||
qProps.aStatus = 0;
|
||
|
||
//-------------------------------------------------------
|
||
// Create the queue
|
||
hr = MQCreateQueue( pSecurityDescriptor,// Queue permissions.
|
||
&qProps, // Queue properties.
|
||
pwsFormat, // Format Name [out].
|
||
pdwFormatSize ); // Size of Format Name [in,out].
|
||
|
||
return hr;
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------
|
||
// ConnectToServerQueue()
|
||
//
|
||
//----------------------------------------------------------------
|
||
RPC_STATUS ConnectToServerQueue( MQ_ADDRESS *pAddress,
|
||
RPC_CHAR *pNetworkAddress,
|
||
RPC_CHAR *pEndpoint )
|
||
{
|
||
HRESULT hr;
|
||
DWORD dwSize;
|
||
RPC_CHAR wsQPathName[MAX_PATHNAME_LEN];
|
||
|
||
|
||
//
|
||
// First, check the end point:
|
||
//
|
||
if ( (pEndpoint == NULL)
|
||
|| (*pEndpoint == '\0')
|
||
|| (RpcpStringLength(pEndpoint) >= MQ_MAX_Q_NAME_LEN) )
|
||
{
|
||
return RPC_S_INVALID_ENDPOINT_FORMAT;
|
||
}
|
||
|
||
memset(pAddress,0,sizeof(MQ_ADDRESS));
|
||
|
||
RpcpStringCopy(pAddress->wsQName,pEndpoint);
|
||
|
||
|
||
//
|
||
// Now, if the server was specified, then use it as is,
|
||
// otherwise use the local machine name:
|
||
//
|
||
if ( (pNetworkAddress == NULL) || (*pNetworkAddress == '\0') )
|
||
{
|
||
dwSize = sizeof(pAddress->wsMachine);
|
||
GetComputerName((RPC_SCHAR *)pAddress->wsMachine,&dwSize);
|
||
}
|
||
else if (RpcpStringLength(pNetworkAddress) >= MAX_COMPUTERNAME_LEN)
|
||
{
|
||
return RPC_S_INVALID_ENDPOINT_FORMAT;
|
||
}
|
||
else
|
||
{
|
||
RpcpStringCopy(pAddress->wsMachine,pNetworkAddress);
|
||
}
|
||
|
||
|
||
//
|
||
// If the server name is a "*", then locate a server (andy) that
|
||
// has the specified queue name:
|
||
//
|
||
if (!RpcpStringCompare(pAddress->wsMachine,WS_ASTRISK))
|
||
{
|
||
hr = LocateQueueViaQName(pAddress);
|
||
if (FAILED(hr))
|
||
{
|
||
return RPC_S_SERVER_UNAVAILABLE;
|
||
}
|
||
}
|
||
|
||
#if FALSE
|
||
//
|
||
// Try to use a direct format to get to the server queue:
|
||
//
|
||
dwSize = sizeof(pAddress->wsQFormat);
|
||
if (!ConstructDirectFormat( pAddress->wsMachine,
|
||
pAddress->wsQName,
|
||
pAddress->wsQFormat,
|
||
&dwSize))
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "ConnectToServerQueue(): ConstructDirectFormat() failed.\n"));
|
||
|
||
return RPC_S_SERVER_UNAVAILABLE;
|
||
}
|
||
|
||
|
||
hr = MQOpenQueue( pAddress->wsQFormat,
|
||
MQ_SEND_ACCESS, 0, &(pAddress->hQueue) );
|
||
|
||
if (!FAILED(hr))
|
||
{
|
||
return RPC_S_OK;
|
||
}
|
||
#endif
|
||
|
||
|
||
//
|
||
// If we get here, then the direct format failed, so try using
|
||
// a lookup (MQPathNameToFormatName()):
|
||
//
|
||
dwSize = sizeof(wsQPathName);
|
||
if (!ConstructQueuePathName( pAddress->wsMachine,
|
||
pAddress->wsQName,
|
||
wsQPathName,
|
||
&dwSize ))
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "ConnectToServerQueue(): ConstructQueuePathName() failed.\n"));
|
||
|
||
return RPC_S_SERVER_UNAVAILABLE;
|
||
}
|
||
|
||
dwSize = sizeof(pAddress->wsQFormat);
|
||
hr = MQPathNameToFormatName( wsQPathName,
|
||
pAddress->wsQFormat,
|
||
&dwSize );
|
||
if (FAILED(hr))
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL, RPCTRANS "ConnectToServerQueue(): MQPathNameToFormatName() failed: 0x%x\n",
|
||
hr));
|
||
|
||
return RPC_S_SERVER_UNAVAILABLE;
|
||
}
|
||
|
||
|
||
|
||
hr = MQOpenQueue( pAddress->wsQFormat,
|
||
MQ_SEND_ACCESS, 0, &(pAddress->hQueue) );
|
||
|
||
if (FAILED(hr))
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "ConnectToServerQueue(): MQOpenQueue() failed: 0x%x\n",
|
||
hr));
|
||
|
||
return RPC_S_SERVER_UNAVAILABLE;
|
||
}
|
||
|
||
return RPC_S_OK;
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------
|
||
// DisconnectFromServer()
|
||
//
|
||
//----------------------------------------------------------------
|
||
RPC_STATUS DisconnectFromServer( IN OUT MQ_ADDRESS *pAddress )
|
||
{
|
||
DWORD Status = NO_ERROR;
|
||
|
||
if ((pAddress) && (pAddress->hQueue))
|
||
{
|
||
MQCloseQueue(pAddress->hQueue);
|
||
pAddress->hQueue = 0;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
//----------------------------------------------------------------
|
||
// SetQueueProperties()
|
||
//
|
||
// Set the properties for an already existing queue. Currently
|
||
// the only two Falcon queue properties that need to be set are
|
||
// for forcing message authentication and encryption.
|
||
//
|
||
// Return Value: MQ HRESULT value.
|
||
//
|
||
//----------------------------------------------------------------
|
||
HRESULT SetQueueProperties( IN RPC_CHAR *pwsQFormat,
|
||
IN ULONG ulQueueFlags )
|
||
{
|
||
HRESULT hr = MQ_OK;
|
||
DWORD cProps = 0;
|
||
DWORD dwSize;
|
||
MQQUEUEPROPS qProps;
|
||
MQPROPVARIANT aPropVar[MAX_VAR];
|
||
QUEUEPROPID aqPropId[MAX_VAR];
|
||
HRESULT aStatus[MAX_VAR];
|
||
|
||
// Do we want to force authentication of messages
|
||
// on the queue?
|
||
if (ulQueueFlags & RPC_C_MQ_AUTHN_LEVEL_PKT_INTEGRITY)
|
||
{
|
||
aqPropId[cProps] = PROPID_Q_AUTHENTICATE;
|
||
aPropVar[cProps].vt = VT_UI1;
|
||
aPropVar[cProps].bVal = TRUE;
|
||
cProps++;
|
||
}
|
||
else
|
||
{
|
||
aqPropId[cProps] = PROPID_Q_AUTHENTICATE;
|
||
aPropVar[cProps].vt = VT_UI1;
|
||
aPropVar[cProps].bVal = FALSE;
|
||
cProps++;
|
||
}
|
||
|
||
// Do we want to force encrypted messages?
|
||
if (ulQueueFlags & RPC_C_MQ_AUTHN_LEVEL_PKT_PRIVACY)
|
||
{
|
||
aqPropId[cProps] = PROPID_Q_PRIV_LEVEL;
|
||
aPropVar[cProps].vt = VT_UI4;
|
||
aPropVar[cProps].ulVal = MQ_PRIV_LEVEL_BODY;
|
||
cProps++;
|
||
}
|
||
else
|
||
{
|
||
aqPropId[cProps] = PROPID_Q_PRIV_LEVEL;
|
||
aPropVar[cProps].vt = VT_UI4;
|
||
aPropVar[cProps].ulVal = MQ_PRIV_LEVEL_OPTIONAL;
|
||
cProps++;
|
||
}
|
||
|
||
// Assemble the QUEUEPROPS structure:
|
||
qProps.cProp = cProps;
|
||
qProps.aPropID = aqPropId;
|
||
qProps.aPropVar = aPropVar;
|
||
qProps.aStatus = 0;
|
||
|
||
// Set the new queue properties:
|
||
hr = MQSetQueueProperties(pwsQFormat,&qProps);
|
||
|
||
return hr;
|
||
}
|
||
|
||
|
||
//--------------------------------------------------------------------
|
||
// ClearQueue()
|
||
//
|
||
// Clear out all waiting messages from the specified queue (if
|
||
// any).
|
||
//
|
||
//--------------------------------------------------------------------
|
||
HRESULT ClearQueue( QUEUEHANDLE hQueue )
|
||
{
|
||
HRESULT hr;
|
||
DWORD cProps = 0;
|
||
MQMSGPROPS msgProps;
|
||
MSGPROPID aMsgPropID[MAX_RECV_VAR];
|
||
MQPROPVARIANT aMsgPropVar[MAX_RECV_VAR];
|
||
RPC_CHAR wsMsgLabel[MQ_MAX_MSG_LABEL_LEN];
|
||
|
||
|
||
//
|
||
// MQ doesn't seem to let me clear out the queue (by reading
|
||
// messages) unless we have at least one queue property.
|
||
//
|
||
aMsgPropID[cProps] = PROPID_M_LABEL;
|
||
aMsgPropVar[cProps].vt = VT_LPTSTR;
|
||
aMsgPropVar[cProps].ptszVal = (RPC_SCHAR *)wsMsgLabel;
|
||
cProps++;
|
||
|
||
aMsgPropID[cProps] = PROPID_M_LABEL_LEN;
|
||
aMsgPropVar[cProps].vt = VT_UI4;
|
||
aMsgPropVar[cProps].ulVal = sizeof(wsMsgLabel);
|
||
cProps++;
|
||
|
||
msgProps.cProp = cProps;
|
||
msgProps.aPropID = aMsgPropID;
|
||
msgProps.aPropVar = aMsgPropVar;
|
||
msgProps.aStatus = 0;
|
||
|
||
//
|
||
// pull up all pending MQ messages.
|
||
//
|
||
while (TRUE)
|
||
{
|
||
hr = MQReceiveMessage(hQueue,0,MQ_ACTION_RECEIVE,
|
||
&msgProps,NULL,NULL,NULL,NULL);
|
||
if (FAILED(hr))
|
||
break;
|
||
}
|
||
|
||
//
|
||
// A timeout means the queue is empty:
|
||
//
|
||
if (hr == MQ_ERROR_IO_TIMEOUT)
|
||
hr = MQ_OK;
|
||
|
||
return hr;
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------
|
||
// ClientSetupQueue()
|
||
//
|
||
// Called by MQ_CreateEndpoint() to create and initialize the
|
||
// client's message queue (to read server responses).
|
||
//
|
||
// pEP -- The structure that will hold information about
|
||
// the queue being created and setup.
|
||
//
|
||
// pwsMachine -- The machine to create the queue on.
|
||
//
|
||
// pwsEndpoint -- RPC_CHAR string name of the endpoint. This will
|
||
// be used as the queue name.
|
||
//
|
||
//----------------------------------------------------------------
|
||
HRESULT ClientSetupQueue( MQ_DATAGRAM_ENDPOINT *pEndpoint,
|
||
RPC_CHAR *pwsMachine,
|
||
RPC_CHAR *pwsEndpoint )
|
||
{
|
||
HRESULT hr;
|
||
DWORD dwSize;
|
||
|
||
|
||
// The computer name for the server process:
|
||
if (pEndpoint->wsMachine != pwsMachine)
|
||
{
|
||
RpcpStringCopy(pEndpoint->wsMachine,pwsMachine);
|
||
}
|
||
|
||
// The endpoint string (RPC_CHAR) is used as the queue name:
|
||
RpcpStringCopy(pEndpoint->wsQName,pwsEndpoint);
|
||
|
||
|
||
// Build the path name for the server queue:
|
||
dwSize = sizeof(pEndpoint->wsQPathName);
|
||
ConstructQueuePathName( pEndpoint->wsMachine, // [in]
|
||
pEndpoint->wsQName, // [in]
|
||
pEndpoint->wsQPathName, // [out]
|
||
&dwSize ); // [in,out]
|
||
|
||
// Try to create the client process's receive queue (for
|
||
// responses back from the RPC server):
|
||
UuidFromString( CLNT_QTYPE_UUID_STR, &(pEndpoint->uuidQType) );
|
||
dwSize = sizeof(pEndpoint->wsQFormat);
|
||
hr = CreateQueue( NULL, // [in] No security descriptor.
|
||
&(pEndpoint->uuidQType), // [in]
|
||
pEndpoint->wsQPathName, // [in]
|
||
pEndpoint->wsQName, // [in] Use QName as the QLabel.
|
||
0x00000000, // [in] Flags
|
||
pEndpoint->wsQFormat, // [out]
|
||
&dwSize ); // [in,out]
|
||
|
||
if ( (FAILED(hr)) && (hr != MQ_ERROR_QUEUE_EXISTS) )
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "ClientSetupQueue(): CreateQueue(): 0x%x\n",
|
||
hr));
|
||
|
||
return hr;
|
||
}
|
||
|
||
//
|
||
// If the queue already exists, then locate it.
|
||
//
|
||
// NOTE: Currently client queues are temporary, but if cases
|
||
// are added in the future where client queues can be
|
||
// presistent, then this code will be needed.
|
||
//
|
||
if (hr == MQ_ERROR_QUEUE_EXISTS)
|
||
{
|
||
dwSize = sizeof(pEndpoint->wsQFormat);
|
||
hr = MQPathNameToFormatName( pEndpoint->wsQPathName,
|
||
pEndpoint->wsQFormat,
|
||
&dwSize );
|
||
if (FAILED(hr))
|
||
return hr;
|
||
}
|
||
|
||
//
|
||
// Ok, open the receive queue:
|
||
//
|
||
hr = MQOpenQueue( pEndpoint->wsQFormat, MQ_RECEIVE_ACCESS, 0, &(pEndpoint->hQueue));
|
||
|
||
#if FALSE
|
||
if (!FAILED(hr))
|
||
{
|
||
pEndpoint->fInitialized = TRUE;
|
||
}
|
||
#endif
|
||
|
||
#ifdef DBG
|
||
if (FAILED(hr))
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "ClientSetupQueue(): MQOpenQueueFailed(): 0x%x\n",
|
||
hr));
|
||
}
|
||
#endif
|
||
|
||
return hr;
|
||
}
|
||
|
||
//----------------------------------------------------------------
|
||
// ClientCloseQueue()
|
||
//
|
||
//----------------------------------------------------------------
|
||
HRESULT ClientCloseQueue( MQ_DATAGRAM_ENDPOINT *pEndpoint )
|
||
{
|
||
ASSERT(pEndpoint);
|
||
|
||
if (pEndpoint->hQueue)
|
||
{
|
||
MQCloseQueue(pEndpoint->hQueue);
|
||
pEndpoint->hQueue = 0;
|
||
}
|
||
|
||
g_pQueueMap->Remove(pEndpoint->wsQFormat);
|
||
|
||
MQDeleteQueue(pEndpoint->wsQFormat);
|
||
|
||
return MQ_OK;
|
||
}
|
||
|
||
//----------------------------------------------------------------
|
||
// QueryQM()
|
||
//
|
||
//----------------------------------------------------------------
|
||
HRESULT QueryQM( RPC_CHAR *pwsMachine,
|
||
DWORD *pdwSize )
|
||
{
|
||
DWORD cProps = 0;
|
||
HRESULT hr;
|
||
MQQMPROPS msgProps;
|
||
MSGPROPID aMsgPropID[MAX_RECV_VAR];
|
||
MQPROPVARIANT aMsgPropVar[MAX_RECV_VAR];
|
||
|
||
|
||
aMsgPropID[cProps] = PROPID_QM_PATHNAME; // 0
|
||
aMsgPropVar[cProps].vt = VT_NULL;
|
||
cProps++;
|
||
|
||
ASSERT( cProps < MAX_RECV_VAR );
|
||
|
||
msgProps.cProp = cProps;
|
||
msgProps.aPropID = aMsgPropID;
|
||
msgProps.aPropVar = aMsgPropVar;
|
||
msgProps.aStatus = 0;
|
||
|
||
// The following receive should always fail, we're just calling
|
||
// it to get the size of the message body:
|
||
hr = MQGetMachineProperties( NULL, NULL, &msgProps );
|
||
|
||
if (FAILED(hr))
|
||
{
|
||
return hr;
|
||
}
|
||
|
||
RpcpStringCopy(pwsMachine,aMsgPropVar[0].pwszVal);
|
||
|
||
MQFreeStringFromProperty(&aMsgPropVar[0]);
|
||
|
||
#ifdef MAJOR_DBG
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "QueryQM(): wsMachine: %S\n",
|
||
pwsMachine));
|
||
#endif
|
||
|
||
return hr;
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------
|
||
// ServerSetupQueue()
|
||
//
|
||
// Called by MQ_CreateEndpoint() to create and initialize the
|
||
// server process's message queue (endpoint).
|
||
//
|
||
// pEP -- The struct to hold information about the queue
|
||
// that is being set up.
|
||
//
|
||
// pwsMachine -- The machine to create the queue on. For RPC this
|
||
// is always the machine that the server process is
|
||
// running on.
|
||
//
|
||
// pwsEndpoint - The name of the endpoint. This will be used as
|
||
// the queue name.
|
||
//
|
||
// pSecurityDescriptor -- User may specify a security descriptor
|
||
// to apply to this queue (may be NULL).
|
||
//
|
||
// dwEndpointFlags -- Flags to control queue properties.
|
||
//
|
||
//----------------------------------------------------------------
|
||
HRESULT ServerSetupQueue( MQ_DATAGRAM_ENDPOINT *pEndpoint,
|
||
RPC_CHAR *pwsMachine,
|
||
RPC_CHAR *pwsEndpoint,
|
||
void *pSecurityDescriptor,
|
||
DWORD dwEndpointFlags )
|
||
{
|
||
HRESULT hr;
|
||
DWORD dwSize;
|
||
|
||
|
||
// The computer name for the server process:
|
||
if (pEndpoint->wsMachine != pwsMachine)
|
||
{
|
||
RpcpStringCopy(pEndpoint->wsMachine,pwsMachine);
|
||
}
|
||
|
||
// The endpoint string (RPC_CHAR) is used as the queue name:
|
||
if (pEndpoint->wsQName != pwsEndpoint)
|
||
{
|
||
RpcpStringCopy(pEndpoint->wsQName,pwsEndpoint);
|
||
}
|
||
|
||
// Build the path name for the server queue:
|
||
dwSize = sizeof(pEndpoint->wsQPathName);
|
||
if (!ConstructQueuePathName( pEndpoint->wsMachine, // [in]
|
||
pEndpoint->wsQName, // [in]
|
||
pEndpoint->wsQPathName, // [out]
|
||
&dwSize )) // [in,out]
|
||
{
|
||
return MQ_ERROR_ILLEGAL_QUEUE_PATHNAME;
|
||
}
|
||
|
||
// Try to create the server process receive queue;
|
||
UuidFromString( SVR_QTYPE_UUID_STR, &(pEndpoint->uuidQType) );
|
||
dwSize = sizeof(pEndpoint->wsQFormat);
|
||
hr = CreateQueue( (SECURITY_DESCRIPTOR*)pSecurityDescriptor,
|
||
&(pEndpoint->uuidQType), // [in]
|
||
pEndpoint->wsQPathName, // [in]
|
||
pEndpoint->wsQName, // [in] Use QName as the QLabel.
|
||
dwEndpointFlags, // [in]
|
||
pEndpoint->wsQFormat, // [out]
|
||
&dwSize ); // [in,out]
|
||
|
||
// If the queue already exists, then locate it:
|
||
if (hr == MQ_ERROR_QUEUE_EXISTS)
|
||
{
|
||
dwSize = sizeof(pEndpoint->wsQFormat);
|
||
hr = MQPathNameToFormatName( pEndpoint->wsQPathName,
|
||
pEndpoint->wsQFormat,
|
||
&dwSize );
|
||
if (FAILED(hr))
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "ServerSetupQueue(): MQPathNameToFormatName() failed: 0x%x\n",
|
||
hr));
|
||
|
||
return hr;
|
||
}
|
||
|
||
if ( !(dwEndpointFlags & RPC_C_MQ_USE_EXISTING_SECURITY) )
|
||
{
|
||
hr = SetQueueProperties(pEndpoint->wsQFormat,dwEndpointFlags);
|
||
if (FAILED(hr))
|
||
{
|
||
return hr;
|
||
}
|
||
}
|
||
}
|
||
else if (FAILED(hr))
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "ServerSetupQueue(): CreateQueue() failed: 0x%x\n",
|
||
hr));
|
||
|
||
return hr;
|
||
}
|
||
|
||
//
|
||
// Ok, open the receive queue:
|
||
//
|
||
hr = MQOpenQueue( pEndpoint->wsQFormat, MQ_RECEIVE_ACCESS, 0, &(pEndpoint->hQueue));
|
||
|
||
//
|
||
// Does the user want to make sure the queue is empty (in case it
|
||
// was a perminent queue):
|
||
//
|
||
if ( (hr == MQ_OK) && (dwEndpointFlags & RPC_C_MQ_CLEAR_ON_OPEN) )
|
||
{
|
||
hr = ClearQueue(pEndpoint->hQueue);
|
||
}
|
||
|
||
#ifdef DBG
|
||
if (FAILED(hr))
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "ServerSetupQueue(): MQOpenQueue() failed: 0x%x\n",
|
||
hr));
|
||
}
|
||
#endif
|
||
|
||
return hr;
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------
|
||
// ServerCloseQueue()
|
||
//
|
||
//----------------------------------------------------------------
|
||
HRESULT ServerCloseQueue( MQ_DATAGRAM_ENDPOINT *pEndpoint )
|
||
{
|
||
ASSERT(pEndpoint);
|
||
|
||
if (pEndpoint->hQueue)
|
||
{
|
||
MQCloseQueue(pEndpoint->hQueue);
|
||
pEndpoint->hQueue = 0;
|
||
}
|
||
|
||
// MQDeleteQueue(pEndpoint->wsQFormat);
|
||
|
||
return MQ_OK;
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------
|
||
// AsyncPeekQueue()
|
||
//
|
||
//----------------------------------------------------------------
|
||
HRESULT AsyncPeekQueue( IN MQ_DATAGRAM_ENDPOINT *pEndpoint,
|
||
IN MQ_OVERLAPPED *pOl )
|
||
{
|
||
DWORD cProps = 0;
|
||
HRESULT hr;
|
||
|
||
pOl->aMsgPropID[cProps] = PROPID_M_BODY_SIZE;
|
||
pOl->aMsgPropVar[cProps].vt = VT_UI4;
|
||
pOl->aMsgPropVar[cProps].ulVal = 0;
|
||
cProps++;
|
||
|
||
ASSERT( cProps < MAX_RECV_VAR );
|
||
|
||
pOl->msgProps.cProp = cProps;
|
||
pOl->msgProps.aPropID = pOl->aMsgPropID;
|
||
pOl->msgProps.aPropVar = pOl->aMsgPropVar;
|
||
pOl->msgProps.aStatus = pOl->aStatus;
|
||
|
||
hr = MQReceiveMessage( pEndpoint->hQueue,
|
||
INFINITE,
|
||
MQ_ACTION_PEEK_CURRENT,
|
||
&pOl->msgProps,
|
||
&pOl->ol, // Asynchronous.
|
||
NULL, // No callback.
|
||
NULL, // Message filter.
|
||
NULL ); // Transaction object.
|
||
if (FAILED(hr))
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "AsyncPeekQueue() failed: 0x%x\n",
|
||
hr));
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
//----------------------------------------------------------------
|
||
// AsyncReadQueue()
|
||
//
|
||
//----------------------------------------------------------------
|
||
HRESULT AsyncReadQueue( IN MQ_DATAGRAM_ENDPOINT *pEndpoint,
|
||
IN MQ_OVERLAPPED *pOl,
|
||
OUT MQ_ADDRESS *pAddress,
|
||
OUT UCHAR *pBuffer,
|
||
IN DWORD dwBufferSize )
|
||
{
|
||
DWORD cProps = 0;
|
||
HRESULT hr;
|
||
|
||
pOl->aMsgPropID[cProps] = PROPID_M_BODY; // [0]
|
||
pOl->aMsgPropVar[cProps].vt = (VT_UI1 | VT_VECTOR);
|
||
pOl->aMsgPropVar[cProps].caub.cElems = dwBufferSize;
|
||
pOl->aMsgPropVar[cProps].caub.pElems = pBuffer;
|
||
cProps++;
|
||
|
||
ASSERT(cProps == I_MESSAGE_SIZE);
|
||
pOl->aMsgPropID[cProps] = PROPID_M_BODY_SIZE; // [1]
|
||
pOl->aMsgPropVar[cProps].vt = VT_UI4;
|
||
cProps++;
|
||
|
||
ASSERT(cProps == I_MESSAGE_LABEL);
|
||
pOl->aMsgPropID[cProps] = PROPID_M_LABEL; // [2]
|
||
pOl->aMsgPropVar[cProps].vt = VT_LPWSTR;
|
||
pOl->aMsgPropVar[cProps].pwszVal = (WCHAR *)pAddress->wsMsgLabel;
|
||
cProps++;
|
||
|
||
pOl->aMsgPropID[cProps] = PROPID_M_LABEL_LEN; // [3]
|
||
pOl->aMsgPropVar[cProps].vt = VT_UI4;
|
||
pOl->aMsgPropVar[cProps].ulVal = sizeof(pAddress->wsMsgLabel);
|
||
cProps++;
|
||
|
||
pOl->aMsgPropID[cProps] = PROPID_M_RESP_QUEUE; // [4]
|
||
pOl->aMsgPropVar[cProps].vt = VT_LPWSTR;
|
||
pOl->aMsgPropVar[cProps].pwszVal = (WCHAR *)pAddress->wsQFormat;
|
||
cProps++;
|
||
|
||
pOl->aMsgPropID[cProps] = PROPID_M_RESP_QUEUE_LEN; // [5]
|
||
pOl->aMsgPropVar[cProps].vt = VT_UI4;
|
||
pOl->aMsgPropVar[cProps].ulVal = sizeof(pAddress->wsQFormat);
|
||
cProps++;
|
||
|
||
//
|
||
// These message properties are for authentication and privacy:
|
||
//
|
||
ASSERT(cProps == I_AUTHENTICATED);
|
||
pOl->aMsgPropID[cProps] = PROPID_M_AUTHENTICATED; // [6]
|
||
pOl->aMsgPropVar[cProps].vt = VT_UI1;
|
||
pOl->aMsgPropVar[cProps].bVal = 0;
|
||
cProps++;
|
||
|
||
ASSERT(cProps == I_PRIVACY_LEVEL);
|
||
pOl->aMsgPropID[cProps] = PROPID_M_PRIV_LEVEL; // [7]
|
||
pOl->aMsgPropVar[cProps].vt = VT_UI4;
|
||
pOl->aMsgPropVar[cProps].ulVal = 0;
|
||
cProps++;
|
||
|
||
//
|
||
// WARNING: these always need to be the last two properties
|
||
// in the arrays (see the while loop below):
|
||
//
|
||
pOl->aMsgPropID[cProps] = PROPID_M_SENDERID_TYPE; // [8]
|
||
pOl->aMsgPropVar[cProps].vt = VT_UI4;
|
||
pOl->aMsgPropVar[cProps].ulVal = 0;
|
||
cProps++;
|
||
|
||
pOl->aMsgPropID[cProps] = PROPID_M_SENDERID; // [9]
|
||
pOl->aMsgPropVar[cProps].vt = (VT_UI1 | VT_VECTOR);
|
||
pOl->aMsgPropVar[cProps].caub.cElems = sizeof(pAddress->aSidBuffer);
|
||
pOl->aMsgPropVar[cProps].caub.pElems = pAddress->aSidBuffer;
|
||
cProps++;
|
||
|
||
ASSERT( cProps < MAX_RECV_VAR );
|
||
|
||
pOl->msgProps.cProp = cProps;
|
||
pOl->msgProps.aPropID = pOl->aMsgPropID;
|
||
pOl->msgProps.aPropVar = pOl->aMsgPropVar;
|
||
pOl->msgProps.aStatus = pOl->aStatus;
|
||
|
||
hr = MQReceiveMessage( pEndpoint->hQueue,
|
||
INFINITE,
|
||
MQ_ACTION_RECEIVE,
|
||
&pOl->msgProps,
|
||
&pOl->ol, // Asynchronous.
|
||
NULL, // No callback.
|
||
NULL, // Message filter.
|
||
NULL ); // Transaction object.
|
||
#ifdef DBG
|
||
if ( (hr != MQ_OK)
|
||
&& (hr != MQ_INFORMATION_OPERATION_PENDING)
|
||
&& (hr != MQ_ERROR_BUFFER_OVERFLOW) )
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "AsyncReadQueue() failed: 0x%x\n",
|
||
hr));
|
||
}
|
||
#endif
|
||
|
||
return hr;
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------
|
||
// MQ_SendToQueue()
|
||
//
|
||
// Send a PDU to somebody (specified by pAddress).
|
||
//
|
||
// pEndpoint -- My (endpoint) for responses.
|
||
//
|
||
// pAddress -- The destination queue.
|
||
//
|
||
// pBuffer -- The data PDU to send.
|
||
//
|
||
// dwBufferSize The size of the PDU (bytes).
|
||
//
|
||
//----------------------------------------------------------------
|
||
HRESULT MQ_SendToQueue( IN MQ_DATAGRAM_ENDPOINT *pEndpoint,
|
||
IN MQ_ADDRESS *pAddress,
|
||
IN UCHAR *pBuffer,
|
||
IN DWORD dwBufferSize )
|
||
{
|
||
HRESULT hr;
|
||
DWORD cProps = 0;
|
||
MQMSGPROPS msgProps;
|
||
MSGPROPID aMsgPropID[MAX_SEND_VAR];
|
||
MQPROPVARIANT aMsgPropVar[MAX_SEND_VAR];
|
||
HRESULT aStatus[MAX_SEND_VAR];
|
||
|
||
// NOTE: If you add MQ properties to be sent, make sure that
|
||
// MAX_SEND_VAR is large enough...
|
||
|
||
// Message body contains the packet being sent:
|
||
aMsgPropID[cProps] = PROPID_M_BODY;
|
||
aMsgPropVar[cProps].vt = (VT_UI1 | VT_VECTOR);
|
||
aMsgPropVar[cProps].caub.cElems = dwBufferSize;
|
||
aMsgPropVar[cProps].caub.pElems = pBuffer;
|
||
cProps++;
|
||
|
||
// The size of the packet:
|
||
#if FALSE
|
||
aMsgPropID[cProps] = PROPID_M_BODY_SIZE;
|
||
aMsgPropVar[cProps].vt = VT_UI4;
|
||
aMsgPropVar[cProps].ulVal = dwBufferSize;
|
||
cProps++;
|
||
#endif
|
||
|
||
// Pass the sender (me) as the queue label. The queue label
|
||
// holds the my Queue Path Name:
|
||
aMsgPropID[cProps] = PROPID_M_LABEL;
|
||
aMsgPropVar[cProps].vt = VT_LPTSTR;
|
||
aMsgPropVar[cProps].ptszVal = (RPC_SCHAR *)pEndpoint->wsQPathName;
|
||
cProps++;
|
||
|
||
// Delivery (express or recoverable):
|
||
aMsgPropID[cProps] = PROPID_M_DELIVERY;
|
||
aMsgPropVar[cProps].vt = VT_UI1;
|
||
aMsgPropVar[cProps].bVal = (unsigned char)(pEndpoint->ulDelivery);
|
||
cProps++;
|
||
|
||
// Priority (MQ_MIN_PRIORITY to MQ_MAX_PRIORITY):
|
||
aMsgPropID[cProps] = PROPID_M_PRIORITY;
|
||
aMsgPropVar[cProps].vt = VT_UI1;
|
||
aMsgPropVar[cProps].bVal = (unsigned char)(pEndpoint->ulPriority);
|
||
cProps++;
|
||
|
||
// Journaling (none, deadletter or journal):
|
||
aMsgPropID[cProps] = PROPID_M_JOURNAL;
|
||
aMsgPropVar[cProps].vt = VT_UI1;
|
||
aMsgPropVar[cProps].bVal = (unsigned char)(pEndpoint->ulJournaling);
|
||
cProps++;
|
||
|
||
// Time limit to reach destination queue (seconds):
|
||
aMsgPropID[cProps] = PROPID_M_TIME_TO_REACH_QUEUE;
|
||
aMsgPropVar[cProps].vt = VT_UI4;
|
||
aMsgPropVar[cProps].ulVal = pEndpoint->ulTimeToReachQueue;
|
||
cProps++;
|
||
|
||
// Time limit for call to be received (seconds):
|
||
aMsgPropID[cProps] = PROPID_M_TIME_TO_BE_RECEIVED;
|
||
aMsgPropVar[cProps].vt = VT_UI4;
|
||
aMsgPropVar[cProps].ulVal = pEndpoint->ulTimeToReceive;
|
||
cProps++;
|
||
|
||
// Response Queue:
|
||
aMsgPropID[cProps] = PROPID_M_RESP_QUEUE;
|
||
aMsgPropVar[cProps].vt = VT_LPTSTR;
|
||
aMsgPropVar[cProps].ptszVal = (RPC_SCHAR *)pEndpoint->wsQFormat;
|
||
cProps++;
|
||
|
||
// Authentication:
|
||
aMsgPropID[cProps] = PROPID_M_AUTH_LEVEL;
|
||
aMsgPropVar[cProps].vt = VT_UI4;
|
||
aMsgPropVar[cProps].ulVal = (pEndpoint->fAuthenticate)? MQMSG_AUTH_LEVEL_ALWAYS : MQMSG_AUTH_LEVEL_NONE;
|
||
cProps++;
|
||
|
||
// Encryption:
|
||
aMsgPropID[cProps] = PROPID_M_PRIV_LEVEL;
|
||
aMsgPropVar[cProps].vt = VT_UI4;
|
||
aMsgPropVar[cProps].ulVal = (pEndpoint->fEncrypt)? MQMSG_PRIV_LEVEL_BODY : MQMSG_PRIV_LEVEL_NONE;
|
||
cProps++;
|
||
|
||
// Call (message) acknowledgment:
|
||
if (pEndpoint->fAck)
|
||
{
|
||
aMsgPropID[cProps] = PROPID_M_ACKNOWLEDGE;
|
||
aMsgPropVar[cProps].vt = VT_UI1;
|
||
aMsgPropVar[cProps].bVal = MQMSG_ACKNOWLEDGMENT_FULL_REACH_QUEUE;
|
||
cProps++;
|
||
|
||
aMsgPropID[cProps] = PROPID_M_ADMIN_QUEUE;
|
||
aMsgPropVar[cProps].vt = VT_LPTSTR;
|
||
aMsgPropVar[cProps].ptszVal = (RPC_SCHAR *)pEndpoint->wsAdminQFormat;
|
||
cProps++;
|
||
}
|
||
|
||
ASSERT( cProps < MAX_SEND_VAR );
|
||
|
||
|
||
msgProps.cProp = cProps;
|
||
msgProps.aPropID = aMsgPropID;
|
||
msgProps.aPropVar = aMsgPropVar;
|
||
msgProps.aStatus = aStatus;
|
||
|
||
if ( (!pAddress->hQueue)
|
||
&& !(pAddress->hQueue = g_pQueueMap->Lookup(pAddress->wsQFormat)) )
|
||
{
|
||
hr = MQOpenQueue( pAddress->wsQFormat,
|
||
MQ_SEND_ACCESS, 0, &(pAddress->hQueue) );
|
||
if (FAILED(hr))
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "MQ_SendToQueue(): MQOpenQueue() failed: 0x%x\n",
|
||
hr));
|
||
|
||
return hr;
|
||
}
|
||
|
||
if (!g_pQueueMap->Add(pAddress->wsQFormat, pAddress->hQueue))
|
||
{
|
||
return MQ_ERROR_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
|
||
hr = MQSendMessage( pAddress->hQueue, &msgProps, NULL );
|
||
|
||
if ( (!FAILED(hr)) && (pEndpoint->fAck) )
|
||
{
|
||
hr = WaitForAck(pEndpoint);
|
||
|
||
if (hr == MQ_ERROR_QUEUE_NOT_FOUND)
|
||
{
|
||
MQCloseQueue(pEndpoint->hQueue);
|
||
pEndpoint->hQueue = 0;
|
||
}
|
||
}
|
||
|
||
#ifdef DBG
|
||
if (hr != MQ_OK)
|
||
{
|
||
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
||
DPFLTR_WARNING_LEVEL,
|
||
RPCTRANS "MQ_SendToQueue(): MQSendMessage() failed: 0x%x\n",
|
||
hr));
|
||
}
|
||
#endif
|
||
|
||
return hr;
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------
|
||
// ReadQueue()
|
||
//
|
||
// Blocking read of the next message from the queue in pEndpoint.
|
||
// If there is no pending message on the queue, wait around for
|
||
// timeoutMsec.
|
||
//
|
||
// pInfo -- Holds information about the queue that we are
|
||
// doing a read on.
|
||
//
|
||
// timeoutMsec -- How long to wait around if there are no messages
|
||
// pending.
|
||
//
|
||
// pAddress -- Where to replace information about the queue to
|
||
// respond queue (who sent the message).
|
||
//
|
||
// pBuffer -- The MQ message body is returned in the memory
|
||
// pointed to by pBuffer. This is the RPC packet.
|
||
//
|
||
// pdwBufferSize On entry it is passed in as the total size of
|
||
// pBuffer, and it is returned with the actual
|
||
// number of bytes in the message.
|
||
//
|
||
//----------------------------------------------------------------
|
||
HRESULT ReadQueue( IN MQ_DATAGRAM_ENDPOINT *pEndpoint,
|
||
IN DWORD timeoutMsec,
|
||
OUT MQ_ADDRESS *pAddress,
|
||
OUT UCHAR *pBuffer,
|
||
IN OUT DWORD *pdwBufferSize )
|
||
{
|
||
DWORD cProps = 0;
|
||
HRESULT hr;
|
||
MQMSGPROPS msgProps;
|
||
MSGPROPID aMsgPropID[MAX_RECV_VAR];
|
||
MQPROPVARIANT aMsgPropVar[MAX_RECV_VAR];
|
||
HRESULT aStatus[MAX_RECV_VAR];
|
||
|
||
|
||
aMsgPropID[cProps] = PROPID_M_BODY; // [0]
|
||
aMsgPropVar[cProps].vt = (VT_UI1 | VT_VECTOR);
|
||
aMsgPropVar[cProps].caub.cElems = *pdwBufferSize;
|
||
aMsgPropVar[cProps].caub.pElems = pBuffer;
|
||
cProps++;
|
||
|
||
ASSERT(cProps == I_MESSAGE_SIZE);
|
||
aMsgPropID[cProps] = PROPID_M_BODY_SIZE; // [1]
|
||
aMsgPropVar[cProps].vt = VT_UI4;
|
||
cProps++;
|
||
|
||
ASSERT(cProps == I_MESSAGE_LABEL);
|
||
aMsgPropID[cProps] = PROPID_M_LABEL; // [2]
|
||
aMsgPropVar[cProps].vt = VT_LPTSTR;
|
||
aMsgPropVar[cProps].ptszVal = (RPC_SCHAR *)pAddress->wsMsgLabel;
|
||
cProps++;
|
||
|
||
aMsgPropID[cProps] = PROPID_M_LABEL_LEN; // [3]
|
||
aMsgPropVar[cProps].vt = VT_UI4;
|
||
aMsgPropVar[cProps].ulVal = sizeof(pAddress->wsMsgLabel);
|
||
cProps++;
|
||
|
||
aMsgPropID[cProps] = PROPID_M_RESP_QUEUE; // [4]
|
||
aMsgPropVar[cProps].vt = VT_LPTSTR;
|
||
aMsgPropVar[cProps].ptszVal = (RPC_SCHAR *)pAddress->wsQFormat;
|
||
cProps++;
|
||
|
||
aMsgPropID[cProps] = PROPID_M_RESP_QUEUE_LEN; // [5]
|
||
aMsgPropVar[cProps].vt = VT_UI4;
|
||
aMsgPropVar[cProps].ulVal = MAX_FORMAT_LEN;
|
||
cProps++;
|
||
|
||
//
|
||
// These message properties are for authentication and privacy:
|
||
//
|
||
ASSERT(cProps == I_AUTHENTICATED);
|
||
aMsgPropID[cProps] = PROPID_M_AUTHENTICATED; // [6]
|
||
aMsgPropVar[cProps].vt = VT_UI1;
|
||
aMsgPropVar[cProps].bVal = 0;
|
||
cProps++;
|
||
|
||
ASSERT(cProps == I_PRIVACY_LEVEL);
|
||
aMsgPropID[cProps] = PROPID_M_PRIV_LEVEL; // [7]
|
||
aMsgPropVar[cProps].vt = VT_UI4;
|
||
aMsgPropVar[cProps].ulVal = 0;
|
||
cProps++;
|
||
|
||
aMsgPropID[cProps] = PROPID_M_SENDERID_TYPE; // [8]
|
||
aMsgPropVar[cProps].vt = VT_UI4;
|
||
aMsgPropVar[cProps].ulVal = 0;
|
||
cProps++;
|
||
|
||
aMsgPropID[cProps] = PROPID_M_SENDERID; // [9]
|
||
aMsgPropVar[cProps].vt = (VT_UI1 | VT_VECTOR);
|
||
aMsgPropVar[cProps].caub.cElems = sizeof(pAddress->aSidBuffer);
|
||
aMsgPropVar[cProps].caub.pElems = pAddress->aSidBuffer;
|
||
cProps++;
|
||
|
||
|
||
ASSERT( cProps < MAX_RECV_VAR );
|
||
|
||
msgProps.cProp = cProps;
|
||
msgProps.aPropID = aMsgPropID;
|
||
msgProps.aPropVar = aMsgPropVar;
|
||
msgProps.aStatus = aStatus;
|
||
|
||
hr = MQReceiveMessage( pEndpoint->hQueue,
|
||
timeoutMsec,
|
||
MQ_ACTION_RECEIVE,
|
||
&msgProps,
|
||
NULL, // No overlap (synchronous).
|
||
NULL, // No callback.
|
||
NULL, // Message filter.
|
||
NULL ); // Transaction object.
|
||
|
||
#ifdef DBG
|
||
if ( (hr != MQ_OK) && (hr != MQ_ERROR_IO_TIMEOUT) )
|
||
{
|
||
DbgPrint("ReadQueue(): ERROR: hr: 0x%x\n",hr);
|
||
}
|
||
#endif
|
||
|
||
if (!FAILED(hr))
|
||
{
|
||
pAddress->hQueue = 0;
|
||
*pdwBufferSize = msgProps.aPropVar[I_MESSAGE_SIZE].ulVal;
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------
|
||
// PeekQueue()
|
||
//
|
||
// Do a peek on the queue for the specified endpoint in order to
|
||
// find out how big the next message is. If there are no messages
|
||
// in the queue, wait around for dwTimeoutMsec. Return the size
|
||
// of the message in *pdwSize.
|
||
//
|
||
//----------------------------------------------------------------
|
||
HRESULT PeekQueue( IN MQ_DATAGRAM_ENDPOINT *pEndpoint,
|
||
IN DWORD dwTimeoutMsec,
|
||
OUT DWORD *pdwSize )
|
||
{
|
||
DWORD cProps = 0;
|
||
BOOL bSuccess;
|
||
HRESULT hr;
|
||
MQMSGPROPS msgProps;
|
||
MSGPROPID aMsgPropID[MAX_RECV_VAR];
|
||
MQPROPVARIANT aMsgPropVar[MAX_RECV_VAR];
|
||
RPC_CHAR wsMsgLabel[MQ_MAX_MSG_LABEL_LEN];
|
||
|
||
|
||
aMsgPropID[cProps] = PROPID_M_BODY; // 0
|
||
aMsgPropVar[cProps].vt = (VT_UI1 | VT_VECTOR);
|
||
aMsgPropVar[cProps].caub.cElems = 0;
|
||
aMsgPropVar[cProps].caub.pElems = NULL;
|
||
cProps++;
|
||
|
||
aMsgPropID[cProps] = PROPID_M_BODY_SIZE; // 1
|
||
aMsgPropVar[cProps].vt = VT_UI4;
|
||
cProps++;
|
||
|
||
aMsgPropID[cProps] = PROPID_M_LABEL; // 2
|
||
aMsgPropVar[cProps].vt = VT_LPTSTR;
|
||
aMsgPropVar[cProps].ptszVal = (RPC_SCHAR *)wsMsgLabel;
|
||
cProps++;
|
||
|
||
aMsgPropID[cProps] = PROPID_M_LABEL_LEN; // 3
|
||
aMsgPropVar[cProps].vt = VT_UI4;
|
||
aMsgPropVar[cProps].ulVal = sizeof(wsMsgLabel);
|
||
cProps++;
|
||
|
||
ASSERT( cProps < MAX_RECV_VAR );
|
||
|
||
msgProps.cProp = cProps;
|
||
msgProps.aPropID = aMsgPropID;
|
||
msgProps.aPropVar = aMsgPropVar;
|
||
msgProps.aStatus = 0;
|
||
|
||
// The following receive should always fail, we're just calling
|
||
// it to get the size of the message body:
|
||
hr = MQReceiveMessage( pEndpoint->hQueue,
|
||
dwTimeoutMsec,
|
||
MQ_ACTION_RECEIVE,
|
||
&msgProps,
|
||
NULL, // No overlap (synchronous).
|
||
NULL, // No callback.
|
||
NULL, // Message filter.
|
||
NULL ); // Transaction object.
|
||
|
||
if (hr == MQ_ERROR_BUFFER_OVERFLOW)
|
||
{
|
||
*pdwSize = aMsgPropVar[1].ulVal;
|
||
hr = MQ_OK;
|
||
}
|
||
else
|
||
*pdwSize = 0;
|
||
|
||
#ifdef DBG
|
||
if ( (hr != MQ_OK) && (hr != MQ_ERROR_IO_TIMEOUT) )
|
||
{
|
||
DbgPrint("ClntPeekQueue(): ERROR: hr: 0x%x (%d)\n",hr,hr);
|
||
}
|
||
#endif
|
||
|
||
return hr;
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------
|
||
// EvaluateAckMessage()
|
||
//
|
||
//----------------------------------------------------------------
|
||
HRESULT EvaluateAckMessage( IN USHORT msgClass )
|
||
{
|
||
HRESULT hr = msgClass;
|
||
|
||
switch (msgClass)
|
||
{
|
||
case MQMSG_CLASS_ACK_REACH_QUEUE:
|
||
case MQMSG_CLASS_ACK_RECEIVE:
|
||
hr = MQ_OK;
|
||
break;
|
||
|
||
case MQMSG_CLASS_NACK_BAD_DST_Q:
|
||
hr = MQ_ERROR_QUEUE_NOT_FOUND;
|
||
break;
|
||
|
||
// All other cases are handled in MQ_MapStatusCode()...
|
||
}
|
||
|
||
return hr;
|
||
}
|
||
|
||
//----------------------------------------------------------------
|
||
// ClntWaitForAck()
|
||
//
|
||
// Used by the client side to wait for a MQ acknowledgement when
|
||
// ClntSendToQueue() sends a call. An ACK is sent when the call
|
||
// message reaches the destination (server) queue.
|
||
//
|
||
//----------------------------------------------------------------
|
||
HRESULT WaitForAck( IN MQ_DATAGRAM_ENDPOINT *pEndpoint )
|
||
{
|
||
HRESULT hr;
|
||
DWORD cProps = 0;
|
||
UCHAR msgClass;
|
||
MQMSGPROPS msgProps;
|
||
MSGPROPID aMsgPropID[MAX_RECV_VAR];
|
||
MQPROPVARIANT aMsgPropVar[MAX_RECV_VAR];
|
||
HRESULT aMsgHr[MAX_RECV_VAR];
|
||
RPC_CHAR wsMsgLabel[MQ_MAX_MSG_LABEL_LEN];
|
||
|
||
|
||
// The message class will tell us the message acknowledgement:
|
||
aMsgPropID[cProps] = PROPID_M_CLASS;
|
||
aMsgPropVar[cProps].vt = VT_UI2;
|
||
aMsgPropVar[cProps].uiVal = 0;
|
||
aMsgHr[cProps] = MQ_OK;
|
||
cProps++;
|
||
|
||
ASSERT( cProps < MAX_RECV_VAR );
|
||
|
||
msgProps.cProp = cProps;
|
||
msgProps.aPropID = aMsgPropID;
|
||
msgProps.aPropVar = aMsgPropVar;
|
||
msgProps.aStatus = aMsgHr;
|
||
|
||
hr = MQReceiveMessage( pEndpoint->hAdminQueue,
|
||
INFINITE,
|
||
MQ_ACTION_RECEIVE,
|
||
&msgProps,
|
||
NULL, NULL, NULL, NULL );
|
||
|
||
if (!FAILED(hr))
|
||
{
|
||
hr = EvaluateAckMessage( aMsgPropVar[0].uiVal );
|
||
}
|
||
# ifdef DBG
|
||
else
|
||
{
|
||
DbgPrint("WaitForAck(): FAILED: hr: 0x%x aMsgHr[0]: 0x%x\n", hr, aMsgHr[0] );
|
||
}
|
||
# endif
|
||
|
||
return hr;
|
||
}
|
||
|
||
//----------------------------------------------------------------
|
||
// SetupAdminQueue()
|
||
//
|
||
//
|
||
//----------------------------------------------------------------
|
||
HRESULT SetupAdminQueue( MQ_DATAGRAM_ENDPOINT *pEndpoint )
|
||
{
|
||
HRESULT hr;
|
||
DWORD dwSize;
|
||
UUID uuidQType;
|
||
RPC_CHAR wsQName[MQ_MAX_Q_NAME_LEN];
|
||
RPC_CHAR wsQPathName[MAX_PATHNAME_LEN];
|
||
|
||
|
||
RpcpStringCopy(wsQName,TEXT("Admin"));
|
||
RpcpStringCat(wsQName,pEndpoint->wsQName);
|
||
|
||
// Build the path name for the admin queue (NOTE: that this
|
||
// is a private queue):
|
||
dwSize = sizeof(pEndpoint->wsQPathName);
|
||
ConstructPrivateQueuePathName( pEndpoint->wsMachine, // [in]
|
||
wsQName, // [in]
|
||
wsQPathName, // [out]
|
||
&dwSize ); // [in,out]
|
||
|
||
|
||
// Try to create the server process receive queue;
|
||
UuidFromString( CLNT_ADMIN_QTYPE_UUID_STR, &uuidQType );
|
||
dwSize = sizeof(pEndpoint->wsAdminQFormat);
|
||
hr = CreateQueue( NULL, // [in] No security descriptor.
|
||
&uuidQType, // [in]
|
||
wsQPathName, // [in]
|
||
wsQName, // [in] Use QName as the QLabel.
|
||
0x00000000, // [in] Flags
|
||
pEndpoint->wsAdminQFormat, // [out]
|
||
&dwSize ); // [in,out]
|
||
|
||
if ( (FAILED(hr)) && (hr != MQ_ERROR_QUEUE_EXISTS) )
|
||
{
|
||
#ifdef DBG
|
||
DbgPrint("SetupAdminQueue(): %S FAILED: 0x%x (%d)\n", wsQPathName, hr, hr );
|
||
#endif
|
||
return hr;
|
||
}
|
||
|
||
//
|
||
// If the queue already exists, then locate it.
|
||
//
|
||
if (hr == MQ_ERROR_QUEUE_EXISTS)
|
||
{
|
||
dwSize = sizeof(pEndpoint->wsQPathName);
|
||
hr = MQPathNameToFormatName( pEndpoint->wsQPathName,
|
||
pEndpoint->wsQFormat,
|
||
&dwSize );
|
||
if (FAILED(hr))
|
||
{
|
||
#ifdef DBG
|
||
DbgPrint("SetupAdminQueue(): %S FAILED: 0x%x (%d)\n", wsQPathName, hr, hr );
|
||
#endif
|
||
return hr;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Ok, open the admin queue for receive:
|
||
//
|
||
hr = MQOpenQueue( pEndpoint->wsAdminQFormat,
|
||
MQ_RECEIVE_ACCESS, 0, &(pEndpoint->hAdminQueue));
|
||
|
||
#ifdef DBG
|
||
if (FAILED(hr))
|
||
{
|
||
DbgPrint("SetupAdminQueue(): %S FAILED: 0x%x (%d)\n", wsQPathName, hr, hr );
|
||
}
|
||
#endif
|
||
|
||
return hr;
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------
|
||
// Debug test code -- DG_DbgPrintPacket().
|
||
//----------------------------------------------------------------
|
||
|
||
#ifdef MAJOR_DBG
|
||
|
||
const static char *packetTypeStrs[] =
|
||
{
|
||
"REQUEST",
|
||
"PING ",
|
||
"RESP ",
|
||
"FAULT ",
|
||
"WORKING",
|
||
"NOCALL ",
|
||
"REJECT ",
|
||
"ACK ",
|
||
"QUIT ",
|
||
"FACK ",
|
||
"QUACK ",
|
||
"Unknown",
|
||
};
|
||
|
||
const static char asciiByteChars[] =
|
||
{
|
||
'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
|
||
};
|
||
|
||
#define HIGH_NIBBLE(uc) ((uc) >> 4)
|
||
#define LOW_NIBBLE(uc) ((uc) & 0x0f)
|
||
|
||
//----------------------------------------------------------------
|
||
// DbgPacketType()
|
||
//
|
||
//----------------------------------------------------------------
|
||
static char *DbgPacketType( unsigned char *pPacket )
|
||
{
|
||
if ( (pPacket[1] >= 0) && (pPacket[1] < 11) )
|
||
return packetTypeStrs[pPacket[1]];
|
||
else
|
||
return packetTypeStrs[11];
|
||
}
|
||
|
||
//----------------------------------------------------------------
|
||
// DbgUuidToStr()
|
||
//
|
||
//----------------------------------------------------------------
|
||
static char *DbgUuidToStr( unsigned char *pUuidArg, char *pszUuid )
|
||
{
|
||
int i = 0;
|
||
int j;
|
||
GUID uuid;
|
||
unsigned char *pUuid;
|
||
|
||
// Work with local copy of the UUID:
|
||
pUuid = (unsigned char*)&uuid;
|
||
CopyMemory(pUuid,pUuidArg,sizeof(GUID));
|
||
|
||
// Assume this is intel and byte-swap it...
|
||
uuid.Data1 = RpcpByteSwapLong(uuid.Data1);
|
||
uuid.Data2 = RpcpByteSwapShort(uuid.Data2);
|
||
uuid.Data3 = RpcpByteSwapShort(uuid.Data3);
|
||
|
||
for (j=0; j<16; j++)
|
||
{
|
||
pszUuid[i++] = asciiByteChars[HIGH_NIBBLE(pUuid[j])];
|
||
pszUuid[i++] = asciiByteChars[LOW_NIBBLE(pUuid[j])];
|
||
if ( (j==3)||(j==5)||(j==7)||(j==9) )
|
||
pszUuid[i++] = '-';
|
||
}
|
||
|
||
pszUuid[i] = '\0';
|
||
return pszUuid;
|
||
}
|
||
|
||
//----------------------------------------------------------------
|
||
// DbgPrintPacket()
|
||
//
|
||
//----------------------------------------------------------------
|
||
void DG_DbgPrintPacket( unsigned char *pPacket )
|
||
{
|
||
char szIf[50];
|
||
char szAct[50];
|
||
ULONG ulSequenceNumber;
|
||
|
||
if (pPacket)
|
||
{
|
||
ulSequenceNumber = *((unsigned long*)(&(pPacket[56])));
|
||
ulSequenceNumber = RpcpByteSwapLong(ulSequenceNumber);
|
||
|
||
DbgPrint(" Type: %s:0x%x:0x%x:0x%x:0x%x\n Intferface: %s\n Activity : %s\n SequenceNumber: %d\n",
|
||
DbgPacketType(pPacket),
|
||
pPacket[0], pPacket[1], pPacket[2], pPacket[3],
|
||
DbgUuidToStr(&(pPacket[24]),szIf),
|
||
DbgUuidToStr(&(pPacket[40]),szAct),
|
||
ulSequenceNumber );
|
||
}
|
||
else
|
||
{
|
||
DbgPrint(" NULL Packet.\n" );
|
||
}
|
||
}
|
||
#endif
|