/*--------------------------------------------------- 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