/*========================================================================== * * Copyright (C) 2000-2002 Microsoft Corporation. All Rights Reserved. * * File: Enum.cpp * Content: Enumeration routines *@@BEGIN_MSINTERNAL * History: * Date By Reason * ==== == ====== * 04/10/00 mjn Created * 04/17/00 mjn Fixed DNCompleteEnumQuery to clean up properly * 04/18/00 mjn Return User Buffer in DNProcessEnumQuery * 04/19/00 mjn Removed DPN_BUFFER_DESC from DPNMSG_ENUM_HOSTS_QUERY and DPNMSG_ENUM_HOSTS_RESPONSE structs * 05/02/00 mjn Allow application to reject ENUM_QUERY's * 06/25/00 mjn Fixed payload problem in DNProcessEnumQuery() * 07/10/00 mjn Removed DNCompleteEnumQuery() and DNCompleteEnumResponse() * 07/12/00 mjn Ensure connected before replying to ENUMs * 07/29/00 mjn Verify enum responses sizes * 08/05/00 RichGr IA64: Use %p format specifier in DPFs for 32/64-bit pointers and handles. * 08/05/00 mjn Ensure cancelled operations don't proceed * 08/29/00 mjn Cancel EnumHosts if non-DPN_OK returned from response notification * 09/04/00 mjn Added CApplicationDesc * 09/14/00 mjn AddRef Protocol refcount when invoking protocol * 01/22/01 mjn Add SP reference on AsyncOp in DNProcessEnumQuery() * 01/25/01 mjn Fixed 64-bit alignment problem in received messages * 03/13/01 mjn Don't copy user response buffer when responding to enums *@@END_MSINTERNAL * ***************************************************************************/ #include "dncorei.h" //********************************************************************** // ------------------------------ // DNProcessEnumQuery - process enum query // // Entry: Pointer to this DNet interface object // Pointer to the associated listen operation // Pointer to protocol's enum data // // Exit: Nothing // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNProcessEnumQuery" void DNProcessEnumQuery(DIRECTNETOBJECT *const pdnObject, CAsyncOp *const pListen, const PROTOCOL_ENUM_DATA *const pEnumQueryData ) { HRESULT hResultCode; DPNMSG_ENUM_HOSTS_QUERY AppData; CPackedBuffer PackedBuffer; CRefCountBuffer *pRefCountBuffer; CAsyncOp *pAsyncOp; HANDLE hProtocol; const DN_ENUM_QUERY_PAYLOAD *pEnumQueryPayload; DN_ENUM_RESPONSE_OP_DATA *pEnumResponseOpData; DN_ENUM_RESPONSE_PAYLOAD *pEnumResponsePayload; DWORD dwPayloadOffset; IDP8ServiceProvider *pIDP8SP; SPGETCAPSDATA spGetCapsData; CServiceProvider *pSP; DWORD dwBufferCount; BOOL fNeedToReturnBuffer; DPFX(DPFPREP, 6,"Parameters: pListen [0x%p], pEnumQueryData [0x%p]",pListen,pEnumQueryData); DNASSERT( pdnObject != NULL ); DNASSERT( pListen != NULL ); DNASSERT( pEnumQueryData != NULL ); pAsyncOp = NULL; pRefCountBuffer = NULL; pIDP8SP = NULL; pSP = NULL; fNeedToReturnBuffer = FALSE; // Is this needed ? // // Ensure we are in a position to reply to this message. // We must be CONNECTED and not be HOST_MIGRATING // DNEnterCriticalSection(&pdnObject->csDirectNetObject); if (!(pdnObject->dwFlags & DN_OBJECT_FLAG_CONNECTED) || (pdnObject->dwFlags & DN_OBJECT_FLAG_HOST_MIGRATING)) { DNLeaveCriticalSection(&pdnObject->csDirectNetObject); DPFX(DPFPREP, 7, "Not connected or host is migrating (object 0x%p flags = 0x%x), ignoring enum.", pdnObject, pdnObject->dwFlags); goto Failure; } DNLeaveCriticalSection(&pdnObject->csDirectNetObject); // // Check to see if this message is for this game type. Since the application // GUID cannot be changed while the session is running, there's no need to // enter a critical section. // pEnumQueryPayload = reinterpret_cast( pEnumQueryData->ReceivedData.pBufferData ); if ( pEnumQueryPayload == NULL ) { // // no enum payload (there needs to be at least one byte!) // DPFX(DPFPREP, 4, "No enum payload, object 0x%p ignoring enum.", pdnObject); goto Failure; } dwPayloadOffset = 0; switch ( pEnumQueryPayload->QueryType ) { // // an application guid was specified, make sure it matches this application's // guid before further processing // case DN_ENUM_QUERY_WITH_APPLICATION_GUID: { if ( pEnumQueryData->ReceivedData.dwBufferSize < sizeof( DN_ENUM_QUERY_PAYLOAD ) ) { DNASSERTX( ! "Received data too small to be valid enum query with application guid!", 2 ); goto Failure; } if ( !pdnObject->ApplicationDesc.IsEqualApplicationGuid( &pEnumQueryPayload->guidApplication ) ) { #ifdef DBG GUID guidApplication; // // GUID might not be aligned, so copy it to a temp variable // guidApplication = pEnumQueryPayload->guidApplication; DPFX(DPFPREP, 7, "Application GUID {%-08.8X-%-04.4X-%-04.4X-%02.2X%02.2X-%02.2X%02.2X%02.2X%02.2X%02.2X%02.2X} doesn't match, object 0x%p, ignoring enum.", guidApplication.Data1, guidApplication.Data2, guidApplication.Data3, guidApplication.Data4[0], guidApplication.Data4[1], guidApplication.Data4[2], guidApplication.Data4[3], guidApplication.Data4[4], guidApplication.Data4[5], guidApplication.Data4[6], guidApplication.Data4[7], pdnObject); #endif // DBG goto Failure; } dwPayloadOffset = sizeof( DN_ENUM_QUERY_PAYLOAD ); break; } // // no application guid was specified, continue processing // case DN_ENUM_QUERY_WITHOUT_APPLICATION_GUID: { if ( pEnumQueryData->ReceivedData.dwBufferSize < ( sizeof( DN_ENUM_QUERY_PAYLOAD ) - sizeof( GUID ) ) ) { DNASSERTX( ! "Received data too small to be valid enum query without application guid!", 2 ); goto Failure; } dwPayloadOffset = sizeof( DN_ENUM_QUERY_PAYLOAD ) - sizeof( GUID ); break; } default: { DNASSERTX( ! "Unrecognized enum query payload type!", 2 ); goto Failure; break; } } // // buld message structure, be nice and clear the user payload pointer if // there is no payload // AppData.dwSize = sizeof( AppData ); AppData.pAddressSender = pEnumQueryData->pSenderAddress; AppData.pAddressDevice = pEnumQueryData->pDeviceAddress; DPFX(DPFPREP, 7,"AppData.pAddressSender: [0x%p]",AppData.pAddressSender); DPFX(DPFPREP, 7,"AppData.pAddressDevice: [0x%p]",AppData.pAddressDevice); if (pEnumQueryData->ReceivedData.dwBufferSize > dwPayloadOffset) { DNASSERT( pEnumQueryData->ReceivedData.pBufferData ); DNASSERT( pEnumQueryData->ReceivedData.dwBufferSize ); AppData.pvReceivedData = static_cast(static_cast(pEnumQueryData->ReceivedData.pBufferData) + dwPayloadOffset); AppData.dwReceivedDataSize = pEnumQueryData->ReceivedData.dwBufferSize - dwPayloadOffset; } else { AppData.pvReceivedData = NULL; AppData.dwReceivedDataSize = 0; } // // Response Info // AppData.pvResponseData = NULL; AppData.dwResponseDataSize = 0; AppData.pvResponseContext = NULL; // // Determine max size of response // - Get SP interface from listen SP (listen's parent) // - Get SP caps on the interface to determine the total available buffer size // - Figure out what the DNET enum response size will be // - Determine space available to user // DNASSERT(pListen->GetParent() != NULL); DNASSERT(pListen->GetParent()->GetSP() != NULL); pListen->GetParent()->GetSP()->AddRef(); pSP = pListen->GetParent()->GetSP(); if ((hResultCode = pSP->GetInterfaceRef( &pIDP8SP )) != DPN_OK) { DPFERR("Could not get ListenSP SP interface"); DisplayDNError(0,hResultCode); DNASSERT(FALSE); goto Failure; } memset( &spGetCapsData, 0x00, sizeof( SPGETCAPSDATA ) ); spGetCapsData.dwSize = sizeof( SPGETCAPSDATA ); spGetCapsData.hEndpoint = INVALID_HANDLE_VALUE; if ((hResultCode = IDP8ServiceProvider_GetCaps( pIDP8SP, &spGetCapsData )) != DPN_OK) { DPFERR("Could not get SP caps"); DisplayDNError(0,hResultCode); goto Failure; } IDP8ServiceProvider_Release( pIDP8SP ); pIDP8SP = NULL; PackedBuffer.Initialize(NULL,0); PackedBuffer.AddToFront(NULL,sizeof(DN_ENUM_RESPONSE_PAYLOAD)); pdnObject->ApplicationDesc.PackInfo(&PackedBuffer,DN_APPDESCINFO_FLAG_SESSIONNAME|DN_APPDESCINFO_FLAG_RESERVEDDATA| DN_APPDESCINFO_FLAG_APPRESERVEDDATA); AppData.dwMaxResponseDataSize = spGetCapsData.dwEnumFrameSize - PackedBuffer.GetSizeRequired(); // // pass message to the user // hResultCode = DNUserEnumQuery(pdnObject,&AppData); // // Only ENUMs which are accepted get responded to // if (hResultCode != DPN_OK) { DPFX(DPFPREP, 9, "EnumQuery rejected"); DisplayDNError(9, hResultCode); goto Failure; } // // get an async operation to track the progress of the response // if ((hResultCode = AsyncOpNew(pdnObject,&pAsyncOp)) != DPN_OK) { DPFERR("Could not allocate Async Op struct for enum response"); DisplayDNError( 0, hResultCode ); DNASSERT( FALSE ); goto Failure; } pAsyncOp->SetOpType( ASYNC_OP_ENUM_RESPONSE ); // // compute the size needed to pack up an application description with any // user data and send it back // DNEnterCriticalSection(&pdnObject->csDirectNetObject); PackedBuffer.Initialize(NULL,0); PackedBuffer.AddToFront(NULL,sizeof(DN_ENUM_RESPONSE_PAYLOAD)); hResultCode = pdnObject->ApplicationDesc.PackInfo(&PackedBuffer,DN_APPDESCINFO_FLAG_SESSIONNAME| DN_APPDESCINFO_FLAG_RESERVEDDATA|DN_APPDESCINFO_FLAG_APPRESERVEDDATA); DNASSERT( hResultCode == DPNERR_BUFFERTOOSMALL ); // // Ensure this enum response will fit in SP enum frame - only indicate this if there was a response // if (((AppData.pvResponseData != NULL) && (AppData.dwResponseDataSize != 0)) && (PackedBuffer.GetSizeRequired() + AppData.dwResponseDataSize > spGetCapsData.dwEnumFrameSize)) { DNLeaveCriticalSection(&pdnObject->csDirectNetObject); DPFERR("Enum response is too large"); DNUserReturnBuffer(pdnObject,DPNERR_ENUMRESPONSETOOLARGE,AppData.pvResponseData,AppData.pvResponseContext); goto Failure; } hResultCode = RefCountBufferNew(pdnObject, PackedBuffer.GetSizeRequired(), EnumReplyMemoryBlockAlloc, EnumReplyMemoryBlockFree, &pRefCountBuffer); if ( hResultCode != DPN_OK ) { DNASSERT( FALSE ); DNLeaveCriticalSection(&pdnObject->csDirectNetObject); goto Failure; } PackedBuffer.Initialize(pRefCountBuffer->GetBufferAddress(), pRefCountBuffer->GetBufferSize()); pEnumResponsePayload = static_cast(PackedBuffer.GetHeadAddress()); hResultCode = PackedBuffer.AddToFront(NULL,sizeof(DN_ENUM_RESPONSE_PAYLOAD)); if (hResultCode != DPN_OK) { DNASSERT(FALSE); DNLeaveCriticalSection(&pdnObject->csDirectNetObject); goto Failure; } if ((AppData.pvResponseData != NULL) && (AppData.dwResponseDataSize != 0)) { pEnumResponsePayload->dwResponseOffset = pRefCountBuffer->GetBufferSize(); pEnumResponsePayload->dwResponseSize = AppData.dwResponseDataSize; } else { pEnumResponsePayload->dwResponseOffset = 0; pEnumResponsePayload->dwResponseSize = 0; } pdnObject->ApplicationDesc.PackInfo(&PackedBuffer,DN_APPDESCINFO_FLAG_SESSIONNAME|DN_APPDESCINFO_FLAG_RESERVEDDATA| DN_APPDESCINFO_FLAG_APPRESERVEDDATA); if ( hResultCode != DPN_OK ) { DNASSERT( FALSE ); goto Failure; } DNLeaveCriticalSection(&pdnObject->csDirectNetObject); // // build enum response and send it down to the protocol // pEnumResponseOpData = pAsyncOp->GetLocalEnumResponseOpData(); pEnumResponseOpData->BufferDesc[DN_ENUM_BUFFERDESC_RESPONSE_DN_PAYLOAD].pBufferData = pRefCountBuffer->GetBufferAddress(); pEnumResponseOpData->BufferDesc[DN_ENUM_BUFFERDESC_RESPONSE_DN_PAYLOAD].dwBufferSize = pRefCountBuffer->GetBufferSize(); pAsyncOp->SetCompletion( DNCompleteEnumResponse ); pAsyncOp->SetRefCountBuffer( pRefCountBuffer ); pRefCountBuffer->Release(); pRefCountBuffer = NULL; if ((AppData.pvResponseData != NULL) && (AppData.dwResponseDataSize != 0)) { pEnumResponseOpData->BufferDesc[DN_ENUM_BUFFERDESC_RESPONSE_USER_PAYLOAD].pBufferData = static_cast(AppData.pvResponseData); pEnumResponseOpData->BufferDesc[DN_ENUM_BUFFERDESC_RESPONSE_USER_PAYLOAD].dwBufferSize = AppData.dwResponseDataSize; pEnumResponseOpData->pvUserContext = AppData.pvResponseContext; dwBufferCount = DN_ENUM_BUFFERDESC_RESPONSE_COUNT; } else { pEnumResponseOpData->BufferDesc[DN_ENUM_BUFFERDESC_RESPONSE_USER_PAYLOAD].pBufferData = NULL; pEnumResponseOpData->BufferDesc[DN_ENUM_BUFFERDESC_RESPONSE_USER_PAYLOAD].dwBufferSize = 0; pEnumResponseOpData->pvUserContext = NULL; dwBufferCount = DN_ENUM_BUFFERDESC_RESPONSE_COUNT - 1; } DNASSERT(pListen->GetParent() != NULL); DNASSERT(pListen->GetParent()->GetSP() != NULL); DNASSERT(pListen->GetParent()->GetSP()->GetHandle() != NULL); // // AddRef Protocol so that it won't go away until this completes // DNProtocolAddRef(pdnObject); pAsyncOp->AddRef(); hResultCode = DNPEnumRespond( pdnObject->pdnProtocolData, pListen->GetParent()->GetSP()->GetHandle(), pEnumQueryData->hEnumQuery, &pEnumResponseOpData->BufferDesc[DN_ENUM_BUFFERDESC_RESPONSE_DN_PAYLOAD], dwBufferCount, 0, reinterpret_cast(pAsyncOp), &hProtocol); if ( hResultCode != DPNERR_PENDING ) { pAsyncOp->Release(); DNProtocolRelease(pdnObject); goto Failure; } // // Save Protocol Handle // pAsyncOp->Lock(); if (pAsyncOp->IsCancelled()) { HRESULT hrCancel; pAsyncOp->Unlock(); DPFX(DPFPREP, 7,"Operation marked for cancel"); if ((hrCancel = DNPCancelCommand(pdnObject->pdnProtocolData,hProtocol)) == DPN_OK) { hResultCode = DPNERR_USERCANCEL; goto Failure; } DPFERR("Could not cancel operation"); DisplayDNError(0,hrCancel); pAsyncOp->Lock(); } pAsyncOp->SetSP( pSP ); pAsyncOp->SetProtocolHandle(hProtocol); pAsyncOp->Unlock(); pAsyncOp->Release(); pAsyncOp = NULL; pSP->Release(); pSP = NULL; Exit: DPFX(DPFPREP, 6,"Returning"); return; Failure: if (pAsyncOp) { pAsyncOp->Release(); pAsyncOp = NULL; } if (pRefCountBuffer) { pRefCountBuffer->Release(); pRefCountBuffer = NULL; } if (pIDP8SP) { IDP8ServiceProvider_Release(pIDP8SP); pIDP8SP = NULL; } if (pSP) { pSP->Release(); pSP = NULL; } goto Exit; } //********************************************************************** //********************************************************************** // ------------------------------ // DNProcessEnumResponse - process response to enum query // // Entry: Pointer to this DNet interface object // Pointer to the associated enum operation // Pointer to protocol's enum response data // // Exit: Nothing // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNProcessEnumResponse" void DNProcessEnumResponse(DIRECTNETOBJECT *const pdnObject, CAsyncOp *const pAsyncOp, const PROTOCOL_ENUM_RESPONSE_DATA *const pEnumResponseData) { HRESULT hResultCode; DPNMSG_ENUM_HOSTS_RESPONSE AppData; BYTE *pWorkingItem; UNALIGNED DN_ENUM_RESPONSE_PAYLOAD *pEnumResponsePayload; DPN_APPLICATION_DESC dpnAppDesc; UNALIGNED DPN_APPLICATION_DESC_INFO *pInfo; BYTE AppDescReservedData[DPN_MAX_APPDESC_RESERVEDDATA_SIZE]; DNASSERT( pdnObject != NULL ); DNASSERT( pAsyncOp != NULL ); DNASSERT( pEnumResponseData != NULL ); pWorkingItem = pEnumResponseData->ReceivedData.pBufferData; // // Unpack the ENUM response. // It will be in the following format: // // // // // // Verify buffer size // if (pEnumResponseData->ReceivedData.dwBufferSize < (sizeof(DN_ENUM_RESPONSE_PAYLOAD) + sizeof(DPN_APPLICATION_DESC_INFO))) { DPFERR("Received invalid enum response - buffer is smaller than minimum size"); goto Exit; } pEnumResponsePayload = reinterpret_cast(pEnumResponseData->ReceivedData.pBufferData); // // Application Description // pInfo = reinterpret_cast(pEnumResponsePayload + 1); memset(&dpnAppDesc,0,sizeof(DPN_APPLICATION_DESC)); if (pInfo->dwSessionNameOffset) { if ((pInfo->dwSessionNameOffset > pEnumResponseData->ReceivedData.dwBufferSize) || (pInfo->dwSessionNameOffset+pInfo->dwSessionNameSize > pEnumResponseData->ReceivedData.dwBufferSize)) { DPFERR("Received invalid enum response - session name is outside of buffer"); goto Exit; } dpnAppDesc.pwszSessionName = reinterpret_cast(pWorkingItem + pInfo->dwSessionNameOffset); } if (pInfo->dwReservedDataOffset) { if ((pInfo->dwReservedDataOffset > pEnumResponseData->ReceivedData.dwBufferSize) || (pInfo->dwReservedDataOffset+pInfo->dwReservedDataSize > pEnumResponseData->ReceivedData.dwBufferSize)) { DPFERR("Received invalid enum response - reserved data is outside of buffer"); goto Exit; } dpnAppDesc.pvReservedData = static_cast(pWorkingItem + pInfo->dwReservedDataOffset); dpnAppDesc.dwReservedDataSize = pInfo->dwReservedDataSize; // // If we understand the reserved data, we want to pad the buffer so the user doesn't // assume the data is less than DPN_MAX_APPDESC_RESERVEDDATA_SIZE bytes long. // if ((dpnAppDesc.dwReservedDataSize == sizeof(SPSESSIONDATA_XNET)) && (*((DWORD*) dpnAppDesc.pvReservedData) == SPSESSIONDATAINFO_XNET)) { SPSESSIONDATA_XNET * pSessionDataXNet; pSessionDataXNet = (SPSESSIONDATA_XNET*) AppDescReservedData; memcpy(pSessionDataXNet, dpnAppDesc.pvReservedData, dpnAppDesc.dwReservedDataSize); memset((pSessionDataXNet + 1), (((BYTE*) (&pSessionDataXNet->ullKeyID))[1] ^ ((BYTE*) (&pSessionDataXNet->guidKey))[2]), (DPN_MAX_APPDESC_RESERVEDDATA_SIZE - sizeof(SPSESSIONDATA_XNET))); dpnAppDesc.pvReservedData = AppDescReservedData; dpnAppDesc.dwReservedDataSize = DPN_MAX_APPDESC_RESERVEDDATA_SIZE; } } if (pInfo->dwApplicationReservedDataOffset) { if ((pInfo->dwApplicationReservedDataOffset > pEnumResponseData->ReceivedData.dwBufferSize) || (pInfo->dwApplicationReservedDataOffset+pInfo->dwApplicationReservedDataSize > pEnumResponseData->ReceivedData.dwBufferSize)) { DPFERR("Received invalid enum response - application reserved data is outside of buffer"); goto Exit; } dpnAppDesc.pvApplicationReservedData = static_cast(pWorkingItem + pInfo->dwApplicationReservedDataOffset); dpnAppDesc.dwApplicationReservedDataSize = pInfo->dwApplicationReservedDataSize; } dpnAppDesc.guidApplication = pInfo->guidApplication; dpnAppDesc.guidInstance = pInfo->guidInstance; dpnAppDesc.dwFlags = pInfo->dwFlags; dpnAppDesc.dwCurrentPlayers = pInfo->dwCurrentPlayers; dpnAppDesc.dwMaxPlayers = pInfo->dwMaxPlayers; dpnAppDesc.dwSize = sizeof(DPN_APPLICATION_DESC); // // Fill in AppData // AppData.dwSize = sizeof( AppData ); AppData.pAddressSender = pEnumResponseData->pSenderAddress; AppData.pAddressDevice = pEnumResponseData->pDeviceAddress; AppData.pApplicationDescription = &dpnAppDesc; AppData.dwRoundTripLatencyMS = pEnumResponseData->dwRoundTripTime; if (pEnumResponsePayload->dwResponseOffset) { if ((pEnumResponsePayload->dwResponseOffset > pEnumResponseData->ReceivedData.dwBufferSize) || (pEnumResponsePayload->dwResponseOffset+pEnumResponsePayload->dwResponseSize > pEnumResponseData->ReceivedData.dwBufferSize)) { DPFERR("Received invalid enum response - response data is outside of buffer"); goto Exit; } AppData.pvResponseData = (pEnumResponseData->ReceivedData.pBufferData + pEnumResponsePayload->dwResponseOffset); AppData.dwResponseDataSize = pEnumResponsePayload->dwResponseSize; } else { AppData.pvResponseData = NULL; AppData.dwResponseDataSize = 0; } AppData.pvUserContext = pAsyncOp->GetContext(); // // pass message to the user // hResultCode = DNUserEnumResponse(pdnObject,&AppData); // // Check to see if this is to be cancelled // if (hResultCode != DPN_OK) { CAsyncOp *pCancelOp = NULL; // // Get top level operation (may be async op handle) // pAsyncOp->Lock(); pCancelOp = pAsyncOp; while (pCancelOp->IsChild()) { DNASSERT(pCancelOp->GetParent() != NULL); pCancelOp = pCancelOp->GetParent(); } pCancelOp->AddRef(); pAsyncOp->Unlock(); // // Cancel // DNCancelChildren(pdnObject,pCancelOp); pCancelOp->Release(); pCancelOp = NULL; } Exit: return; }