mirror of https://github.com/tongzx/nt5src
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.
2357 lines
68 KiB
2357 lines
68 KiB
/*++
|
|
|
|
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
|