/**************************************************************************** * * $Archive: S:/STURGEON/SRC/Q931/VCS/q931.c_v $ * * INTEL Corporation Prorietary Information * * This listing is supplied under the terms of a license agreement * with INTEL Corporation and may not be copied nor disclosed except * in accordance with the terms of that agreement. * * Copyright (c) 1993-1996 Intel Corporation. * * $Revision: 1.122.1.0 $ * $Date: 20 Jun 1997 14:12:46 $ * $Author: MANDREWS $ * * BCL's revision info: * Revision: 1.99 * Date: 19 Nov 1996 14:54:02 * Author: rodellx * * Deliverable: * * Abstract: * * * Notes: * ***************************************************************************/ // [ ] Add facility ie to FACILITY MSG. // [ ] Read Q931 Appendix D. #pragma warning ( disable : 4057 4100 4115 4201 4214 4514 ) #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include #include "apierror.h" #include "isrg.h" #include "incommon.h" #include "linkapi.h" #include "common.h" #include "q931.h" #include "utils.h" #include "hlisten.h" #include "hcall.h" #include "q931pdu.h" #include "provider.h" #if (defined(_DEBUG) && defined(PCS_COMPLIANCE)) #include "interop.h" #include "q931plog.h" LPInteropLogger Q931Logger; #endif #define RECEIVE_BUFFER_SIZE 0x2000 #define HANGUP_TIMEOUT 1000 // 1 second #define CANCEL_LISTEN_TIMEOUT 5000 // 5 seconds // variable needed to support ISR debug facility. #if (ISRDEBUGINFO >= 1) WORD ghISRInst = 0; #endif #ifdef UNICODE_TRACE // We include this header to fix problems with macro expansion when Unicode is turned on. #include "unifix.h" #endif #define _Unicode(x) L ## x #define Unicode(x) _Unicode(x) // global data used by WinSock. static BOOL bQ931Initialized = FALSE; static Q931_RECEIVE_PDU_CALLBACK gReceivePDUHookProc = NULL; static struct { DWORD TempID; CC_CONFERENCEID ConferenceID; CRITICAL_SECTION Lock; } ConferenceIDSource; //==================================================================================== // // PRIVATE FUNCTIONS // //==================================================================================== //==================================================================================== //==================================================================================== void _FreeSetupASN(Q931_SETUP_ASN *pSetupASN) { ASSERT(pSetupASN != NULL); // Cleanup any dynamically allocated fields within SetupASN if (pSetupASN->NonStandardData.sData.pOctetString) { Free(pSetupASN->NonStandardData.sData.pOctetString); pSetupASN->NonStandardData.sData.pOctetString = NULL; } if (pSetupASN->VendorInfo.pProductNumber) { Free(pSetupASN->VendorInfo.pProductNumber); pSetupASN->VendorInfo.pProductNumber = NULL; } if (pSetupASN->VendorInfo.pVersionNumber) { Free(pSetupASN->VendorInfo.pVersionNumber); pSetupASN->VendorInfo.pVersionNumber = NULL; } Q931FreeAliasNames(pSetupASN->pCallerAliasList); pSetupASN->pCallerAliasList = NULL; Q931FreeAliasNames(pSetupASN->pCalleeAliasList); pSetupASN->pCalleeAliasList = NULL; Q931FreeAliasNames(pSetupASN->pExtraAliasList); pSetupASN->pExtraAliasList = NULL; Q931FreeAliasItem(pSetupASN->pExtensionAliasItem); pSetupASN->pExtensionAliasItem = NULL; } void _FreeReleaseCompleteASN(Q931_RELEASE_COMPLETE_ASN *pReleaseCompleteASN) { ASSERT(pReleaseCompleteASN != NULL); // Cleanup any dynamically allocated fields within SetupASN if (pReleaseCompleteASN->NonStandardData.sData.pOctetString) { Free(pReleaseCompleteASN->NonStandardData.sData.pOctetString); pReleaseCompleteASN->NonStandardData.sData.pOctetString = NULL; } } void _FreeFacilityASN(Q931_FACILITY_ASN *pFacilityASN) { ASSERT(pFacilityASN != NULL); // Cleanup any dynamically allocated fields within SetupASN if (pFacilityASN->NonStandardData.sData.pOctetString) { Free(pFacilityASN->NonStandardData.sData.pOctetString); pFacilityASN->NonStandardData.sData.pOctetString = NULL; } } void _FreeProceedingASN(Q931_CALL_PROCEEDING_ASN *pProceedingASN) { ASSERT(pProceedingASN != NULL); // Cleanup any dynamically allocated fields within SetupASN if (pProceedingASN->NonStandardData.sData.pOctetString) { Free(pProceedingASN->NonStandardData.sData.pOctetString); pProceedingASN->NonStandardData.sData.pOctetString = NULL; } } void _FreeAlertingASN(Q931_ALERTING_ASN *pAlertingASN) { ASSERT(pAlertingASN != NULL); // Cleanup any dynamically allocated fields within SetupASN if (pAlertingASN->NonStandardData.sData.pOctetString) { Free(pAlertingASN->NonStandardData.sData.pOctetString); pAlertingASN->NonStandardData.sData.pOctetString = NULL; } } void _FreeConnectASN(Q931_CONNECT_ASN *pConnectASN) { ASSERT(pConnectASN != NULL); // Cleanup any dynamically allocated fields within SetupASN if (pConnectASN->NonStandardData.sData.pOctetString) { Free(pConnectASN->NonStandardData.sData.pOctetString); pConnectASN->NonStandardData.sData.pOctetString = NULL; } if (pConnectASN->VendorInfo.pProductNumber) { Free(pConnectASN->VendorInfo.pProductNumber); pConnectASN->VendorInfo.pProductNumber = NULL; } if (pConnectASN->VendorInfo.pVersionNumber) { Free(pConnectASN->VendorInfo.pVersionNumber); pConnectASN->VendorInfo.pVersionNumber = NULL; } } void _ConferenceIDNew( CC_CONFERENCEID *pConferenceID) { UUID id; int iresult; EnterCriticalSection(&(ConferenceIDSource.Lock)); memset(ConferenceIDSource.ConferenceID.buffer, 0, sizeof(ConferenceIDSource.ConferenceID.buffer)); iresult = UuidCreate(&id); if ((iresult == RPC_S_OK) || (iresult ==RPC_S_UUID_LOCAL_ONLY)) { memcpy(ConferenceIDSource.ConferenceID.buffer, &id, min(sizeof(ConferenceIDSource.ConferenceID.buffer), sizeof(UUID))); } else ASSERT(0); memcpy(pConferenceID->buffer, ConferenceIDSource.ConferenceID.buffer, sizeof(pConferenceID->buffer)); LeaveCriticalSection(&(ConferenceIDSource.Lock)); return; } //==================================================================================== // This function is used internally whenever a function needs to send a PDU. // Note that before a datalinkSendRequest() is done, the call object is unlocked // and then subsequently locked after returning. This is necessary to prevent deadlock // in MT apps. Further, it is the responsibility of calling functions to revalidate // the call object before using it. //==================================================================================== CS_STATUS Q931SendMessage( P_CALL_OBJECT pCallObject, BYTE* CodedPtrPDU, DWORD CodedLengthPDU, BOOL bOkToUnlock) { HQ931CALL hQ931Call; DWORD dwPhysicalId; HRESULT result; ASSERT(pCallObject != NULL); ASSERT(CodedPtrPDU != NULL); ASSERT(CodedLengthPDU != 0); hQ931Call = pCallObject->hQ931Call; dwPhysicalId = pCallObject->dwPhysicalId; // send the message. if (pCallObject->bConnected) { // Unlock the call object before we call down to Link Layer (if caller said it was ok) if(bOkToUnlock) CallObjectUnlock(pCallObject); #if (defined(_DEBUG) && defined(PCS_COMPLIANCE)) InteropOutput(Q931Logger, (BYTE FAR*)CodedPtrPDU, CodedLengthPDU, Q931LOG_SENT_PDU); #endif result = datalinkSendRequest(dwPhysicalId, CodedPtrPDU, CodedLengthPDU); // Now attempt to lock the object again. Note: higher level funcs must // be sure to call CallObjectValidate before assuming they have a valid lock if (bOkToUnlock && ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL))) { Q931DBG((DBGERROR, "CallObjectLock() returned error (object not found).")); } // Note: if we can't get the lock, perhaps we should pass back a specific return code // that higher layers can check for??? For now, they should call CallObjectValidate() // before assuming the call object is still good. if (FAILED(result)) { Q931DBG((DBGERROR, "datalinkSendRequest() failed")); Free(CodedPtrPDU); } return result; } Q931DBG((DBGWARNING, "Q931SendMessage: message not sent because bConnected is FALSE")); Free(CodedPtrPDU); return CS_OK; } //==================================================================================== //==================================================================================== CS_STATUS Q931RingingInternal(P_CALL_OBJECT pCallObject, WORD wCRV) { CC_ENDPOINTTYPE EndpointType; DWORD CodedLengthASN; BYTE *CodedPtrASN; BINARY_STRING UserUserData; DWORD CodedLengthPDU; BYTE *CodedPtrPDU; HRESULT result = CS_OK; int nError = 0; HQ931CALL hQ931Call = pCallObject->hQ931Call; if (pCallObject->VendorInfoPresent) EndpointType.pVendorInfo = &pCallObject->VendorInfo; else EndpointType.pVendorInfo = NULL; EndpointType.bIsTerminal = pCallObject->bIsTerminal; EndpointType.bIsGateway = pCallObject->bIsGateway; result = Q931AlertingEncodeASN( NULL, /* pNonStandardData */ NULL, /* h245Addr */ &EndpointType, &pCallObject->World, &CodedPtrASN, &CodedLengthASN); if (result != CS_OK || CodedLengthASN == 0 || CodedPtrASN == NULL) { Q931DBG((DBGERROR, "Q931AlertingEncodeASN() failed, nothing to send.")); if (CodedPtrASN != NULL) { Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN); } return (result != CS_OK) ? result : CS_INTERNAL_ERROR; } UserUserData.length = (WORD)CodedLengthASN; UserUserData.ptr = CodedPtrASN; result = Q931AlertingEncodePDU(wCRV, &UserUserData, &CodedPtrPDU, &CodedLengthPDU); if (CodedPtrASN != NULL) { Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN); } if ((result != CS_OK) || (CodedLengthPDU == 0) || (CodedPtrPDU == NULL)) { Q931DBG((DBGERROR, "Q931AlertingEncodePDU() failed, nothing to send.")); if (CodedPtrPDU != NULL) { Free(CodedPtrPDU); } if (result != CS_OK) { return result; } return CS_INTERNAL_ERROR; } else { result = Q931SendMessage(pCallObject, CodedPtrPDU, CodedLengthPDU, TRUE); if(CallObjectValidate(hQ931Call) != CS_OK) return(CS_INTERNAL_ERROR); } return result; } //==================================================================================== //==================================================================================== CS_STATUS Q931OnCallSetup( P_CALL_OBJECT pCallObject, Q931MESSAGE *pMessage, Q931_SETUP_ASN *pSetupASN) { DWORD result; HQ931CALL hQ931Call; HRESULT Status; // if the callstate is anything other than null, ignore... // if (pCallObject->bCallState != CALLSTATE_NULL) // { // return CS_OK; // } hQ931Call = pCallObject->hQ931Call; if (pMessage->CallReference & 0x8000) { // the message came from the callee, so this should be ignored??? } pMessage->CallReference &= ~(0x8000); // strip off the high bit. pCallObject->wCRV = pMessage->CallReference; pCallObject->wGoal = pSetupASN->wGoal; pCallObject->bCallerIsMC = pSetupASN->bCallerIsMC; pCallObject->wCallType = pSetupASN->wCallType; pCallObject->ConferenceID = pSetupASN->ConferenceID; pCallObject->bCallState = CALLSTATE_PRESENT; { CSS_CALL_INCOMING EventData; WCHAR szUnicodeDisplay[CC_MAX_DISPLAY_LENGTH + 1]; WCHAR szUnicodeCalledPartyNumber[CC_MAX_PARTY_NUMBER_LEN + 1]; WCHAR szUnicodeCallingPartyNumber[CC_MAX_PARTY_NUMBER_LEN + 1]; EventData.wGoal = pCallObject->wGoal; EventData.wCallType = pCallObject->wCallType; EventData.bCallerIsMC = pCallObject->bCallerIsMC; EventData.ConferenceID = pCallObject->ConferenceID; EventData.pSourceAddr = NULL; if (pSetupASN->SourceAddrPresent) { EventData.pSourceAddr = &(pSetupASN->SourceAddr); } EventData.pCallerAddr = NULL; if (pSetupASN->CallerAddrPresent) { EventData.pCallerAddr = &(pSetupASN->CallerAddr); } EventData.pCalleeDestAddr = NULL; if (pSetupASN->CalleeDestAddrPresent) { EventData.pCalleeDestAddr = &(pSetupASN->CalleeDestAddr); } EventData.pLocalAddr = NULL; if (pSetupASN->CalleeAddrPresent) { EventData.pLocalAddr = &(pSetupASN->CalleeAddr); } if (!(pSetupASN->NonStandardDataPresent) || (pSetupASN->NonStandardData.sData.wOctetStringLength == 0) || (pSetupASN->NonStandardData.sData.pOctetString == NULL)) { EventData.pNonStandardData = NULL; } else { EventData.pNonStandardData = &(pSetupASN->NonStandardData); } EventData.pCallerAliasList = pSetupASN->pCallerAliasList; EventData.pCalleeAliasList = pSetupASN->pCalleeAliasList; EventData.pExtraAliasList = pSetupASN->pExtraAliasList; EventData.pExtensionAliasItem = pSetupASN->pExtensionAliasItem; EventData.pszDisplay = NULL; if (pMessage->Display.Present && pMessage->Display.Contents) { MultiByteToWideChar(CP_ACP, 0, (const char *)pMessage->Display.Contents, -1, szUnicodeDisplay, sizeof(szUnicodeDisplay) / sizeof(szUnicodeDisplay[0])); EventData.pszDisplay = szUnicodeDisplay; } EventData.pszCalledPartyNumber = NULL; if (pMessage->CalledPartyNumber.Present && pMessage->CalledPartyNumber.PartyNumberLength) { MultiByteToWideChar(CP_ACP, 0, (const char *)pMessage->CalledPartyNumber.PartyNumbers, -1, szUnicodeCalledPartyNumber, sizeof(szUnicodeCalledPartyNumber) / sizeof(szUnicodeCalledPartyNumber[0])); EventData.pszCalledPartyNumber = szUnicodeCalledPartyNumber; } //nikhilb:change for CPR if (pMessage->CallingPartyNumber.Present && (pMessage->CallingPartyNumber.Length > 1) ) { if( (EventData.pCallerAliasList == NULL) || (EventData.pCallerAliasList->wCount == 0) || (EventData.pCallerAliasList->pItems == NULL) ) { // Always skip 1st byte in the contents. // Skip the 2nd byte if its not an e.164 char (could be the screening indicator byte) BYTE* pstrTemp = pMessage->CallingPartyNumber.Contents + ((pMessage->CallingPartyNumber.Contents[1] & 0x80)? 2 : 1); MultiByteToWideChar(CP_ACP, 0, (const char *)(pstrTemp), -1, szUnicodeCallingPartyNumber, sizeof(szUnicodeCallingPartyNumber) / sizeof(szUnicodeCallingPartyNumber[0])); if( EventData.pCallerAliasList == NULL ) { EventData.pCallerAliasList = (PCC_ALIASNAMES)malloc( sizeof(CC_ALIASNAMES) ); } if( EventData.pCallerAliasList != NULL ) { EventData.pCallerAliasList->wCount = 1; EventData.pCallerAliasList->pItems = (PCC_ALIASITEM)malloc( sizeof(CC_ALIASITEM) ); if( EventData.pCallerAliasList->pItems == NULL ) { free( EventData.pCallerAliasList ); EventData.pCallerAliasList = NULL; } else { ZeroMemory( EventData.pCallerAliasList->pItems, sizeof(CC_ALIASITEM) ); EventData.pCallerAliasList->pItems[0].wType = CC_ALIAS_H323_PHONE; EventData.pCallerAliasList->pItems[0].wDataLength = (WORD)wcslen( szUnicodeCallingPartyNumber ); EventData.pCallerAliasList->pItems[0].pData = (WCHAR*) malloc( (EventData.pCallerAliasList->pItems[0].wDataLength+1) * sizeof(WCHAR) ); if( EventData.pCallerAliasList->pItems[0].pData == NULL ) { free( EventData.pCallerAliasList->pItems ); free( EventData.pCallerAliasList ); EventData.pCallerAliasList = NULL; } else { CopyMemory( EventData.pCallerAliasList->pItems[0].pData, szUnicodeCallingPartyNumber, (EventData.pCallerAliasList->pItems[0].wDataLength+1) * sizeof(WCHAR) ); } } } } } //nikhilb:change for CPR if (pMessage->CalledPartyNumber.Present && (pMessage->CalledPartyNumber.PartyNumberLength > 0) ) { if( (EventData.pCalleeAliasList == NULL) || (EventData.pCalleeAliasList->wCount == 0) || (EventData.pCalleeAliasList->pItems == NULL) ) { MultiByteToWideChar(CP_ACP, 0, (const char *)(pMessage->CalledPartyNumber.PartyNumbers), -1, szUnicodeCalledPartyNumber, sizeof(szUnicodeCalledPartyNumber) / sizeof(szUnicodeCalledPartyNumber[0])); if( EventData.pCalleeAliasList == NULL ) { EventData.pCalleeAliasList = (PCC_ALIASNAMES)malloc( sizeof(CC_ALIASNAMES) ); } if( EventData.pCalleeAliasList != NULL ) { EventData.pCalleeAliasList->wCount = 1; EventData.pCalleeAliasList->pItems = (PCC_ALIASITEM)malloc( sizeof(CC_ALIASITEM) ); if( EventData.pCalleeAliasList->pItems == NULL ) { free( EventData.pCalleeAliasList ); EventData.pCalleeAliasList = NULL; } else { ZeroMemory( EventData.pCalleeAliasList->pItems, sizeof(CC_ALIASITEM) ); EventData.pCalleeAliasList->pItems[0].wType = CC_ALIAS_H323_PHONE; EventData.pCalleeAliasList->pItems[0].wDataLength = (WORD)wcslen( szUnicodeCalledPartyNumber ); EventData.pCalleeAliasList->pItems[0].pData = (WCHAR*) malloc( (EventData.pCalleeAliasList->pItems[0].wDataLength+1) * sizeof(WCHAR) ); if( EventData.pCalleeAliasList->pItems[0].pData == NULL ) { free( EventData.pCalleeAliasList->pItems ); free( EventData.pCalleeAliasList ); EventData.pCalleeAliasList = NULL; } else { CopyMemory( EventData.pCalleeAliasList->pItems[0].pData, szUnicodeCalledPartyNumber, (EventData.pCalleeAliasList->pItems[0].wDataLength+1) * sizeof(WCHAR) ); } } } } } EventData.pSourceEndpointType = &(pSetupASN->EndpointType); EventData.wCallReference = pMessage->CallReference; result = pCallObject->Callback((BYTE)Q931_CALL_INCOMING, pCallObject->hQ931Call, pCallObject->dwListenToken, pCallObject->dwUserToken, &EventData); } Status = CallObjectValidate(hQ931Call); if (Status != CS_OK) return Status; if (result == 0) { WORD wCRV = (WORD)(pMessage->CallReference | 0x8000); HRESULT Status; Status = Q931RingingInternal(pCallObject, wCRV); if (Status != CS_OK) { return Status; } pCallObject->bCallState = CALLSTATE_RECEIVED; } return CS_OK; } //==================================================================================== //==================================================================================== CS_STATUS Q931Ringing( HQ931CALL hQ931Call, WORD *pwCRV) { P_CALL_OBJECT pCallObject = NULL; CS_STATUS Status; WORD wCRV; if (bQ931Initialized == FALSE) { ASSERT(FALSE); return CS_NOT_INITIALIZED; } Q931DBG((DBGTRACE, "Entering Q931Ringing()...")); // need parameter checking... if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL)) { Q931DBG((DBGERROR, "CallObjectLock() returned error (object not found).")); return CS_BAD_PARAM; } if (pwCRV != NULL) { wCRV = *pwCRV; } else { wCRV = pCallObject->wCRV; } Status = Q931RingingInternal(pCallObject, wCRV); if (Status != CS_OK) { return Status; } pCallObject->bCallState = CALLSTATE_RECEIVED; Status = CallObjectUnlock(pCallObject); return Status; } //==================================================================================== //==================================================================================== CS_STATUS Q931OnCallProceeding( P_CALL_OBJECT pCallObject, Q931MESSAGE *pMessage, Q931_CALL_PROCEEDING_ASN *pProceedingASN) { pCallObject->bCallState = CALLSTATE_OUTGOING; Q931StopTimer(pCallObject, Q931_TIMER_303); return CS_OK; } //==================================================================================== //==================================================================================== CS_STATUS Q931OnCallAlerting( P_CALL_OBJECT pCallObject, Q931MESSAGE *pMessage, Q931_ALERTING_ASN *pAlertingASN) { DWORD result; pCallObject->bCallState = CALLSTATE_DELIVERED; if (pAlertingASN != NULL) { // we could pass h245addr, userinfo, and conferenceid // if desired later... // (this would be passed in the pAlertingASN field) } Q931StopTimer(pCallObject, Q931_TIMER_303); Q931StartTimer(pCallObject, Q931_TIMER_301); result = pCallObject->Callback((BYTE)Q931_CALL_RINGING, pCallObject->hQ931Call, pCallObject->dwListenToken, pCallObject->dwUserToken, NULL); return CS_OK; } //==================================================================================== //==================================================================================== CS_STATUS Q931OnCallConnect( P_CALL_OBJECT pCallObject, Q931MESSAGE *pMessage, Q931_CONNECT_ASN *pConnectASN) { DWORD result; if ((pMessage->CallReference & 0x8000) == 0) { // the message came from the caller, so this should be ignored??? } pMessage->CallReference &= ~(0x8000); // strip off the high bit. pCallObject->ConferenceID = pConnectASN->ConferenceID; pCallObject->bCallState = CALLSTATE_ACTIVE; { CSS_CALL_ACCEPTED EventData; WCHAR szUnicodeDisplay[CC_MAX_DISPLAY_LENGTH + 1]; // populate the event data struct. EventData.ConferenceID = pCallObject->ConferenceID; if (pCallObject->PeerCallAddrPresent) { EventData.pCalleeAddr = &(pCallObject->PeerCallAddr); } else { EventData.pCalleeAddr = NULL; } EventData.pLocalAddr = &(pCallObject->LocalAddr); EventData.pH245Addr = NULL; if (pConnectASN->h245AddrPresent) { EventData.pH245Addr = &(pConnectASN->h245Addr); } if (!(pConnectASN->NonStandardDataPresent) || (pConnectASN->NonStandardData.sData.wOctetStringLength == 0) || (pConnectASN->NonStandardData.sData.pOctetString == NULL)) { EventData.pNonStandardData = NULL; } else { EventData.pNonStandardData = &(pConnectASN->NonStandardData); } EventData.pszDisplay = NULL; if (pMessage->Display.Present && pMessage->Display.Contents) { MultiByteToWideChar(CP_ACP, 0, (const char *)pMessage->Display.Contents, -1, szUnicodeDisplay, sizeof(szUnicodeDisplay) / sizeof(szUnicodeDisplay[0])); EventData.pszDisplay = szUnicodeDisplay; } EventData.pDestinationEndpointType = &(pConnectASN->EndpointType); EventData.wCallReference = pMessage->CallReference; Q931StopTimer(pCallObject, Q931_TIMER_303); Q931StopTimer(pCallObject, Q931_TIMER_301); result = pCallObject->Callback((BYTE)Q931_CALL_ACCEPTED, pCallObject->hQ931Call, pCallObject->dwListenToken, pCallObject->dwUserToken, &EventData); } return CS_OK; } //==================================================================================== //==================================================================================== CS_STATUS Q931OnCallReleaseComplete( P_CALL_OBJECT pCallObject, Q931MESSAGE *pMessage, Q931_RELEASE_COMPLETE_ASN *pReleaseCompleteASN) { DWORD result; BYTE bCause = 0; if (pMessage && pMessage->Cause.Present && (pMessage->Cause.Length >= 3)) { bCause = (BYTE)(pMessage->Cause.Contents[2] & (~CAUSE_EXT_BIT)); } Q931StopTimer(pCallObject, Q931_TIMER_303); Q931StopTimer(pCallObject, Q931_TIMER_301); // if this is the callee, or the call has been connected already, // then this message should be treated as hangup (not reject). if (!(pCallObject->fIsCaller) || (pCallObject->bCallState == CALLSTATE_ACTIVE) || (bCause == CAUSE_VALUE_NORMAL_CLEAR)) { CSS_CALL_REMOTE_HANGUP EventData; EventData.bReason = CC_REJECT_NORMAL_CALL_CLEARING; pCallObject->bCallState = CALLSTATE_NULL; result = pCallObject->Callback((BYTE)Q931_CALL_REMOTE_HANGUP, pCallObject->hQ931Call, pCallObject->dwListenToken, pCallObject->dwUserToken, &EventData); } else { CSS_CALL_REJECTED EventData; pCallObject->bCallState = CALLSTATE_NULL; // populate the event data struct. switch (bCause) { case CAUSE_VALUE_NORMAL_CLEAR: EventData.bRejectReason = CC_REJECT_NORMAL_CALL_CLEARING; break; case CAUSE_VALUE_USER_BUSY: EventData.bRejectReason = CC_REJECT_USER_BUSY; break; case CAUSE_VALUE_NO_ANSWER: EventData.bRejectReason = CC_REJECT_NO_ANSWER; break; case CAUSE_VALUE_NOT_IMPLEMENTED: EventData.bRejectReason = CC_REJECT_NOT_IMPLEMENTED; break; case CAUSE_VALUE_INVALID_CRV: EventData.bRejectReason = CC_REJECT_INVALID_IE_CONTENTS; break; case CAUSE_VALUE_IE_MISSING: EventData.bRejectReason = CC_REJECT_MANDATORY_IE_MISSING; break; case CAUSE_VALUE_IE_CONTENTS: EventData.bRejectReason = CC_REJECT_INVALID_IE_CONTENTS; break; case CAUSE_VALUE_TIMER_EXPIRED: EventData.bRejectReason = CC_REJECT_TIMER_EXPIRED; break; default: EventData.bRejectReason = pReleaseCompleteASN->bReason; break; } EventData.ConferenceID = pCallObject->ConferenceID; EventData.pAlternateAddr = NULL; if (!(pReleaseCompleteASN->NonStandardDataPresent) || (pReleaseCompleteASN->NonStandardData.sData.wOctetStringLength == 0) || (pReleaseCompleteASN->NonStandardData.sData.pOctetString == NULL)) { EventData.pNonStandardData = NULL; } else { EventData.pNonStandardData = &(pReleaseCompleteASN->NonStandardData); } result = pCallObject->Callback((BYTE)Q931_CALL_REJECTED, pCallObject->hQ931Call, pCallObject->dwListenToken, pCallObject->dwUserToken, &EventData); } return CS_OK; } //==================================================================================== //==================================================================================== CS_STATUS Q931OnCallFacility( P_CALL_OBJECT pCallObject, Q931MESSAGE *pMessage, Q931_FACILITY_ASN *pFacilityASN) { DWORD result; // if this is the callee, or the call has been connected already, // then this message should be treated as hangup (not reject). if (!(pCallObject->fIsCaller) || (pCallObject->bCallState == CALLSTATE_ACTIVE)) { CSS_CALL_REMOTE_HANGUP EventData; EventData.bReason = pFacilityASN->bReason; pCallObject->bCallState = CALLSTATE_NULL; result = pCallObject->Callback((BYTE)Q931_CALL_REMOTE_HANGUP, pCallObject->hQ931Call, pCallObject->dwListenToken, pCallObject->dwUserToken, &EventData); } else { CSS_CALL_REJECTED EventData; pCallObject->bCallState = CALLSTATE_NULL; // populate the event data struct. EventData.bRejectReason = pFacilityASN->bReason; EventData.ConferenceID = pFacilityASN->ConferenceIDPresent ? pFacilityASN->ConferenceID : pCallObject->ConferenceID; EventData.pAlternateAddr = &(pFacilityASN->AlternativeAddr); if (!(pFacilityASN->NonStandardDataPresent) || (pFacilityASN->NonStandardData.sData.wOctetStringLength == 0) || (pFacilityASN->NonStandardData.sData.pOctetString == NULL)) { EventData.pNonStandardData = NULL; } else { EventData.pNonStandardData = &(pFacilityASN->NonStandardData); } result = pCallObject->Callback((BYTE)Q931_CALL_REJECTED, pCallObject->hQ931Call, pCallObject->dwListenToken, pCallObject->dwUserToken, &EventData); } return CS_OK; } //==================================================================================== //==================================================================================== CS_STATUS Q931SendReleaseCompleteMessage( P_CALL_OBJECT pCallObject, BYTE bRejectReason, PCC_CONFERENCEID pConferenceID, PCC_ADDR pAlternateAddr, PCC_NONSTANDARDDATA pNonStandardData) { CS_STATUS result = CS_OK; HQ931CALL hQ931Call = pCallObject->hQ931Call; // since this call is going away, mark the call object for deletion so any other // threads attempting to use this object will fail to get a lock on it. CallObjectMarkForDelete(hQ931Call); if((bRejectReason == CC_REJECT_ROUTE_TO_GATEKEEPER) || (bRejectReason == CC_REJECT_CALL_FORWARDED) || (bRejectReason == CC_REJECT_ROUTE_TO_MC)) { // send the FACILITY message to the peer to reject the call. DWORD CodedLengthASN; BYTE *CodedPtrASN; HRESULT ResultASN = CS_OK; CC_ADDR AltAddr; MakeBinaryADDR(pAlternateAddr, &AltAddr); ResultASN = Q931FacilityEncodeASN(pNonStandardData, (pAlternateAddr ? &AltAddr : NULL), bRejectReason, pConferenceID, NULL, &pCallObject->World, &CodedPtrASN, &CodedLengthASN); if ((ResultASN != CS_OK) || (CodedLengthASN == 0) || (CodedPtrASN == NULL)) { Q931DBG((DBGERROR, "Q931FacilityEncodeASN() failed, nothing to send.")); if (CodedPtrASN != NULL) { Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN); } result = CS_INTERNAL_ERROR; } else { DWORD CodedLengthPDU; BYTE *CodedPtrPDU; BINARY_STRING UserUserData; HRESULT ResultEncode = CS_OK; WORD wCRV; if (pCallObject->fIsCaller) { wCRV = (WORD)(pCallObject->wCRV & 0x7FFF); } else { wCRV = (WORD)(pCallObject->wCRV | 0x8000); } UserUserData.length = (WORD)CodedLengthASN; UserUserData.ptr = CodedPtrASN; ResultEncode = Q931FacilityEncodePDU(wCRV, &UserUserData, &CodedPtrPDU, &CodedLengthPDU); if (CodedPtrASN != NULL) { Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN); } if ((ResultEncode != CS_OK) || (CodedLengthPDU == 0) || (CodedPtrPDU == NULL)) { Q931DBG((DBGERROR, "Q931FacilityEncodePDU() failed, nothing to send.")); if (CodedPtrPDU != NULL) { Free(CodedPtrPDU); } result = CS_INTERNAL_ERROR; } else { result = Q931SendMessage(pCallObject, CodedPtrPDU, CodedLengthPDU, FALSE); } } } else { // send the RELEASE COMPLETE message to the peer to reject call. DWORD CodedLengthASN; BYTE *CodedPtrASN; HRESULT ResultASN = CS_OK; BYTE bReasonUU = bRejectReason; BYTE *pbReasonUU = &bReasonUU; switch (bReasonUU) { case CC_REJECT_NO_BANDWIDTH: case CC_REJECT_GATEKEEPER_RESOURCES: case CC_REJECT_UNREACHABLE_DESTINATION: case CC_REJECT_DESTINATION_REJECTION: case CC_REJECT_INVALID_REVISION: case CC_REJECT_NO_PERMISSION: case CC_REJECT_UNREACHABLE_GATEKEEPER: case CC_REJECT_GATEWAY_RESOURCES: case CC_REJECT_BAD_FORMAT_ADDRESS: case CC_REJECT_ADAPTIVE_BUSY: case CC_REJECT_IN_CONF: case CC_REJECT_CALL_DEFLECTION: case CC_REJECT_USER_BUSY: break; default: pbReasonUU = NULL; break; } ResultASN = Q931ReleaseCompleteEncodeASN(pNonStandardData, pConferenceID, pbReasonUU, &pCallObject->World, &CodedPtrASN, &CodedLengthASN); if ((ResultASN != CS_OK) || (CodedLengthASN == 0) || (CodedPtrASN == NULL)) { Q931DBG((DBGERROR, "Q931ReleaseCompleteEncodeASN() failed, nothing to send.")); if (CodedPtrASN != NULL) { Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN); } result = CS_INTERNAL_ERROR; } else { DWORD CodedLengthPDU; BYTE *CodedPtrPDU; BINARY_STRING UserUserData; HRESULT ResultEncode = CS_OK; BYTE bCause = 0; BYTE *pbCause = &bCause; WORD wCRV; if (pCallObject->fIsCaller) { wCRV = (WORD)(pCallObject->wCRV & 0x7FFF); } else { wCRV = (WORD)(pCallObject->wCRV | 0x8000); } UserUserData.length = (WORD)CodedLengthASN; UserUserData.ptr = CodedPtrASN; switch (bRejectReason) { case CC_REJECT_NORMAL_CALL_CLEARING: bCause = CAUSE_VALUE_NORMAL_CLEAR; break; case CC_REJECT_USER_BUSY: bCause = CAUSE_VALUE_USER_BUSY; break; case CC_REJECT_NO_ANSWER: bCause = CAUSE_VALUE_NO_ANSWER; break; case CC_REJECT_NOT_IMPLEMENTED: bCause = CAUSE_VALUE_NOT_IMPLEMENTED; break; case CC_REJECT_MANDATORY_IE_MISSING: bCause = CAUSE_VALUE_IE_MISSING; break; case CC_REJECT_INVALID_IE_CONTENTS: bCause = CAUSE_VALUE_IE_CONTENTS; break; case CC_REJECT_TIMER_EXPIRED: bCause = CAUSE_VALUE_TIMER_EXPIRED; break; default: pbCause = NULL; break; } ResultEncode = Q931ReleaseCompleteEncodePDU(wCRV, pbCause, &UserUserData, &CodedPtrPDU, &CodedLengthPDU); if (CodedPtrASN != NULL) { Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN); } if ((ResultEncode != CS_OK) || (CodedLengthPDU == 0) || (CodedPtrPDU == NULL)) { Q931DBG((DBGERROR, "Q931ReleaseCompleteEncodePDU() failed, nothing to send.")); if (CodedPtrPDU != NULL) { Free(CodedPtrPDU); } result = CS_INTERNAL_ERROR; } else { result = Q931SendMessage(pCallObject, CodedPtrPDU, CodedLengthPDU, FALSE); } } } pCallObject->bCallState = CALLSTATE_NULL; if (result != CS_OK) { return result; } return CS_OK; } //==================================================================================== //==================================================================================== CS_STATUS Q931SendStatusMessage( P_CALL_OBJECT pCallObject, Q931MESSAGE *pMessage, BYTE bCause) { CS_STATUS result = CS_OK; DWORD CodedLengthPDU; BYTE *CodedPtrPDU; HRESULT EncodePDU = CS_OK; int nError = 0; HQ931CALL hQ931Call = pCallObject->hQ931Call; WORD wCRV; if (pCallObject->fIsCaller) { wCRV = (WORD)(pCallObject->wCRV & 0x7FFF); } else { wCRV = (WORD)(pCallObject->wCRV | 0x8000); } EncodePDU = Q931StatusEncodePDU(wCRV, NULL, bCause, pCallObject->bCallState, &CodedPtrPDU, &CodedLengthPDU); if ((EncodePDU != CS_OK) || (CodedLengthPDU == 0) || (CodedPtrPDU == NULL)) { Q931DBG((DBGERROR, "Q931StatusEncodePDU() failed, nothing to send.")); if (CodedPtrPDU != NULL) { Free(CodedPtrPDU); } result = CS_INTERNAL_ERROR; } else { result = Q931SendMessage(pCallObject, CodedPtrPDU, CodedLengthPDU, TRUE); if(CallObjectValidate(hQ931Call) != CS_OK) return(CS_INTERNAL_ERROR); } return(result); } //==================================================================================== //==================================================================================== CS_STATUS Q931SendProceedingMessage( HQ931CALL hQ931Call, WORD wCallReference, PCC_ENDPOINTTYPE pDestinationEndpointType, PCC_NONSTANDARDDATA pNonStandardData) { CS_STATUS result = CS_OK; DWORD CodedLengthASN; BYTE *CodedPtrASN; HRESULT ResultASN = CS_OK; DWORD dwPhysicalId = INVALID_PHYS_ID; P_CALL_OBJECT pCallObject = NULL; if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL)) { Q931DBG((DBGERROR, "CallObjectLock() returned error")); return CS_SUBSYSTEM_FAILURE; } dwPhysicalId = pCallObject->dwPhysicalId; // first build the ASN portion of the message (user to user part) ResultASN = Q931ProceedingEncodeASN( pNonStandardData, NULL, // No H245 address. pDestinationEndpointType, // EndpointType information. &pCallObject->World, &CodedPtrASN, &CodedLengthASN); if ((ResultASN != CS_OK) || (CodedLengthASN == 0) || (CodedPtrASN == NULL)) { Q931DBG((DBGERROR, "Q931ProceedingEncodeASN() failed, nothing to send.")); if (CodedPtrASN != NULL) { Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN); } result = CS_INTERNAL_ERROR; } else { // now build the rest of the message DWORD CodedLengthPDU; BYTE *CodedPtrPDU; BINARY_STRING UserUserData; HRESULT ResultEncode = CS_OK; WORD wCRV = (WORD)(wCallReference | 0x8000); UserUserData.length = (WORD)CodedLengthASN; UserUserData.ptr = CodedPtrASN; ResultEncode = Q931ProceedingEncodePDU(wCRV, &UserUserData, &CodedPtrPDU, &CodedLengthPDU); if (CodedPtrASN != NULL) { Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN); } if ((ResultEncode != CS_OK) || (CodedLengthPDU == 0) || (CodedPtrPDU == NULL)) { Q931DBG((DBGERROR, "Q931ProceedingEncodePDU() failed, nothing to send.")); if (CodedPtrPDU != NULL) { Free(CodedPtrPDU); } result = CS_INTERNAL_ERROR; } else { result = Q931SendMessage(pCallObject, CodedPtrPDU, CodedLengthPDU, TRUE); if (CallObjectValidate(hQ931Call) != CS_OK) return(CS_INTERNAL_ERROR); } } CallObjectUnlock(pCallObject); return(result); } //==================================================================================== //==================================================================================== CS_STATUS Q931SendPDU(HQ931CALL hQ931Call, BYTE* CodedPtrPDU, DWORD CodedLengthPDU) { CS_STATUS result = CS_OK; HRESULT TempResult; P_CALL_OBJECT pCallObject = NULL; if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL)) { Q931DBG((DBGERROR, "CallObjectLock() returned error")); return CS_SUBSYSTEM_FAILURE; } TempResult = Q931SendMessage(pCallObject, CodedPtrPDU, CodedLengthPDU, TRUE); if (CallObjectValidate(hQ931Call) != CS_OK) return(CS_INTERNAL_ERROR); if(FAILED(TempResult)) { CSS_CALL_FAILED EventData; EventData.error = TempResult; if ((pCallObject->bCallState == CALLSTATE_ACTIVE) && (pCallObject->bResolved)) { pCallObject->Callback(Q931_CALL_CONNECTION_CLOSED, pCallObject->hQ931Call, pCallObject->dwListenToken, pCallObject->dwUserToken, NULL); } else { pCallObject->Callback(Q931_CALL_FAILED, pCallObject->hQ931Call, pCallObject->dwListenToken, pCallObject->dwUserToken, &EventData); } if (CallObjectValidate(hQ931Call) == CS_OK) { DWORD dwId = pCallObject->dwPhysicalId; if ((pCallObject->bCallState != CALLSTATE_ACTIVE) || (!pCallObject->bResolved)) { CallObjectDestroy(pCallObject); pCallObject = NULL; } linkLayerShutdown(dwId); if (pCallObject) { pCallObject->bConnected = FALSE; } } return TempResult; } CallObjectUnlock(pCallObject); return CS_OK; } //==================================================================================== //==================================================================================== CS_STATUS Q931OnCallStatusEnquiry( P_CALL_OBJECT pCallObject, Q931MESSAGE *pMessage) { CS_STATUS SendStatus; SendStatus = Q931SendStatusMessage(pCallObject, pMessage, CAUSE_VALUE_ENQUIRY_RESPONSE); return SendStatus; } //==================================================================================== //==================================================================================== void Q931SendComplete(DWORD instance, HRESULT msg, PBYTE buf, DWORD length) { HQ931CALL hQ931Call = (HQ931CALL)instance; P_CALL_OBJECT pCallObject = NULL; Q931DBG((DBGTRACE, "Entering Q931SendComplete()...")); if (buf != NULL) { Free(buf); } if (FAILED(msg)) { // shut down link layer; report failure to client CSS_CALL_FAILED EventData; Q931DBG((DBGERROR, "error in datalinkSendRequest()")); if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL)) { Q931DBG((DBGERROR, "CallObjectLock() returned error")); return; } EventData.error = msg; if ((pCallObject->bCallState == CALLSTATE_ACTIVE) && (pCallObject->bResolved)) { pCallObject->Callback(Q931_CALL_CONNECTION_CLOSED, pCallObject->hQ931Call, pCallObject->dwListenToken, pCallObject->dwUserToken, NULL); } else { pCallObject->Callback(Q931_CALL_FAILED, pCallObject->hQ931Call, pCallObject->dwListenToken, pCallObject->dwUserToken, &EventData); } if (CallObjectValidate(hQ931Call) == CS_OK) { DWORD dwId = pCallObject->dwPhysicalId; if ((pCallObject->bCallState != CALLSTATE_ACTIVE) || (!pCallObject->bResolved)) { CallObjectDestroy(pCallObject); pCallObject = NULL; } linkLayerShutdown(dwId); if (pCallObject) { pCallObject->bConnected = FALSE; } } return; } return; } //==================================================================================== //==================================================================================== static DWORD PostReceiveBuffer(DWORD dwPhysicalId, BYTE *buf) { if (buf == NULL) { buf = Malloc(RECEIVE_BUFFER_SIZE); } return datalinkReceiveRequest(dwPhysicalId, buf, RECEIVE_BUFFER_SIZE); } //==================================================================================== //==================================================================================== void OnReceiveCallback(DWORD instance, HRESULT message, Q931MESSAGE *pMessage, BYTE *buf, DWORD nbytes) { HQ931CALL hQ931Call = (HQ931CALL)instance; P_CALL_OBJECT pCallObject = NULL; DWORD dwPhysicalId; Q931DBG((DBGTRACE, "Entering ReceiveCallback()...")); if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL)) { if (buf) { Free(buf); } Q931DBG((DBGTRACE, "Call Object no longer available: 0x%08lx", hQ931Call)); return; } if (message == LINK_RECV_DATA) { HRESULT Result = CS_OK; if ((buf == NULL) || (nbytes == 0)) { Q931DBG((DBGERROR, "Empty buffer received as data.")); CallObjectUnlock(pCallObject); return; } // This block is the Q931 call re-connect implementation: // if the object related to the incoming message is not yet resolved... if (pCallObject->bResolved == FALSE) { // try to resolve the object. HQ931CALL hFoundCallObject; P_CALL_OBJECT pFoundCallObject = NULL; // If found another object with matching CRV/Addr... if (CallObjectFind(&hFoundCallObject, pCallObject->wCRV, &(pCallObject->PeerConnectAddr)) && ((CallObjectLock(hFoundCallObject, &pFoundCallObject) == CS_OK) && (pFoundCallObject != NULL))) { // friendly channel close of the pFoundCallObject. Q931SendReleaseCompleteMessage(pFoundCallObject, CC_REJECT_UNDEFINED_REASON, &(pFoundCallObject->ConferenceID), NULL, NULL); // unlock the call object before calling shutdown CallObjectUnlock(pFoundCallObject); linkLayerShutdown(pFoundCallObject->dwPhysicalId); if((CallObjectLock(hFoundCallObject, &pFoundCallObject) != CS_OK) || (pFoundCallObject == NULL)) return; // assign the new dwPhysicalId to found object. pFoundCallObject->dwPhysicalId = pCallObject->dwPhysicalId; // new object should be destroyed. CallObjectDestroy(pCallObject); pCallObject = pFoundCallObject; } else { // The call is a newly established call, so resolve it now. pCallObject->bResolved = TRUE; } } Result = Q931ParseMessage((BYTE *)buf, nbytes, pMessage); #if (defined(_DEBUG) && defined(PCS_COMPLIANCE)) InteropOutput(Q931Logger, buf, nbytes, Q931LOG_RECEIVED_PDU); #endif // A message is to be ignored (per 5.8.1 and 5.8.2) if: // - protocol discriminator is not recognized // - message is too short to contain a complete message // type information element if ( Result != CS_OK && Result != CS_MESSAGE_TOO_SHORT && Result != CS_INVALID_PROTOCOL) { Result = Q931SendStatusMessage(pCallObject, pMessage, CAUSE_VALUE_INVALID_MSG); Q931DBG((DBGERROR, "Q931ParseMessage(): failed.")); if(CallObjectValidate(hQ931Call) != CS_OK) { if (buf) { Free(buf); } return; } dwPhysicalId = pCallObject->dwPhysicalId; CallObjectUnlock(pCallObject); PostReceiveBuffer(dwPhysicalId, buf); return; } if (pMessage->Shift.Present) { Q931DBG((DBGERROR, "Shift present in message: dropped.")); dwPhysicalId = pCallObject->dwPhysicalId; CallObjectUnlock(pCallObject); PostReceiveBuffer(dwPhysicalId, buf); return; } // If a hooking procedure has been installed, // give it first shot at acting on the received PDU. // If it returns TRUE, then processing is finished. if (gReceivePDUHookProc) { BOOL bHookProcessedMessage; bHookProcessedMessage = gReceivePDUHookProc(pMessage, pCallObject->hQ931Call, pCallObject->dwListenToken, pCallObject->dwUserToken); if (bHookProcessedMessage) { if (CallObjectValidate(hQ931Call) == CS_OK) { dwPhysicalId = pCallObject->dwPhysicalId; CallObjectUnlock(pCallObject); PostReceiveBuffer(dwPhysicalId, buf); } return; } } // Message now contains the values of the Q931 PDU elements... switch (pMessage->MessageType) { case SETUPMESSAGETYPE: { Q931_SETUP_ASN SetupASN; if (!pMessage->UserToUser.Present || (pMessage->UserToUser.UserInformationLength == 0)) { Q931DBG((DBGERROR, "ReceiveCallback(): Message is missing ASN.1 UserUser data...")); dwPhysicalId = pCallObject->dwPhysicalId; CallObjectUnlock(pCallObject); PostReceiveBuffer(dwPhysicalId, buf); return; } Q931DBG((DBGTRACE, "ReceiveCallback(): received Setup message...")); Result = Q931SetupParseASN(&pCallObject->World, pMessage->UserToUser.UserInformation, pMessage->UserToUser.UserInformationLength, &SetupASN); if (Result == CS_OPTION_NOT_IMPLEMENTED) { //... maybe callback callcont in later drop. // initiate a disconnect sequence from the caller side. if (Q931SendReleaseCompleteMessage(pCallObject, CC_REJECT_TIMER_EXPIRED, NULL, NULL, NULL) != CS_OK) { // nothing to do if this fails. } dwPhysicalId = pCallObject->dwPhysicalId; CallObjectDestroy(pCallObject); linkLayerShutdown(dwPhysicalId); if (buf) { Free(buf); buf = NULL; } return; } if (Result != CS_OK) { Q931DBG((DBGERROR, "ReceiveCallback(): Unable to parse ASN.1 data.")); break; } // The "CallerAddr is not passed in the PDU, so the // only valuable addr to use is the connection addr // passed from the link layer and saved into the call // object at connect-time. SetupASN.CallerAddrPresent = TRUE; SetupASN.CallerAddr = pCallObject->PeerConnectAddr; // The "CalleeAddr" which is passed in the PDU is ignored // by the ASN parser, and supplied by the link layer // instead and saved into the call object at connect-time. // here, this address is used as the callee addr. SetupASN.CalleeAddrPresent = TRUE; SetupASN.CalleeAddr = pCallObject->LocalAddr; Result = Q931OnCallSetup(pCallObject, pMessage, &SetupASN); _FreeSetupASN(&SetupASN); } break; case RELEASECOMPLMESSAGETYPE: { Q931_RELEASE_COMPLETE_ASN ReleaseCompleteASN; if (!pMessage->UserToUser.Present || (pMessage->UserToUser.UserInformationLength == 0)) { Q931DBG((DBGERROR, "ReceiveCallback(): Message is missing ASN.1 UserUser data...")); dwPhysicalId = pCallObject->dwPhysicalId; CallObjectUnlock(pCallObject); PostReceiveBuffer(dwPhysicalId, buf); return; } Q931DBG((DBGTRACE, "ReceiveCallback(): received ReleaseComplete message...")); Result = Q931ReleaseCompleteParseASN(&pCallObject->World, pMessage->UserToUser.UserInformation, pMessage->UserToUser.UserInformationLength, &ReleaseCompleteASN); if (Result != CS_OK) { Q931DBG((DBGERROR, "ReceiveCallback(): Unable to parse ASN.1 data.")); break; } Result = Q931OnCallReleaseComplete(pCallObject, pMessage, &ReleaseCompleteASN); if (CallObjectValidate(hQ931Call) == CS_OK) { dwPhysicalId = pCallObject->dwPhysicalId; CallObjectDestroy(pCallObject); linkLayerShutdown(dwPhysicalId); } Free(buf); _FreeReleaseCompleteASN(&ReleaseCompleteASN); return; } break; case FACILITYMESSAGETYPE: { Q931_FACILITY_ASN FacilityASN; if (!pMessage->UserToUser.Present || (pMessage->UserToUser.UserInformationLength == 0)) { Q931DBG((DBGERROR, "ReceiveCallback(): Message is missing ASN.1 UserUser data...")); dwPhysicalId = pCallObject->dwPhysicalId; CallObjectUnlock(pCallObject); PostReceiveBuffer(dwPhysicalId, buf); return; } Q931DBG((DBGTRACE, "ReceiveCallback(): received Facility message...")); Result = Q931FacilityParseASN(&pCallObject->World, pMessage->UserToUser.UserInformation, pMessage->UserToUser.UserInformationLength, &FacilityASN); if (Result != CS_OK) { Q931DBG((DBGERROR, "ReceiveCallback(): Unable to parse ASN.1 data.")); break; } // initiate a disconnect sequence from the caller side. Q931SendReleaseCompleteMessage(pCallObject, CC_REJECT_CALL_DEFLECTION, NULL, NULL, NULL); Result = Q931OnCallFacility(pCallObject, pMessage, &FacilityASN); _FreeFacilityASN(&FacilityASN); dwPhysicalId = pCallObject->dwPhysicalId; CallObjectDestroy(pCallObject); linkLayerShutdown(dwPhysicalId); Free(buf); return; } break; case CONNECTMESSAGETYPE: { Q931_CONNECT_ASN ConnectASN; if (!pMessage->UserToUser.Present || (pMessage->UserToUser.UserInformationLength == 0)) { Q931DBG((DBGERROR, "ReceiveCallback(): Message is missing ASN.1 UserUser data...")); dwPhysicalId = pCallObject->dwPhysicalId; CallObjectUnlock(pCallObject); PostReceiveBuffer(dwPhysicalId, buf); return; } Q931DBG((DBGTRACE, "ReceiveCallback(): received Connect message...")); Result = Q931ConnectParseASN(&pCallObject->World, pMessage->UserToUser.UserInformation, pMessage->UserToUser.UserInformationLength, &ConnectASN); if (Result != CS_OK) { Q931DBG((DBGERROR, "ReceiveCallback(): Unable to parse ASN.1 data.")); break; } Result = Q931OnCallConnect(pCallObject, pMessage, &ConnectASN); _FreeConnectASN(&ConnectASN); } break; case PROCEEDINGMESSAGETYPE: { Q931_CALL_PROCEEDING_ASN ProceedingASN; Q931DBG((DBGTRACE, "ReceiveCallback(): received Proceeding message...")); if (!pMessage->UserToUser.Present || (pMessage->UserToUser.UserInformationLength == 0)) { Result = Q931OnCallProceeding(pCallObject, pMessage, NULL); } else { Result = Q931ProceedingParseASN(&pCallObject->World, pMessage->UserToUser.UserInformation, pMessage->UserToUser.UserInformationLength, &ProceedingASN); if (Result != CS_OK) { Q931DBG((DBGERROR, "ReceiveCallback(): Unable to parse ASN.1 data.")); break; } Result = Q931OnCallProceeding(pCallObject, pMessage, &ProceedingASN); _FreeProceedingASN(&ProceedingASN); } } break; case ALERTINGMESSAGETYPE: { Q931_ALERTING_ASN AlertingASN; Q931DBG((DBGTRACE, "ReceiveCallback(): received Alerting message...")); if (!pMessage->UserToUser.Present || (pMessage->UserToUser.UserInformationLength == 0)) { Result = Q931OnCallAlerting(pCallObject, pMessage, NULL); } else { Result = Q931AlertingParseASN(&pCallObject->World, pMessage->UserToUser.UserInformation, pMessage->UserToUser.UserInformationLength, &AlertingASN); if (Result != CS_OK) { Q931DBG((DBGERROR, "ReceiveCallback(): Unable to parse ASN.1 data.")); break; } Result = Q931OnCallAlerting(pCallObject, pMessage, &AlertingASN); _FreeAlertingASN(&AlertingASN); } } break; case RELEASEMESSAGETYPE: case STATUSMESSAGETYPE: Q931DBG((DBGWARNING, "ReceiveCallback(): message not yet supported.")); break; case STATUSENQUIRYMESSAGETYPE: Q931DBG((DBGWARNING, "ReceiveCallback(): message not yet supported.")); Result = Q931OnCallStatusEnquiry(pCallObject, pMessage); break; default: Q931DBG((DBGERROR, "ReceiveCallback(): unknown message received.")); break; } // re-validate the call object: if (CallObjectValidate(hQ931Call) == CS_OK) { dwPhysicalId = pCallObject->dwPhysicalId; CallObjectUnlock(pCallObject); PostReceiveBuffer(dwPhysicalId, buf); if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL)) return; } else { if (buf) { Free(buf); } return; } if ( Result == CS_INCOMPATIBLE_VERSION || Result == CS_NO_MEMORY || Result == CS_SUBSYSTEM_FAILURE) { // initiate a disconnect sequence from the caller side. Q931SendReleaseCompleteMessage(pCallObject, CC_REJECT_INVALID_REVISION, NULL, NULL, NULL); dwPhysicalId = pCallObject->dwPhysicalId; CallObjectDestroy(pCallObject); linkLayerShutdown(dwPhysicalId); return; } if (Result == CS_MANDATORY_IE_MISSING) { Q931SendStatusMessage(pCallObject, pMessage, CAUSE_VALUE_IE_MISSING); } else if ( Result == CS_BAD_IE_CONTENT || Result == CS_BAD_PARAM || Result == CS_NO_FIELD_DATA) { Q931SendStatusMessage(pCallObject, pMessage, CAUSE_VALUE_IE_CONTENTS); } } else if (message == LINK_RECV_CLOSED) { // Socket closed if (buf) { Free(buf); } pCallObject->Callback(Q931_CALL_CONNECTION_CLOSED, pCallObject->hQ931Call, pCallObject->dwListenToken, pCallObject->dwUserToken, NULL); if (CallObjectValidate(hQ931Call) == CS_OK) { dwPhysicalId = pCallObject->dwPhysicalId; pCallObject->bConnected = FALSE; CallObjectUnlock(pCallObject); linkLayerShutdown(dwPhysicalId); } return; } else if (buf) { // unknown condition? Free(buf); } if (CallObjectValidate(hQ931Call) == CS_OK) { CallObjectUnlock(pCallObject); } return; } //==================================================================================== //==================================================================================== void Q931ReceiveCallback(DWORD instance, HRESULT message, BYTE *buf, DWORD nbytes) { Q931MESSAGE *pMessage = NULL; if (message == LINK_RECV_DATA) { pMessage = (Q931MESSAGE *)Malloc(sizeof(Q931MESSAGE)); if (pMessage == NULL) { Q931DBG((DBGERROR, "Not enough memory to process Q931 message.")); // something more should be done here to indicate SERIOUS error... return; } } OnReceiveCallback(instance, message, pMessage, buf, nbytes); if (pMessage) { Free(pMessage); } return; } //==================================================================================== //==================================================================================== void Q931ConnectCallback(DWORD dwInstance, HRESULT dwMessage, CC_ADDR *pLocalAddr, CC_ADDR *pPeerAddr) { HQ931CALL hQ931Call = (HQ931CALL)dwInstance; P_CALL_OBJECT pCallObject = NULL; HRESULT TempResult; DWORD dwPhysicalId; Q931DBG((DBGTRACE, "Entering Q931ConnectCallback()...")); if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL)) { Q931DBG((DBGERROR, "CallObjectLock() returned error")); return; } pCallObject->bConnected = TRUE; if (FAILED(dwMessage)) { // shut down link layer; report failure to client CSS_CALL_FAILED EventData; Q931DBG((DBGERROR, "error in connect")); EventData.error = dwMessage; pCallObject->Callback(Q931_CALL_FAILED, pCallObject->hQ931Call, pCallObject->dwListenToken, pCallObject->dwUserToken, &EventData); if (CallObjectValidate(hQ931Call) == CS_OK) { DWORD dwId = pCallObject->dwPhysicalId; CallObjectDestroy(pCallObject); linkLayerShutdown(dwId); } return; } if (dwMessage != LINK_CONNECT_COMPLETE) { Q931DBG((DBGERROR, "unexpected connect callback")); CallObjectUnlock(pCallObject); return; } if (pCallObject->bCallState == CALLSTATE_NULL) { pCallObject->bCallState = CALLSTATE_INITIATED; } pCallObject->LocalAddr = *pLocalAddr; pCallObject->PeerConnectAddr = *pPeerAddr; // if the user specified a binary source address with address = 0, // fill in the address with the local address and send. if ((pCallObject->SourceAddrPresent) && (pCallObject->SourceAddr.nAddrType == CC_IP_BINARY) && (!pCallObject->SourceAddr.Addr.IP_Binary.dwAddr)) { pCallObject->SourceAddr = *pLocalAddr; } if ((pCallObject->fIsCaller) && (pCallObject->bCallState == CALLSTATE_INITIATED)) { // send the SETUP message to the peer. DWORD CodedLengthASN; BYTE *CodedPtrASN; HRESULT ResultASN = CS_OK; DWORD CodedLengthPDU; BYTE *CodedPtrPDU; HRESULT ResultPDU = CS_OK; int nError = 0; BOOL ResultSend = FALSE; BINARY_STRING UserUserData; PCC_VENDORINFO pVendorInfo = NULL; CC_NONSTANDARDDATA *pNonStandardData = NULL; DWORD dwId; if (pCallObject->VendorInfoPresent) { pVendorInfo = &(pCallObject->VendorInfo); } if (pCallObject->NonStandardDataPresent) { pNonStandardData = &(pCallObject->NonStandardData); } // if there is a special callee alias list, load the calledparty#. if (pCallObject->szCalledPartyNumber[0] == 0 && pCallObject->pCalleeAliasList != NULL && pCallObject->pCalleeAliasList->wCount == 1 && pCallObject->pCalleeAliasList->pItems[0].wType == CC_ALIAS_H323_PHONE && pCallObject->pCalleeAliasList->pItems[0].wDataLength > 0 && pCallObject->pCalleeAliasList->pItems[0].pData != NULL) { PCC_ALIASITEM pItem = &pCallObject->pCalleeAliasList->pItems[0]; WCHAR szWidePartyNumber[CC_MAX_PARTY_NUMBER_LEN + 1]; memset(szWidePartyNumber, 0 , CC_MAX_PARTY_NUMBER_LEN + 1); if (pItem->wPrefixLength > 0 && pItem->pPrefix != NULL) { ASSERT((pItem->wPrefixLength + pItem->wDataLength +1) <= (sizeof(szWidePartyNumber)/sizeof(szWidePartyNumber[0]))); memcpy(&szWidePartyNumber[0], pItem->pPrefix, (pItem->wPrefixLength) * sizeof(WCHAR)); memcpy(&szWidePartyNumber[pItem->wPrefixLength], pItem->pData, pItem->wDataLength * sizeof(WCHAR)); } else { ASSERT((pItem->wDataLength +1) <= (sizeof(szWidePartyNumber)/sizeof(szWidePartyNumber[0]))); memcpy(szWidePartyNumber, pCallObject->pCalleeAliasList->pItems[0].pData, pItem->wDataLength * sizeof(WCHAR)); } WideCharToMultiByte(CP_ACP, 0, szWidePartyNumber, pItem->wPrefixLength + pItem->wDataLength * sizeof(WCHAR), pCallObject->szCalledPartyNumber, sizeof(pCallObject->szCalledPartyNumber), NULL, NULL); } // may wish to pass alias parms later instead of NULL, NULL. ResultASN = Q931SetupEncodeASN(pNonStandardData, pCallObject->SourceAddrPresent ? &(pCallObject->SourceAddr) : NULL, pCallObject->PeerCallAddrPresent ? &(pCallObject->PeerCallAddr) : NULL, // callee pCallObject->wGoal, pCallObject->wCallType, pCallObject->bCallerIsMC, &(pCallObject->ConferenceID), pCallObject->pCallerAliasList, pCallObject->pCalleeAliasList, pCallObject->pExtraAliasList, pCallObject->pExtensionAliasItem, pVendorInfo, pCallObject->bIsTerminal, pCallObject->bIsGateway, &pCallObject->World, &CodedPtrASN, &CodedLengthASN); if ((ResultASN != CS_OK) || (CodedLengthASN == 0) || (CodedPtrASN == NULL)) { CSS_CALL_FAILED EventData; Q931DBG((DBGERROR, "Q931SetupEncodeASN() failed, nothing to send.")); if (CodedPtrASN != NULL) { Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN); } EventData.error = CS_INTERNAL_ERROR; dwId = pCallObject->dwPhysicalId; pCallObject->Callback(Q931_CALL_FAILED, pCallObject->hQ931Call, pCallObject->dwListenToken, pCallObject->dwUserToken, &EventData); linkLayerShutdown(dwId); if (CallObjectValidate(hQ931Call) == CS_OK) CallObjectDestroy(pCallObject); return; } UserUserData.length = (WORD)CodedLengthASN; UserUserData.ptr = CodedPtrASN; ResultPDU = Q931SetupEncodePDU(pCallObject->wCRV, pCallObject->szDisplay, pCallObject->szCalledPartyNumber, pCallObject->dwBandwidth, &UserUserData, &CodedPtrPDU, &CodedLengthPDU); if (CodedPtrASN != NULL) { Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN); } if ((ResultPDU != CS_OK) || (CodedLengthPDU == 0) || (CodedPtrPDU == NULL)) { CSS_CALL_FAILED EventData; Q931DBG((DBGERROR, "Q931SetupEncodePDU() failed, nothing to send.")); if (CodedPtrPDU != NULL) { Free(CodedPtrPDU); } EventData.error = CS_INTERNAL_ERROR; dwId = pCallObject->dwPhysicalId; pCallObject->Callback(Q931_CALL_FAILED, pCallObject->hQ931Call, pCallObject->dwListenToken, pCallObject->dwUserToken, &EventData); linkLayerShutdown(dwId); if (CallObjectValidate(hQ931Call) == CS_OK) CallObjectDestroy(pCallObject); return; } if (pCallObject->NonStandardDataPresent) { if (pCallObject->NonStandardData.sData.pOctetString != NULL) { Free(pCallObject->NonStandardData.sData.pOctetString); pCallObject->NonStandardData.sData.pOctetString = NULL; } pCallObject->NonStandardDataPresent = FALSE; } Q931FreeAliasNames(pCallObject->pCallerAliasList); pCallObject->pCallerAliasList = NULL; Q931FreeAliasNames(pCallObject->pCalleeAliasList); pCallObject->pCalleeAliasList = NULL; Q931FreeAliasNames(pCallObject->pExtraAliasList); pCallObject->pExtraAliasList = NULL; Q931FreeAliasItem(pCallObject->pExtensionAliasItem); pCallObject->pExtensionAliasItem = NULL; TempResult=Q931SendMessage(pCallObject, CodedPtrPDU, CodedLengthPDU, TRUE); if (CallObjectValidate(hQ931Call) != CS_OK) return; if(FAILED(TempResult)) { CSS_CALL_FAILED EventData; EventData.error = TempResult; dwId = pCallObject->dwPhysicalId; pCallObject->Callback(Q931_CALL_FAILED, pCallObject->hQ931Call, pCallObject->dwListenToken, pCallObject->dwUserToken, &EventData); linkLayerShutdown(dwId); if (CallObjectValidate(hQ931Call) == CS_OK) CallObjectDestroy(pCallObject); return; } Q931StartTimer(pCallObject, Q931_TIMER_303); } dwPhysicalId = pCallObject->dwPhysicalId; CallObjectUnlock(pCallObject); PostReceiveBuffer(dwPhysicalId, NULL); } //==================================================================================== //==================================================================================== void Q931ListenCallback(DWORD dwInstance, HRESULT dwMessage, CC_ADDR *LocalAddr, CC_ADDR *PeerAddr) { HQ931LISTEN hListenObject = (HQ931LISTEN)dwInstance; P_LISTEN_OBJECT pListenObject = NULL; CS_STATUS CreateObjectResult; HQ931CALL hQ931Call; P_CALL_OBJECT pCallObject = NULL; HRESULT TempResult; DWORD dwPhysicalId; Q931DBG((DBGTRACE, "Q931ListenCallback.")); if (dwMessage != LINK_CONNECT_REQUEST) { Q931DBG((DBGERROR, "unexpected callback received on listen socket")); return; } if (FAILED(dwMessage)) { Q931DBG((DBGERROR, "error on listen socket")); return; } if ((ListenObjectLock(hListenObject, &pListenObject) != CS_OK) || (pListenObject == NULL)) { Q931DBG((DBGERROR, "ListenObjectLock() returned error")); return; } // create call object with all known attributes of this call. // a handle of the call object is returned in phQ931Call. CreateObjectResult = CallObjectCreate(&hQ931Call, pListenObject->dwUserToken, CC_INVALID_HANDLE, pListenObject->ListenCallback, FALSE, // I am NOT the caller. LocalAddr, // Local address on which channel is connected PeerAddr, // Address to which channel is connected NULL, // Address of opposite call end-point. NULL, // no source addr NULL, // no conference id yet. CSG_NONE, // no goal yet. CC_CALLTYPE_UNKNOWN, // no call type yet. FALSE, // caller is assumed to not be the MC. NULL, // no display yet. NULL, // no called party number yet. NULL, // no caller aliases yet. NULL, // no callee aliases yet. NULL, // no extra aliases yet. NULL, // no extension aliases. NULL, // no EndpointType info yet. NULL, 0, // no bandwidth yet 0); // no CRV yet. if (CreateObjectResult != CS_OK) { Q931DBG((DBGERROR, "CallObjectCreate() failed.")); ListenObjectUnlock(pListenObject); return; } if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL)) { Q931DBG((DBGERROR, "CallObjectLock() returned error")); ListenObjectUnlock(pListenObject); return; } TempResult = linkLayerInit(&pCallObject->dwPhysicalId, hQ931Call, Q931ReceiveCallback, Q931SendComplete); if (FAILED(TempResult)) { Q931DBG((DBGERROR, "linkLayerInit() failed")); CallObjectDestroy(pCallObject); ListenObjectUnlock(pListenObject); return; } // pCallObject->bCallState = CALLSTATE_NULL; // unlock CallObject before calling down into h245ws in order to prevent deadlock - which // is probably unlikely with linkLayerAccept(), but just to be safe and consistent... // not sure if we need to worry about unlocking the listen object??? dwPhysicalId = pCallObject->dwPhysicalId; CallObjectUnlock(pCallObject); TempResult = linkLayerAccept(pListenObject->dwPhysicalId, dwPhysicalId, Q931ConnectCallback); if (FAILED(TempResult)) { if((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL)) { ListenObjectUnlock(pListenObject); return; } Q931DBG((DBGERROR, "linkLayerAccept() failed")); { DWORD dwId = pCallObject->dwPhysicalId; CallObjectDestroy(pCallObject); linkLayerShutdown(dwId); } ListenObjectUnlock(pListenObject); return; } ListenObjectUnlock(pListenObject); } //==================================================================================== // // PUBLIC FUNCTIONS // //==================================================================================== //==================================================================================== //==================================================================================== CS_STATUS Q931Init() { CS_STATUS result; if (bQ931Initialized == TRUE) { ASSERT(FALSE); return CS_DUPLICATE_INITIALIZE; } bQ931Initialized = TRUE; // Register Call Setup for debug output ISRREGISTERMODULE(&ghISRInst, "Q931", "Q931 Call Setup"); // Initialize the current conference ID to 0's, which is intentionally // assigned to an invalid conference ID. Must create one for it // to be valid. memset(&(ConferenceIDSource), 0, sizeof(ConferenceIDSource)); __try { InitializeCriticalSectionAndSpinCount(&(ConferenceIDSource.Lock),H323_SPIN_COUNT); } __except ((GetExceptionCode() == STATUS_NO_MEMORY) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { // failure return CS_NO_MEMORY; } if ((result = ListenListCreate()) != CS_OK) { return result; } if ((result = CallListCreate()) != CS_OK) { ListenListDestroy(); return result; } #if (defined(_DEBUG) && defined(PCS_COMPLIANCE)) Q931Logger = InteropLoad(Q931LOG_PROTOCOL); #endif return CS_OK; } //==================================================================================== //==================================================================================== CS_STATUS Q931DeInit() { CS_STATUS result1; CS_STATUS result2; if (bQ931Initialized == FALSE) { return CS_NOT_INITIALIZED; } #if (defined(_DEBUG) && defined(PCS_COMPLIANCE)) // This causes a protection exception, so don't do it for now. DAC 12/9/96 // InteropUnload(Q931Logger); #endif result1 = ListenListDestroy(); result2 = CallListDestroy(); DeleteCriticalSection(&(ConferenceIDSource.Lock)); bQ931Initialized = FALSE; if (result1 != CS_OK) { return result1; } return result2; } //==================================================================================== //==================================================================================== CS_STATUS Q931Listen( PHQ931LISTEN phQ931Listen, PCC_ADDR pListenAddr, DWORD dwListenToken, Q931_CALLBACK ListenCallback) { CS_STATUS CreateObjectResult; P_LISTEN_OBJECT pListenObject = NULL; HRESULT TempResult; // make sure q931 is initialized with an initialize flag. if (bQ931Initialized == FALSE) { return CS_NOT_INITIALIZED; } // make sure parms are validated. if ((phQ931Listen == NULL) || (ListenCallback == NULL) || (pListenAddr == NULL)) { ASSERT(FALSE); return CS_BAD_PARAM; } SetDefaultPort(pListenAddr); // create listen object with all known attributes of this listen session. // a handle of the listen object is returned in phQ931Listen. CreateObjectResult = ListenObjectCreate(phQ931Listen, dwListenToken, ListenCallback); if (CreateObjectResult != CS_OK) { return CS_SUBSYSTEM_FAILURE; } if (ListenObjectLock(*phQ931Listen, &pListenObject) != CS_OK) { return CS_BAD_PARAM; } TempResult = linkLayerListen(&pListenObject->dwPhysicalId, *phQ931Listen, pListenAddr, Q931ListenCallback); ListenObjectUnlock(pListenObject); if (FAILED(TempResult)) { Q931DBG((DBGERROR, "Q931Listen() linkLayerListen failed.")); return TempResult; } Q931DBG((DBGTRACE, "Q931Listen() completed successfully.")); return CS_OK; } //==================================================================================== // In the old code, this blocked until thread and socket were finished // closing... //==================================================================================== CS_STATUS Q931CancelListen( HQ931LISTEN hQ931Listen) { P_LISTEN_OBJECT pListenObject = NULL; CS_STATUS Status; // make sure q931 is initialized with an initialize flag. if (bQ931Initialized == FALSE) { return CS_NOT_INITIALIZED; } Q931DBG((DBGTRACE, "Q931CancelListen() finding listen object...")); // lock the listen object, get the event to wait for, and unlock the listen object. if (ListenObjectLock(hQ931Listen, &pListenObject) != CS_OK) { return CS_BAD_PARAM; } { DWORD dwId = pListenObject->dwPhysicalId; linkLayerShutdown(dwId); // destroy the object. dont need to unlock it since entire object will be destroyed. Q931DBG((DBGTRACE, "Q931CancelListen(): destroying listen object...")); Status = ListenObjectDestroy(pListenObject); } return Status; } //==================================================================================== //==================================================================================== CS_STATUS Q931PlaceCall( PHQ931CALL phQ931Call, LPWSTR pszDisplay, PCC_ALIASNAMES pCallerAliasList, PCC_ALIASNAMES pCalleeAliasList, PCC_ALIASNAMES pExtraAliasList, PCC_ALIASITEM pExtensionAliasItem, PCC_NONSTANDARDDATA pNonStandardData, PCC_ENDPOINTTYPE pSourceEndpointType, LPWSTR pszCalledPartyNumber, PCC_ADDR pControlAddr, PCC_ADDR pDestinationAddr, PCC_ADDR pSourceAddr, BOOL bCallerIsMC, CC_CONFERENCEID *pConferenceID, WORD wGoal, WORD wCallType, DWORD dwUserToken, Q931_CALLBACK ConnectCallback, DWORD dwBandwidth, WORD wCRV) { CS_STATUS CreateObjectResult; P_CALL_OBJECT pCallObject = NULL; CC_ADDR PeerCallAddr; CC_ADDR PeerConnectAddr; CC_ADDR SourceAddr; HRESULT TempResult; char szAsciiDisplay[CC_MAX_DISPLAY_LENGTH + 1]; char szAsciiPartyNumber[CC_MAX_PARTY_NUMBER_LEN + 1]; DWORD dwPhysicalId; // make sure q931 is initialized with an initialize flag. if (bQ931Initialized == FALSE) { return CS_NOT_INITIALIZED; } // make sure parms are validated. if ((phQ931Call == NULL) || (ConnectCallback == NULL) || ((pControlAddr == NULL) && (pDestinationAddr == NULL)) || (pSourceEndpointType == NULL)) { return CS_BAD_PARAM; } { CS_STATUS TempStatus; TempStatus = Q931ValidateAddr(pControlAddr); if (TempStatus != CS_OK) { return TempStatus; } TempStatus = Q931ValidateAddr(pDestinationAddr); if (TempStatus != CS_OK) { return TempStatus; } TempStatus = Q931ValidateAddr(pSourceAddr); if (TempStatus != CS_OK) { return TempStatus; } TempStatus = Q931ValidateVendorInfo(pSourceEndpointType->pVendorInfo); if (TempStatus != CS_OK) { return TempStatus; } TempStatus = Q931ValidateDisplay(pszDisplay); if (TempStatus != CS_OK) { return TempStatus; } TempStatus = Q931ValidatePartyNumber(pszCalledPartyNumber); if (TempStatus != CS_OK) { return TempStatus; } szAsciiDisplay[0] = '\0'; if (pszDisplay && WideCharToMultiByte(CP_ACP, 0, pszDisplay, -1, szAsciiDisplay, sizeof(szAsciiDisplay), NULL, NULL) == 0) { return CS_BAD_PARAM; } szAsciiPartyNumber[0] = '\0'; if (pszCalledPartyNumber && WideCharToMultiByte(CP_ACP, 0, pszCalledPartyNumber, -1, szAsciiPartyNumber, sizeof(szAsciiPartyNumber), NULL, NULL) == 0) { return CS_BAD_PARAM; } TempStatus = Q931ValidateNonStandardData(pNonStandardData); if (TempStatus != CS_OK) { return TempStatus; } TempStatus = Q931ValidateAliasNames(pCallerAliasList); if (TempStatus != CS_OK) { return TempStatus; } TempStatus = Q931ValidateAliasNames(pCalleeAliasList); if (TempStatus != CS_OK) { return TempStatus; } TempStatus = Q931ValidateAliasNames(pExtraAliasList); if (TempStatus != CS_OK) { return TempStatus; } TempStatus = Q931ValidateAliasItem(pExtensionAliasItem); if (TempStatus != CS_OK) { return TempStatus; } } // get the correct callee and control address to use for the call. if (pDestinationAddr) { if (!MakeBinaryADDR(pDestinationAddr, &PeerCallAddr)) { return CS_BAD_PARAM; } SetDefaultPort(&PeerCallAddr); } if (pControlAddr) { if (!MakeBinaryADDR(pControlAddr, &PeerConnectAddr)) { return CS_BAD_PARAM; } SetDefaultPort(&PeerConnectAddr); } else { PeerConnectAddr = PeerCallAddr; } // get the correct callee and control address to use for the call. if (pSourceAddr) { if (!MakeBinaryADDR(pSourceAddr, &SourceAddr)) { return CS_BAD_PARAM; } SetDefaultPort(&SourceAddr); } if (wGoal == CSG_CREATE) { // caller is asking to start a new conference. if (((DWORD *)pConferenceID->buffer)[0] == 0 && ((DWORD *)pConferenceID->buffer)[1] == 0 && ((DWORD *)pConferenceID->buffer)[2] == 0 && ((DWORD *)pConferenceID->buffer)[3] == 0) { _ConferenceIDNew(pConferenceID); } } // create call object with all known attributes of this call. // a handle of the call object is returned in phQ931Call. CreateObjectResult = CallObjectCreate(phQ931Call, CC_INVALID_HANDLE, dwUserToken, ConnectCallback, TRUE, // I am the caller. NULL, // no local address yet. &PeerConnectAddr, pDestinationAddr ? &PeerCallAddr : NULL, pSourceAddr ? &SourceAddr : NULL, pConferenceID, wGoal, wCallType, bCallerIsMC, pszDisplay ? szAsciiDisplay : NULL, pszCalledPartyNumber ? szAsciiPartyNumber : NULL, pCallerAliasList, pCalleeAliasList, pExtraAliasList, pExtensionAliasItem, pSourceEndpointType, pNonStandardData, dwBandwidth, wCRV); if (CreateObjectResult != CS_OK) { return CS_SUBSYSTEM_FAILURE; } if ((CallObjectLock(*phQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL)) { Q931DBG((DBGERROR, "CallObjectLock() returned error")); return CS_SUBSYSTEM_FAILURE; } TempResult = linkLayerInit(&pCallObject->dwPhysicalId, *phQ931Call, Q931ReceiveCallback, Q931SendComplete); if (FAILED(TempResult)) { Q931DBG((DBGERROR, "linkLayerInit() failed")); CallObjectDestroy(pCallObject); *phQ931Call = 0; return TempResult; } // unlock CallObject before calling down into h245ws in order to prevent deadlock - which // is probably unlikely with linkLayerConnect(), but just to be safe and consistent... dwPhysicalId = pCallObject->dwPhysicalId; CallObjectUnlock(pCallObject); TempResult = linkLayerConnect(dwPhysicalId, &PeerConnectAddr, Q931ConnectCallback); if((CallObjectLock(*phQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL)) { *phQ931Call = 0; return(CS_INTERNAL_ERROR); } if (FAILED(TempResult)) { Q931DBG((DBGERROR, "linkLayerConnect() failed")); { DWORD dwId = pCallObject->dwPhysicalId; CallObjectDestroy(pCallObject); linkLayerShutdown(dwId); } *phQ931Call = 0; return TempResult; } // pCallObject->bCallState = CALLSTATE_NULL; CallObjectUnlock(pCallObject); Q931DBG((DBGTRACE, "Q931PlaceCall() completed successfully.")); return CS_OK; } //==================================================================================== // In the old code, this blocked until thread and socket were finished // closing... //==================================================================================== CS_STATUS Q931Hangup( HQ931CALL hQ931Call, BYTE bReason) { P_CALL_OBJECT pCallObject = NULL; CS_STATUS Status; if (bQ931Initialized == FALSE) { return CS_NOT_INITIALIZED; } Q931DBG((DBGTRACE, "Entering Q931Hangup()...")); // need parameter checking... if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL)) { Q931DBG((DBGTRACE, "Call Object no longer available: 0x%08lx", hQ931Call)); return CS_BAD_PARAM; } { CS_STATUS SendStatus = CS_OK; if (pCallObject->bCallState != CALLSTATE_NULL) { // send the RELEASE COMPLETE message to the peer to hang-up. SendStatus = Q931SendReleaseCompleteMessage(pCallObject, bReason, &(pCallObject->ConferenceID), NULL, NULL); } { DWORD dwId = pCallObject->dwPhysicalId; Status = CallObjectDestroy(pCallObject); linkLayerShutdown(dwId); } if (FAILED(SendStatus)) { return SendStatus; } } return Status; } //==================================================================================== //==================================================================================== CS_STATUS Q931AcceptCall( HQ931CALL hQ931Call, LPWSTR pszDisplay, PCC_NONSTANDARDDATA pNonStandardData, PCC_ENDPOINTTYPE pDestinationEndpointType, PCC_ADDR pH245Addr, DWORD dwBandwidth, DWORD dwUserToken) { P_CALL_OBJECT pCallObject = NULL; CS_STATUS result = CS_OK; char szAsciiDisplay[CC_MAX_DISPLAY_LENGTH + 1]; if (bQ931Initialized == FALSE) { return CS_NOT_INITIALIZED; } Q931DBG((DBGTRACE, "Entering Q931AcceptCall()...")); if ((pDestinationEndpointType == NULL) || (pDestinationEndpointType->pVendorInfo == NULL)) { return CS_BAD_PARAM; } { CS_STATUS TempStatus; TempStatus = Q931ValidateVendorInfo(pDestinationEndpointType->pVendorInfo); if (TempStatus != CS_OK) { return TempStatus; } TempStatus = Q931ValidateDisplay(pszDisplay); if (TempStatus != CS_OK) { return TempStatus; } szAsciiDisplay[0] = '\0'; if (pszDisplay && WideCharToMultiByte(CP_ACP, 0, pszDisplay, -1, szAsciiDisplay, sizeof(szAsciiDisplay), NULL, NULL) == 0) { return CS_BAD_PARAM; } TempStatus = Q931ValidateNonStandardData(pNonStandardData); if (TempStatus != CS_OK) { return TempStatus; } } if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL)) { Q931DBG((DBGERROR, "CallObjectLock() returned error (Socket not found).")); return CS_INTERNAL_ERROR; } if (pCallObject->fIsCaller) { Q931DBG((DBGERROR, "Caller attempted to accept call.")); CallObjectUnlock(pCallObject); return CS_OUT_OF_SEQUENCE; } // label with the user supplied UserToken for this call object. pCallObject->dwUserToken = dwUserToken; pCallObject->dwBandwidth = dwBandwidth; // send the CONNECT message to peer to accept call. { DWORD CodedLengthASN; BYTE *CodedPtrASN; HRESULT ResultASN = CS_OK; CC_ADDR h245Addr; if (pH245Addr != NULL) { MakeBinaryADDR(pH245Addr, &h245Addr); } ResultASN = Q931ConnectEncodeASN(pNonStandardData, &(pCallObject->ConferenceID), (pH245Addr ? &h245Addr : NULL), pDestinationEndpointType, &pCallObject->World, &CodedPtrASN, &CodedLengthASN); if ((ResultASN != CS_OK) || (CodedLengthASN == 0) || (CodedPtrASN == NULL)) { Q931DBG((DBGERROR, "Q931ConnectEncodeASN() failed, nothing to send.")); if (CodedPtrASN != NULL) { Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN); } CallObjectUnlock(pCallObject); return CS_SUBSYSTEM_FAILURE; } else { DWORD CodedLengthPDU; BYTE *CodedPtrPDU; BINARY_STRING UserUserData; HRESULT ResultEncode = CS_OK; HRESULT TempResult; WORD wCRV = (WORD)(pCallObject->wCRV | 0x8000); UserUserData.length = (WORD)CodedLengthASN; UserUserData.ptr = CodedPtrASN; ResultEncode = Q931ConnectEncodePDU(wCRV, szAsciiDisplay, pCallObject->dwBandwidth, &UserUserData, &CodedPtrPDU, &CodedLengthPDU); if (CodedPtrASN != NULL) { Q931FreeEncodedBuffer(&pCallObject->World, CodedPtrASN); } if ((ResultEncode != CS_OK) || (CodedLengthPDU == 0) || (CodedPtrPDU == NULL)) { Q931DBG((DBGERROR, "Q931ConnectEncodePDU() failed, nothing to send.")); if (CodedPtrPDU != NULL) { Free(CodedPtrPDU); } CallObjectUnlock(pCallObject); return CS_SUBSYSTEM_FAILURE; } TempResult = Q931SendMessage(pCallObject, CodedPtrPDU, CodedLengthPDU, TRUE); if (CallObjectValidate(hQ931Call) != CS_OK) return CS_INTERNAL_ERROR; if (FAILED(TempResult)) { Q931DBG((DBGERROR, "datalinkSendRequest() failed")); // when the connect notification fails...what should we do anyway???? CallObjectUnlock(pCallObject); return TempResult; } } } pCallObject->bCallState = CALLSTATE_ACTIVE; CallObjectUnlock(pCallObject); return CS_OK; } //==================================================================================== //==================================================================================== CS_STATUS Q931RejectCall( HQ931CALL hQ931Call, BYTE bRejectReason, PCC_CONFERENCEID pConferenceID, PCC_ADDR pAlternateAddr, PCC_NONSTANDARDDATA pNonStandardData) { P_CALL_OBJECT pCallObject = NULL; CS_STATUS result = CS_OK; CS_STATUS Status = CS_OK; if (bQ931Initialized == FALSE) { return CS_NOT_INITIALIZED; } Q931DBG((DBGTRACE, "Entering Q931RejectCall()...")); { CS_STATUS TempStatus; TempStatus = Q931ValidateNonStandardData(pNonStandardData); if (TempStatus != CS_OK) { return TempStatus; } } // if reason is alternate addr, but there is no alternate addr -->err if (((bRejectReason == CC_REJECT_ROUTE_TO_GATEKEEPER) || (bRejectReason == CC_REJECT_CALL_FORWARDED) || (bRejectReason == CC_REJECT_ROUTE_TO_MC)) && (pAlternateAddr == NULL)) { return CS_BAD_PARAM; } if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL)) { Q931DBG((DBGERROR, "CallObjectLock() returned error (Socket not found).")); return CS_INTERNAL_ERROR; } if (pCallObject->fIsCaller) { Q931DBG((DBGERROR, "Caller attempted to reject call.")); CallObjectUnlock(pCallObject); return CS_OUT_OF_SEQUENCE; } result = Q931SendReleaseCompleteMessage(pCallObject, bRejectReason, pConferenceID, pAlternateAddr, pNonStandardData); { DWORD dwId = pCallObject->dwPhysicalId; Status = CallObjectDestroy(pCallObject); linkLayerShutdown(dwId); } if (result != CS_OK) { return result; } return Status; } //==================================================================================== //==================================================================================== CS_STATUS Q931ReOpenConnection( HQ931CALL hQ931Call) { P_CALL_OBJECT pCallObject = NULL; HRESULT TempResult = CS_OK; CC_ADDR PeerConnectAddr; DWORD dwPhysicalId; if (bQ931Initialized == FALSE) { return CS_NOT_INITIALIZED; } Q931DBG((DBGTRACE, "Entering Q931ReOpenConnection()...")); if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL)) { Q931DBG((DBGERROR, "CallObjectLock() returned error.")); return CS_INTERNAL_ERROR; } if (pCallObject->bConnected) { return CS_OUT_OF_SEQUENCE; } Q931MakePhysicalID(&pCallObject->dwPhysicalId); TempResult = linkLayerInit(&pCallObject->dwPhysicalId, hQ931Call, Q931ReceiveCallback, Q931SendComplete); if (FAILED(TempResult)) { Q931DBG((DBGERROR, "linkLayerInit() failed on re-connect.")); CallObjectUnlock(pCallObject); return TempResult; } // unlock CallObject before calling down into h245ws in order to prevent deadlock - which // is probably unlikely with linkLayerConnect, but just to be safe and consistent... // copy stuff we need out of call object before we unlock it dwPhysicalId = pCallObject->dwPhysicalId; PeerConnectAddr = pCallObject->PeerConnectAddr; CallObjectUnlock(pCallObject); TempResult = linkLayerConnect(dwPhysicalId, &PeerConnectAddr, Q931ConnectCallback); if((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL)) { return(CS_INTERNAL_ERROR); } if (FAILED(TempResult)) { Q931DBG((DBGERROR, "linkLayerConnect() failed on re-connect.")); linkLayerShutdown(pCallObject->dwPhysicalId); CallObjectUnlock(pCallObject); return TempResult; } CallObjectUnlock(pCallObject); Q931DBG((DBGTRACE, "Q931ReOpenConnection() completed successfully.")); return CS_OK; } //==================================================================================== //==================================================================================== CS_STATUS Q931GetVersion( WORD wLength, LPWSTR pszVersion) { WCHAR pszQ931Version[255]; // parameter validation. if ((wLength == 0) || (pszVersion == NULL)) { return CS_BAD_PARAM; } wcscpy(pszQ931Version, L"Call Setup "); wcscat(pszQ931Version, Unicode(__DATE__)); wcscat(pszQ931Version, L" "); wcscat(pszQ931Version, Unicode(__TIME__)); if (wcslen(pszQ931Version) >= wLength) { memcpy(pszVersion, pszQ931Version, (wLength-1)*sizeof(WCHAR)); pszQ931Version[wLength-1] = L'\0'; return CS_BAD_SIZE; } wcscpy(pszVersion, pszQ931Version); return CS_OK; } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Timer Routines... //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //==================================================================================== // Timer 301 has expired for this object... //==================================================================================== void CallBackT301(P_CALL_OBJECT pCallObject) { CSS_CALL_FAILED EventData; HQ931CALL hQ931Call = pCallObject->hQ931Call; EventData.error = CS_RINGING_TIMER_EXPIRED; pCallObject->Callback(Q931_CALL_FAILED, pCallObject->hQ931Call, pCallObject->dwListenToken, pCallObject->dwUserToken, &EventData); if (CallObjectValidate(hQ931Call) == CS_OK) { if (Q931SendReleaseCompleteMessage(pCallObject, CC_REJECT_TIMER_EXPIRED, NULL, NULL, NULL) == CS_OK) { // nothing to do... } { DWORD dwId = pCallObject->dwPhysicalId; CallObjectDestroy(pCallObject); linkLayerShutdown(dwId); } } return; } //==================================================================================== // Timer 303 has expired for this object... //==================================================================================== void CallBackT303(P_CALL_OBJECT pCallObject) { CSS_CALL_FAILED EventData; HQ931CALL hQ931Call = pCallObject->hQ931Call; EventData.error = CS_SETUP_TIMER_EXPIRED; pCallObject->Callback(Q931_CALL_FAILED, pCallObject->hQ931Call, pCallObject->dwListenToken, pCallObject->dwUserToken, &EventData); if (CallObjectValidate(hQ931Call) == CS_OK) { if (Q931SendReleaseCompleteMessage(pCallObject, CC_REJECT_TIMER_EXPIRED, NULL, NULL, NULL) == CS_OK) { // nothing to do... } { DWORD dwId = pCallObject->dwPhysicalId; CallObjectDestroy(pCallObject); linkLayerShutdown(dwId); } } return; } //==================================================================================== //==================================================================================== void Q931SetReceivePDUHook(Q931_RECEIVE_PDU_CALLBACK Q931ReceivePDUCallback) { gReceivePDUHookProc = Q931ReceivePDUCallback; return; } //==================================================================================== //==================================================================================== CS_STATUS Q931FlushSendQueue( HQ931CALL hQ931Call) { P_CALL_OBJECT pCallObject = NULL; HRESULT TempResult = CS_OK; DWORD dwPhysicalId; if (bQ931Initialized == FALSE) { return CS_NOT_INITIALIZED; } Q931DBG((DBGTRACE, "Entering Q931FlushSendQueue()...")); // need parameter checking... if ((CallObjectLock(hQ931Call, &pCallObject) != CS_OK) || (pCallObject == NULL)) { Q931DBG((DBGTRACE, "Call Object no longer available: 0x%08lx", hQ931Call)); return CS_INTERNAL_ERROR; } dwPhysicalId = pCallObject->dwPhysicalId; CallObjectUnlock(pCallObject); TempResult = linkLayerFlushChannel(dwPhysicalId, DATALINK_TRANSMIT); if (FAILED(TempResult)) { Q931DBG((DBGERROR, "datalinkSendRequest() failed")); } return(TempResult); } #ifdef __cplusplus } #endif