Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

906 lines
21 KiB

/*++
Copyright (c) 1994 Microsoft Corporation
Module Name :
conninfo.cxx
Abstract:
This module defines the functions for Communications Package as
a whole. This is a wrapper around WinSock interface to provide
a thread for each listen socket. Also this object accepts connections
and dispatches the same to the callback function.
Author:
Murali R. Krishnan ( MuraliK ) 10-Oct-1994
Project:
Gopher Server DLL. ( Internet Services Common DLL)
Functions Exported:
TS_CONNECTION_INFO::TS_CONNECTION_INFO()
TS_CONNECTION_INFO::~TS_CONNECTION_INFO()
DWORD TS_CONNECTION_INFO::CreateListenSocket( ...)
DWORD TS_CONNECTION_INFO::CloseListenSocket( VOID)
BOOL TS_CONNECTION_INFO::StartConnectionPump( IN PFNCONNECT )
BOOL TS_CONNECTION_INFO::StopConnectionPump( VOID)
DWORD TS_CONNECTION_INFO::ConnectionLoop( VOID)
Revision History:
MuraliK 6-March-1995 Modified to support transport independence.
--*/
/************************************************************
* Include Headers
************************************************************/
# include <tcpdllp.hxx>
# include "conninfo.hxx"
# include "inetreg.h"
//
// Prototypes of local functions
//
static DWORD ConnectionThread( LPVOID lpv_pConnInfo);
# define DEF_INITIAL_ACCEPTEX_SOCKETS ( 40)
# define DEF_INITIAL_ACCEPTEX_TIMEOUT ( 900) // seconds
/************************************************************
* Functions ( Member Functions for TS_CONNECTION_INFO object.
************************************************************/
TS_CONNECTION_INFO_BASE::TS_CONNECTION_INFO_BASE(
IN PFN_CONNECT_CALLBACK pfnConnect
)
/*++
Constructor function for communication package.
Arguments:
pfnConnect pointer to callback function for new connections
( This is optional)
The callback function should be supplied at least
when StartConnectionThread() is called.
--*/
: m_pfnConnect ( pfnConnect),
m_listenSocket ( INVALID_SOCKET)
{
InitializeCriticalSection( &m_csLock);
} // TS_CONNECTION_INFO_BASE::TS_CONNECTION_INFO_BASE()
TS_CONNECTION_INFO_BASE::~TS_CONNECTION_INFO_BASE( VOID)
/*++
Destructor function for TS_CONNECTION_INFO object,
Frees all dynamically allocated memory and cleans up member objects.
--*/
{
Lock();
IF_DEBUG( DLL_CONNECTION) {
DBGPRINTF( ( DBG_CONTEXT,
"Deleting ConnectionObject %08x.\n",
this));
}
//
// Close the listen socket and stop any connections
//
DBG_REQUIRE( CloseListenSocket() == NO_ERROR);
UnLock();
DeleteCriticalSection( &m_csLock);
} // TS_CONNECTION_INFO_BASE::~TS_CONNECTION_INFO_BASE()
DWORD
TS_CONNECTION_INFO_BASE::CreateListenSocket(
IN LPSOCKADDR lpSockAddress,
IN int lenSockAddress,
IN int socketType,
IN int socketProtocol,
IN DWORD nBackLog)
/*++
Creates a socket for listening to connections on given address.
Arguments:
lpSockAddress pointer to local socket address structure used to bind
the given connection.
lenSockAddress length of the socket address structure.
socketType integer containing the type of the socket ( stream )
socketProtocol protocol to be used for the socket.
nBackLog Maximum length to which a queue of pending connections
may grow.
Returns:
NO_ERROR on success; otherwise returns Sockets error code.
--*/
{
INT serr;
SOCKET sNew;
if ( m_listenSocket != INVALID_SOCKET) {
//
// Already a socket present. Return
//
return ( NO_ERROR);
}
DBG_ASSERT( lpSockAddress != NULL);
//
// Create a new socket
//
sNew = socket( lpSockAddress->sa_family, socketType, socketProtocol);
serr = ( sNew == INVALID_SOCKET) ? WSAGetLastError() : NO_ERROR;
IF_DEBUG( DLL_SOCKETS) {
DBGPRINTF( ( DBG_CONTEXT,
" socket( %d, %d, %d) = Socket( %d) and Error = %u\n",
lpSockAddress->sa_family, socketType, socketProtocol,
sNew, serr));
}
if ( serr == NO_ERROR) {
BOOL fReuseAddr = FALSE;
//
// Used to disallow reusing the same address twice
//
if ( setsockopt( sNew, SOL_SOCKET, SO_REUSEADDR,
(const CHAR *) &fReuseAddr,
sizeof( fReuseAddr)) != 0) {
serr = WSAGetLastError();
DBGPRINTF( ( DBG_CONTEXT,
" setsockopt( %d, REUSE_ADDR, FALSE) failed."
" Error = %d\n",
sNew, serr));
}
}
if ( serr == NO_ERROR) {
//
// Bind an address to socket
//
if ( bind( sNew, lpSockAddress, lenSockAddress) != 0) {
serr = WSAGetLastError();
}
IF_DEBUG( DLL_SOCKETS) {
DBGPRINTF( ( DBG_CONTEXT,
"bind ( socket = %d, Address = %08x, len = %d) "
" returns error = %u\n",
sNew, lpSockAddress, lenSockAddress, serr));
}
}
if ( serr == NO_ERROR) {
//
// Put the socket in listen mode
//
if ( listen( sNew, nBackLog) != 0) {
serr = WSAGetLastError();
}
IF_DEBUG( DLL_SOCKETS) {
DBGPRINTF( ( DBG_CONTEXT,
" listen( %d, %d) returned %d.\n",
sNew, nBackLog, serr));
}
}
if ( serr == NO_ERROR) {
//
// Success in creating a listen socket. Set the listen socket value.
//
ASSERT( sNew != INVALID_SOCKET);
Lock();
m_listenSocket = sNew;
UnLock();
IF_DEBUG( DLL_SOCKETS) {
DBGPRINTF( ( DBG_CONTEXT,
"TS_CONNECTION_INFO::CreateListenSocket() Listen Socket = %d\n",
sNew));
}
} else {
//
// Failure to create socket or put in listen mode
//
IF_DEBUG( DLL_SOCKETS) {
DBGPRINTF( ( DBG_CONTEXT,
" TS_CONNECTION_INFO::CreateListenSocket()."
" No connection Socket (error = %d)\n",
serr));
}
if ( sNew != INVALID_SOCKET) {
ResetSocket( sNew);
}
}
return ( (DWORD ) serr);
} // TS_CONNECTION_INFO::CreateListenSocket()
DWORD
TS_CONNECTION_INFO_BASE::CloseListenSocket( VOID)
/*++
Closes the socket on which a listen was possibly established.
This function should be called after locking this TS_CONNECTION_INFO object.
Returns:
NO_ERROR on success and WinSock error code on failure.
--*/
{
INT serr = NO_ERROR;
if ( m_listenSocket != INVALID_SOCKET) {
serr = ResetSocket( m_listenSocket);
IF_DEBUG( DLL_SOCKETS) {
DBGPRINTF( ( DBG_CONTEXT,
" closing listen socket %d returns Error = %d\n",
m_listenSocket, serr));
}
m_listenSocket = INVALID_SOCKET;
}
return ( serr);
} // CloseListenSocket()
INT
TS_CONNECTION_INFO_BASE::ResetSocket( SOCKET sock)
/*++
Performs a hard close of given socket.
Arguments:
sock socket to be closed.
Returns:
0 on success; otherwise Sockets error code.
--*/
{
DWORD serr = NO_ERROR;
LINGER linger;
//
// Enable linger with timeout of ZERO for "hard" close
//
// Error code from sock option is ignored, since we are
// anyway closing the socket
//
linger.l_onoff = TRUE;
linger.l_linger = 0;
setsockopt( sock, SOL_SOCKET, SO_LINGER,
( CHAR *) & linger,
sizeof( linger));
//
// Close the socket
//
if ( closesocket( sock) != 0) {
serr = WSAGetLastError();
}
return ( serr);
} // TS_CONNECTION_INFO::ResetSocket()
# if DBG
VOID
TS_CONNECTION_INFO_BASE::Print( VOID) const
{
DBGPRINTF( ( DBG_CONTEXT,
"Printing TS_CONNECTION_INFO_BASE object (%08x)\n"
" CallBackFunction = %08x;"
" ListenSocket = %d\n",
this,
m_pfnConnect,
m_listenSocket ));
return;
} // TS_CONNECTION_INFO_BASE::Print()
# endif // DBG
TS_CONNECTION_INFO::TS_CONNECTION_INFO( IN PFN_CONNECT_CALLBACK pfnConnect)
/*++
Constructor function for communication package.
Arguments:
pfnConnect pointer to callback function for new connections
( This is optional)
The callback function should be supplied at least
when StartConnectionThread() is called.
--*/
: TS_CONNECTION_INFO_BASE( pfnConnect ),
m_fShutDown ( FALSE),
m_hConnectionThread ( NULL)
{
} // TS_CONNECTION_INFO::TS_CONNECTION_INFO()
TS_CONNECTION_INFO::~TS_CONNECTION_INFO( VOID)
/*++
Destructor function for TS_CONNECTION_INFO object,
Frees all dynamically allocated memory and cleans up member objects.
--*/
{
Lock();
IF_DEBUG( DLL_CONNECTION) {
DBGPRINTF( ( DBG_CONTEXT,
"Deleting ConnectionObject %08x.\n",
this));
}
//
// Release the thread handle if still active.
//
if ( m_hConnectionThread != NULL) {
IF_DEBUG( DLL_CONNECTION) {
DBGPRINTF( ( DBG_CONTEXT,
"Connection Thread(%08x) is not synchronized \n",
m_hConnectionThread));
}
}
UnLock();
} // TS_CONNECTION_INFO::~TS_CONNECTION_INFO()
BOOL
TS_CONNECTION_INFO::StartConnectionPump(
IN PFN_CONNECT_CALLBACK pfnConnect,
IN ATQ_COMPLETION pfnConnectEx,
IN ATQ_COMPLETION pfnIOCompletion,
IN DWORD cbAcceptExReceiveBuffer,
IN const CHAR * pszRegParamKey
)
/*++
Start a connection Thread and makes it loop waiting for connections.
Arguments:
pfnConnect pointer to callback function for new connections.
pfnConnectEx pointer to AcceptEx Connection establishment callback
pfnIOCompletion pointerto IO completion call back function.
cbAcceptexReceiveBuffer
count of bytes of buffer to be used for initial receive
(If 0 no receive buffer isallocated).
pszRegParamKey - Registry parameter key of the service the connection
pump is running for
Returns:
TRUE on successfully spawning off a new thread.
FALSE on failure.
--*/
{
if ( pfnConnect != NULL) {
//
// Override the previous NULL value.
//
DBG_ASSERT( m_pfnConnect == NULL);
m_pfnConnect = pfnConnect;
}
DBG_ASSERT( m_pfnConnect != NULL);
//
// Check for any previous threads before proceeding.
//
if ( m_hConnectionThread == NULL) {
HANDLE hThread;
DWORD idConnectThread;
hThread = CreateThread( NULL, // lpSecurityAttributes
0, // stack space - default to parent
ConnectionThread, // pointer to startup routine
(LPVOID ) this, // arguments for function
0, // create flags
&idConnectThread);// pointer to thread id
if ( hThread != NULL) {
m_hConnectionThread = hThread;
}
}
return ( m_hConnectionThread != NULL);
} // TS_CONNECTION_INFO::StartConnectionPump()
BOOL
TS_CONNECTION_INFO::StopConnectionPump( VOID)
/*++
Stop the connection thread synchronously and
free all associated CPU resources.
Returns:
TRUE on success and FALSE on failure.
if FALSE, the TS_CONNECTION_INFO object should not be deleted.
--*/
{
BOOL fReturn = TRUE;
if ( m_hConnectionThread != NULL) {
DWORD dwResult;
Lock();
m_fShutDown = TRUE;
// Ignore return value as the thread will be killed.
CloseListenSocket();
dwResult = WaitForSingleObject( m_hConnectionThread,
CP_CONNECTION_THREAD_TIMEOUT);
if ( dwResult == WAIT_TIMEOUT) {
//
// Could not sync with the connection thread
//
fReturn = FALSE;
} else {
m_hConnectionThread = NULL;
}
UnLock();
}
return ( fReturn);
} // TS_CONNECTION_INFO::StopConnectionPump()
static DWORD
ConnectionThread( LPVOID lpv_pConnInfo)
/*++
A wrapper function for calling TS_CONNECTION_INFO::ConnectionLoop.
Since Win32 API CreateThread() requires a function taking
32 bit argument and returing 32 bit value, this fuction is
used.
Arguments:
lpv_pConnInfo pointer to the TS_CONNECTION_INFO object
( give as VOID pointer becoz windows does not like structure :()
Returns:
the return value from TS_CONNECTION_INFO::ConnectionLoop()
--*/
{
PTS_CONNECTION_INFO pConnInfo = (PTS_CONNECTION_INFO ) ( lpv_pConnInfo);
ASSERT( pConnInfo != NULL);
return ( pConnInfo->ConnectionLoop());
} // ConnectionThread()
DWORD
TS_CONNECTION_INFO::ConnectionLoop( VOID)
/*++
Main loop waiting for connections. ( The core of server)
The thread loops around waiting on an accept() call on
listenSocket.
If there is a new message on socket, it invokes the
callback function for connection.
NEVER returns untill it is requested to stop by someother
thread using a call to TS_CONNECTION_INFO::StopConnectionThread().
Returns:
0 on success and error code if there is a fatal error.
--*/
{
INT serr;
register SOCKET sNewConnection;
SOCKADDR_IN sockAddrRemote;
//
// Loop Forever
//
for( ; ;) {
int cbAddr = sizeof( sockAddrRemote);
//
// Wait for a connection
//
if ((sNewConnection = accept( m_listenSocket, // socket
(LPSOCKADDR ) &sockAddrRemote, // sokaddr
&cbAddr) // size of address
) != INVALID_SOCKET) {
//
// Valid Connection has been established.
// Invoke the callback function to process this connection
// and then continue the loop
//
( *m_pfnConnect)( sNewConnection, &sockAddrRemote);
} else {
//
// Some low level error has occured.
//
if ( ( serr = WSAGetLastError()) == WSAEINTR) {
//
// Socket was closed by low-level call. Get out.
//
break;
}
IF_DEBUG( DLL_SOCKETS) {
DBGPRINTF( ( DBG_CONTEXT, "Received Error %d from accept()\n",
serr));
}
//
// Check if we are shutting down and if so QUIT
//
if ( m_fShutDown) {
break;
}
//
// Perform a graceful recovery from failure. NYI
// ( Tricky code). Both FTP and Web server are to test it!
// Will add this code later. ( MuraliK)
//
} // if ( INVALID_SOCKET from accept
} // for( ; ; )
//
// Cleanup & Exit. Cleanup is done by the code which called the shut down.
//
IF_DEBUG( DLL_SOCKETS) {
DBGPRINTF( ( DBG_CONTEXT, "ConnectionLoop is stopped.\n"));
}
return ( 0); // No errors
} // TS_CONNECTION_INFO::ConnectionLoop()
# if DBG
VOID
TS_CONNECTION_INFO::Print( VOID) const
{
DBGPRINTF( ( DBG_CONTEXT,
"Printing TS_CONNECTION_INFO object (%08x)\n"
" ConnectionThread = %08x; CallBackFunction = %08x;"
" ListenSocket = %d; ShutDownFlag = %d\n",
this,
m_hConnectionThread,
m_pfnConnect,
m_listenSocket,
m_fShutDown));
return;
} // TS_CONNECTION_INFO::Print()
#endif // DBG
TS_ACCEPTEX_CONNECTION_INFO::TS_ACCEPTEX_CONNECTION_INFO(
IN PFN_CONNECT_CALLBACK pfnConnect
)
/*++
Constructor function for communication package.
Arguments:
pfnConnect pointer to callback function for new connections
( This is optional)
The callback function should be supplied at least
when StartConnectionThread() is called.
--*/
: TS_CONNECTION_INFO_BASE( pfnConnect )
{
} // TS_ACCEPTEX_CONNECTION_INFO::TS_ACCEPTEX_CONNECTION_INFO()
TS_ACCEPTEX_CONNECTION_INFO::~TS_ACCEPTEX_CONNECTION_INFO( VOID)
/*++
Destructor function for TS_CONNECTION_INFO object,
Frees all dynamically allocated memory and cleans up member objects.
--*/
{
Lock();
IF_DEBUG( DLL_CONNECTION) {
DBGPRINTF( ( DBG_CONTEXT,
"Deleting ConnectionObject %08x.\n",
this));
}
UnLock();
} // TS_ACCEPTEX_CONNECTION_INFO::~TS_ACCEPTEX_CONNECTION_INFO()
BOOL
TS_ACCEPTEX_CONNECTION_INFO::StartConnectionPump(
IN PFN_CONNECT_CALLBACK pfnConnect,
IN ATQ_COMPLETION pfnConnectEx,
IN ATQ_COMPLETION pfnIOCompletion,
IN DWORD cbAcceptExReceiveBuffer,
IN const CHAR * pszRegParamKey
)
/*++
Start a connection Thread and makes it loop waiting for connections.
Arguments:
pfnConnect pointer to callback function for new connections.
pfnConnectEx pointer to AcceptEx Connection establishment callback
pfnIOCompletion pointerto IO completion call back function.
cbAcceptexReceiveBuffer
count of bytes of buffer to be used for initial receive
(If 0 no receive buffer isallocated).
pszRegParamKey - Registry parameter key of the service the connection
pump is running for
Returns:
TRUE on successfully spawning off a new thread.
FALSE on failure.
--*/
{
HKEY hkey;
DWORD nAcceptExOutstanding = INETA_DEF_ACCEPTEX_OUTSTANDING;
DWORD nAcceptExTimeout = INETA_DEF_ACCEPTEX_TIMEOUT;
if ( pfnConnectEx != NULL) {
//
// Override the previous NULL value.
//
DBG_ASSERT( m_pfnConnect == NULL);
m_pfnConnect = (PFN_CONNECT_CALLBACK) pfnConnectEx; // BUGBUG - bad cast
}
DBG_ASSERT( m_pfnConnect != NULL);
//
// Get the acceptex backlog and the initial acceptex timeout for this
// service
//
if ( !RegOpenKeyEx( HKEY_LOCAL_MACHINE,
pszRegParamKey,
0,
KEY_READ,
&hkey )) {
nAcceptExOutstanding = ReadRegistryDword( hkey,
INETA_ACCEPTEX_OUTSTANDING,
nAcceptExOutstanding );
nAcceptExTimeout = ReadRegistryDword( hkey,
INETA_ACCEPTEX_TIMEOUT,
nAcceptExTimeout );
RegCloseKey( hkey );
}
//
// Create a bunch of listen socket handles and add them to ATQ
//
#ifndef CHICAGO
return ( AtqAddAcceptExSockets( m_listenSocket,
pfnConnectEx,
pfnIOCompletion,
nAcceptExOutstanding,
cbAcceptExReceiveBuffer,
nAcceptExTimeout )
);
#else
return TRUE;
#endif
} // TS_CONNECTION_INFO::StartConnectionPump()
BOOL
TS_ACCEPTEX_CONNECTION_INFO::StopConnectionPump( VOID)
/*++
Stop the connection thread synchronously and
free all associated CPU resources.
Returns:
TRUE on success and FALSE on failure.
if FALSE, the TS_CONNECTION_INFO object should not be deleted.
--*/
{
BOOL fReturn = TRUE;
if ( m_listenSocket != INVALID_SOCKET )
{
#ifndef CHICAGO
DBG_REQUIRE( AtqRemoveAcceptExSockets( m_listenSocket ));
#endif
DBG_REQUIRE( CloseListenSocket() == NO_ERROR );
}
return ( fReturn);
} // TS_CONNECTION_INFO::StopConnectionPump()
# if DBG
VOID
TS_ACCEPTEX_CONNECTION_INFO::Print( VOID) const
{
DBGPRINTF( ( DBG_CONTEXT,
"Printing TS_ACCEPTEX_CONNECTION_INFO object (%08x)\n"
" CallBackFunction = %08x;"
" ListenSocket = %d;\n",
this,
m_pfnConnect,
m_listenSocket ));
return;
} // TS_CONNECTION_INFO::Print()
# endif // DBG
/************************ End of File ***********************/