Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1556 lines
44 KiB

/*---------------------------------------------------
Copyright (c) 1998, Microsoft Corporation
File: h245.cpp
Purpose:
Contains the H245 related function definitions.
These include the base classes -
LOGICAL_CHANNEL
H245_INFO
History:
1. written as part of cbridge.cpp
Byrisetty Rajeev (rajeevb) 12-Jun-1998
2. moved to a separate file on 21-Aug-1998. This removes
atl, rend, tapi headers and decreases file size
---------------------------------------------------*/
#include "stdafx.h"
#include "portmgmt.h"
#include "timerval.h"
#include "cbridge.h"
#include "main.h"
#if DBG
#define NUM_H245_MEDIA_TYPES 8
TCHAR* h245MediaTypes[NUM_H245_MEDIA_TYPES + 1] = {
_T("Unknown"),
_T("Non-standard"),
_T("Null-data"),
_T("Video"),
_T("Audio"),
_T("Data"),
_T("Encrypted"),
_T("H235 control"),
_T("H235 media")
};
#define NUM_H245_REQUEST_PDU_TYPES 13
TCHAR* h245RequestTypes[NUM_H245_REQUEST_PDU_TYPES + 1] = {
_T("Unknown"),
_T("Non-standard"),
_T("Master-Slave Determination"),
_T("Terminal Capability Set"),
_T("Open Logical Channel"),
_T("Close Logical Channel"),
_T("Request Channel Close"),
_T("Multiplex Entry Send"),
_T("Request Multiplex Entry"),
_T("Request Mode"),
_T("Round Trip Delay Request"),
_T("Maintenance Loop Request"),
_T("Communication Mode Request"),
_T("Conference Request")
};
#define NUM_H245_RESPONSE_PDU_TYPES 21
TCHAR* h245ResponseTypes[NUM_H245_RESPONSE_PDU_TYPES + 1] = {
_T("Unknown"),
_T("Non-Standard Message"),
_T("Master-Slave Determination Ack"),
_T("Master-Slave Determination Reject"),
_T("Terminal Capability Set Ack"),
_T("Terminal Capability Set Reject"),
_T("Open Logical Channel Ack"),
_T("Open Logical Channel Reject"),
_T("Close Logical Channel Ack"),
_T("Request Channel Close Ack"),
_T("Request Channel Close Reject"),
_T("Multiplex Entry Send Ack"),
_T("Multiplex Entry Send Reject"),
_T("Request Multiplex Entry Ack"),
_T("Request Multiplex Entry Reject"),
_T("Request Mode Ack"),
_T("Request Mode Reject"),
_T("Round Trip Delay Response"),
_T("Maintenance Loop Ack"),
_T("Maintenance Loop Reject"),
_T("Communication Mode Response"),
_T("Conference Response")
};
#endif // DBG
inline HRESULT
H245_INFO::QueueReceive()
{
HRESULT HResult = EventMgrIssueRecv (m_SocketInfo.Socket, *this);
if (FAILED(HResult))
{
DebugF (_T("H245_INFO::QueueReceive: Async Receive call failed.\n"));
}
return HResult;
}
inline HRESULT
H245_INFO::QueueSend(
IN MultimediaSystemControlMessage *pH245pdu
)
{
BYTE *pBuf = NULL;
DWORD BufLen = 0;
// This function encodes the TPKT header also into the buffer
HRESULT HResult = EncodeH245PDU(*pH245pdu, // decoded ASN.1 part
&pBuf,
&BufLen);
if (FAILED(HResult))
{
DebugF(_T("EncodeH245PDU() failed: %x\n"), HResult);
return HResult;
}
// call the event manager to make the async send call
// the event mgr will free the buffer.
HResult = EventMgrIssueSend (m_SocketInfo.Socket, *this, pBuf, BufLen);
if (FAILED(HResult))
{
DebugF(_T("H245_INFO::QueueSend(), AsyncSend() failed: 0x%x\n"),
HResult);
}
// Issuing the send succeeded.
return HResult;
}
/* virtual */ HRESULT
H245_INFO::SendCallback(
IN HRESULT CallbackHResult
)
{
CALL_BRIDGE *pCallBridge = &GetCallBridge();
HRESULT Result = S_OK;
pCallBridge->Lock();
if (!pCallBridge -> IsTerminated ()) {
if (FAILED(CallbackHResult))
{
pCallBridge->Terminate ();
_ASSERTE(pCallBridge->IsTerminated());
Result = CallbackHResult;
}
} else {
// This is here to take care of closing the socket
// when callbridge sends EndSession PDU during
// termination path.
GetSocketInfo ().Clear (TRUE);
}
pCallBridge->Unlock();
return Result;
}
// This clearly assumes that the H.245 listen address will
// always be within the Connect PDU and the dest side will be the one
// which will be calling connect()
inline HRESULT
DEST_H245_INFO::ConnectToCallee(
)
{
// we must be in H245_STATE_CON_INFO
_ASSERTE(H245_STATE_CON_INFO == m_H245State);
// we saved the callee's h245 address/port in the call to SetCalleeInfo
_ASSERTE(ntohl (m_CalleeAddress.sin_addr.s_addr));
_ASSERTE(ntohs (m_CalleeAddress.sin_port));
// try to connect to this address/port
// and save the address/port
HRESULT HResult = m_SocketInfo.Connect (&m_CalleeAddress);
if (FAILED(HResult))
{
DebugF (_T("H245: 0x%x failed to connect to callee %08X:%04X.\n"),
&GetCallBridge (),
SOCKADDR_IN_PRINTF (&m_CalleeAddress));
return HResult;
}
//_ASSERTE(S_FALSE != HResult);
// queue receive
HResult = QueueReceive();
if (FAILED(HResult))
{
DebugF (_T("H245: 0x%x failed to queue receive.\n"),
&GetCallBridge ());
DumpError (HResult);
return HResult;
}
//_ASSERTE(S_FALSE != HResult);
// transition to state H245_STATE_CON_ESTD
m_H245State = H245_STATE_CON_ESTD;
return HResult;
}
HRESULT SOURCE_H245_INFO::ListenForCaller (
IN SOCKADDR_IN * ListenAddress)
{
// queues an overlapped accept and returns the
// port on which its listening for incoming connections
// it uses the same local ip address as the source q931 connection
SOCKET ListenSocket;
HRESULT Result;
WORD Port = 0; // in HOST order
SOCKADDR_IN TrivialRedirectSourceAddress = {0};
SOCKADDR_IN TrivialRedirectDestAddress = {0};
ULONG SourceAdapterIndex;
ULONG Status;
ListenSocket = INVALID_SOCKET;
Result = EventMgrIssueAccept(
ntohl (ListenAddress -> sin_addr.s_addr),
*this,
Port, // out param
ListenSocket);
ListenAddress -> sin_port = htons (Port);
if (FAILED (Result)) {
DebugF (_T("H245: 0x%x failed to issue accept from caller.\n"), &GetCallBridge ());
DumpError (Result);
return Result;
}
//_ASSERTE(S_FALSE != HResult);
// save the listen socket, address and port in our socket info
m_SocketInfo.SetListenInfo (ListenSocket, ListenAddress);
// Open trivial source-side NAT mapping
//
// The purpose of this mapping is to puncture the firewall for H.245
// session if the firewall happened to be activated.
// Note that this assumes that the caller sends both Q.931 and H.245
// traffic from the same IP address.
SourceAdapterIndex = ::NhMapAddressToAdapter (htonl (GetCallBridge().GetSourceInterfaceAddress()));
if(SourceAdapterIndex == (ULONG)-1)
{
DebugF (_T("H245: 0x%x failed to map address %08X to adapter index.\n"),
&GetCallBridge (),
GetCallBridge().GetSourceInterfaceAddress());
return E_FAIL;
}
#if PARTIAL_TRIVIAL_REDIRECTS_ENABLED // enable this when it would be possible to set up a trivial redirect
// with only source port (new and old) unspecified.
GetCallBridge().GetSourceAddress(&TrivialRedirectSourceAddress);
#endif // PARTIAL_TRIVIAL_REDIRECTS_ENABLED
TrivialRedirectDestAddress.sin_addr.s_addr = ListenAddress->sin_addr.s_addr;
TrivialRedirectDestAddress.sin_port = ListenAddress->sin_port;
Status = m_SocketInfo.CreateTrivialNatRedirect(
&TrivialRedirectDestAddress,
&TrivialRedirectSourceAddress,
SourceAdapterIndex);
if (Status != S_OK) {
return E_FAIL;
}
// transition state to H245_STATE_CON_LISTEN
m_H245State = H245_STATE_CON_LISTEN;
return S_OK;
}
// virtual
HRESULT SOURCE_H245_INFO::AcceptCallback (
IN DWORD Status,
IN SOCKET Socket,
IN SOCKADDR_IN * LocalAddress,
IN SOCKADDR_IN * RemoteAddress)
{
HRESULT HResult = Status;
CALL_BRIDGE *pCallBridge = &GetCallBridge();
///////////////////////////////
//// LOCK the CALL_BRIDGE
///////////////////////////////
pCallBridge->Lock();
if (!pCallBridge -> IsTerminated ()) {
do {
if (FAILED (HResult))
{
// An error occured. Terminate the CALL_BRIDGE
DebugF (_T("H245: 0x%x accept failed, terminating.\n"), &GetCallBridge ());
DumpError (HResult);
break;
}
// we must be in H245_STATE_CON_LISTEN state
_ASSERTE(H245_STATE_CON_LISTEN == m_H245State);
// call the dest instance to establish a connection with the callee
HResult = GetDestH245Info().ConnectToCallee();
if (FAILED(HResult))
break;
//_ASSERTE(S_FALSE != HResult);
// close the listen socket, don't cancel trivial NAT redirect
m_SocketInfo.Clear(FALSE);
m_SocketInfo.Init(Socket, LocalAddress, RemoteAddress);
// queue receive
HResult = QueueReceive();
if (FAILED(HResult))
break;
//_ASSERTE(S_FALSE != HResult);
// transition state to H245_STATE_CON_ESTD
m_H245State = H245_STATE_CON_ESTD;
} while (FALSE);
if (FAILED (HResult))
{
// initiate shutdown
pCallBridge->Terminate ();
_ASSERTE(pCallBridge->IsTerminated());
}
}
///////////////////////////////
//// UNLOCK the CALL_BRIDGE
///////////////////////////////
pCallBridge->Unlock();
return HResult;
}
/*++
Routine Description:
This function is responsible for freeing pBuf (if it is not stored).
Arguments:
Return Values:
--*/
//virtual
HRESULT
H245_INFO::ReceiveCallback(
IN HRESULT CallbackHResult,
IN BYTE *pBuf,
IN DWORD BufLen
)
{
MultimediaSystemControlMessage *pDecodedH245pdu = NULL;
CALL_BRIDGE *pCallBridge = &GetCallBridge();
pCallBridge->Lock();
if (!pCallBridge -> IsTerminated ()) {
if (SUCCEEDED(CallbackHResult))
{
CallbackHResult = DecodeH245PDU(pBuf, BufLen, &pDecodedH245pdu);
if (SUCCEEDED(CallbackHResult))
{
// Process the PDU
CallbackHResult = ReceiveCallback(pDecodedH245pdu);
FreeH245PDU(pDecodedH245pdu);
}
else
{
DebugF (_T("H245: 0x%x error 0x%x on decode.\n"),
&GetCallBridge (),
CallbackHResult);
pCallBridge->Terminate ();
}
}
else
{
// An error occured. Terminate the CALL_BRIDGE
DebugF (_T("H245: 0x%x error 0x%x on receive callback.\n"),
&GetCallBridge (),
CallbackHResult);
pCallBridge->Terminate ();
}
}
pCallBridge -> Unlock();
EM_FREE(pBuf);
return CallbackHResult;
}
/* virtual */ HRESULT
H245_INFO::ReceiveCallback(
IN MultimediaSystemControlMessage *pH245pdu
)
/*++
Routine Description:
Only Request PDUs are handled by this H245_INFO instance.
All the other PDUs are just passed on for processing to the
other instance.
CODEWORK: How should we handle endSessionCommand PDUs ?
Arguments:
Return Values:
S_OK on success.
E_INVALIDARG if the PDU is invalid.
--*/
{
HRESULT HResult;
// check to see if we must destroy self
// CHECK_TERMINATION;
// we must have a valid decoded PDU
_ASSERTE(NULL != pH245pdu);
// we must be in H245_STATE_CON_ESTD state
_ASSERTE(H245_STATE_CON_ESTD == m_H245State);
// check message type
if (MultimediaSystemControlMessage_request_chosen ==
pH245pdu->choice)
{
// we only handle requests in the H245 instance which
// actually receives the PDU
HResult = HandleRequestMessage(pH245pdu);
}
else
{
// we don't process these here, pass them on to the other
// H245 instance.
HResult = GetOtherH245Info().ProcessMessage(pH245pdu);
}
// CODEWORK: Which errors should result in just dropped PDUs
// and which ones should result in shutting down the whole call ???
// if there is an error
if (FAILED(HResult) && HResult != E_INVALIDARG)
{
goto shutdown;
}
// CODEWORK: If HResult is E_INVALIDARG, this means that the PDU
// should be dropped. We need to let the caller know about this
// and send him a closeLC message or some such.
// Probably an OLC PDU should get an OLCReject etc.
// we must queue a receive irrespective of whether or not the
// previous message was dropped.
// queue an async receive
HResult = QueueReceive();
if (FAILED(HResult))
{
goto shutdown;
}
return HResult;
shutdown:
// initiate shutdown
GetCallBridge().Terminate ();
return HResult;
}
///////////////////////////////////////////////////////////////////////////////
// //
// Routines for processing H.245 PDUs //
// //
///////////////////////////////////////////////////////////////////////////////
HRESULT
H245_INFO::HandleRequestMessage(
IN MultimediaSystemControlMessage *pH245pdu
)
/*++
Routine Description:
Only the OLC and CLC PDUs are handled here. All the rest are
just passed on to the other H245_INFO instance for processing.
Arguments:
Return Values:
S_OK on success.
E_INVALIDARG if the PDU is invalid.
--*/
{
BOOL IsDestH245Info = (&GetCallBridge().GetDestH323State().GetH245Info() == this);
LogicalChannelNumber LCN;
// it must be a request message
_ASSERTE(MultimediaSystemControlMessage_request_chosen ==
pH245pdu->choice);
// we must be in connected state
_ASSERTE(H245_STATE_CON_ESTD == m_H245State);
HRESULT HResult = E_FAIL;
// check the PDU type
switch(pH245pdu->u.request.choice)
{
case openLogicalChannel_chosen:
{
LCN = pH245pdu->u.request.u.openLogicalChannel.forwardLogicalChannelNumber;
#if DBG
DebugF (_T("H245: 0x%x calle%c sent 'Open Logical Channel' (%s, LCN - %d).\n"),
&GetCallBridge(), IsDestH245Info ? 'e' : 'r',
h245MediaTypes[pH245pdu->u.request.u.openLogicalChannel.forwardLogicalChannelParameters.dataType.choice],
LCN);
#endif
HResult = HandleOpenLogicalChannelPDU(pH245pdu);
}
break;
case closeLogicalChannel_chosen:
{
LCN = pH245pdu->u.request.u.closeLogicalChannel.forwardLogicalChannelNumber;
DebugF (_T("H245: 0x%x calle%c sent 'Close Logical Channel' (LCN - %d).\n"),
&GetCallBridge(), IsDestH245Info ? 'e' : 'r', LCN);
HResult = HandleCloseLogicalChannelPDU(pH245pdu);
}
break;
default:
{
// pass it on to the other H245 instance
#if DBG
DebugF (_T("H245: 0x%x calle%c sent '%s'. Forwarding without processing.\n"),
&GetCallBridge(), IsDestH245Info ? 'e' : 'r',
h245RequestTypes[pH245pdu->u.request.choice]);
#endif
HResult = GetOtherH245Info().ProcessMessage(
pH245pdu);
}
break;
};
return HResult;
}
HRESULT
H245_INFO::ProcessMessage(
IN MultimediaSystemControlMessage *pH245pdu
)
/*++
Routine Description:
Only Response PDUs need processing. All other PDUs
are simply sent out.
Arguments:
Return Values:
S_OK on success.
E_INVALIDARG if the PDU is invalid.
--*/
{
BOOL IsDestH245Info = (&GetCallBridge().GetDestH323State().GetH245Info() == this);
// we must be in H245_STATE_CON_ESTD state
_ASSERTE(H245_STATE_CON_ESTD == m_H245State);
// all messages are passed on
// we only do special processing of response messages here
if (MultimediaSystemControlMessage_response_chosen ==
pH245pdu->choice)
{
HRESULT HResult = E_FAIL;
// we only process requests in the H245 instance which
// actually receives the PDU
HResult = ProcessResponseMessage(pH245pdu);
// XXX This if is redundant
// if we are dropping the PDU, no further processing is required
if (HResult == E_INVALIDARG)
{
DebugF(_T("DEST_Q931_INFO::ProcessMessage(&%x), ")
_T("dropping response message, returning E_INVALIDARG\n"),
pH245pdu);
return E_INVALIDARG;
}
else if (FAILED(HResult))
{
DebugF( _T("DEST_Q931_INFO::ProcessMessage(&%x), ")
_T("unable to process response message, returning 0x%x\n"),
pH245pdu, HResult);
return HResult;
}
}
else if (MultimediaSystemControlMessage_command_chosen ==
pH245pdu->choice)
{
DebugF (_T("H245: 0x%x calle%c sent 'Command Message' (Type %d). Forwarding without processing.\n"),
&GetCallBridge(), IsDestH245Info ? 'r' : 'e',
pH245pdu -> u.command.choice);
}
else if (indication_chosen == pH245pdu->choice)
{
DebugF (_T("H245: 0x%x calle%c sent 'Indication Message' (Type %d). Forwarding without processing.\n"),
&GetCallBridge(), IsDestH245Info ? 'r' : 'e',
pH245pdu -> u.indication.choice);
}
// queue async send for the PDU
HRESULT HResult = E_FAIL;
HResult = QueueSend(pH245pdu);
if (HResult != S_OK) {
DebugF( _T("DEST_Q931_INFO::ProcessMessage(&%x) QueueSend failed, returning %x\n"),
pH245pdu, HResult);
return HResult;
}
return S_OK;
}
//
// we process response messages here
// since the logical channel info for these messages resides in this H245,
// the other H245 instance doesn't process them
//
HRESULT
H245_INFO::ProcessResponseMessage(
IN MultimediaSystemControlMessage *pH245pdu
)
{
BOOL IsDestH245Info = (&GetCallBridge().GetDestH323State().GetH245Info() == this);
// it must be a response message
_ASSERTE(MultimediaSystemControlMessage_response_chosen == \
pH245pdu->choice);
// NOTE: LogicalChannelNumber is USHORT, but we can treat it as an
// unsigned WORD
_ASSERTE(sizeof(LogicalChannelNumber) == sizeof(WORD));
// obtain the logical channel number
WORD LogChanNum = 0;
switch(pH245pdu->u.response.choice)
{
case openLogicalChannelAck_chosen:
{
OpenLogicalChannelAck &OlcAckPDU =
pH245pdu->u.response.u.openLogicalChannelAck;
LogChanNum = OlcAckPDU.forwardLogicalChannelNumber;
DebugF (_T("H245: 0x%x calle%c sent 'Open Logical Channel Ack' (LCN - %d).\n"),
&GetCallBridge (), IsDestH245Info ? 'r' : 'e', LogChanNum);
}
break;
case openLogicalChannelReject_chosen:
{
OpenLogicalChannelReject &OlcRejectPDU =
pH245pdu->u.response.u.openLogicalChannelReject;
LogChanNum = OlcRejectPDU.forwardLogicalChannelNumber;
DebugF (_T("H245: 0x%x calle%c sent 'Open Logical Channel Reject' (LCN - %d).\n"),
&GetCallBridge (), IsDestH245Info ? 'r' : 'e', LogChanNum);
}
break;
#if 0 // 0 ******* Region Commented Out Begins *******
case closeLogicalChannelAck_chosen:
{
CloseLogicalChannelAck &ClcAckPDU =
pH245pdu->u.response.u.closeLogicalChannelAck;
LogChanNum = ClcAckPDU.forwardLogicalChannelNumber;
}
break;
#endif // 0 ******* Region Commented Out Ends *******
// Let the CLCAck messages also go right through
default:
{
#if DBG
// pass it on to the other H245 instance
DebugF (_T("H245: 0x%x calle%c sent '%s'. Forwarding without processing.\n"),
&GetCallBridge(), IsDestH245Info ? 'r' : 'e',
h245ResponseTypes[pH245pdu->u.request.choice]);
#endif
return S_OK;
}
break;
};
// find the logical channel that must process this PDU,
// if none found, drop the PDU
LOGICAL_CHANNEL *pLogicalChannel =
m_LogicalChannelArray.FindByLogicalChannelNum(LogChanNum);
if (NULL == pLogicalChannel)
{
DebugF(_T("H245_INFO::ProcessResponseMessage(&%x), ")
_T("no logical channel with the forward logical channel num = %d, ")
_T("returning E_INVALIDARG\n"),
pH245pdu, LogChanNum);
return E_INVALIDARG;
}
// pass the PDU to the logical channel for processing
HRESULT HResult = E_FAIL;
switch(pH245pdu->u.response.choice)
{
case openLogicalChannelAck_chosen:
{
HResult = pLogicalChannel->ProcessOpenLogicalChannelAckPDU(
pH245pdu
);
}
break;
case openLogicalChannelReject_chosen:
{
HResult = pLogicalChannel->ProcessOpenLogicalChannelRejectPDU(
pH245pdu
);
}
break;
#if 0 // 0 ******* Region Commented Out Begins *******
case closeLogicalChannelAck_chosen:
{
HResult = pLogicalChannel->ProcessCloseLogicalChannelAckPDU(
pH245pdu
);
}
break;
#endif // 0 ******* Region Commented Out Ends *******
default:
{
// do nothing, let it go to the client terminal
DebugF(_T("H245_INFO::ProcessResponseMessage(&%x), we shouldn't have come here, returning E_UNEXPECTED\n"),
pH245pdu);
return E_UNEXPECTED;
}
break;
};
return HResult;
}
// Make Checks specific to RTP logical channels
HRESULT
H245_INFO::CheckOpenRtpLogicalChannelPDU(
IN OpenLogicalChannel &OlcPDU,
OUT SOCKADDR_IN * ReturnSourceAddress)
/*++
Routine Description:
Arguments:
Return Values:
S_OK on success.
E_INVALIDARG if the PDU is invalid.
--*/
{
HRESULT HResult;
// it must be unidirectional
// bidirectional channels are only present for data channels
if (OpenLogicalChannel_reverseLogicalChannelParameters_present &
OlcPDU.bit_mask)
{
DebugF( _T("H245_INFO::CheckOpenLogicalChannelPDU(&H245pdu) ")
_T("has reverse logical channel parameters, ")
_T("returning E_INVALIDARG\n")
);
return E_INVALIDARG;
}
// there shouldn't be a separate stack
// we don't proxy data on the separate stack address/port
if (OpenLogicalChannel_separateStack_present &
OlcPDU.bit_mask)
{
DebugF( _T("H245_INFO::CheckOpenLogicalChannelPDU(&H245pdu)")
_T("has a separate stack, returning E_INVALIDARG\n")
);
return E_INVALIDARG;
}
OpenLogicalChannel_forwardLogicalChannelParameters_multiplexParameters &MultiplexParams =
OlcPDU.forwardLogicalChannelParameters.multiplexParameters;
H2250LogicalChannelParameters & H2250Params =
MultiplexParams.u.h2250LogicalChannelParameters;
// we must have a media control channel
if (!(H2250LogicalChannelParameters_mediaControlChannel_present &
H2250Params.bit_mask))
{
DebugF( _T("H245_INFO::CheckOpenLogicalChannelPDU(&H245pdu) ")
_T("doesn't have a mediaControlChannel, returning E_INVALIDARG\n")
);
return E_INVALIDARG;
}
// we only proxy best-effort UDP RTCP streams for now
if ((H2250LogicalChannelParameters_mediaControlGuaranteedDelivery_present &
H2250Params.bit_mask) &&
(TRUE ==
H2250Params.mediaControlGuaranteedDelivery))
{
DebugF( _T("H245_INFO::CheckOpenLogicalChannelPDU(&H245pdu) ")
_T("requires guaranteed media delivery for RTCP, returning E_INVALIDARG\n")
);
return E_INVALIDARG;
}
// the proposed RTCP address should be a unicast IPv4 address
if ((unicastAddress_chosen != H2250Params.mediaControlChannel.choice) ||
(UnicastAddress_iPAddress_chosen !=
H2250Params.mediaControlChannel.u.unicastAddress.choice))
{
DebugF( _T("H245_INFO::CheckOpenLogicalChannelPDU(&H245pdu) ")
_T("RTCP address is not a unicast IPv4 address, ")
_T("address type = %d, unicast address type = %d")
_T("returning E_INVALIDARG\n"),
H2250Params.mediaControlChannel.choice,
H2250Params.mediaControlChannel.u.unicastAddress.choice);
return E_INVALIDARG;
}
// we only proxy best-effort UDP data streams for now
if ((H2250LogicalChannelParameters_mediaGuaranteedDelivery_present & H2250Params.bit_mask)
&& H2250Params.mediaGuaranteedDelivery) {
DebugF( _T("H245_INFO::CheckOpenLogicalChannelPDU(&H245pdu) ")
_T("requires guaranteed media delivery for RTP, returning E_INVALIDARG\n")
);
return E_INVALIDARG;
}
// get in the source ipv4 address and RTCP port
HResult = GetH245TransportInfo(
H2250Params.mediaControlChannel,
ReturnSourceAddress);
return HResult;
}
// Make checks needed for the DataType member in forward and reverse
// LogicalChannelParameters
inline HRESULT
CheckT120DataType(
IN DataType &dataType
)
/*++
Routine Description:
Arguments:
Return Values:
S_OK on success.
E_INVALIDARG if the PDU is invalid.
--*/
{
// The reverse logical channel should also be of type data.
if (dataType.choice != DataType_data_chosen)
{
return E_INVALIDARG;
}
// and of type T120 alone
if (dataType.u.data.application.choice !=
DataApplicationCapability_application_t120_chosen)
return E_INVALIDARG;
// Separate LAN stack is needed
if (dataType.u.data.application.u.t120.choice != separateLANStack_chosen)
return E_INVALIDARG;
return S_OK;
}
// Make Checks specific to T.120 logical channels
// If SeparateStack is not present the routine returns
// INADDR_NONE for the T120ConnectToIPAddr
HRESULT
H245_INFO::CheckOpenT120LogicalChannelPDU(
IN OpenLogicalChannel &OlcPDU,
OUT DWORD &T120ConnectToIPAddr,
OUT WORD &T120ConnectToPort
)
/*++
Routine Description:
Arguments:
Return Values:
S_OK on success.
E_INVALIDARG if the PDU is invalid.
--*/
{
HRESULT HResult;
// This function is called only for data channels
_ASSERTE(OlcPDU.forwardLogicalChannelParameters.dataType.choice ==
DataType_data_chosen);
// It must be bidirectional since this is a data channel.
if (!(OpenLogicalChannel_reverseLogicalChannelParameters_present &
OlcPDU.bit_mask))
{
DebugF( _T("H245_INFO::CheckT120OpenLogicalChannelPDU() ")
_T("has no reverse logical channel parameters, ")
_T("returning E_INVALIDARG\n"));
return E_INVALIDARG;
}
// Ensure that for both forward and reverse LogicalchannelParmeters
// the datatype is application t120 and separateLANStack
HResult = CheckT120DataType(
OlcPDU.forwardLogicalChannelParameters.dataType
);
if (HResult == E_INVALIDARG)
{
return E_INVALIDARG;
}
HResult = CheckT120DataType (OlcPDU.reverseLogicalChannelParameters.dataType);
if (HResult == E_INVALIDARG)
return E_INVALIDARG;
// CODEWORK: What all other checks are needed here ?
// CODEWORK: there could probably be other ways to send the address
// Investigate all possibilities.
// This means we have the address the T.120 endpoint is listening on
if (OpenLogicalChannel_separateStack_present &
OlcPDU.bit_mask)
{
return(GetT120ConnectToAddress(
OlcPDU.separateStack,
T120ConnectToIPAddr,
T120ConnectToPort)
);
}
// If the address is not present, return INADDR_NONE
T120ConnectToIPAddr = INADDR_NONE;
T120ConnectToPort = 0;
// CODEWORK: Do we need a separate success code for this scenario
// (in which the address is not present) ?
return S_OK;
}
HRESULT
H245_INFO::CheckOpenLogicalChannelPDU(
IN MultimediaSystemControlMessage &H245pdu,
OUT BYTE &SessionId,
OUT MEDIA_TYPE &MediaType
)
/*++
Routine Description:
Arguments:
Return Values:
S_OK on success.
E_INVALIDARG if the PDU is invalid.
--*/
{
// it must be an open logical channel request message
_ASSERTE (openLogicalChannel_chosen == H245pdu.u.request.choice);
OpenLogicalChannel &OlcPDU = H245pdu.u.request.u.openLogicalChannel;
// the forward logical channel number cannot be 0 as thats reserved
// for the H245 channel
if (0 == OlcPDU.forwardLogicalChannelNumber)
{
DebugF( _T("H245_INFO::CheckOpenLogicalChannelPDU(&H245pdu) ")
_T("has a forward logical channel number of 0, ")
_T("returning E_INVALIDARG\n"));
return E_INVALIDARG;
}
if (DataType_videoData_chosen ==
OlcPDU.forwardLogicalChannelParameters.dataType.choice)
{
MediaType = MEDIA_TYPE_VIDEO;
}
else if (DataType_audioData_chosen ==
OlcPDU.forwardLogicalChannelParameters.dataType.choice)
{
MediaType = MEDIA_TYPE_AUDIO;
}
else if (DataType_data_chosen ==
OlcPDU.forwardLogicalChannelParameters.dataType.choice)
{
MediaType = MEDIA_TYPE_DATA;
}
else
{
// we only support audio, video and data types
DebugF( _T("H245_INFO::CheckOpenLogicalChannelPDU(&H245pdu) ")
_T("has a non audio/video data type = %d, ")
_T("returning E_INVALIDARG\n"),
OlcPDU.forwardLogicalChannelParameters.dataType.choice);
return E_INVALIDARG;
}
// it should have the h2250 parameters
// TODO : check if this is a requirement
// now THESE are some identifiers to be PROUD of! :/
OpenLogicalChannel_forwardLogicalChannelParameters_multiplexParameters &
MultiplexParams = OlcPDU.forwardLogicalChannelParameters.multiplexParameters;
if (MultiplexParams.choice !=
OpenLogicalChannel_forwardLogicalChannelParameters_multiplexParameters_h2250LogicalChannelParameters_chosen)
{
DebugF( _T("H245_INFO::CheckOpenLogicalChannelPDU(&H245pdu) ")
_T("has an unexpected multiplex param type (non h2250)= %d, ")
_T("returning E_INVALIDARG\n"),
MultiplexParams.choice);
return E_INVALIDARG;
}
// there shouldn't be a mediaChannel as the ITU spec requires it not
// to be present when the transport is unicast and we only support
// unicast IPv4 addresses
H2250LogicalChannelParameters &H2250Params =
MultiplexParams.u.h2250LogicalChannelParameters;
if (H2250LogicalChannelParameters_mediaChannel_present &
H2250Params.bit_mask)
{
DebugF( _T("H245_INFO::CheckOpenLogicalChannelPDU(&H245pdu) ")
_T("has a mediaChannel, returning E_INVALIDARG\n"));
return E_INVALIDARG;
}
// get the session id
// BYTE cast is intentional as the value should be in [0.255]
_ASSERTE(H2250Params.sessionID <= 255);
SessionId = (BYTE)H2250Params.sessionID;
return S_OK;
}
HRESULT
H245_INFO::CreateRtpLogicalChannel(
IN OpenLogicalChannel &OlcPDU,
IN BYTE SessionId,
IN MEDIA_TYPE MediaType,
IN MultimediaSystemControlMessage *pH245pdu,
OUT LOGICAL_CHANNEL **ppReturnLogicalChannel
)
/*++
Routine Description:
Arguments:
Return Values:
S_OK on success.
E_INVALIDARG if the PDU is invalid.
--*/
{
SOCKADDR_IN SourceRtcpAddress;
HRESULT HResult;
*ppReturnLogicalChannel = NULL;
HResult = CheckOpenRtpLogicalChannelPDU (OlcPDU, &SourceRtcpAddress);
if (E_INVALIDARG == HResult) // XXX
{
return E_INVALIDARG;
}
// CODEWORK: We have to check only the
// RTP LOGICAL_CHANNELS
// check if there is a logical channel with the same non-zero
// session id with the other H245 instance
LOGICAL_CHANNEL *pAssocLogicalChannel =
(0 == SessionId) ?
NULL :
GetOtherH245Info().GetLogicalChannelArray().FindBySessionId(SessionId);
// For audio and video data create an RTP Logical Channel
WORD LogChanNum = OlcPDU.forwardLogicalChannelNumber;
RTP_LOGICAL_CHANNEL *pLogicalChannel = new RTP_LOGICAL_CHANNEL();
if (NULL == pLogicalChannel)
{
DebugF( _T("H245_INFO::CreateRtpLogicalChannel() ")
_T("cannot create a RTP_LOGICAL_CHANNEL, returning E_OUTOFMEMORY\n")
);
return E_OUTOFMEMORY;
}
// intialize the logical channel
HResult = pLogicalChannel->HandleOpenLogicalChannelPDU(
*this, // H245_INFO
MediaType, // The type of the media
ntohl (m_SocketInfo.LocalAddress.sin_addr.s_addr), // our local address
ntohl (m_SocketInfo.RemoteAddress.sin_addr.s_addr), // our remote address
ntohl (GetOtherH245Info().GetSocketInfo().LocalAddress.sin_addr.s_addr), // other h245 local address
ntohl (GetOtherH245Info().GetSocketInfo().RemoteAddress.sin_addr.s_addr), // other h245 remote address
LogChanNum, // logical channel number
SessionId, // session id
(RTP_LOGICAL_CHANNEL* )pAssocLogicalChannel,
// associated logical channel
// XXX What is a clean way of doing this ?
ntohl (SourceRtcpAddress.sin_addr.s_addr),
ntohs (SourceRtcpAddress.sin_port),
pH245pdu // h245 pdu (OLC)
);
if (FAILED(HResult))
{
// destroy the logical channel
delete pLogicalChannel;
DebugF( _T("H245_INFO::CreateRtpLogicalChannel(&%x) ")
_T("cannot initialize RTP_LOGICAL_CHANNEL, returning 0x%x\n"),
pH245pdu, HResult);
}
else
{
*ppReturnLogicalChannel = pLogicalChannel;
}
_ASSERTE(S_FALSE != HResult);
return HResult;
}
HRESULT
H245_INFO::CreateT120LogicalChannel(
IN OpenLogicalChannel &OlcPDU,
IN BYTE SessionId,
IN MEDIA_TYPE MediaType,
IN MultimediaSystemControlMessage *pH245pdu,
OUT LOGICAL_CHANNEL **ppReturnLogicalChannel
)
/*++
Routine Description:
Arguments:
Return Values:
S_OK on success.
E_INVALIDARG if the PDU is invalid.
--*/
{
DWORD T120ConnectToIPAddr;
WORD T120ConnectToPort;
HRESULT HResult;
*ppReturnLogicalChannel = NULL;
// CODEWORK: Have success code which returns a value saying
// this PDU does not have the T120 listen address
HResult = CheckOpenT120LogicalChannelPDU(OlcPDU,
T120ConnectToIPAddr,
T120ConnectToPort
);
if (E_INVALIDARG == HResult) // XXX
{
return E_INVALIDARG;
}
// For data create a T.120 Logical Channel
WORD LogChanNum = OlcPDU.forwardLogicalChannelNumber;
T120_LOGICAL_CHANNEL *pLogicalChannel = new T120_LOGICAL_CHANNEL();
if (NULL == pLogicalChannel)
{
DebugF( _T("H245_INFO::CreateT120LogicalChannel(&%x) ")
_T("cannot create a T120_LOGICAL_CHANNEL, returning E_OUTOFMEMORY\n"),
pH245pdu);
return E_OUTOFMEMORY;
}
// intialize the logical channel
HResult = pLogicalChannel->HandleOpenLogicalChannelPDU(
*this, // H245_INFO
MediaType, // The type of the media
LogChanNum, // logical channel number
SessionId, // session id
T120ConnectToIPAddr, // T.120 end point is listening on this
T120ConnectToPort, // IPAddr and Port
pH245pdu // h245 pdu (OLC)
);
if (FAILED(HResult))
{
// destroy the logical channel
delete pLogicalChannel;
DebugF( _T("H245_INFO::CreateT120LogicalChannel(&%x) ")
_T("cannot initialize T120_LOGICAL_CHANNEL, returning 0x%x\n"),
pH245pdu, HResult);
}
else
{
*ppReturnLogicalChannel = pLogicalChannel;
}
_ASSERTE(S_FALSE != HResult);
return HResult;
}
HRESULT
H245_INFO::HandleOpenLogicalChannelPDU(
IN MultimediaSystemControlMessage *pH245pdu
)
/*++
Routine Description:
Arguments:
Return Values:
S_OK on success.
E_INVALIDARG if the PDU is invalid.
--*/
{
// it must be an open logical channel request message
_ASSERTE(MultimediaSystemControlMessage_request_chosen == \
pH245pdu->choice);
_ASSERTE(openLogicalChannel_chosen == \
pH245pdu->u.request.choice);
HRESULT HResult = E_FAIL;
OpenLogicalChannel &OlcPDU =
pH245pdu->u.request.u.openLogicalChannel;
// check to see if there is already a logical channel with the
// same forward logical channel number
// NOTE: the array indices are not the same as the forward logical
// channel number (0 is reserved for H245 channel and the ITU spec
// allows a terminal to use any other value for it - i.e. they need
// not be consecutive)
// NOTE: LogicalChannelNumber is USHORT, but we can treat it as an
// unsigned WORD
_ASSERTE(sizeof(LogicalChannelNumber) == sizeof(WORD));
WORD LogChanNum = OlcPDU.forwardLogicalChannelNumber;
if (NULL != m_LogicalChannelArray.FindByLogicalChannelNum(LogChanNum))
{
DebugF( _T("H245_INFO::HandleOpenLogicalChannelPDU(&%x) ")
_T("a logical channel with the forward logical channel num = %d ")
_T("already exists, returning E_INVALIDARG\n"),
pH245pdu, LogChanNum);
return E_INVALIDARG;
}
// check to see if we can handle this OLC PDU and
// return its session id, source ipv4 address, RTCP port
BYTE SessionId;
MEDIA_TYPE MediaType;
HResult = CheckOpenLogicalChannelPDU(
*pH245pdu,
SessionId,
MediaType
);
if (FAILED(HResult))
{
DebugF( _T("H245_INFO::HandleOpenLogicalChannelPDU(&%x) ")
_T("cannot handle Open Logical Channel PDU, returning 0x%x\n"),
pH245pdu, HResult);
return HResult;
}
// check to see if we already have a logical channel with this session id
if ( (0 != SessionId) &&
(NULL != m_LogicalChannelArray.FindBySessionId(SessionId)) )
{
DebugF( _T("H245_INFO::HandleOpenLogicalChannelPDU(&%x) ")
_T("another Logical Channel exists with same session id = %u, ")
_T("returning E_INVALIDARG\n"),
pH245pdu, SessionId);
return E_INVALIDARG;
}
LOGICAL_CHANNEL *pLogicalChannel = NULL;
// create an instance of a LOGICAL_CHANNEL
if (IsMediaTypeRtp(MediaType))
{
HResult = CreateRtpLogicalChannel(
OlcPDU,
SessionId,
MediaType,
pH245pdu,
&pLogicalChannel
);
}
else
{
HResult = CreateT120LogicalChannel(
OlcPDU,
SessionId,
MediaType,
pH245pdu,
&pLogicalChannel
);
}
if (FAILED(HResult))
{
DebugF( _T("H245_INFO::HandleOpenLogicalChannelPDU(&%x) ")
_T("Creating Logical channel failed, returning 0x%x\n"),
pH245pdu, HResult);
return HResult;
}
// insert the logical channel into the array
// we add this to the array so that the logical channel is
// available to the other h245 instance when its processing the PDU
// this is only being done for keeping the code clean
// and not really needed now
HResult = m_LogicalChannelArray.Add(*pLogicalChannel);
if (FAILED(HResult))
{
// destroy the logical channel
// this also removes any associations with any logical channel
// in the other H245 instance
delete pLogicalChannel;
DebugF( _T("H245_INFO::HandleOpenLogicalChannelPDU(&%x) ")
_T("cannot add new LOGICAL_CHANNEL to the array, returning 0x%x"),
pH245pdu, HResult);
return HResult;
}
_ASSERTE(S_FALSE != HResult);
return HResult;
}
// handles a request message to close a logical channel
// we start a timer and close the channel on receiving either a
// CloseLogicalChannelAck PDU or a timeout
HRESULT
H245_INFO::HandleCloseLogicalChannelPDU(
IN MultimediaSystemControlMessage *pH245pdu
)
/*++
Routine Description:
Arguments:
Return Values:
S_OK on success.
E_INVALIDARG if the PDU is invalid.
--*/
{
// it must be an open logical channel request message
_ASSERTE(closeLogicalChannel_chosen == pH245pdu->u.request.choice);
HRESULT HResult = E_FAIL;
CloseLogicalChannel &ClcPDU =
pH245pdu->u.request.u.closeLogicalChannel;
// verify that the logical channel indicated in the message exists
// NOTE: LogicalChannelNumber is USHORT, but we can treat it as an
// unsigned WORD
_ASSERTE(sizeof(LogicalChannelNumber) == sizeof(WORD));
WORD LogChanNum = ClcPDU.forwardLogicalChannelNumber;
LOGICAL_CHANNEL *pLogicalChannel =
m_LogicalChannelArray.FindByLogicalChannelNum(LogChanNum);
if (NULL == pLogicalChannel)
{
DebugF( _T("H245_INFO::HandleCloseLogicalChannelPDU(&%x), ")
_T("no logical channel with the forward logical channel num = %d, ")
_T("returning E_INVALIDARG\n"),
pH245pdu, LogChanNum);
return E_INVALIDARG;
}
// let the Logical Channel instance process the message
// it also forwards the message to the other H245 instance
// NOTE: the logical channel should not be used after this call as it
// may have deleted and removed itself from the array. it only returns
// an error in case the error cannot be handled by simply deleting
// itself (the logical channel)
HResult = pLogicalChannel->HandleCloseLogicalChannelPDU(
pH245pdu
);
if (FAILED(HResult))
{
DebugF( _T("H245_INFO::HandleCloseLogicalChannelPDU(&%x), ")
_T("logical channel (%d) couldn't handle close logical channel PDU, ")
_T("returning 0x%x\n"),
pH245pdu, LogChanNum, HResult);
return HResult;
}
_ASSERTE(S_OK == HResult);
DebugF( _T("H245_INFO::HandleCloseLogicalChannelPDU(&%x) returning 0x%x\n"),
pH245pdu, HResult);
return HResult;
}
/* virtual */
H245_INFO::~H245_INFO (void)
{
}
HRESULT
H245_INFO::SendEndSessionCommand (
void
)
/*++
Routine Description:
Encodes and sends H.245 EndSession PDU
Arguments:
None
Return Values:
Passes through the result of calling another function
Notes:
--*/
{
MultimediaSystemControlMessage EndSessionCommand;
HRESULT Result;
EndSessionCommand.choice = MultimediaSystemControlMessage_command_chosen;
EndSessionCommand.u.command.choice = endSessionCommand_chosen;
EndSessionCommand.u.command.u.endSessionCommand.choice = disconnect_chosen;
Result = QueueSend (&EndSessionCommand);
if (FAILED(Result))
{
DebugF(_T("H245: 0x%x failed to send EndSession PDU. Error=0x%x\n"),
&GetCallBridge (),
Result);
}
return Result;
} // H245_INFO::SendEndSessionCommand