windows-nt/Source/XPSP1/NT/net/rras/ip/nath323/t120lc.cpp

576 lines
19 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
#include "stdafx.h"
#include "portmgmt.h"
#include "timerval.h"
#include "cbridge.h"
#include "main.h"
// destructor
// virtual
T120_LOGICAL_CHANNEL::~T120_LOGICAL_CHANNEL(
)
{
// Free the ports if they have been allocated
FreePorts();
}
// All params in host order
HRESULT
T120_LOGICAL_CHANNEL::SetPorts(
DWORD T120ConnectToIPAddr,
WORD T120ConnectToPort,
DWORD T120ListenOnIPAddr,
DWORD T120ConnectFromIPAddr
)
{
HRESULT HResult;
// CODEWORK: Decide on the maximum number of TCP/IP connections to
// to allow to the same port. CurtSm suggests 8. MaxM thinks 4 for
// NM3.0 and 5 in general - currently allow 5.
// Allocate m_T120ConnectFromPorts and m_T120ListenOnPort
// Note that I am using the same routine I use to reserve
// ports for RTP/RTCP. This call reserves a pair of ports.
// CODEWORK: The port pool should have functions which
// reserve more than 2 ports (6 ports).
HResult = PortPoolAllocRTPPort(&m_T120ListenOnPort);
if (FAILED(HResult))
{
return HResult;
}
// This port also has been reserved by the above function call.
m_T120ConnectFromPorts[0] = m_T120ListenOnPort + 1;
for (int i = 1; i < MAX_T120_TCP_CONNECTIONS_ALLOWED; i += 2)
{
HResult = PortPoolAllocRTPPort(&m_T120ConnectFromPorts[i]);
if (FAILED(HResult))
{
return HResult;
}
// This port also has been reserved by the above function call.
// CODEWORK: Note that if MAX_T120_TCP_CONNECTIONS_ALLOWED is
// even then we will be wasting a port. We need to fix the port
// reservation function for this.
if ((i + 1) < MAX_T120_TCP_CONNECTIONS_ALLOWED)
m_T120ConnectFromPorts[i+1] = m_T120ConnectFromPorts[i] + 1;
}
m_T120ConnectToIPAddr = T120ConnectToIPAddr;
m_T120ConnectToPort = T120ConnectToPort;
m_T120ListenOnIPAddr = T120ListenOnIPAddr;
m_T120ConnectFromIPAddr = T120ConnectFromIPAddr;
HResult = CreateNatRedirect();
if (FAILED(HResult))
{
return HResult;
}
_ASSERTE(S_OK == HResult);
return S_OK;
}
HRESULT
T120_LOGICAL_CHANNEL::FreePorts()
{
HRESULT Result;
CancelNatRedirect();
if (m_T120ListenOnPort != 0)
{
Result = PortPoolFreeRTPPort(m_T120ListenOnPort);
if (FAILED(Result))
{
DebugF( _T("T120_LOGICAL_CHANNEL::FreePorts: PortPoolFreeRTPPort ")
_T("failed error: 0x%x\n"),
Result);
}
m_T120ListenOnPort = 0;
m_T120ConnectFromPorts[0] = 0;
}
for (int i = 1; i < MAX_T120_TCP_CONNECTIONS_ALLOWED; i += 2)
{
if (m_T120ConnectFromPorts[i] != 0)
{
Result = PortPoolFreeRTPPort(m_T120ConnectFromPorts[i]);
if (FAILED(Result))
{
DebugF( _T("T120_LOGICAL_CHANNEL::FreePorts: PortPoolFreeRTPPort ")
_T("failed error: 0x%x\n"),
Result);
}
m_T120ConnectFromPorts[i] = 0;
if ((i + 1) < MAX_T120_TCP_CONNECTIONS_ALLOWED)
m_T120ConnectFromPorts[i+1] = 0;
}
}
return S_OK;
}
///////////////////////////////////////////////////////////////////////////////
// //
// Routines for setting up and tearing down NAT Redirects //
// //
///////////////////////////////////////////////////////////////////////////////
// This is defined in rtplc.cpp
// This should not be required. But currently there is a bug in the API impl.
// Create the NAT redirect
HRESULT
T120_LOGICAL_CHANNEL::CreateNatRedirect(
)
{
// XXX Actually so many checks are not needed
if (m_T120ConnectToIPAddr == INADDR_NONE ||
m_T120ConnectToPort == 0 ||
m_T120ListenOnPort == 0 ||
m_T120ConnectFromPorts[0] == 0)
{
DebugF( _T("T120_LOGICAL_CHANNEL::CreateNatRedirect() Ports not set")
_T("m_120ConnectToIPAddr: %d.%d.%d.%d\n"),
BYTES0123(m_T120ConnectToIPAddr)
);
// INVALID state or some such
return E_UNEXPECTED;
}
for (int i = 0; i < MAX_T120_TCP_CONNECTIONS_ALLOWED; i ++)
{
ULONG Status;
Status = NatCreateRedirectEx (
NatHandle,
NatRedirectFlagNoTimeout | NatRedirectFlagLoopback, // flags
IPPROTO_TCP, // UDP
htonl(m_T120ListenOnIPAddr), // source packet dest address (local)
htons(m_T120ListenOnPort), // source packet dest port (local)
htonl(0), // wildcard - source packet source address
htons(0), // wildcard - source packet source port
htonl(m_T120ConnectToIPAddr), // NewDestinationAddress
htons(m_T120ConnectToPort), // NewDestinationPort
htonl(m_T120ConnectFromIPAddr), // NewSourceAddress
htons(m_T120ConnectFromPorts[i]), // NewSourcePort
NULL, // RestrictedAdapterIndex
NULL, // CompletionRoutine
NULL, // CompletionContext
NULL // NotifyEvent
);
if (Status != STATUS_SUCCESS) {
DebugF (_T ("T120: failed to set up redirect (*.* -> %08X:%04X) => (%08X:%04X -> %08X:%04X).\n"),
m_T120ListenOnIPAddr, // source packet dest address (local)
m_T120ListenOnPort, // source packet dest port (local)
m_T120ConnectFromIPAddr, // NewSourceAddress
m_T120ConnectFromPorts[i], // NewSourcePort
m_T120ConnectToIPAddr, // NewDestinationAddress
m_T120ConnectToPort); // NewDestinationPort
return (HRESULT) Status;
}
else
{
DebugF (_T ("T120: 0x%x set up redirect (*.* -> %08X:%04X) => (%08X:%04X -> %08X:%04X).\n"),
&GetCallBridge (),
m_T120ListenOnIPAddr, // source packet dest address (local)
m_T120ListenOnPort, // source packet dest port (local)
m_T120ConnectFromIPAddr, // NewSourceAddress
m_T120ConnectFromPorts[i], // NewSourcePort
m_T120ConnectToIPAddr, // NewDestinationAddress
m_T120ConnectToPort); // NewDestinationPort
}
}
return S_OK;
}
void
T120_LOGICAL_CHANNEL::CancelNatRedirect(
)
{
// CODEWORK: CODEWORK:
// Note that this routine gets called every time the destructor
// gets called and this means that only half of the redirects could
// have been established or whatever. So we need to check whether
// each of the redirects has been established. For this purpose
// it is probably advisable to have one more field storing whether
// the redirect has been estd. so that we can appropriately clean
// it up. This field should also be useful in the WSP filter scenario
// where we don't actually store the ports.
// if our current state is LC_STATE_OPEN_ACK_RCVD or
// LC_STATE_OPENED_CLOSE_RCVD, we have a NAT mapping
for (int i = 0; i < MAX_T120_TCP_CONNECTIONS_ALLOWED; i ++)
{
#if 1
// XXX just a hack for now
if (m_T120ConnectFromPorts[i] == 0)
continue;
#endif 1
ULONG Win32ErrorCode;
DebugF (_T("T120: 0x%x cancels redirect (*:* -> %08X:%04X) => (%08X:%04X -> %08X:%04X).\n"),
&GetCallBridge (),
m_T120ListenOnIPAddr, // source packet dest address (local)
m_T120ListenOnPort, // source packet dest port (local)
m_T120ConnectFromIPAddr, // NewSourceAddress
m_T120ConnectFromPorts[i], // NewSourcePort
m_T120ConnectToIPAddr, // NewDestinationAddress
m_T120ConnectToPort); // NewDestinationPort
Win32ErrorCode =
NatCancelRedirect(
NatHandle,
IPPROTO_TCP, // UDP
htonl(m_T120ListenOnIPAddr), // source packet dest address (local)
htons(m_T120ListenOnPort), // source packet dest port (local)
htonl(0), // wildcard - source packet source address
htons(0), // wildcard - source packet source port
htonl(m_T120ConnectToIPAddr), // NewDestinationAddress
htons(m_T120ConnectToPort), // NewDestinationPort
htonl(m_T120ConnectFromIPAddr), // NewSourceAddress
htons(m_T120ConnectFromPorts[i]) // NewSourcePort
);
}
return;
}
///////////////////////////////////////////////////////////////////////////////
// //
// Routines for processing H.245 PDUs //
// //
///////////////////////////////////////////////////////////////////////////////
// all of these are available in the OPEN LOGICAL CHANNEL message
// it modifies the OLC PDU and passes it on to the other H245
// instance for forwarding ???
HRESULT
T120_LOGICAL_CHANNEL::HandleOpenLogicalChannelPDU(
IN H245_INFO &H245Info,
IN MEDIA_TYPE MediaType,
IN WORD LogicalChannelNumber,
IN BYTE SessionId,
IN DWORD T120ConnectToIPAddr,
IN WORD T120ConnectToPort,
IN OUT MultimediaSystemControlMessage *pH245pdu
)
/*++
Routine Description:
This routine handles a T120 OLC PDU. The T120_LOGICAL_CHANNEL
is create by H245_INFO::HandleOpenLogicalChannelPDU().
If T120ConnectToIPAddr and Port are specified, then
m_T120ListenOnPort and m_T120ConnectFromPorts are allocated and
the listen address field in pH245pdu are replaced with an IP address
and port on the other edge of the proxy.
Arguments:
H245Info -
MediaType -
LogicalChannelNumber -
SessionId -
T120ConnectToIPAddr -
T120ConnectToPort -
pH245pdu - If the T120ConnectToIPAddr and Port are specified then
the listen address field in the H245 pdu is replaced with an
IP address and port on the other edge of the proxy.
Return Values:
S_OK on success.
E_INVALIDARG if the PDU is invalid.
--*/
{
// CODEWORK: assert that we are dealing with a T120 PDU
// this should be the first call to this instance after its
// created - hence, these fields must be as asserted
_ASSERTE(LC_STATE_NOT_INIT == m_LogicalChannelState);
_ASSERTE(NULL == m_pH245Info);
HRESULT HResult = E_FAIL;
m_pH245Info = &H245Info;
// If the IP address that we need to connect to is specified in the
// OLC PDU, then we need to allocate the port for listening on the
// other interface.
if (T120ConnectToIPAddr != INADDR_NONE)
{
HResult = SetPorts(
T120ConnectToIPAddr,
T120ConnectToPort,
ntohl (m_pH245Info->GetOtherH245Info().GetSocketInfo().LocalAddress.sin_addr.s_addr),
// listen on other h245 local address
ntohl (m_pH245Info->m_SocketInfo.LocalAddress.sin_addr.s_addr)
// connect from our local address
);
if (FAILED(HResult))
{
DebugF( _T("T120_LOGICAL_CHANNEL::HandleOpenLogicalChannelPDU, ")
_T("failed to set its ports, returning 0x%x\n"),
HResult);
return HResult;
}
//_ASSERTE(S_FALSE != HResult);
OpenLogicalChannel &OlcPDU =
pH245pdu->u.request.u.openLogicalChannel;
// modify the OLC PDU by replacing the RTCP address/port
// with the h245 address and RTCP port
FillH245TransportAddress(
m_T120ListenOnIPAddr,
m_T120ListenOnPort,
OlcPDU.separateStack.networkAddress.u.localAreaAddress
);
}
// Should the part below be pushed into H245_INFO::HandleOpenLogicalChannelPDU ?????
// let the other H245 instance process the PDU
HResult = m_pH245Info->GetOtherH245Info().ProcessMessage(
pH245pdu);
if (FAILED(HResult))
{
DebugF( _T("T120_LOGICAL_CHANNEL::HandleOpenLogicalChannelPDU")
_T("(&H245Info, %u, %u, %d.%d.%d.%d, %u, 0x%x, 0x%x)")
_T("other H245 instance failed to process OLC PDU, returning 0x%x\n"),
LogicalChannelNumber, SessionId, BYTES0123(T120ConnectToIPAddr),
T120ConnectToPort, pH245pdu, HResult);
return HResult;
}
// start timer for a response
// TO DO *** creating timers after queueing the send is sufficient.
// change back earlier policy of creating these only after the send
// callback (to be consistent). creating timers that way would be too
// complex for logical channels
HResult = CreateTimer(LC_POST_OPEN_TIMER_VALUE);
if (FAILED(HResult))
{
DebugF (_T("T120: 0x%x failed to create timer for duration %d milliseconds ('Open Logical Channel'). Error - %x.\n"),
&GetCallBridge (),
LC_POST_OPEN_TIMER_VALUE,
HResult);
return HResult;
}
DebugF (_T("T120: 0x%x created timer for duration %d milliseconds ('Open Logical Channel').\n"),
&GetCallBridge (),
LC_POST_OPEN_TIMER_VALUE);
//_ASSERTE(S_FALSE != HResult);
InitLogicalChannel(&H245Info, MediaType,
LogicalChannelNumber,
SessionId, LC_STATE_OPEN_RCVD);
// transition state to LC_STATE_OPEN_RCVD
m_LogicalChannelState = LC_STATE_OPEN_RCVD;
return S_OK;
}
// If there is no T.120 Listen address in the PDU
// T120ConnectToIPAddr will contain INADDR_NONE
HRESULT
T120_LOGICAL_CHANNEL::CheckOpenLogicalChannelAckPDU(
IN OpenLogicalChannelAck &OlcAckPDU,
OUT DWORD &T120ConnectToIPAddr,
OUT WORD &T120ConnectToPort
)
/*++
Routine Description:
Arguments:
OlcAckPDU -
T120ConnectToIPAddr -
T120ConnectToPort -
Return Values:
S_OK on success.
E_INVALIDARG if the PDU is invalid.
--*/
{
HRESULT HResult = S_OK;
// These are the return values in case of a failure
// or if the address is not present in the PDU
T120ConnectToIPAddr = INADDR_NONE;
T120ConnectToPort = 0;
// there should be reverse logical channel parameters
if (!(OpenLogicalChannelAck_reverseLogicalChannelParameters_present &
OlcAckPDU.bit_mask))
{
DebugF( _T("T120_LOGICAL_CHANNEL::CheckOpenLogicalChannelAckPDU, NO")
_T("reverse logical channel params, returning E_INVALIDARG\n"));
return E_INVALIDARG;
}
// there should be a separate stack if we do not have
// a T.120 end point address to connect to (from the OLC PDU).
if (!(OpenLogicalChannelAck_separateStack_present &
OlcAckPDU.bit_mask) &&
m_T120ConnectToIPAddr == INADDR_NONE)
{
DebugF( _T("T120_LOGICAL_CHANNEL::CheckOpenLogicalChannelAckPDU, ")
_T("NO separate stack, returning E_INVALIDARG\n"));
return E_INVALIDARG;
}
if (OpenLogicalChannelAck_separateStack_present &
OlcAckPDU.bit_mask)
{
HResult = GetT120ConnectToAddress(
OlcAckPDU.separateStack,
T120ConnectToIPAddr,
T120ConnectToPort
);
}
return HResult;
}
HRESULT
T120_LOGICAL_CHANNEL::ProcessOpenLogicalChannelAckPDU(
IN MultimediaSystemControlMessage *pH245pdu
)
/*++
Routine Description:
Arguments:
pH245pdu -
Return Values:
S_OK on success.
E_INVALIDARG if the PDU is invalid.
--*/
{
//The type of this pdu should be OLC Ack
_ASSERTE(pH245pdu->u.response.choice == openLogicalChannelAck_chosen);
HRESULT HResult = E_FAIL;
OpenLogicalChannelAck &OlcAckPDU =
pH245pdu->u.response.u.openLogicalChannelAck;
switch(m_LogicalChannelState)
{
case LC_STATE_OPEN_RCVD:
DWORD T120ConnectToIPAddr;
WORD T120ConnectToPort;
HResult = CheckOpenLogicalChannelAckPDU(
OlcAckPDU,
T120ConnectToIPAddr,
T120ConnectToPort
);
if (FAILED(HResult))
{
return HResult;
}
_ASSERTE(S_OK == HResult);
if (T120ConnectToIPAddr != INADDR_NONE)
{
HResult = SetPorts(
T120ConnectToIPAddr,
T120ConnectToPort,
ntohl (m_pH245Info->m_SocketInfo.LocalAddress.sin_addr.s_addr),
// listen on our local address
ntohl (m_pH245Info->GetOtherH245Info().GetSocketInfo().LocalAddress.sin_addr.s_addr)
// connect from other h245 local address
);
if (FAILED(HResult))
{
return HResult;
}
// modify the OLC PDU by replacing the RTCP address/port
// with the h245 address and RTCP port
FillH245TransportAddress(
m_T120ListenOnIPAddr,
m_T120ListenOnPort,
OlcAckPDU.separateStack.networkAddress.u.localAreaAddress
);
}
// reset timer, we must have one (ignore error code if any)
//_ASSERTE(NULL != m_TimerHandle);
TimprocCancelTimer();
DebugF (_T("T120: 0x%x cancelled timer.\n"),
&GetCallBridge ());
// transition to LC_STATE_OPEN_ACK_RCVD
m_LogicalChannelState = LC_STATE_OPEN_ACK_RCVD;
break;
case LC_STATE_CLOSE_RCVD:
// if we have received a close logical channel PDU, we must throw
// OLC ACKs away and continue to wait
DebugF( _T("T120_LOGICAL_CHANNEL::ProcessOpenLogicalChannelAckPDU")
_T("(&%x), in close state %d, returning E_INVALIDARG\n"),
pH245pdu, m_LogicalChannelState);
return E_INVALIDARG;
break;
case LC_STATE_NOT_INIT:
case LC_STATE_OPEN_ACK_RCVD:
case LC_STATE_OPENED_CLOSE_RCVD:
default:
DebugF( _T("T120_LOGICAL_CHANNEL::ProcessOpenLogicalChannelAckPDU")
_T("(&%x), in state %d, returning E_UNEXPECTED\n"),
pH245pdu, m_LogicalChannelState);
_ASSERTE(FALSE);
return E_UNEXPECTED;
break;
} // switch (m_LogicalChannelState)
return HResult;
}