1557 lines
44 KiB
C++
1557 lines
44 KiB
C++
|
/*---------------------------------------------------
|
|||
|
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
|