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.
1038 lines
25 KiB
1038 lines
25 KiB
/*++
|
|
|
|
Copyright (c) 1994 Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
iclient.cxx
|
|
|
|
Abstract:
|
|
|
|
This module defines the functions for base class of connections
|
|
for Internet Services ( class ICLIENT_CONNECTION)
|
|
|
|
Author:
|
|
|
|
Murali R. Krishnan ( MuraliK ) 27-Sept-1994
|
|
|
|
Project:
|
|
|
|
Gopher Server DLL
|
|
|
|
Functions Exported:
|
|
|
|
ICLIENT_CONNECTION::Initialize()
|
|
ICLIENT_CONNECTION::Cleanup()
|
|
ICLIENT_CONNECTION::~ICLIENT_CONNECTION()
|
|
BOOL ICLIENT_CONNECTION::ProcessClient( IN DWORD cbWritten,
|
|
IN DWORD dwCompletionStatus,
|
|
IN BOOL fIOCompletion)
|
|
VOID ICLIENT_CONNECTION::DisconnectClient( IN DWORD ErrorReponse)
|
|
|
|
BOOL ICLIENT_CONNECTION::StartupSession( VOID)
|
|
BOOL ICLIENT_CONNECTION::ReceiveRequest(
|
|
OUT LPBOOL pfFullRequestRecd)
|
|
|
|
BOOL ICLIENT_CONNECTION::ReadFile( OUT LPVOID pvBuffer,
|
|
IN DWORD dwSize)
|
|
BOOL ICLIENT_CONNECTION::WriteFile( IN LPVOID pvBuffer,
|
|
IN DWORD dwSize)
|
|
BOOL ICLIENT_CONNECTION::TransmitFile( IN HANDLE hFile,
|
|
IN DWORD cbToSend)
|
|
|
|
Revision History:
|
|
|
|
MuraliK 13-March-1995 Added Support for Persistent connections
|
|
|
|
--*/
|
|
|
|
|
|
/************************************************************
|
|
* Include Headers
|
|
************************************************************/
|
|
|
|
//
|
|
// include all NT standard header files before including iclient.hxx
|
|
// Std NT headers come from gdpriv.h
|
|
//
|
|
# include "gdpriv.h"
|
|
|
|
# include "iclient.hxx"
|
|
|
|
|
|
|
|
/************************************************************
|
|
* Functions
|
|
************************************************************/
|
|
|
|
|
|
|
|
ICLIENT_CONNECTION::Initialize(
|
|
IN SOCKET sClient,
|
|
IN const SOCKADDR_IN * psockAddrRemote,
|
|
IN PFN_ATQ_COMPLETION pfnAtqCompletion,
|
|
IN const SOCKADDR_IN * psockAddrLocal /* Default = NULL */,
|
|
IN PATQ_CONTEXT pAtqContext /* Default = NULL */,
|
|
IN PVOID pvInitialRequest/* Default = NULL */,
|
|
IN DWORD cbInitialData /* Default = 0 */
|
|
)
|
|
/*++
|
|
|
|
ICLIENT_CONNECTION::Initialize()
|
|
|
|
Constructor for ICLIENT_CONNECTION object.
|
|
Initializes the fields of the client connection.
|
|
|
|
Arguments:
|
|
|
|
sClient socket for communicating with client
|
|
|
|
psockAddrRemote pointer to address of the remote client
|
|
( the value should be copied).
|
|
pfnAtqCompletion pointer to call back function for ATQ module on
|
|
completion of a request.
|
|
psockAddrLocal pointer to address for the local card through
|
|
which the client came in.
|
|
pAtqContext pointer to ATQ Context used for AcceptEx'ed conn.
|
|
pvInitialRequest pointer to void buffer containing the initial request
|
|
cbInitialData count of bytes of data read initially.
|
|
|
|
Note:
|
|
TO keep the number of connected users <= Max connections specified.
|
|
Make sure to add this object to global list of connections,
|
|
after creating it.
|
|
If there is a failure to add to global list, delete this object.
|
|
|
|
--*/
|
|
{
|
|
DWORD dwError = NO_ERROR;
|
|
|
|
StartProcessingTimer(); // take a snap shot of the current time.
|
|
|
|
m_cReferences = ( 1);
|
|
m_sClient = ( sClient);
|
|
m_pAtqContext = pAtqContext;
|
|
m_pfnAtqCompletion = pfnAtqCompletion;
|
|
m_fPersistentConnection = ( FALSE);
|
|
m_state = ( ICLIENT_CONNECTION::CcsStartup);
|
|
m_cbReceived = 0;
|
|
|
|
m_pvInitial = pvInitialRequest;
|
|
m_cbInitial = cbInitialData;
|
|
|
|
//
|
|
// Validate this object by storing valid signature
|
|
//
|
|
StoreValidSignature();
|
|
|
|
DBG_ASSERT( m_pfnAtqCompletion != NULL);
|
|
DBG_ASSERT( psockAddrRemote != NULL);
|
|
|
|
m_saClient = *psockAddrRemote;
|
|
|
|
//
|
|
// Obtain the socket addresses for the socket
|
|
//
|
|
m_pchRemoteHostName[0] = m_pchLocalHostName[0] = '\0';
|
|
|
|
// InetNtoa() wants just 16 byte buffer.
|
|
DBG_ASSERT( 16 <= MAX_HOST_NAME_LEN);
|
|
dwError = InetNtoa( psockAddrRemote->sin_addr, m_pchRemoteHostName);
|
|
|
|
DBG_ASSERT( dwError == NO_ERROR); // since we had given sufficient buffer
|
|
|
|
if ( psockAddrLocal != NULL) {
|
|
|
|
dwError = InetNtoa( psockAddrLocal->sin_addr, m_pchLocalHostName);
|
|
|
|
} else {
|
|
|
|
SOCKADDR_IN sockAddr;
|
|
int cbAddr = sizeof( sockAddr);
|
|
|
|
if ( getsockname( sClient,
|
|
(struct sockaddr *) &sockAddr,
|
|
&cbAddr )
|
|
) {
|
|
|
|
dwError = InetNtoa( sockAddr.sin_addr, m_pchLocalHostName );
|
|
}
|
|
}
|
|
|
|
DBG_ASSERT( dwError == NO_ERROR); // since we had given sufficient buffer
|
|
|
|
DBG_CODE(
|
|
if ( dwError != NO_ERROR) {
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
"Obtaining Local Host Name Failed. Error = %u\n",
|
|
dwError)
|
|
);
|
|
SetLastError( dwError);
|
|
}
|
|
);
|
|
|
|
DEBUG_IF( CLIENT, {
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
" Constructed ICLIENT_CONNECTION object ( %08x)."
|
|
" Socket (%d), Host (%s), Ref ( %d).\n",
|
|
this,
|
|
sClient,
|
|
QueryHostName(),
|
|
m_cReferences));
|
|
});
|
|
|
|
return ( TRUE);
|
|
|
|
} // ICLIENT_CONNECTION::Initialize()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
ICLIENT_CONNECTION::Cleanup( VOID)
|
|
/*++
|
|
ICLIENT_CONNECTION::Cleanup()
|
|
|
|
Destructor function for client connection object.
|
|
Checks and frees the AtqContext.
|
|
|
|
Note:
|
|
If enlisted in the global list of connections,
|
|
ensure that this object is freed from that list before deletion.
|
|
|
|
--*/
|
|
{
|
|
|
|
ASSERT( QueryReferenceCount() == 0);
|
|
|
|
DEBUG_IF( CLIENT, {
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
" Destructing Connection Object( %08x). Ref ( %d) \n",
|
|
this,
|
|
m_cReferences));
|
|
});
|
|
|
|
//
|
|
// Check and free the AtqContext object
|
|
//
|
|
if ( QueryAtqContext() != NULL) {
|
|
|
|
AtqFreeContext( QueryAtqContext(), TRUE );
|
|
m_pAtqContext = NULL;
|
|
}
|
|
|
|
// Seems that socket is closed by ATQ by post AcceptEx fix in Atq.
|
|
m_sClient = INVALID_SOCKET;
|
|
|
|
//
|
|
// Invalidate the signature. since this connection is thrashed.
|
|
//
|
|
StoreInvalidSignature();
|
|
|
|
return;
|
|
} // ICLIENT_CONNECTION::Cleanup()
|
|
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
ICLIENT_CONNECTION::ProcessClient(
|
|
IN DWORD cbWritten,
|
|
IN DWORD dwCompletionStatus,
|
|
IN BOOL fIOCompletion)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Main function for this class. Processes the connection based
|
|
on current state of the connection.
|
|
It may invoke or be invoked by ATQ functions.
|
|
|
|
Arguments:
|
|
|
|
cbWritten count of bytes written
|
|
|
|
dwCompletionStatus Error Code for last IO operation
|
|
|
|
fIOCompletion TRUE if this was an IO completion
|
|
|
|
|
|
Returns:
|
|
|
|
TRUE when processing is incomplete.
|
|
FALSE when the connection is completely processed and this
|
|
object may be deleted.
|
|
|
|
--*/
|
|
{
|
|
BOOL fReturn = TRUE;
|
|
|
|
DEBUG_IF( CLIENT, {
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
"[Time = %8d] Entering Connection(%08x)::Process("
|
|
" cbWritten( %d), Status( %d), fIOCompletion(%s)). "
|
|
" State( %d) Ref( %d)\n",
|
|
GetTickCount(), this, cbWritten, dwCompletionStatus,
|
|
fIOCompletion ? "TRUE" : "FALSE",
|
|
QueryState(), QueryReferenceCount()));
|
|
});
|
|
|
|
if ( fIOCompletion ) {
|
|
|
|
//
|
|
// IO request has completed for this client connection.
|
|
// Reduce reference count
|
|
//
|
|
|
|
GOPHERD_REQUIRE( DeReference() > 0);
|
|
}
|
|
|
|
//
|
|
// If IO request error and we are not already cleaning up,
|
|
// start cleaning up and abort the connection.
|
|
//
|
|
|
|
if ( dwCompletionStatus != NO_ERROR && !IsCleaningUp()) {
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
"[Time:%08d] Error in CONNECTION(%08x):"
|
|
" Process( cbWritten(%d), Status(%d), fIOCompletion(%s))\n"
|
|
" from %s, State( %d), Ref( %d)\n",
|
|
GetTickCount(),
|
|
this,
|
|
cbWritten,
|
|
dwCompletionStatus,
|
|
fIOCompletion ? "TRUE" : "FALSE",
|
|
QueryHostName(),
|
|
QueryState(),
|
|
QueryReferenceCount()));
|
|
|
|
DisconnectClient( dwCompletionStatus);
|
|
|
|
return ( TRUE); // since we did a disconnect!
|
|
}
|
|
|
|
//
|
|
// Process the results of last IO based on current state
|
|
//
|
|
|
|
switch ( QueryState()) {
|
|
|
|
case CcsStartup:
|
|
|
|
fReturn = StartupSession( m_pvInitial, m_cbInitial);
|
|
|
|
if ( m_pvInitial == NULL) {
|
|
//
|
|
// No initial data. So stop here and wait for read to complete.
|
|
//
|
|
break;
|
|
}
|
|
cbWritten = m_cbInitial; // reset the value of data to be data read.
|
|
|
|
// Fall through for processing
|
|
|
|
case CcsGettingRequest: {
|
|
|
|
BOOL fFullRequestReceived = FALSE;
|
|
|
|
fReturn = ReceiveRequest( cbWritten, &fFullRequestReceived);
|
|
|
|
if ( !fReturn ||
|
|
!fFullRequestReceived) {
|
|
|
|
//
|
|
// Full request is not received or error in reception
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Full request received. Fall Through for processing request.
|
|
//
|
|
|
|
SetState( CcsProcessingRequest);
|
|
|
|
} // case CcsGettingRequest
|
|
|
|
|
|
case CcsProcessingRequest: {
|
|
|
|
BOOL fFinished = FALSE;
|
|
|
|
fReturn = Process( &fFinished); // invoke virtual method
|
|
|
|
if ( fReturn) {
|
|
|
|
if ( fFinished) {
|
|
|
|
//
|
|
// Some connections may require connections to be kept
|
|
// alive for subsequent requests
|
|
//
|
|
|
|
if ( IsPersistentConnection()) {
|
|
|
|
if (fReturn = EndRequest()) {
|
|
|
|
SetState( CcsStartup); // go back to starting state
|
|
fReturn = StartupSession();
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// We are done processing this request completely or error!
|
|
// We dont want to keep around the connection -> Disconnect.
|
|
// Disconnect automatically calls EndRequest() if need be.
|
|
//
|
|
|
|
DisconnectClient();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if ( !fReturn) {
|
|
|
|
//
|
|
// Error in processing request.
|
|
// We need to send an appropriate error message.
|
|
//
|
|
|
|
DEBUG_IF( CLIENT, {
|
|
DWORD dwError = GetLastError();
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
" Processing request from %s failed. "
|
|
" Error = %d.\n",
|
|
QueryHostName(),
|
|
dwError));
|
|
SetLastError( dwError);
|
|
});
|
|
}
|
|
|
|
break;
|
|
} // case CcsProcessingRequest
|
|
|
|
case CcsDisconnecting:
|
|
|
|
//
|
|
// Someone went into this state just before ShutDown.
|
|
// Perform the remaining actions for disconnection.
|
|
//
|
|
|
|
DisconnectClient();
|
|
break;
|
|
|
|
case CcsShutdown:
|
|
|
|
//
|
|
// No need to do anything. It has been taken care of.
|
|
//
|
|
|
|
break;
|
|
|
|
default:
|
|
fReturn = FALSE;
|
|
DEBUG_IF( CLIENT, {
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
"Error in processing client connection (%s)."
|
|
" UnknownState %d\n",
|
|
QueryHostName(),
|
|
QueryState()));
|
|
});
|
|
|
|
ASSERT( FALSE); // This code should not be reached.
|
|
break;
|
|
|
|
} // switch( GetState())
|
|
|
|
|
|
if ( !fReturn) {
|
|
|
|
//
|
|
// Failure in processing the connection. Disconnect from client.
|
|
//
|
|
|
|
IF_DEBUG( ERROR) {
|
|
|
|
DWORD dwError = GetLastError();
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Connection (%08x)::Process() failed. "
|
|
" State(%d), Ref(%d), Error = %d\n",
|
|
this, QueryState(), QueryReferenceCount(),
|
|
dwError));
|
|
SetLastError( dwError);
|
|
}
|
|
|
|
DisconnectClient( GetLastError());
|
|
}
|
|
|
|
DEBUG_IF( CLIENT, {
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
"[Time = %8d]Leaving Connection( %08x)::Process()."
|
|
" State( %d), Ref( %d)\n",
|
|
GetTickCount(),
|
|
this,
|
|
QueryState(),
|
|
QueryReferenceCount()));
|
|
|
|
});
|
|
|
|
return ( fReturn);
|
|
} // ICLIENT_CONNECTION::ProcessClient()
|
|
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
ICLIENT_CONNECTION::ReadFile(
|
|
IN LPVOID pvBuffer,
|
|
IN DWORD cbSize)
|
|
/*++
|
|
|
|
Reads contents using ATQ into the given buffer.
|
|
( thin wrapper for ATQ call and managing references)
|
|
|
|
Arguments:
|
|
|
|
pvBuffer pointer to buffer where to read in the contents
|
|
|
|
cbSize size of the buffer
|
|
|
|
Returns:
|
|
|
|
TRUE on success and FALSE on a failure.
|
|
|
|
--*/
|
|
{
|
|
BOOL fReturn = TRUE;
|
|
|
|
ASSERT( pvBuffer != NULL && cbSize > 0);
|
|
|
|
Reference();
|
|
|
|
if ( !AtqReadFile( m_pAtqContext, // Atq context
|
|
pvBuffer, // Buffer
|
|
cbSize, // BytesToRead
|
|
NULL )
|
|
|
|
) {
|
|
|
|
GOPHERD_REQUIRE( DeReference() > 0);
|
|
fReturn = FALSE;
|
|
}
|
|
|
|
return ( fReturn);
|
|
} // ICLIENT_CONNECTION::ReadFile()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
ICLIENT_CONNECTION::WriteFile(
|
|
IN LPVOID pvBuffer,
|
|
IN DWORD cbSize)
|
|
/*++
|
|
|
|
Writes contents from given buffer using ATQ.
|
|
( thin wrapper for ATQ call and managing references)
|
|
|
|
Arguments:
|
|
|
|
pvBuffer pointer to buffer containing contents for write
|
|
|
|
cbSize size of the buffer
|
|
|
|
Returns:
|
|
|
|
TRUE on success and FALSE on a failure.
|
|
|
|
--*/
|
|
{
|
|
BOOL fReturn = TRUE;
|
|
|
|
ASSERT( pvBuffer != NULL && cbSize > 0);
|
|
|
|
Reference();
|
|
|
|
if ( !AtqWriteFile( m_pAtqContext, // Atq context
|
|
pvBuffer, // Buffer
|
|
cbSize, // BytesToWrite
|
|
NULL )
|
|
) {
|
|
|
|
GOPHERD_REQUIRE( DeReference() > 0);
|
|
fReturn = FALSE;
|
|
}
|
|
|
|
return ( fReturn);
|
|
} // ICLIENT_CONNECTION::WriteFile()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
ICLIENT_CONNECTION::TransmitFile(
|
|
IN HANDLE hFile,
|
|
IN LARGE_INTEGER & liSize,
|
|
IN LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers)
|
|
/*++
|
|
|
|
Transmits contents of the file ( of specified size)
|
|
using the ATQ and client socket.
|
|
( thin wrapper for ATQ call and managing references)
|
|
|
|
Arguments:
|
|
|
|
hFile handle for file to be transmitted
|
|
|
|
liSize large integer containing the size of file
|
|
|
|
lpTransmitBuffers
|
|
buffers containing the head and tail buffers that
|
|
need to be transmitted along with the file.
|
|
|
|
Returns:
|
|
|
|
TRUE on success and FALSE on a failure.
|
|
|
|
--*/
|
|
{
|
|
BOOL fReturn = TRUE;
|
|
|
|
ASSERT( hFile != INVALID_HANDLE_VALUE);
|
|
ASSERT( liSize.QuadPart > 0);
|
|
|
|
Reference();
|
|
|
|
if ( !AtqTransmitFile( m_pAtqContext, // Atq context
|
|
hFile, // file data comes from
|
|
liSize, // Bytes To Send
|
|
lpTransmitBuffers, // header/tail buffers
|
|
(TF_DISCONNECT | TF_REUSE_SOCKET) // Flags
|
|
)
|
|
) {
|
|
|
|
GOPHERD_REQUIRE( DeReference());
|
|
fReturn = FALSE;
|
|
}
|
|
|
|
return ( fReturn);
|
|
} // ICLIENT_CONNECTION::TransmitFile()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# define GdMax( a, b) ((a) > (b) ? (a) : (b))
|
|
|
|
BOOL
|
|
ICLIENT_CONNECTION::StartupSession( IN PVOID pvInitial,
|
|
IN DWORD cbInitial)
|
|
/*++
|
|
|
|
Starts up a session for new client.
|
|
Adds the client socket to the ATQ completion port and gets an ATQ context.
|
|
Then prepares receive buffer and starts off a receive request from client.
|
|
( Also moves the client connection to CcsGettingRequest state)
|
|
|
|
Parameters:
|
|
pvInitial pointer to void buffer containing the initial data
|
|
cbWritten count of bytes in the buffer
|
|
|
|
Returns:
|
|
|
|
TRUE on success and FALSE if there is any error.
|
|
--*/
|
|
{
|
|
BOOL fReturn = TRUE;
|
|
|
|
|
|
ASSERT( QueryState() == CcsStartup);
|
|
|
|
//
|
|
// Create a new ATQ context for this client, if necessary
|
|
//
|
|
|
|
fReturn = ( ( QueryAtqContext() != NULL) ||
|
|
AddToAtqHandles( (HANDLE ) m_sClient));
|
|
|
|
|
|
if ( !fReturn) {
|
|
DWORD dwError = GetLastError();
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
"Connection( %08x) Unable to add ATQ handle. Error = %u\n",
|
|
this,
|
|
dwError));
|
|
SetLastError( dwError);
|
|
|
|
} else {
|
|
|
|
ASSERT( m_pAtqContext != NULL);
|
|
|
|
//
|
|
// Prepare buffer for receiving client request
|
|
//
|
|
|
|
m_cbReceived = 0; // Number of bytes of request received.
|
|
fReturn = (m_recvBuffer.
|
|
Resize( GdMax( DEFAULT_REQUEST_BUFFER_SIZE, cbInitial + 10))
|
|
);
|
|
|
|
if ( fReturn) {
|
|
|
|
//
|
|
// Move to next state and perform an IO operation.
|
|
//
|
|
|
|
SetState( CcsGettingRequest);
|
|
if ( (fReturn = StartRequest()) ) {
|
|
|
|
if ( pvInitial != NULL) {
|
|
|
|
// already data is available. Copy to local buffer.
|
|
memcpy( m_recvBuffer.QueryPtr(), pvInitial, cbInitial);
|
|
|
|
} else {
|
|
|
|
fReturn = ReadFile( m_recvBuffer.QueryPtr(),
|
|
m_recvBuffer.QuerySize());
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
DWORD dwError = GetLastError();
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
" Connection (%08x). Create Receive Buffer Failed."
|
|
" Error = %u\n",
|
|
this,
|
|
dwError));
|
|
SetLastError(dwError);
|
|
}
|
|
}
|
|
|
|
return ( fReturn);
|
|
} // ICLIENT_CONNECTION::StartupSession()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
ICLIENT_CONNECTION::ReceiveRequest(
|
|
IN DWORD cbWritten,
|
|
OUT LPBOOL pfFullRequestRecvd)
|
|
/*++
|
|
|
|
Receive full Request from the client.
|
|
If the entire request is received,
|
|
*pfFullRequestRecvd will be set to TRUE and
|
|
the request will be parsed.
|
|
|
|
Arguments:
|
|
|
|
cbWritten count of bytes written in last IO operation.
|
|
pfFullRequestRecvd pointer to boolean, which on successful return
|
|
indicates if the full request was received.
|
|
|
|
Returns:
|
|
|
|
TRUE on success and
|
|
FALSE if there is any error ( to abort this connection).
|
|
|
|
--*/
|
|
{
|
|
BOOL fReturn;
|
|
|
|
ASSERT( QueryState() == CcsGettingRequest && pfFullRequestRecvd != NULL);
|
|
|
|
*pfFullRequestRecvd = FALSE;
|
|
|
|
m_cbReceived += cbWritten; // written from last IO operation
|
|
|
|
|
|
//
|
|
// Allow the Request to be parsed to find if complete request has been
|
|
// received as well as any errors in parsing.
|
|
// Call the virtual method to parse the request.
|
|
//
|
|
|
|
*pfFullRequestRecvd = FALSE;
|
|
|
|
//
|
|
// An Implicit Assumption that the request strings will not contain binary
|
|
// Zeros ( ones which terminate string).
|
|
// If there is a null character in the buffer, the parsing machinery
|
|
// will stop at the first null character and will not recognize the
|
|
// request.
|
|
//
|
|
|
|
// Terminate the request
|
|
*((char * ) m_recvBuffer.QueryPtr() + m_cbReceived) = '\0';
|
|
|
|
// call the virtual method for parsing the input.
|
|
fReturn = Parse( QueryRequest(), m_cbReceived, pfFullRequestRecvd);
|
|
|
|
if ( !fReturn) {
|
|
|
|
//
|
|
// Error in parsing the given buffer. Abort this connection.
|
|
//
|
|
|
|
return ( fReturn);
|
|
}
|
|
|
|
if ( *pfFullRequestRecvd) {
|
|
|
|
DEBUG_IF( CLIENT, {
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT, "Request From %s: %s\n",
|
|
QueryHostName(),
|
|
QueryRequest()));
|
|
});
|
|
|
|
//
|
|
// Complete Request received.
|
|
// Logon the user and validate uesr.
|
|
// This part of the code is migrated to Parse() virtual function
|
|
// 03/10/95
|
|
//
|
|
// fReturn = m_request.LogonUser();
|
|
|
|
} else {
|
|
|
|
//
|
|
// Request not complete. Allocate more buffer and continue reading.
|
|
//
|
|
|
|
fReturn = m_recvBuffer.Resize( m_cbReceived +
|
|
DEFAULT_REQUEST_BUFFER_SIZE);
|
|
|
|
if ( !fReturn ||
|
|
!ReadFile(((char *) m_recvBuffer.QueryPtr()) + m_cbReceived,
|
|
m_recvBuffer.QuerySize() - m_cbReceived)) {
|
|
|
|
DWORD dwError = GetLastError();
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
"Connection(%08x). Unable to continue reading."
|
|
" Error = %d\n",
|
|
this,
|
|
dwError));
|
|
SetLastError( dwError);
|
|
fReturn = FALSE;
|
|
}
|
|
}
|
|
|
|
return ( fReturn);
|
|
|
|
} // ICLIENT_CONNECTION::ReceiveRequest()
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
ICLIENT_CONNECTION::DisconnectClient(
|
|
IN DWORD dwErrorCode)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Initiates a disconnect operation for current connection.
|
|
If already shutdown, this function returns doing nothing.
|
|
Optionally if there is any error message to be sent, they may be sent
|
|
by the REQUEST object. But the disconnection occurs immediately; Hence
|
|
the REQUEST object should send synchronous error messages.
|
|
|
|
Arguments:
|
|
|
|
dwErrorCode
|
|
error code for server errors if any ( Win 32 error code)
|
|
If dwErrorCode != NO_ERROR, then there is a system level error code.
|
|
|
|
Returns:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
BOOL fDisconnectNow = TRUE;
|
|
|
|
switch ( QueryState()) {
|
|
|
|
case CcsShutdown:
|
|
//
|
|
// We are already in disconnect mode. Do nothing now.
|
|
//
|
|
fDisconnectNow = FALSE;
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// We need to enter Disconnection.
|
|
// Check and send any client disconnection request.
|
|
//
|
|
|
|
DEBUG_IF( CLIENT, {
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
"Disconnecting connection ( %08x), with RefCount %d\n",
|
|
this,
|
|
QueryReferenceCount()));
|
|
});
|
|
|
|
SetState( CcsDisconnecting);
|
|
EndRequest( dwErrorCode); // Ignore reslts. Since we will disconnect!
|
|
// Fall Through for disconnection
|
|
|
|
case CcsDisconnecting:
|
|
//
|
|
// We are attempting disconnection.
|
|
//
|
|
break;
|
|
} // switch()
|
|
|
|
if ( fDisconnectNow) {
|
|
// Disconnect immediately
|
|
|
|
SetState( CcsShutdown);
|
|
|
|
if ( QueryAtqContext() != NULL) {
|
|
|
|
//
|
|
// Do a graceful disconnect of no status code was indicated
|
|
//
|
|
|
|
AtqCloseSocket( QueryAtqContext(), (dwErrorCode == NO_ERROR) );
|
|
// Hard close client socket.
|
|
} else {
|
|
|
|
ShutAndCloseSocket( m_sClient);
|
|
}
|
|
|
|
m_sClient = INVALID_SOCKET;
|
|
|
|
// remove ref count that was set when this object was instantiated.
|
|
GOPHERD_REQUIRE(DeReference() >= 0);
|
|
}
|
|
|
|
return;
|
|
} // ICLIENT_CONNECTION::DisconnectClient()
|
|
|
|
|
|
|
|
|
|
//
|
|
// Private Functions
|
|
//
|
|
|
|
|
|
|
|
|
|
# if DBG
|
|
|
|
VOID
|
|
ICLIENT_CONNECTION::Print( VOID) const
|
|
{
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT, "ClientConnection Object( %08x)\n", this));
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
" Socket = %d; ClientHost = %s; LocalAddr = %s;"
|
|
" State = %d; RefCount = %d;"
|
|
" Persistence = %d\n",
|
|
m_sClient,
|
|
QueryHostName(),
|
|
QueryLocalHostName(),
|
|
QueryState(),
|
|
QueryReferenceCount(),
|
|
IsPersistentConnection()));
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
"AtqContext = %08x; Completion Function = %08x\n",
|
|
m_pAtqContext,
|
|
QueryAtqCompletionFunction()));
|
|
|
|
DBGPRINTF( ( DBG_CONTEXT,
|
|
"Received Request(%d bytes). Buffer = %s. Size = %d\n",
|
|
m_cbReceived,
|
|
QueryRequest(),
|
|
m_recvBuffer.QuerySize()));
|
|
|
|
return;
|
|
|
|
} // ICLIENT_CONNECTION::Print()
|
|
|
|
|
|
# endif // DBG
|
|
|
|
|
|
|
|
|
|
|
|
INT
|
|
ShutAndCloseSocket( IN SOCKET sock)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
Performs a hard close on the socket using shutdown before close.
|
|
|
|
Arguments:
|
|
|
|
sock socket to be closed
|
|
|
|
Returns:
|
|
|
|
0 if no errors or
|
|
socket specific error code
|
|
|
|
--*/
|
|
{
|
|
|
|
INT serr = 0;
|
|
|
|
//
|
|
// Shut the socket. ( Assumes this to be a TCP socket.)
|
|
// Prevent future sends from occuring. hence 2nd param is "1"
|
|
//
|
|
|
|
if ( shutdown( sock, 1) == SOCKET_ERROR) {
|
|
|
|
serr = WSAGetLastError();
|
|
}
|
|
|
|
DEBUG_IF( SOCKETS, {
|
|
|
|
DBGPRINTF(( DBG_CONTEXT, "shutdown(%d) %s. Returns %d as error\n",
|
|
sock, (serr == 0) ? "succeeded" : "failed",
|
|
serr));
|
|
});
|
|
|
|
closesocket( sock); // forcibly close socket.
|
|
|
|
return ( serr);
|
|
|
|
} // ShutAndCloseSocket()
|
|
|
|
|
|
/************************ End of File ***********************/
|