windows-nt/Source/XPSP1/NT/net/rras/ip/nathlp/natsvc/cudpbcast.cpp

759 lines
15 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 2001, Microsoft Corporation
Module Name:
cudpbcast.cpp
Abstract:
Implementation of CUdpBroadcastMapper -- support for mapping
a public UDP port to the private network's broadcast address.
Author:
Jonathan Burstein (jonburs) 12 April 2001
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#define INADDR_LOOPBACK_NO 0x0100007f // 127.0.0.1 in network order
//
// ATL Methods
//
HRESULT
CUdpBroadcastMapper::FinalConstruct()
{
HRESULT hr = S_OK;
DWORD dwError;
//
// Create our UDP listening socket and obtain
// its port.
//
dwError =
NhCreateDatagramSocket(
INADDR_LOOPBACK_NO,
0,
&m_hsUdpListen
);
if (ERROR_SUCCESS == dwError)
{
m_usUdpListenPort = NhQueryPortSocket(m_hsUdpListen);
}
else
{
hr = HRESULT_FROM_WIN32(dwError);
}
if (SUCCEEDED(hr))
{
//
// Create our raw UDP send socket
//
dwError = NhCreateRawDatagramSocket(&m_hsUdpRaw);
if (ERROR_SUCCESS != dwError)
{
hr = HRESULT_FROM_WIN32(dwError);
}
}
if (SUCCEEDED(hr))
{
//
// Obtain a handle to the NAT
//
dwError = NatOpenDriver(&m_hNat);
if (ERROR_SUCCESS != dwError)
{
hr = HRESULT_FROM_WIN32(dwError);
}
}
return hr;
}
HRESULT
CUdpBroadcastMapper::FinalRelease()
{
if (INVALID_SOCKET != m_hsUdpListen)
{
closesocket(m_hsUdpListen);
}
if (INVALID_SOCKET != m_hsUdpRaw)
{
closesocket(m_hsUdpRaw);
}
if (NULL != m_hNat)
{
CloseHandle(m_hNat);
}
ASSERT(IsListEmpty(&m_MappingList));
return S_OK;
}
//
// Initialization
//
HRESULT
CUdpBroadcastMapper::Initialize(
PCOMPONENT_REFERENCE pComponentReference
)
{
HRESULT hr = S_OK;
if (NULL != pComponentReference)
{
m_pCompRef = pComponentReference;
}
else
{
hr = E_INVALIDARG;
}
return hr;
}
//
// IUdpBroadcastMapper methods
//
STDMETHODIMP
CUdpBroadcastMapper::CreateUdpBroadcastMapping(
USHORT usPublicPort,
DWORD dwPublicInterfaceIndex,
ULONG ulDestinationAddress,
VOID **ppvCookie
)
{
HRESULT hr = S_OK;
CUdpBroadcast *pMapping;
CUdpBroadcast *pDuplicate;
PLIST_ENTRY pInsertionPoint;
ULONG ulError;
if (NULL != ppvCookie)
{
*ppvCookie = NULL;
if (0 == usPublicPort
|| 0 == dwPublicInterfaceIndex
|| 0 == ulDestinationAddress)
{
hr = E_INVALIDARG;
}
else if (!m_fActive)
{
//
// We've already been shutdown
//
hr = E_UNEXPECTED;
}
}
else
{
hr = E_POINTER;
}
if (SUCCEEDED(hr))
{
pMapping = new CUdpBroadcast(
usPublicPort,
dwPublicInterfaceIndex,
ulDestinationAddress
);
if (NULL == pMapping)
{
hr = E_OUTOFMEMORY;
}
}
if (SUCCEEDED(hr))
{
//
// Check for duplicate and insert on list. It's OK for
// the entry to be on the list before we've created the
// dynamic redirect for it.
//
Lock();
pDuplicate =
LookupMapping(
usPublicPort,
dwPublicInterfaceIndex,
&pInsertionPoint
);
if (NULL == pDuplicate)
{
InsertTailList(pInsertionPoint, &pMapping->Link);
}
else
{
delete pMapping;
hr = HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS);
}
Unlock();
}
if (SUCCEEDED(hr))
{
//
// Create the dynamic redirect for this entry
//
ulError =
NatCreateDynamicAdapterRestrictedPortRedirect(
NatRedirectFlagReceiveOnly,
NAT_PROTOCOL_UDP,
usPublicPort,
INADDR_LOOPBACK_NO,
m_usUdpListenPort,
dwPublicInterfaceIndex,
0,
&pMapping->hDynamicRedirect
);
if (ERROR_SUCCESS != ulError)
{
hr = HRESULT_FROM_WIN32(ulError);
Lock();
RemoveEntryList(&pMapping->Link);
Unlock();
delete pMapping;
}
}
if (SUCCEEDED(hr))
{
//
// Make sure that we've posted a read on our UDP socket
//
hr = StartUdpRead();
if (SUCCEEDED(hr))
{
*ppvCookie = reinterpret_cast<PVOID>(pMapping);
}
else
{
NatCancelDynamicRedirect(pMapping->hDynamicRedirect);
Lock();
RemoveEntryList(&pMapping->Link);
Unlock();
delete pMapping;
}
}
return hr;
}
STDMETHODIMP
CUdpBroadcastMapper::CancelUdpBroadcastMapping(
VOID *pvCookie
)
{
HRESULT hr = S_OK;
CUdpBroadcast *pMapping;
ULONG ulError;
if (NULL != pvCookie)
{
pMapping = reinterpret_cast<CUdpBroadcast*>(pvCookie);
Lock();
RemoveEntryList(&pMapping->Link);
Unlock();
ASSERT(NULL != pMapping->hDynamicRedirect);
ulError = NatCancelDynamicRedirect(pMapping->hDynamicRedirect);
if (ERROR_SUCCESS != ulError)
{
hr = HRESULT_FROM_WIN32(ulError);
}
delete pMapping;
}
else
{
hr = E_INVALIDARG;
}
return hr;
}
STDMETHODIMP
CUdpBroadcastMapper::Shutdown()
{
InterlockedExchange(
reinterpret_cast<LPLONG>(&m_fActive),
FALSE
);
//
// We need to close our read socket handle so that any
// pending reads complete. We don't need to close our
// raw send socket since for that completion will never
// be blocked waiting for an incoming packet.
//
Lock();
if (INVALID_SOCKET != m_hsUdpListen)
{
closesocket(m_hsUdpListen);
m_hsUdpListen = INVALID_SOCKET;
}
Unlock();
return S_OK;
}
//
// Protected methods
//
CUdpBroadcast*
CUdpBroadcastMapper::LookupMapping(
USHORT usPublicPort,
DWORD dwInterfaceIndex,
PLIST_ENTRY * ppInsertionPoint
)
{
PLIST_ENTRY pLink;
CUdpBroadcast *pMapping;
CUdpBroadcast *pMappingToReturn = NULL;
//
// The caller should have already locked the object before calling
// this method to guarantee that what we return will still be
// valid. However, we'll still grab the lock again to ensure that
// it's safe to traverse the list.
//
Lock();
for (pLink = m_MappingList.Flink;
pLink != &m_MappingList;
pLink = pLink->Flink)
{
pMapping = CONTAINING_RECORD(pLink, CUdpBroadcast, Link);
if (pMapping->usPublicPort < usPublicPort)
{
continue;
}
else if (pMapping->usPublicPort > usPublicPort)
{
break;
}
//
// Primary key matches, check secondary key
//
if (pMapping->dwInterfaceIndex < dwInterfaceIndex)
{
continue;
}
else if (pMapping->dwInterfaceIndex > dwInterfaceIndex)
{
break;
}
//
// Found it.
//
pMappingToReturn = pMapping;
break;
}
Unlock();
if (NULL == pMappingToReturn
&& NULL != ppInsertionPoint)
{
*ppInsertionPoint = pLink;
}
return pMappingToReturn;
}
HRESULT
CUdpBroadcastMapper::StartUdpRead()
{
HRESULT hr = S_OK;
ULONG ulError;
LONG fReadStarted;
Lock();
if (!m_fReadStarted)
{
AddRef();
ulError =
NhReadDatagramSocket(
m_pCompRef,
m_hsUdpListen,
NULL,
UdpReadCompletionRoutine,
this,
m_pCompRef
);
if (ERROR_SUCCESS == ulError)
{
m_fReadStarted = TRUE;
}
else
{
hr = HRESULT_FROM_WIN32(ulError);
Release();
}
}
Unlock();
return hr;
}
VOID
CUdpBroadcastMapper::UdpReadCompletionRoutine(
ULONG ulError,
ULONG ulBytesTransferred,
PNH_BUFFER pBuffer
)
{
CUdpBroadcastMapper *pMapper;
PCOMPONENT_REFERENCE pCompRef;
pMapper = reinterpret_cast<CUdpBroadcastMapper*>(pBuffer->Context);
pCompRef = reinterpret_cast<PCOMPONENT_REFERENCE>(pBuffer->Context2);
ASSERT(NULL != pMapper);
ASSERT(NULL != pCompRef);
//
// Do the actual work
//
pMapper->ProcessUdpRead(ulError, ulBytesTransferred, pBuffer);
//
// Release the references obtained on both the object and
// the component
//
pMapper->Release();
ReleaseComponentReference(pCompRef);
}
VOID
CUdpBroadcastMapper::ProcessUdpRead(
ULONG ulError,
ULONG ulBytesTransferred,
PNH_BUFFER pBuffer
)
{
NAT_KEY_SESSION_MAPPING_EX_INFORMATION MappingInfo;
ULONG ulBufferSize;
DWORD dwError;
CUdpBroadcast *pMapping;
ULONG ulDestinationAddress = 0;
//
// If an error occured check to see if we should repost
// the read. If we're not active release the buffer
// and exit.
//
if (ERROR_SUCCESS != ulError || !Active())
{
Lock();
if (Active()
&& !NhIsFatalSocketError(ulError)
&& INVALID_SOCKET != m_hsUdpListen)
{
AddRef();
dwError =
NhReadDatagramSocket(
m_pCompRef,
m_hsUdpListen,
pBuffer,
UdpReadCompletionRoutine,
this,
m_pCompRef
);
if (ERROR_SUCCESS != dwError)
{
Release();
NhReleaseBuffer(pBuffer);
}
}
else
{
NhReleaseBuffer(pBuffer);
}
Unlock();
return;
}
//
// Lookup the original destination address for this packet.
//
ulBufferSize = sizeof(MappingInfo);
dwError =
NatLookupAndQueryInformationSessionMapping(
m_hNat,
NAT_PROTOCOL_UDP,
INADDR_LOOPBACK_NO,
m_usUdpListenPort,
pBuffer->ReadAddress.sin_addr.s_addr,
pBuffer->ReadAddress.sin_port,
&MappingInfo,
&ulBufferSize,
NatKeySessionMappingExInformation
);
if (ERROR_SUCCESS == dwError)
{
//
// See if we have a port mapping for the original destination,
// and, if so, get the destination address
//
Lock();
pMapping =
LookupMapping(
MappingInfo.DestinationPort,
MappingInfo.AdapterIndex,
NULL
);
if (NULL != pMapping)
{
ulDestinationAddress = pMapping->ulDestinationAddress;
}
Unlock();
}
if (0 != ulDestinationAddress)
{
//
// Construct the new packet and send it on its way
//
BuildAndSendRawUdpPacket(
ulDestinationAddress,
MappingInfo.DestinationPort,
pBuffer
);
}
//
// If we're still active repost the read, otherwise free the
// buffer.
//
Lock();
if (Active()
&& INVALID_SOCKET != m_hsUdpListen)
{
AddRef();
dwError =
NhReadDatagramSocket(
m_pCompRef,
m_hsUdpListen,
pBuffer,
UdpReadCompletionRoutine,
this,
m_pCompRef
);
if (ERROR_SUCCESS != dwError)
{
Release();
NhReleaseBuffer(pBuffer);
}
}
else
{
NhReleaseBuffer(pBuffer);
}
Unlock();
}
HRESULT
CUdpBroadcastMapper::BuildAndSendRawUdpPacket(
ULONG ulDestinationAddress,
USHORT usDestinationPort,
PNH_BUFFER pPacketData
)
{
HRESULT hr = S_OK;
PNH_BUFFER pBuffer;
ULONG ulPacketSize;
PIP_HEADER pIpHeader;
UDP_HEADER UNALIGNED *pUdpHeader;
PUCHAR pucData;
DWORD dwError;
//
// Allocate a buffer large enough for the headers and packet data
//
ulPacketSize =
sizeof(IP_HEADER) + sizeof(UDP_HEADER) + pPacketData->BytesTransferred;
pBuffer = NhAcquireVariableLengthBuffer(ulPacketSize);
if (NULL != pBuffer)
{
//
// Locate offsets w/in the buffer
//
pIpHeader = reinterpret_cast<PIP_HEADER>(pBuffer->Buffer);
pUdpHeader =
reinterpret_cast<UDP_HEADER UNALIGNED *>(pBuffer->Buffer + sizeof(IP_HEADER));
pucData = pBuffer->Buffer + sizeof(IP_HEADER) + sizeof(UDP_HEADER);
//
// Copy over the packet data
//
CopyMemory(pucData, pPacketData->Buffer, pPacketData->BytesTransferred);
//
// Fill out the IP header
//
pIpHeader->VersionAndHeaderLength =
(4 << 4) | (sizeof(IP_HEADER) / sizeof(ULONG));
pIpHeader->TypeOfService = 0;
pIpHeader->TotalLength = htons(static_cast<USHORT>(ulPacketSize));
pIpHeader->Identification = htons(++m_usIpId);
pIpHeader->OffsetAndFlags = 0;
pIpHeader->TimeToLive = 128;
pIpHeader->Protocol = NAT_PROTOCOL_UDP;
pIpHeader->Checksum = 0;
pIpHeader->SourceAddress = pPacketData->ReadAddress.sin_addr.s_addr;
pIpHeader->DestinationAddress = ulDestinationAddress;
//
// Fill out the UDP header
//
pUdpHeader->SourcePort = pPacketData->ReadAddress.sin_port;
pUdpHeader->DestinationPort = usDestinationPort;
pUdpHeader->Length =
htons(
static_cast<USHORT>(
sizeof(UDP_HEADER) + pPacketData->BytesTransferred
)
);
pUdpHeader->Checksum = 0;
//
// Send the buffer on its way
//
AddRef();
dwError =
NhWriteDatagramSocket(
m_pCompRef,
m_hsUdpRaw,
ulDestinationAddress,
usDestinationPort,
pBuffer,
ulPacketSize,
RawWriteCompletionRoutine,
this,
m_pCompRef
);
if (ERROR_SUCCESS != dwError)
{
Release();
NhReleaseBuffer(pBuffer);
hr = HRESULT_FROM_WIN32(dwError);
}
}
else
{
hr = E_OUTOFMEMORY;
}
return hr;
}
VOID
CUdpBroadcastMapper::RawWriteCompletionRoutine(
ULONG ulError,
ULONG ulBytesTransferred,
PNH_BUFFER pBuffer
)
{
CUdpBroadcastMapper *pMapper;
PCOMPONENT_REFERENCE pCompRef;
pMapper = reinterpret_cast<CUdpBroadcastMapper*>(pBuffer->Context);
pCompRef = reinterpret_cast<PCOMPONENT_REFERENCE>(pBuffer->Context2);
ASSERT(NULL != pMapper);
ASSERT(NULL != pCompRef);
//
// Free the passed-in buffer
//
NhReleaseBuffer(pBuffer);
//
// Release the references obtained on both the object and
// the component
//
pMapper->Release();
ReleaseComponentReference(pCompRef);
}