Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

677 lines
20 KiB

/************************************************************************************************
Copyright (c) 2001 Microsoft Corporation
File Name: SocketPool.cpp
Abstract: Implementation of the socket pool (CSocketPool class)
and the callback function for IO Context.
Notes:
History: 08/01/2001 Created by Hao Yu (haoyu)
************************************************************************************************/
#include <stdafx.h>
#include <ThdPool.hxx>
#include <SockPool.hxx>
#include <GlobalDef.h>
typedef int (*FUNCGETADDRINFO)(const char *, const char *, const struct addrinfo *, struct addrinfo **);
typedef void (*FUNCFREEADDRINFO)(struct addrinfo *);
//The call back function for IO Context
VOID IOCallBack(PULONG_PTR pCompletionKey ,LPOVERLAPPED pOverlapped, DWORD dwBytesRcvd)
{
ASSERT( NULL != pCompletionKey );
char szBuffer[MAX_PATH]="+OK Server Ready";
WSABUF wszBuf={MAX_PATH, szBuffer};
DWORD dwNumSent=0;
DWORD dwFlag=0;
long lLockValue;
PIO_CONTEXT pIoContext=(PIO_CONTEXT)pCompletionKey;
if(pIoContext->m_ConType == LISTEN_SOCKET)
{
ASSERT(pOverlapped != NULL);
//This is a new connection
g_PerfCounters.IncPerfCntr(e_gcTotConnection);
g_PerfCounters.IncPerfCntr(e_gcConnectionRate);
g_PerfCounters.IncPerfCntr(e_gcConnectedSocketCnt);
pIoContext=CONTAINING_RECORD(pOverlapped, IO_CONTEXT, m_Overlapped);
pIoContext->m_dwLastIOTime=GetTickCount();
pIoContext->m_dwConnectionTime=pIoContext->m_dwLastIOTime;
pIoContext->m_lLock=LOCKED_TO_PROCESS_POP3_CMD;
if(ERROR_SUCCESS!=g_FreeList.RemoveFromList( &(pIoContext->m_ListEntry) ))
{
g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL,POP3SVR_SOCKET_REQUEST_BEFORE_INIT);
}
g_BusyList.AppendToList( &(pIoContext->m_ListEntry) );
g_SocketPool.DecrementFreeSocketCount();
pIoContext->m_pPop3Context->Reset();
pIoContext->m_pPop3Context->ProcessRequest(pIoContext, pOverlapped, dwBytesRcvd);
if(DELETE_PENDING == pIoContext->m_ConType)
{
g_BusyList.RemoveFromList(&(pIoContext->m_ListEntry));
if(g_SocketPool.IsMoreSocketsNeeded())
{
if(g_SocketPool.ReuseIOContext(pIoContext))
{
return;
}
}
delete(pIoContext->m_pPop3Context);
delete(pIoContext);
}
else
{
InterlockedExchange(&(pIoContext->m_lLock), UNLOCKED);
}
if( g_SocketPool.IsMoreSocketsNeeded() )
{
if(!g_SocketPool.AddSockets())
{
g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL,POP3SVR_CREATE_ADDITIONAL_SOCKET_FAILED);
}
}
if( g_SocketPool.IsMaxSocketUsed())
{
SetEvent(g_hDoSEvent);
}
}
else
{
lLockValue = InterlockedCompareExchange(&(pIoContext->m_lLock), LOCKED_TO_PROCESS_POP3_CMD, UNLOCKED);
while(UNLOCKED!=lLockValue)
{
//This thread have to wait for the previous command to finish
//Or the timeout thread to mark it as timed out
Sleep(10);
lLockValue = InterlockedCompareExchange(&(pIoContext->m_lLock), LOCKED_TO_PROCESS_POP3_CMD, UNLOCKED);
}
if(CONNECTION_SOCKET == pIoContext->m_ConType )
{
pIoContext->m_pPop3Context->ProcessRequest(pIoContext, pOverlapped, dwBytesRcvd);
}
if(DELETE_PENDING == pIoContext->m_ConType)
{
g_BusyList.RemoveFromList(&(pIoContext->m_ListEntry));
if(g_SocketPool.IsMoreSocketsNeeded())
{
if(g_SocketPool.ReuseIOContext(pIoContext))
{
return;
}
}
delete(pIoContext->m_pPop3Context);
delete(pIoContext);
}
else
{
pIoContext->m_dwLastIOTime=GetTickCount();
InterlockedExchange(&(pIoContext->m_lLock), UNLOCKED);
}
}
}
CSocketPool::CSocketPool()
{
InitializeCriticalSection(&m_csInitGuard);
m_sMainSocket = INVALID_SOCKET;
m_lMaxSocketCount = 0;
m_lMinSocketCount = 0;
m_lThreshold = 0;
m_lTotalSocketCount = 0;
m_lFreeSocketCount = 0;
m_bInit = FALSE;
m_lAddThreadToken = 1l;
m_iSocketFamily = 0;
m_iSocketType = 0;
m_iSocketProtocol = 0;
}
CSocketPool::~CSocketPool()
{
if(m_bInit)
{
Uninitialize();
}
DeleteCriticalSection(&m_csInitGuard);
}
BOOL CSocketPool::CreateMainSocket(u_short usPort)
{
BOOL bRetVal = TRUE;
PSOCKADDR addr;
SOCKADDR_IN inAddr;
INT addrLength;
OSVERSIONINFOEX osVersion;
HMODULE hMd=NULL;
FUNCGETADDRINFO fgetaddrinfo=NULL;
FUNCFREEADDRINFO ffreeaddrinfo=NULL;
char szPort[33]; //max bytes of buffer for _ultoa
addrinfo aiHints,*paiList=NULL, *paiIndex=NULL;
int iRet;
osVersion.dwOSVersionInfoSize=sizeof(OSVERSIONINFOEX);
if( !GetVersionEx((LPOSVERSIONINFO)(&osVersion)) )
{
// This should never happen
return FALSE;
}
if( (osVersion.dwMajorVersion>=5) //Only work with XP
&&
(osVersion.dwMinorVersion >1)
&&
( (osVersion.wProductType == VER_NT_SERVER ) ||
(osVersion.wProductType == VER_NT_DOMAIN_CONTROLLER) )
&&
(!
((osVersion.wSuiteMask & VER_SUITE_SMALLBUSINESS ) ||
(osVersion.wSuiteMask & VER_SUITE_SMALLBUSINESS_RESTRICTED ) ||
(osVersion.wSuiteMask & VER_SUITE_PERSONAL ) ) ) )
{
//These are the SKUs we support
}
else
{
g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL,
POP3SVR_UNSUPPORTED_OS);
return FALSE;
}
if(osVersion.dwMinorVersion > 0 ) //XP
{
hMd=GetModuleHandle(_T("WS2_32.dll"));
if(NULL == hMd)
{
return FALSE;
}
fgetaddrinfo=(FUNCGETADDRINFO)GetProcAddress(hMd, "getaddrinfo");
ffreeaddrinfo=(FUNCFREEADDRINFO)GetProcAddress(hMd, "freeaddrinfo");
if( (NULL == fgetaddrinfo) ||
(NULL == ffreeaddrinfo))
{
return FALSE;
}
_ultoa(usPort, szPort, 10);
memset(&aiHints, 0, sizeof(aiHints));
aiHints.ai_socktype = SOCK_STREAM;
aiHints.ai_flags = AI_PASSIVE;
iRet=fgetaddrinfo(NULL, szPort, &aiHints, &paiList);
if(iRet!=0)
{
//Error case
g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL,
POP3SVR_FAILED_TO_CREATE_SOCKET,
WSAGetLastError());
bRetVal=FALSE;
goto EXIT;
}
for(paiIndex=paiList; paiIndex!=NULL; paiIndex=paiIndex->ai_next)
{
if( ( (paiIndex->ai_family == PF_INET ) && (g_dwIPVersion != 6 ) ) ||
( (g_dwIPVersion==6) && (paiIndex->ai_family == PF_INET6 ) ) )
{
//Find the first (usually the only) addrinfo
m_iSocketFamily=paiIndex->ai_family; //For create AcceptEx socket
m_iSocketType=paiIndex->ai_socktype;
m_iSocketProtocol=paiIndex->ai_protocol;
m_sMainSocket = WSASocket(
m_iSocketFamily,
m_iSocketType,
m_iSocketProtocol,
NULL, // protocol info
0, // Group ID = 0 => no constraints
WSA_FLAG_OVERLAPPED // completion port notifications
);
if(INVALID_SOCKET == m_sMainSocket)
{
//This is not the socket family supported by the machine.
continue;
}
break;
}
}
if(INVALID_SOCKET==m_sMainSocket)
{
g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL,
POP3SVR_FAILED_TO_CREATE_SOCKET,
WSAGetLastError());
bRetVal=FALSE;
goto EXIT;
}
if ( bind( m_sMainSocket, paiIndex->ai_addr, paiIndex->ai_addrlen) != 0)
{
g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL,
POP3SVR_FAILED_TO_BIND_MAIN_SOCKET,
WSAGetLastError());
bRetVal=FALSE;
goto EXIT;
}
}
else //Win2k
{
m_iSocketFamily=PF_INET;
m_iSocketType=SOCK_STREAM;
m_iSocketProtocol=IPPROTO_TCP;
m_sMainSocket = WSASocket(
m_iSocketFamily,
m_iSocketType,
m_iSocketProtocol,
NULL, // protocol info
0, // Group ID = 0 => no constraints
WSA_FLAG_OVERLAPPED // completion port notifications
);
if(INVALID_SOCKET == m_sMainSocket)
{
g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL,
POP3SVR_FAILED_TO_CREATE_SOCKET,
WSAGetLastError());
bRetVal=FALSE;
goto EXIT;
}
addr = (PSOCKADDR)&inAddr;
addrLength = sizeof(inAddr);
ZeroMemory(addr, addrLength);
inAddr.sin_family = AF_INET;
inAddr.sin_port = htons(usPort);
inAddr.sin_addr.s_addr = INADDR_ANY;
if ( bind( m_sMainSocket, addr, addrLength) != 0)
{
g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL,
POP3SVR_FAILED_TO_BIND_MAIN_SOCKET,
WSAGetLastError());
bRetVal=FALSE;
goto EXIT;
}
}
if ( listen( m_sMainSocket, m_iBackLog) != 0)
{
g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL,
POP3SVR_FAILED_TO_LISTEN_ON_MAIN_SOCKET,
WSAGetLastError());
bRetVal=FALSE;
goto EXIT;
}
m_stMainIOContext.m_hAsyncIO = m_sMainSocket;
m_stMainIOContext.m_ConType = LISTEN_SOCKET;
m_stMainIOContext.m_pCallBack = IOCallBack;
m_stMainIOContext.m_pPop3Context = NULL;
m_stMainIOContext.m_dwLastIOTime = 0; //No Timeout on this socket
m_stMainIOContext.m_lLock = UNLOCKED;
// Associate the main socket to the completion port
bRetVal = g_ThreadPool.AssociateContext(&m_stMainIOContext);
EXIT:
if(!bRetVal)
{
//Clean up the main listening socket
if( INVALID_SOCKET != m_sMainSocket )
{
closesocket(m_sMainSocket);
m_sMainSocket=INVALID_SOCKET;
}
}
if(NULL != paiList)
{
ffreeaddrinfo(paiList);
}
return bRetVal;
}
BOOL CSocketPool::Initialize(DWORD dwMax, DWORD dwMin, DWORD dwThreshold, u_short usPort, int iBackLog)
{
BOOL bRetVal=FALSE;
EnterCriticalSection(&m_csInitGuard);
ASSERT( ( dwMax >= dwMin + dwThreshold ) &&
( dwMin > 0 ) );
if( !( (dwMax >= dwMin + dwThreshold ) &&
( dwMin > 0 ) ) )
{
g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL,
EVENT_POP3_NO_CONFIG_DATA);
goto EXIT;
}
if(!m_bInit)
{
WSADATA wsaData;
INT iErr;
iErr = WSAStartup( MAKEWORD( 2, 0), &wsaData);
if( iErr != 0 )
{
g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL,
POP3SVR_WINSOCK_FAILED_TO_INIT,
iErr);
bRetVal=FALSE;
goto EXIT;
}
m_lMaxSocketCount = dwMax;
m_lMinSocketCount = dwMin;
m_lThreshold = dwThreshold;
m_lFreeSocketCount = 0;
m_lTotalSocketCount= 0;
m_iBackLog = iBackLog;
//First Create the Main socket
if( bRetVal = CreateMainSocket(usPort) )
{
//Now create the initial pool of AcceptEx sockets
bRetVal = AddSocketsP(m_lMinSocketCount);
}
m_bInit=bRetVal;
}
EXIT:
LeaveCriticalSection(&m_csInitGuard);
return bRetVal;
}
// Called after
BOOL CSocketPool::IsMoreSocketsNeeded()
{
if ( (g_dwServerStatus != SERVICE_RUNNING ) &&
(g_dwServerStatus != SERVICE_PAUSED ) )
{
return FALSE;
}
if ( ( m_lTotalSocketCount < m_lMinSocketCount) ||
(( m_lFreeSocketCount < m_lThreshold ) &&
( m_lTotalSocketCount <m_lMaxSocketCount )) )
{
return TRUE;
}
else
{
return FALSE;
}
}
BOOL CSocketPool::MaintainSocketCount()
{
if ( g_dwServerStatus != SERVICE_RUNNING )
{
return FALSE;
}
if(
( ( m_lFreeSocketCount < m_lThreshold ) &&
( m_lTotalSocketCount+m_lThreshold >= m_lMaxSocketCount ) )
||
( m_lTotalSocketCount <= m_lMinSocketCount )
)
{
return TRUE;
}
else
{
return FALSE;
}
}
BOOL CSocketPool::Uninitialize()
{
BOOL bRetVal=TRUE;
EnterCriticalSection(&m_csInitGuard);
if(m_bInit)
{
// Close the main socket here
closesocket(m_sMainSocket);
m_sMainSocket=INVALID_SOCKET;
//AcceptEx Sockes should already have been cleaned with IO Context,
if(WSACleanup () )
{
g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL,
POP3SVR_WINSOCK_FAILED_TO_CLEANUP,
WSAGetLastError());
return FALSE;
}
m_bInit=FALSE;
}
LeaveCriticalSection(&m_csInitGuard);
return bRetVal;
}
// For working threading to call when a new connection was established
BOOL CSocketPool::AddSockets()
{
BOOL bRetVal=TRUE;
if( g_dwServerStatus != SERVICE_RUNNING )
{
return TRUE;
}
ASSERT(TRUE == m_bInit);
// Make sure only one thread get to add the socket
if( InterlockedExchange(&m_lAddThreadToken,0) )
{
bRetVal = AddSocketsP(m_lThreshold);
InterlockedExchange(&m_lAddThreadToken,1);
}
return bRetVal;
}
BOOL CSocketPool::AddSocketsP(DWORD dwNumOfSocket)
{
int i;
BOOL bRetVal=TRUE;
PIO_CONTEXT pIoContext=NULL;
for(i=0; i<dwNumOfSocket; i++)
{
if( (g_dwServerStatus != SERVICE_RUNNING ) &&
(g_dwServerStatus != SERVICE_START_PENDING) )
{
return TRUE;
}
if(m_lMaxSocketCount < InterlockedIncrement(&m_lTotalSocketCount) )
{
InterlockedDecrement(&m_lTotalSocketCount);
return TRUE;
}
pIoContext=new (IO_CONTEXT);
if(NULL==pIoContext)
{
g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL,
POP3SVR_NOT_ENOUGH_MEMORY);
bRetVal=FALSE;
break;
}
pIoContext->m_pPop3Context=new(POP3_CONTEXT);
pIoContext->m_pCallBack = IOCallBack;
if(NULL == pIoContext->m_pPop3Context )
{
g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL,
POP3SVR_NOT_ENOUGH_MEMORY);
bRetVal=FALSE;
delete(pIoContext);
break;
}
bRetVal=CreateAcceptSocket(pIoContext);
if(!bRetVal)
{
delete(pIoContext->m_pPop3Context);
delete(pIoContext);
break;
}
InterlockedIncrement(&m_lFreeSocketCount);
if(m_lTotalSocketCount >= m_lMaxSocketCount)
{
break;
}
}
if(!bRetVal)
{
InterlockedDecrement(&m_lTotalSocketCount);
}
return bRetVal;
}
//Called when a new connection is establised
//and a AcceptEx socket is used
void CSocketPool::DecrementFreeSocketCount()
{
if(0==InterlockedDecrement(&m_lFreeSocketCount))
{
AddSockets();
}
}
//Called when a socket is closed
void CSocketPool::DecrementTotalSocketCount()
{
if(0==InterlockedDecrement(&m_lTotalSocketCount))
{
//Some socket must be created to avoid
//this denial of service problem.
AddSockets();
}
}
//Called when a socked is closed, however, the a new
// AcceptEx socket should be created to maintain
// total socket count, but keep the IOContext
// to re-use
BOOL CSocketPool::ReuseIOContext(PIO_CONTEXT pIoContext)
{
ASSERT( NULL != pIoContext);
if(InterlockedIncrement(&m_lTotalSocketCount) > m_lMaxSocketCount)
{
InterlockedDecrement(&m_lTotalSocketCount);
return FALSE;
}
pIoContext->m_pPop3Context->Reset();
if( CreateAcceptSocket(pIoContext) )
{
InterlockedIncrement(&m_lFreeSocketCount);
return TRUE;
}
else
{
InterlockedDecrement(&m_lTotalSocketCount);
return FALSE;
}
}
BOOL CSocketPool::CreateAcceptSocket(PIO_CONTEXT pIoContext)
{
ASSERT(NULL != pIoContext);
SOCKET sNew;
DWORD dwRcvd;
int iErr;
BOOL bRetVal=FALSE;
BOOL bAddToList=FALSE;
sNew=WSASocket(m_iSocketFamily,
m_iSocketType,
m_iSocketProtocol,
NULL, // protocol info
0, // Group ID = 0 => no constraints
WSA_FLAG_OVERLAPPED // completion port notifications
);
if(INVALID_SOCKET == sNew)
{
g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL,
POP3SVR_FAILED_TO_CREATE_SOCKET,
WSAGetLastError());
goto EXIT;
}
pIoContext->m_hAsyncIO=sNew;
pIoContext->m_ConType=CONNECTION_SOCKET;
pIoContext->m_lLock=UNLOCKED;
ZeroMemory(&(pIoContext->m_Overlapped), sizeof(OVERLAPPED));
g_FreeList.AppendToList( &(pIoContext->m_ListEntry) );
bAddToList=TRUE;
// Now add the new Context to the Completion Port
if( bRetVal = g_ThreadPool.AssociateContext(pIoContext))
{
bRetVal=AcceptEx(m_sMainSocket,
sNew,
(LPVOID)(pIoContext->m_Buffer),
0,
MIN_SOCKADDR_SIZE,
MIN_SOCKADDR_SIZE,
&dwRcvd,
&(pIoContext->m_Overlapped));
if(!bRetVal)
{
iErr= WSAGetLastError();
if(ERROR_IO_PENDING!=iErr)
{
g_EventLogger.LogEvent(LOGTYPE_ERR_CRITICAL,
POP3SVR_CALL_ACCEPTEX_FAILED,
iErr);
}
else
{
bRetVal=TRUE;
}
}
}
EXIT:
if(!bRetVal)
{
if(INVALID_SOCKET != sNew)
{
closesocket(sNew);
}
if(bAddToList)
{
g_FreeList.RemoveFromList(&(pIoContext->m_ListEntry));
}
}
return bRetVal;
}
BOOL CSocketPool::IsMaxSocketUsed()
{
return ( (m_lTotalSocketCount==m_lMaxSocketCount) &&
(m_lFreeSocketCount == 0 ) );
}