|
|
/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
q931obj.cpp
Abstract:
Functionality for accepting the Q931 connections.
Author: Nikhil Bobde (NikhilB)
Revision History:
--*/
#include "globals.h"
#include "q931obj.h"
#include "line.h"
#include "q931pdu.h"
#include "winbase.h"
#include "ras.h"
class Q931_BUFFER_CACHE { private:
enum { RECV_BUFFER_LIST_COUNT_MAX = 0x10 };
CRITICAL_SECTION m_CriticalSection; LIST_ENTRY m_FreeRecvBufferList; DWORD m_FreeRecvBufferListCount;
private:
void Lock (void) { EnterCriticalSection (&m_CriticalSection); } void Unlock (void) { LeaveCriticalSection (&m_CriticalSection); }
public:
Q931_BUFFER_CACHE (void); ~Q931_BUFFER_CACHE (void);
BOOL AllocRecvBuffer ( OUT RECVBUF ** ReturnRecvBuffer);
void FreeRecvBuffer ( IN RECVBUF * RecvBuffer);
void FreeAll (void); };
// global data
Q931_LISTENER Q931Listener; static Q931_BUFFER_CACHE Q931BufferCache;
HRESULT Q931AcceptStart (void) { return Q931Listener.Start(); }
void Q931AcceptStop (void) { H323DBG(( DEBUG_LEVEL_TRACE, "Q931AcceptStop entered." )); Q931Listener.Stop(); Q931Listener.WaitIo();
H323DBG(( DEBUG_LEVEL_TRACE, "Q931AcceptStop exited." )); }
void Q931FreeRecvBuffer ( IN RECVBUF * RecvBuffer) { Q931BufferCache.FreeRecvBuffer (RecvBuffer); }
BOOL Q931AllocRecvBuffer ( OUT RECVBUF ** ReturnRecvBuffer) { return Q931BufferCache.AllocRecvBuffer (ReturnRecvBuffer); }
// Q931_LISTENER ---------------------------------------------------------
Q931_LISTENER::Q931_LISTENER (void) { // No need to check the result of this one since this object is
// not allocated on heap, right when the DLL is loaded
InitializeCriticalSectionAndSpinCount( &m_CriticalSection, 0x80000000 );
m_ListenSocket = INVALID_SOCKET;
InitializeListHead (&m_AcceptPendingList);
H225ASN_Module_Startup(); H4503PP_Module_Startup();
_ASSERTE( H225ASN_Module ); _ASSERTE( H4503PP_Module );
m_StopNotifyEvent = H323CreateEvent (NULL, TRUE, TRUE, _T( "H323TSP_StopIncomingCallNotify" ) );
if( m_StopNotifyEvent == NULL ) { H323DBG(( DEBUG_LEVEL_ERROR, "Q931: failed to create stop notify event -- will be unable to accept Q.931 connections" )); } }
Q931_LISTENER::~Q931_LISTENER (void) { DeleteCriticalSection (&m_CriticalSection);
_ASSERTE( m_ListenSocket == INVALID_SOCKET ); _ASSERTE( IsListEmpty (&m_AcceptPendingList) );
if (m_StopNotifyEvent) { CloseHandle (m_StopNotifyEvent); m_StopNotifyEvent = NULL; }
if( H225ASN_Module ) { H225ASN_Module_Cleanup(); }
if( H4503PP_Module ) { H4503PP_Module_Cleanup(); } }
HRESULT Q931_LISTENER::Start (void) { HRESULT hr;
Lock();
hr = StartLocked(); if (hr != S_OK) { if (m_ListenSocket != INVALID_SOCKET) { closesocket (m_ListenSocket); m_ListenSocket = INVALID_SOCKET; } }
Unlock();
return hr; }
HRESULT Q931_LISTENER::StartLocked (void) { INT SocketAddressLength;
if( m_ListenSocket != INVALID_SOCKET ) { return S_OK; }
m_ListenSocket = WSASocket( AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED );
if (m_ListenSocket == INVALID_SOCKET) { H323DBG(( DEBUG_LEVEL_ERROR, "Q931: failed to create listen socket" )); DumpError (GetLastError()); return GetLastResult(); }
m_SocketAddress.sin_family = AF_INET; m_SocketAddress.sin_addr.s_addr = htonl (INADDR_ANY); m_SocketAddress.sin_port = htons( (WORD)g_RegistrySettings.dwQ931ListenPort );
if( bind( m_ListenSocket, (SOCKADDR *)&m_SocketAddress, sizeof (SOCKADDR_IN) ) == SOCKET_ERROR) { H323DBG(( DEBUG_LEVEL_ERROR, "Q931: failed to bind to requested port (%d), will try to use dynamic port.", g_RegistrySettings.dwQ931ListenPort));
//ReportTSPEvent( _T("Q931 listen socket failed to bind to port 1720") );
if( g_RegistrySettings.fIsGKEnabled ) { m_SocketAddress.sin_port = htons (0);
if( bind( m_ListenSocket, (SOCKADDR *)&m_SocketAddress, sizeof (SOCKADDR_IN) ) == SOCKET_ERROR ) { H323DBG ((DEBUG_LEVEL_ERROR,"Q931: failed to request dynamic " "port for Q.931-cannot accept Q.931 connections" ));
return E_FAIL; } } }
SocketAddressLength = sizeof (SOCKADDR_IN);
if( getsockname( m_ListenSocket, (SOCKADDR *)&m_SocketAddress, &SocketAddressLength) ) { H323DBG(( DEBUG_LEVEL_WARNING, "Q931: failed to query socket address from TCP -- unexpected behavior"));
return E_FAIL; }
if( listen( m_ListenSocket, Q931_CONN_QUEUE_LEN) == SOCKET_ERROR ) { H323DBG ((DEBUG_LEVEL_ERROR, "Q931: failed to begin listening on socket:%d", WSAGetLastError() )); return E_FAIL; }
if( !H323BindIoCompletionCallback( (HANDLE)m_ListenSocket, Q931_LISTENER::IoCompletionCallback, 0) ) { H323DBG ((DEBUG_LEVEL_ERROR, "Q931: failed to bind listen socket to i/o completion callback" )); return E_FAIL; }
H323DBG(( DEBUG_LEVEL_TRACE, "Q931: listen socket created, bound, and ready to receive connections" ));
// all looks good
// issue initial accept buffer(s)
AllocIssueAccept(); AllocIssueAccept(); AllocIssueAccept(); AllocIssueAccept();
return S_OK; }
void Q931_LISTENER::Stop(void) { Lock();
if (m_ListenSocket != INVALID_SOCKET) { // this implicitly cancels all outstanding I/O against this socket
closesocket (m_ListenSocket); m_ListenSocket = INVALID_SOCKET; }
Unlock(); }
void Q931_LISTENER::WaitIo(void) { WaitForSingleObject (m_StopNotifyEvent, INFINITE); }
WORD Q931_LISTENER::GetListenPort(void) { SOCKADDR_IN socketAddress; int SocketAddressLength = sizeof (SOCKADDR_IN); ZeroMemory( (PVOID)&socketAddress, sizeof(SOCKADDR_IN) );
Lock();
if( getsockname( m_ListenSocket, (SOCKADDR *)&socketAddress, &SocketAddressLength) ) { H323DBG(( DEBUG_LEVEL_WARNING, "Q931: failed to query socket address from TCP -- unexpected behavior"));
Unlock(); return 0; }
Unlock(); return ntohs(socketAddress.sin_port); }
void Q931_LISTENER::HandleRegistryChange() { if( g_pH323Line -> GetState() == H323_LINESTATE_LISTENING ) { if( g_RegistrySettings.dwQ931ListenPort != GetListenPort() ) { Q931AcceptStop(); Q931AcceptStart(); } } }
HRESULT Q931_LISTENER::AllocIssueAccept (void) { Q931_ACCEPT_OVERLAPPED * AcceptOverlapped; HRESULT hr;
_ASSERTE( m_ListenSocket != INVALID_SOCKET );
AcceptOverlapped = new Q931_ACCEPT_OVERLAPPED; if( AcceptOverlapped != NULL ) { hr = IssueAccept (AcceptOverlapped); if (hr != S_OK) { delete AcceptOverlapped; } } else { H323DBG(( DEBUG_LEVEL_ERROR, "Q931: failed to allocate connection accept buffer" )); hr = E_OUTOFMEMORY; }
return hr; }
HRESULT Q931_LISTENER::IssueAccept ( IN Q931_ACCEPT_OVERLAPPED * AcceptOverlapped ) { HRESULT hr;
_ASSERTE( m_ListenSocket != INVALID_SOCKET );
ZeroMemory (AcceptOverlapped, sizeof (Q931_ACCEPT_OVERLAPPED));
AcceptOverlapped -> ParentObject = this;
AcceptOverlapped -> Socket = WSASocket ( AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (AcceptOverlapped -> Socket == INVALID_SOCKET) { H323DBG ((DEBUG_LEVEL_ERROR, "Q931: failed to create new accept socket")); DumpLastError();
return GetLastResult(); }
if (!AcceptEx (m_ListenSocket, AcceptOverlapped -> Socket, AcceptOverlapped -> DataBuffer, 0, sizeof (SOCKADDR_IN) + 0x10, sizeof (SOCKADDR_IN) + 0x10, &AcceptOverlapped -> BytesTransferred, &AcceptOverlapped -> Overlapped) && GetLastError() != ERROR_IO_PENDING) { hr = GetLastResult();
H323DBG ((DEBUG_LEVEL_ERROR, "Q931: failed to issue accept on new socket")); DumpLastError();
closesocket (AcceptOverlapped -> Socket);
return hr; }
if (IsListEmpty (&m_AcceptPendingList)) { ResetEvent (m_StopNotifyEvent); }
InsertTailList (&m_AcceptPendingList, &AcceptOverlapped -> ListEntry);
H323DBG ((DEBUG_LEVEL_TRACE, "Q931: created new accept socket (%08XH), issued accept request.", (DWORD) AcceptOverlapped -> Socket));
return S_OK; }
// static
void Q931_LISTENER::IoCompletionCallback ( IN DWORD dwStatus, IN DWORD BytesTransferred, IN OVERLAPPED * Overlapped ) { Q931_ACCEPT_OVERLAPPED * AcceptOverlapped;
_ASSERTE( Overlapped );
#if _WIN64
_ASSERTE( (DWORD_PTR) Overlapped != 0xfeeefeeefeeefeee); _ASSERTE( (DWORD_PTR) Overlapped != 0xbaadf00dbaadf00d); #else
_ASSERTE( (DWORD) Overlapped != 0xfeeefeee); _ASSERTE( (DWORD) Overlapped != 0xbaadf00d); #endif
AcceptOverlapped = CONTAINING_RECORD(Overlapped, Q931_ACCEPT_OVERLAPPED, Overlapped); AcceptOverlapped -> BytesTransferred = BytesTransferred; AcceptOverlapped -> ParentObject -> CompleteAccept( dwStatus, AcceptOverlapped ); }
void Q931_LISTENER::CompleteAccept( IN DWORD dwStatus, IN Q931_ACCEPT_OVERLAPPED * AcceptOverlapped ) { SOCKET Socket = INVALID_SOCKET; SOCKADDR_IN RemoteAddress; SOCKADDR_IN * RemoteAddressPointer; INT RemoteAddressLength; SOCKADDR_IN LocalAddress; SOCKADDR_IN * LocalAddressPointer; INT LocalAddressLength; HRESULT hr; DWORD dwEnable = 1;
Lock();
_ASSERTE( IsInList (&m_AcceptPendingList, &AcceptOverlapped -> ListEntry) ); RemoveEntryList (&AcceptOverlapped -> ListEntry);
if (IsListEmpty (&m_AcceptPendingList)) { SetEvent (m_StopNotifyEvent); }
if (m_ListenSocket != INVALID_SOCKET) { if (dwStatus == ERROR_SUCCESS) { // extract parameters from accepted socket, copy to local stack frame.
// this is necessary, because we will recycle AcceptOverlapped (with
// a newly allocated socket) and process the new client later.
// this gives a high degree of concurrency.
RemoteAddressPointer = NULL; LocalAddressPointer = NULL;
RemoteAddressLength = sizeof RemoteAddress; LocalAddressLength = sizeof LocalAddress;
GetAcceptExSockaddrs (AcceptOverlapped -> DataBuffer, 0, sizeof (SOCKADDR_IN), sizeof (SOCKADDR_IN), (SOCKADDR **) &RemoteAddressPointer, &RemoteAddressLength, (SOCKADDR **) &LocalAddressPointer, &LocalAddressLength);
_ASSERTE( RemoteAddressPointer ); _ASSERTE( LocalAddressPointer );
if( (RemoteAddressPointer == NULL) || (LocalAddressPointer == NULL) ) { return; }
RemoteAddress = *RemoteAddressPointer; LocalAddress = *LocalAddressPointer; Socket = AcceptOverlapped -> Socket;
if( setsockopt( AcceptOverlapped -> Socket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, reinterpret_cast <char *> (&m_ListenSocket), sizeof m_ListenSocket)) { H323DBG(( DEBUG_LEVEL_WARNING, "Q931: successfully accepted socket, but SO_UPDATE_ACCEPT_CONTEXT" "failed -- future operations will fail" )); // don't fail here
}
if( setsockopt( Socket, IPPROTO_TCP, TCP_NODELAY, (char*)&dwEnable, sizeof(DWORD) ) == SOCKET_ERROR ) { H323DBG(( DEBUG_LEVEL_WARNING, "Couldn't set NODELAY option on outgoing call socket:%d, %p", WSAGetLastError(), this )); } } else { H323DBG ((DEBUG_LEVEL_ERROR, "Q931: failed to accept connection")); DumpError (dwStatus);
Socket = INVALID_SOCKET;
// we will allocate a new socket in IssueAccept
// if we hit an error accepting on this socket,
// it can't hurt to use a new socket, anyway.
closesocket (AcceptOverlapped -> Socket); }
// post the accept context for a new receive
hr = IssueAccept (AcceptOverlapped); if (hr != S_OK) { H323DBG(( DEBUG_LEVEL_WARNING, "Q931: failed to issue accept on " "buffer -- reception of new Q.931 connections may stall" ));
delete AcceptOverlapped; } } else { // future accept requests are denied -- module is shutting down.
if( AcceptOverlapped -> Socket != INVALID_SOCKET ) { closesocket( AcceptOverlapped -> Socket ); AcceptOverlapped -> Socket = INVALID_SOCKET; }
delete AcceptOverlapped; }
Unlock();
if (Socket != INVALID_SOCKET) { H323DBG(( DEBUG_LEVEL_TRACE, "Q931: accepted connection, remote address %08XH:%04X.", SOCKADDR_IN_PRINTF (&RemoteAddress)));
// hand the newly accepted connection off to the call processing code.
CallProcessIncomingCall (Socket, &LocalAddress, &RemoteAddress); } }
// Q931_BUFFER_CACHE ----------------------------------------------------
Q931_BUFFER_CACHE::Q931_BUFFER_CACHE (void) { // No need to check the result of this one since this object is
// not allocated on heap, right when the DLL is loaded
InitializeCriticalSectionAndSpinCount( &m_CriticalSection, 0x80000000 );
InitializeListHead (&m_FreeRecvBufferList); m_FreeRecvBufferListCount = 0; }
Q931_BUFFER_CACHE::~Q931_BUFFER_CACHE (void) { Q931BufferCache.FreeAll(); DeleteCriticalSection( &m_CriticalSection ); }
BOOL Q931_BUFFER_CACHE::AllocRecvBuffer ( OUT RECVBUF ** ReturnRecvBuffer ) { LIST_ENTRY * ListEntry; RECVBUF * RecvBuffer;
Lock();
if (m_FreeRecvBufferListCount > 0) { m_FreeRecvBufferListCount--;
_ASSERTE( IsListEmpty (&m_FreeRecvBufferList) == FALSE ); ListEntry = RemoveHeadList (&m_FreeRecvBufferList); RecvBuffer = CONTAINING_RECORD (ListEntry, RECVBUF, ListEntry); } else { // perform global heap allocation after unlocking (better concurrency)
RecvBuffer = NULL; }
Unlock();
if( RecvBuffer == NULL ) { RecvBuffer = new RECVBUF; }
*ReturnRecvBuffer = RecvBuffer; return !!RecvBuffer; }
void Q931_BUFFER_CACHE::FreeRecvBuffer ( IN RECVBUF * RecvBuffer ) { Lock();
_ASSERTE( !IsInList (&m_FreeRecvBufferList, &RecvBuffer -> ListEntry));
if (m_FreeRecvBufferListCount < RECV_BUFFER_LIST_COUNT_MAX) { InsertHeadList (&m_FreeRecvBufferList, &RecvBuffer -> ListEntry); m_FreeRecvBufferListCount++; RecvBuffer = NULL; }
Unlock();
if( RecvBuffer ) { delete RecvBuffer; } }
void Q931_BUFFER_CACHE::FreeAll (void) { LIST_ENTRY * ListEntry; RECVBUF * RecvBuffer;
Lock();
while( IsListEmpty(&m_FreeRecvBufferList) == FALSE ) { _ASSERTE( m_FreeRecvBufferListCount > 0 ); m_FreeRecvBufferListCount--;
ListEntry = RemoveHeadList (&m_FreeRecvBufferList); RecvBuffer = CONTAINING_RECORD (ListEntry, RECVBUF, ListEntry);
delete RecvBuffer; }
_ASSERTE( m_FreeRecvBufferListCount == 0 );
Unlock(); }
|