windows-nt/Source/XPSP1/NT/net/tapi/skywalker/h323/tsp/q931obj.cpp
2020-09-26 16:20:57 +08:00

680 lines
16 KiB
C++

/*++
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();
}