mirror of https://github.com/lianthony/NT4.0
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.
834 lines
22 KiB
834 lines
22 KiB
/*++
|
|
Copyright (c) 1994 Microsoft Corporation
|
|
|
|
Module Name:
|
|
main.cxx
|
|
|
|
Abstract:
|
|
Defines the functions for entry point into Gopher Server DLL.
|
|
The Gopher server DLL lives as part of the TCPSVCS.exe. There
|
|
are two ways to run this server:
|
|
1. From TCPSVS using call to Gopher Server service
|
|
2. As a stand along Gopher server application
|
|
|
|
Author:
|
|
Murali R. Krishnan (MuraliK) 27-Sept-1994
|
|
|
|
Project:
|
|
Gopher Server DLL
|
|
|
|
Revisions:
|
|
MuraliK 15-March-1995 Used functions from tcpsvcs.dll for
|
|
sockets, ipc, and connections init and cleanup
|
|
Also brought in the code for GdNewConnection
|
|
from old connect.cxx
|
|
MuraliK 5-Oct-1995 Support for AcceptEx added
|
|
|
|
--*/
|
|
|
|
|
|
/************************************************************
|
|
* Include Headers
|
|
************************************************************/
|
|
|
|
# include "gdpriv.h"
|
|
# include "gdglobal.hxx"
|
|
|
|
# include "grequest.hxx"
|
|
|
|
//
|
|
// RPC related includes
|
|
//
|
|
extern "C" {
|
|
|
|
# include "inetinfo.h"
|
|
# include "gd_srv.h"
|
|
|
|
};
|
|
|
|
DEFINE_TSVC_INFO_INTERFACE( );
|
|
|
|
DECLARE_DEBUG_PRINTS_OBJECT();
|
|
DECLARE_DEBUG_VARIABLE();
|
|
|
|
/**************************************************
|
|
* Private Prototypes
|
|
**************************************************/
|
|
|
|
static DWORD InitializeService( LPVOID pContext);
|
|
|
|
static DWORD CleanupService( LPVOID pContext);
|
|
|
|
|
|
//
|
|
// GdNewConnection()
|
|
// o Call back function for new connections
|
|
// used by the connection thread.
|
|
//
|
|
|
|
static VOID
|
|
GdNewConnection(
|
|
IN SOCKET sNew,
|
|
IN LPSOCKADDR_IN psockAddr);
|
|
|
|
static VOID
|
|
GdNewConnectionEx(
|
|
IN VOID * patqContext,
|
|
IN DWORD cbWritten,
|
|
IN DWORD dwError,
|
|
IN OVERLAPPED * lpo );
|
|
|
|
|
|
extern VOID
|
|
GdAtqCompletion(
|
|
IN PVOID pContext,
|
|
IN DWORD cbWritten,
|
|
IN DWORD dwCompletionStatus,
|
|
IN OVERLAPPED * lpo );
|
|
|
|
/************************************************************
|
|
* Functions
|
|
************************************************************/
|
|
|
|
|
|
VOID
|
|
ServiceEntry( IN DWORD cArgs,
|
|
IN LPWSTR pArgs[],
|
|
IN PTCPSVCS_GLOBAL_DATA pTcpsvcsGlobalData )
|
|
/*++
|
|
SYNOPSIS: This is the "real" entrypoint for the service. When
|
|
the Service Controller dispatcher is requested to
|
|
start a service, it creates a thread that will begin
|
|
executing this routine.
|
|
|
|
ENTRY: cArgs - Number of command line arguments to this service.
|
|
|
|
pArgs - Pointers to the command line arguments.
|
|
|
|
pTcpsvcsGlobalData - Points to global data shared amongst all
|
|
services that live in TCPSVCS.EXE.
|
|
|
|
EXIT: Does not return until service is stopped.
|
|
|
|
--*/
|
|
{
|
|
DWORD err = NO_ERROR;
|
|
|
|
CREATE_DEBUG_PRINT_OBJECT( GOPHERD_MODULE_NAME);
|
|
SET_DEBUG_FLAGS( 0);
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
"Entered Service Entry Function for Service %s\n",
|
|
GOPHERD_SERVICE_NAME));
|
|
//
|
|
// Create a new TsvcInfo object for this service
|
|
//
|
|
|
|
g_pTsvcInfo = new TSVC_INFO(
|
|
GOPHERD_SERVICE_NAME, // service name
|
|
GOPHERD_MODULE_NAME, // module name
|
|
GOPHERD_PARAMETERS_KEY, // registry key for params
|
|
GOPHERD_ANONYMOUS_SECRET_W, // anon secret for svc.
|
|
GOPHERD_ROOT_SECRET_W, // secret for virtual roots
|
|
INET_GOPHER, // Id for the service
|
|
InitializeService, // initialization function
|
|
CleanupService // cleanup function
|
|
);
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT, " Created New TSVC_INFO %08x\n", g_pTsvcInfo));
|
|
|
|
//
|
|
// Memory Error is serious here. There is no way to report failure
|
|
// to the invoker of this function, since the DLL function returns VOID !
|
|
// We just use Event log to report this error.
|
|
//
|
|
|
|
if ( g_pTsvcInfo != NULL && g_pTsvcInfo->IsValid()) {
|
|
|
|
// save global ptr.
|
|
g_pTsvcInfo->SetTcpsvcsGlobalData( pTcpsvcsGlobalData);
|
|
|
|
//
|
|
// This is a blocking call.
|
|
// Returns only on errors or service termination.
|
|
//
|
|
|
|
err = g_pTsvcInfo->StartServiceOperation( SERVICE_CTRL_HANDLER());
|
|
|
|
// we are done with the service. free the TSVC_INFO object.
|
|
delete g_pTsvcInfo;
|
|
|
|
} else {
|
|
|
|
const char * apsz[1];
|
|
apsz[0] = GOPHERD_SERVICE_NAME;
|
|
err = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
GopherdLogEvent( GD_EVENT_OUT_OF_MEMORY,
|
|
1,
|
|
apsz,
|
|
err);
|
|
|
|
if ( g_pTsvcInfo != NULL) {
|
|
|
|
delete g_pTsvcInfo;
|
|
g_pTsvcInfo = NULL;
|
|
}
|
|
}
|
|
|
|
if ( err != NO_ERROR) {
|
|
|
|
//
|
|
// Error should have been already logged to event logger.
|
|
// Do nothing here.
|
|
//
|
|
|
|
DEBUG_IF( SERVICE_CTRL, {
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
" Error in running the service %s. Error = %d\n",
|
|
g_pTsvcInfo->QueryServiceName(),
|
|
err));
|
|
});
|
|
}
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT, "Leaving GopherServer::ServiceEntry()"
|
|
" Error = %u\n", err));
|
|
|
|
DELETE_DEBUG_PRINT_OBJECT();
|
|
|
|
return;
|
|
} // ServiceEntry()
|
|
|
|
|
|
|
|
|
|
|
|
static DWORD
|
|
GdSetLocalHostName(VOID)
|
|
/*++
|
|
This function should be called immediately after the Sockets are
|
|
initialized and before connection listen socket is established.
|
|
|
|
This function queries the host name from WinSock and sets the same in
|
|
Gopher server configuration data, to be used by all the gopher menu
|
|
queries.
|
|
|
|
Returns:
|
|
Win32 error codes -- NO_ERROR on success.
|
|
|
|
--*/
|
|
{
|
|
DWORD err;
|
|
CHAR szHost[MAXGETHOSTSTRUCT];
|
|
|
|
//
|
|
// Determine the local host name.
|
|
//
|
|
|
|
if( gethostname( szHost, sizeof(szHost) ) >= 0 ) {
|
|
|
|
err = (g_pGserverConfig->SetLocalHostName( szHost)
|
|
? NO_ERROR: GetLastError());
|
|
|
|
} else {
|
|
|
|
err = WSAGetLastError();
|
|
}
|
|
|
|
|
|
return ( err);
|
|
|
|
} // GdSetLocalHostName()
|
|
|
|
|
|
|
|
static DWORD
|
|
InitializeService( LPVOID pContext)
|
|
/*++
|
|
Description:
|
|
Initializes the various Gopher service specific components.
|
|
|
|
Arguments:
|
|
pContext: pointer to the TSVC_INFO object for this service.
|
|
|
|
Returns:
|
|
NO_ERROR on success. Otherwise Win32 error code
|
|
|
|
--*/
|
|
{
|
|
DWORD err = NO_ERROR;
|
|
BOOL fReturn;
|
|
LPTSVC_INFO ptsi = (LPTSVC_INFO ) pContext;
|
|
|
|
ASSERT( ptsi == g_pTsvcInfo);
|
|
|
|
DEBUG_IF( SERVICE_CTRL, {
|
|
DBGPRINTF( ( DBG_CONTEXT, "initializing service\n" ));
|
|
});
|
|
|
|
//
|
|
// Initialize various components. The ordering of the
|
|
// components is somewhat limited. Globals should be
|
|
// initialized first, then the sockets library,
|
|
// then InitializeIpc, Discovery and then Connections.
|
|
// The InitializeConnections starts a listening on
|
|
// the port for new connections.
|
|
//
|
|
if(
|
|
( err = InitializeGlobals()) != NO_ERROR ||
|
|
( err = ptsi->InitializeSockets()) != NO_ERROR ||
|
|
( err = GdSetLocalHostName()) != NO_ERROR ||
|
|
( err = ptsi->InitializeIpc(gopherd_ServerIfHandle)) != NO_ERROR ||
|
|
( err = ptsi->InitializeDiscovery( NULL)) != NO_ERROR ||
|
|
( !ptsi->InitializeConnections( &GdNewConnection,
|
|
&GdNewConnectionEx,
|
|
&GdAtqCompletion,
|
|
0,
|
|
DEF_ACCEPTEX_RECV_BUFFER_SIZE,
|
|
"gopher" ))
|
|
) {
|
|
|
|
if ( err == NO_ERROR) {
|
|
|
|
err = GetLastError();
|
|
}
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"InitializeService() failed. Error = %lu\n",
|
|
err ));
|
|
|
|
if( err == ERROR_SERVICE_SPECIFIC_ERROR ) {
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
" service specific error %lu (%08lX)\n",
|
|
ptsi->QueryServiceSpecificExitCode(),
|
|
ptsi->QueryServiceSpecificExitCode()));
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Success!
|
|
//
|
|
|
|
DEBUG_IF( SERVICE_CTRL, {
|
|
DBGPRINTF( ( DBG_CONTEXT, "Gopher service initialized\n" ));
|
|
});
|
|
|
|
}
|
|
|
|
return ( err);
|
|
} // InitializeService()
|
|
|
|
|
|
|
|
|
|
|
|
static DWORD
|
|
CleanupService( LPVOID pContext)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Cleanups the various Gopher Server components
|
|
|
|
Arguments:
|
|
|
|
pContext: pointer to the TSVC_INFO object for this service.
|
|
|
|
Returns:
|
|
NO_ERROR on success, then all components have been
|
|
successfully Cleanupd.
|
|
WIN32 error code on errors
|
|
|
|
--*/
|
|
{
|
|
DWORD err = NO_ERROR;
|
|
LPTSVC_INFO ptsi = (LPTSVC_INFO ) pContext;
|
|
|
|
ASSERT( ptsi == g_pTsvcInfo);
|
|
|
|
DEBUG_IF( SERVICE_CTRL, {
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
"CleanupService() called for Gopher Server\n" ));
|
|
});
|
|
|
|
|
|
//
|
|
// Components should be Cleanupd in reverse order of initialization
|
|
//
|
|
|
|
if ( !ptsi->CleanupConnections()) {
|
|
|
|
err = GetLastError();
|
|
DBGPRINTF( ( DBG_CONTEXT, " CleanupConnections() failed. Error = %u\n",
|
|
err));
|
|
}
|
|
|
|
g_pGserverConfig->DisconnectAllConnections();
|
|
|
|
if (
|
|
( err != NO_ERROR) ||
|
|
( err = ptsi->TerminateDiscovery()) != NO_ERROR ||
|
|
( err = ptsi->CleanupIpc( gopherd_ServerIfHandle)) != NO_ERROR ||
|
|
( err = ptsi->CleanupSockets()) != NO_ERROR ||
|
|
( err = CleanupGlobals()) != NO_ERROR
|
|
) {
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
"CleanupService() : cannot Cleanup, error %lu\n",
|
|
err ));
|
|
|
|
if( err == ERROR_SERVICE_SPECIFIC_ERROR ) {
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
" service specific error %lu (%08lX)\n",
|
|
ptsi->QueryServiceSpecificExitCode(),
|
|
ptsi->QueryServiceSpecificExitCode()));
|
|
}
|
|
|
|
} else {
|
|
|
|
DEBUG_IF( SERVICE_CTRL, {
|
|
DBGPRINTF( ( DBG_CONTEXT, " service %s Cleaned up\n",
|
|
ptsi->QueryServiceName()));
|
|
});
|
|
|
|
#ifdef ALL_SORT_OF_LOGGING
|
|
//
|
|
// Log to event log that this service has stopped.
|
|
//
|
|
|
|
const char * apsz[1];
|
|
apsz[0] = ptsi->QueryServiceName();
|
|
|
|
GopherdLogEvent( GD_EVENT_SERVICE_STOPPED,
|
|
1,
|
|
apsz,
|
|
0);
|
|
|
|
#endif // ALL_SORT_OF_LOGGING
|
|
|
|
}
|
|
|
|
return ( err);
|
|
} // CleanupService()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static BOOL
|
|
ProcessNewClient(
|
|
IN SOCKET sNew,
|
|
IN SOCKADDR * psockAddrRemote,
|
|
IN SOCKADDR * psockAddrLocal = NULL,
|
|
IN PATQ_CONTEXT patqContext = NULL,
|
|
IN PVOID pvBuff = NULL,
|
|
IN DWORD cbWritten = 0,
|
|
IN LPBOOL lpfAtqFreed = NULL
|
|
)
|
|
{
|
|
PGOPHER_REQUEST pGopherRequest = NULL;
|
|
PICLIENT_CONNECTION pcc = NULL;
|
|
DWORD err = NO_ERROR;
|
|
BOOL fGranted = FALSE;
|
|
BOOL fReturn = FALSE;
|
|
|
|
DBG_CODE( CHAR pchAddr[32];);
|
|
DBG_CODE( InetNtoa( ((SOCKADDR_IN *) psockAddrRemote)->sin_addr, pchAddr));
|
|
|
|
//
|
|
// Set the status if ATQ Context is disconnected or not.
|
|
//
|
|
if ( lpfAtqFreed != NULL) {
|
|
|
|
*lpfAtqFreed = FALSE;
|
|
}
|
|
|
|
if ( !g_pTsvcInfo->IPAccessCheck((SOCKADDR_IN * )psockAddrRemote,
|
|
&fGranted) ||
|
|
!fGranted) {
|
|
|
|
//
|
|
// Site is not permitted to access this server.
|
|
// Dont establish this connection. Maybe we should send a message. NYI
|
|
//
|
|
|
|
DEBUG_IF( ERROR, {
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
"Aborting connection to %s."
|
|
"Either service stopped or it is not a valid site.\n",
|
|
pchAddr));
|
|
});
|
|
|
|
if ( patqContext != NULL) {
|
|
|
|
// ensure that the socket is shut down (send 2 param as TRUE)
|
|
DBG_REQUIRE( AtqCloseSocket( patqContext, TRUE));
|
|
} else {
|
|
|
|
ShutAndCloseSocket( sNew);
|
|
}
|
|
|
|
return ( FALSE);
|
|
}
|
|
|
|
//
|
|
// Create a new connection object
|
|
//
|
|
pGopherRequest = new GOPHER_REQUEST(sNew,
|
|
(SOCKADDR_IN * ) psockAddrRemote,
|
|
(SOCKADDR_IN * ) psockAddrLocal,
|
|
patqContext,
|
|
pvBuff, cbWritten );
|
|
|
|
pcc = (PICLIENT_CONNECTION ) pGopherRequest; // obtain the base class ptr
|
|
|
|
if ( pcc == NULL) {
|
|
|
|
//
|
|
// Unable to create a new connection object. Report error and shut this.
|
|
//
|
|
|
|
const CHAR * apszSubStrings[1];
|
|
char pchAddr2[32] = "";
|
|
|
|
err = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
InetNtoa( ((SOCKADDR_IN *) psockAddrRemote)->sin_addr, pchAddr2);
|
|
apszSubStrings[0] = pchAddr2;
|
|
GopherdLogEvent( GD_EVENT_CANNOT_CREATE_CLIENT_CONN,
|
|
1,
|
|
apszSubStrings,
|
|
err);
|
|
|
|
DEBUG_IF( ERROR, {
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
"Cannot create new Gopher Request object to %s."
|
|
" Error= %u\n",
|
|
pchAddr,
|
|
err));
|
|
});
|
|
|
|
// Maybe we should send error msg back. NYI.
|
|
g_pstat->IncrementErroredConnections();
|
|
if ( patqContext != NULL) {
|
|
|
|
// ensure that socket is shut down.
|
|
DBG_REQUIRE( AtqCloseSocket( patqContext, TRUE));
|
|
} else {
|
|
|
|
ShutAndCloseSocket( sNew);
|
|
}
|
|
|
|
return (FALSE);
|
|
} // if ( pcc == NULL)
|
|
|
|
//
|
|
// Add this connection to global list of connections for this server
|
|
//
|
|
if ( !g_pGserverConfig->InsertNewConnection( pcc)) {
|
|
|
|
//
|
|
// Unable to insert new connection.
|
|
// The maxConnections may have exceeded.
|
|
// Destroy the client connection object and return.
|
|
// Possibly need to send an error message. NYI
|
|
//
|
|
|
|
DEBUG_IF( ERROR, {
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
" MaxConnections Exceeded. "
|
|
" Connection from %s refused at socket %d\n",
|
|
pchAddr, sNew));
|
|
});
|
|
|
|
// abort the request and disconnect the client
|
|
pGopherRequest->SetErrorCode(GOPHER_SERVER_LOAD_HIGH);
|
|
pcc->DisconnectClient(); // this does deref for initial ref count.
|
|
delete pcc; // virtual destructor will handle this
|
|
|
|
*lpfAtqFreed = TRUE; // since delete pcc will free context.
|
|
// freeing of socket is already taken care of by delete pcc (above).
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
DEBUG_IF( CONNECTION, {
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
" Established a new connection to %s (Socket = %d)\n",
|
|
pchAddr, sNew));
|
|
|
|
});
|
|
|
|
//
|
|
// At this point we have the context for the AcceptExed socket.
|
|
// Set the context in the AtqContext
|
|
//
|
|
|
|
if ( patqContext != NULL) {
|
|
|
|
// Associate the client connection object with this socket
|
|
// handle for future completions.
|
|
AtqContextSetInfo( patqContext, ATQ_INFO_COMPLETION_CONTEXT,
|
|
(DWORD ) pcc);
|
|
}
|
|
|
|
|
|
//
|
|
// Start off processing this new connection.
|
|
// pcc->Process() may start off the async read to get client request.
|
|
// for AcceptEx'ed connection, this will start off processing
|
|
//
|
|
|
|
pcc->Reference();
|
|
if ( !( fReturn = pcc->ProcessClient( 0, NO_ERROR, NULL))) {
|
|
|
|
//
|
|
// We failed to read.
|
|
//
|
|
|
|
DEBUG_IF( ERROR, {
|
|
|
|
DWORD dwError = GetLastError();
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
"Unable to read client request from %s (socket %d)."
|
|
" Aborting Connection. Error = %u\n",
|
|
pchAddr, sNew, dwError));
|
|
SetLastError( dwError);
|
|
});
|
|
|
|
// DBG_CODE( pGopherRequest->Print());
|
|
|
|
// abort the request and disconnect the client
|
|
pGopherRequest->SetErrorCode(GOPHER_SERVER_ERROR);
|
|
pcc->DisconnectClient(); // disconnects if already not.
|
|
GOPHERD_ASSERT(pcc->QueryReferenceCount() == 1);
|
|
}
|
|
|
|
if ( !pcc->DeReference()) {
|
|
|
|
//
|
|
// We are done with this connection. Remove it from current list.
|
|
//
|
|
|
|
g_pGserverConfig->RemoveConnection( pcc);
|
|
delete pcc;
|
|
|
|
*lpfAtqFreed = TRUE; // since delete pcc will free context.
|
|
// freeing of socket is already taken care of by delete pcc
|
|
}
|
|
|
|
return (fReturn);
|
|
|
|
} // ProcessNewClient()
|
|
|
|
|
|
|
|
static VOID
|
|
GdNewConnection( IN SOCKET sNew, IN LPSOCKADDR_IN psockAddrRemote)
|
|
/*++
|
|
Description:
|
|
|
|
Callback function for new connections.
|
|
This function verifies if this is a valid connection
|
|
( maybe using IP level authentication)
|
|
and creates a new ICLIENT_CONNECTION object
|
|
( here it is a GOPHER_REQUEST object, which is derived from
|
|
ICLIENT_CONNECTION )
|
|
|
|
The ICLIENT_CONNECTION object is added to list of active connections.
|
|
If the max number of connections permitted is exceeded,
|
|
the client connection object is destroyed and
|
|
connection rejected.
|
|
|
|
|
|
Arguments:
|
|
|
|
sNew: new connection socket
|
|
|
|
psockAddrRemote: pointer to remote client's address
|
|
|
|
Returns:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
BOOL fGranted = FALSE;
|
|
BOOL fProcessed = FALSE;
|
|
DWORD err = NO_ERROR;
|
|
|
|
ASSERT( sNew != INVALID_SOCKET && psockAddrRemote != NULL);
|
|
|
|
g_pstat->IncrementConnectionAttempts(); // a new connection is attempted
|
|
|
|
fGranted = ( g_pTsvcInfo->QueryCurrentServiceState() == SERVICE_RUNNING);
|
|
|
|
if ( fGranted) {
|
|
|
|
IF_DEBUG( CONNECTION ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
" New connection. Socket=%08x, psockAddr=%08x\n",
|
|
sNew, psockAddrRemote));
|
|
}
|
|
|
|
fProcessed = ProcessNewClient( sNew,
|
|
(SOCKADDR * ) psockAddrRemote
|
|
);
|
|
} else {
|
|
|
|
DBG_ASSERT( fProcessed == FALSE);
|
|
}
|
|
|
|
|
|
if ( !fProcessed) {
|
|
|
|
//
|
|
// We failed to process this connection. Free up resources properly
|
|
//
|
|
|
|
g_pstat->IncrementAbortedConnections();
|
|
}
|
|
|
|
return;
|
|
} // GdNewConnection()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static VOID
|
|
GdNewConnectionEx(
|
|
IN VOID * patqContext,
|
|
IN DWORD cbWritten,
|
|
IN DWORD dwError,
|
|
IN OVERLAPPED * lpo )
|
|
/*++
|
|
Description:
|
|
|
|
Callback function for new connections when using AcceptEx.
|
|
This function verifies if this is a valid connection
|
|
( maybe using IP level authentication)
|
|
and creates a new ICLIENT_CONNECTION object
|
|
( here it is a GOPHER_REQUEST object, which is derived from
|
|
ICLIENT_CONNECTION )
|
|
|
|
The ICLIENT_CONNECTION object is added to list of active connections.
|
|
If the max number of connections permitted is exceeded,
|
|
the client connection object is destroyed and
|
|
connection rejected.
|
|
|
|
|
|
Arguments:
|
|
|
|
patqContext: pointer to ATQ context for the IO operation
|
|
cbWritten: count of bytes available from first read operation
|
|
dwError: error if any from initial operation
|
|
lpo !NULL if this completion was from an IO
|
|
|
|
Returns:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
BOOL fGranted = FALSE;
|
|
DWORD err = NO_ERROR;
|
|
|
|
SOCKADDR * psockAddrLocal;
|
|
SOCKADDR * psockAddrRemote;
|
|
SOCKET sNew;
|
|
PVOID pvBuff;
|
|
BOOL fProcessed = FALSE;
|
|
BOOL fAtqFreed = FALSE;
|
|
|
|
|
|
if ( dwError != NO_ERROR || !lpo ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "GdNewConnectionEx() completion failed."
|
|
" Error = %d. AtqContext=%08x\n",
|
|
dwError, patqContext));
|
|
|
|
// For now free up the resources.
|
|
AtqCloseSocket( (PATQ_CONTEXT ) patqContext, FALSE);
|
|
AtqFreeContext( (PATQ_CONTEXT ) patqContext, TRUE );
|
|
return;
|
|
}
|
|
|
|
g_pstat->IncrementConnectionAttempts(); // a new connection is attempted
|
|
|
|
|
|
DBG_ASSERT( patqContext != NULL);
|
|
|
|
|
|
fGranted = ( g_pTsvcInfo->QueryCurrentServiceState() == SERVICE_RUNNING);
|
|
|
|
if ( fGranted) {
|
|
|
|
AtqGetAcceptExAddrs( (PATQ_CONTEXT ) patqContext,
|
|
&sNew,
|
|
&pvBuff,
|
|
&psockAddrLocal,
|
|
&psockAddrRemote);
|
|
|
|
IF_DEBUG( CONNECTION ) {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
" New connection. AtqCont=%08x, buff=%08x, cb=%d\n",
|
|
patqContext, pvBuff, cbWritten));
|
|
}
|
|
|
|
//
|
|
// Set the timeout for all future IOs
|
|
//
|
|
|
|
AtqContextSetInfo( (PATQ_CONTEXT) patqContext,
|
|
ATQ_INFO_TIMEOUT,
|
|
g_pTsvcInfo->QueryConnectionTimeout() );
|
|
|
|
fProcessed = ProcessNewClient( sNew,
|
|
psockAddrRemote,
|
|
psockAddrLocal,
|
|
(PATQ_CONTEXT ) patqContext,
|
|
pvBuff,
|
|
cbWritten,
|
|
&fAtqFreed);
|
|
} else {
|
|
|
|
DBG_ASSERT( fProcessed == FALSE);
|
|
}
|
|
|
|
if ( !fProcessed) {
|
|
|
|
//
|
|
// We failed to process this connection. Free up resources properly
|
|
//
|
|
|
|
g_pstat->IncrementAbortedConnections();
|
|
|
|
if ( !fAtqFreed) {
|
|
|
|
DBG_REQUIRE( AtqCloseSocket( (PATQ_CONTEXT )patqContext, FALSE));
|
|
AtqFreeContext( (PATQ_CONTEXT ) patqContext, TRUE );
|
|
}
|
|
}
|
|
|
|
return;
|
|
} // GdNewConnection()
|
|
|
|
|
|
/**************************** End of File *************************/
|
|
|
|
|