Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

767 lines
19 KiB

/*++
Copyright (c) 1994 Microsoft Corporation
Module Name :
conn.cxx
Abstract:
This module defines the functions for base class of connections
for Internet Services ( class CLIENT_CONNECTION)
Author:
Rohan Phillips ( Rohanp ) 11-Dec-1995
Project:
Gibraltar Services Common Code
Functions Exported:
CLIENT_CONNECTION::Initialize()
CLIENT_CONNECTION::Cleanup()
CLIENT_CONNECTION::~CLIENT_CONNECTION()
BOOL CLIENT_CONNECTION::ProcessClient( IN DWORD cbWritten,
IN DWORD dwCompletionStatus,
IN BOOL fIOCompletion)
VOID CLIENT_CONNECTION::DisconnectClient( IN DWORD ErrorReponse)
BOOL CLIENT_CONNECTION::StartupSession( VOID)
BOOL CLIENT_CONNECTION::ReceiveRequest(
OUT LPBOOL pfFullRequestRecd)
BOOL CLIENT_CONNECTION::ReadFile( OUT LPVOID pvBuffer,
IN DWORD dwSize)
BOOL CLIENT_CONNECTION::WriteFile( IN LPVOID pvBuffer,
IN DWORD dwSize)
BOOL CLIENT_CONNECTION::TransmitFile( IN HANDLE hFile,
IN DWORD cbToSend)
BOOL CLIENT_CONNECTION::PostCompletionStatus( IN DWORD dwBytes )
Revision History:
Revision History:
Richard Kamicar ( rkamicar ) 31-Dec-1995
Moved to common directory, filled in more
--*/
/************************************************************
* Include Headers
************************************************************/
#define INCL_INETSRV_INCS
#include "smtpinc.h"
#include "conn.hxx"
/************************************************************
* Functions
************************************************************/
/*++
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).
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.
--*/
CLIENT_CONNECTION::Initialize(
IN SOCKET sClient,
IN const SOCKADDR_IN * psockAddrRemote,
IN const SOCKADDR_IN * psockAddrLocal /* Default = NULL */,
IN PATQ_CONTEXT pAtqContext /* Default = NULL */,
IN PVOID pvInitialRequest/* Default = NULL */,
IN DWORD cbInitialData /* Default = 0 */
)
{
DWORD dwError = NO_ERROR;
m_sClient = ( sClient);
m_pAtqContext = pAtqContext;
m_cbReceived = 0;
m_pvInitial = pvInitialRequest;
m_cbInitial = cbInitialData;
m_Destroy = FALSE;
_ASSERT( psockAddrRemote != NULL);
m_saClient = *psockAddrRemote;
// Obtain the socket addresses for the socket
m_pchRemoteHostName[0] =
m_pchLocalHostName[0] =
m_pchLocalPortName[0] = '\0';
// InetNtoa() wants just 16 byte buffer.
_ASSERT( 16 <= MAX_HOST_NAME_LEN);
dwError = InetNtoa( psockAddrRemote->sin_addr, m_pchRemoteHostName);
_ASSERT( dwError == NO_ERROR); // since we had given sufficient buffer
if ( psockAddrLocal != NULL)
{
dwError = InetNtoa( psockAddrLocal->sin_addr, m_pchLocalHostName);
_itoa( ntohs(psockAddrLocal->sin_port), m_pchLocalPortName, 10);
} else
{
SOCKADDR_IN sockAddr;
int cbAddr = sizeof( sockAddr);
if ( getsockname( sClient,
(struct sockaddr *) &sockAddr,
&cbAddr ))
{
dwError = InetNtoa( sockAddr.sin_addr, m_pchLocalHostName );
_itoa( ntohs(sockAddr.sin_port), m_pchLocalPortName, 10);
}
}
// DBG_ASSERT( dwError == NO_ERROR); // since we had given sufficient buffer
#if 0
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).\n",
this,
sClient,
QueryClientHostName()
));
});
#endif
return ( TRUE);
}
/*++
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.
--*/
VOID CLIENT_CONNECTION::Cleanup( VOID)
{
PATQ_CONTEXT pAtqContext;
if(m_DoCleanup)
{
//release the context from Atq
pAtqContext = (PATQ_CONTEXT)InterlockedExchangePointer( (PVOID *)&m_pAtqContext, NULL);
if ( pAtqContext != NULL )
{
AtqFreeContext( pAtqContext, TRUE );
}
}
} // ICLIENT_CONNECTION::Cleanup()
/*++
Description:
Checks to see if we have received the complete request from the client.
( A complete request is a line of text terminated by <cr><lf> )
if a CRLF is found, it returns a pointer into the buffer were the CR
starts, else it returns NULL. If this routine finds a CR without a
LF it will return NULL
Arguments:
InputBuffer pointer to character buffer containing received data.
cbRecvd count of bytes of data received
Returns:
a pointer to the CR if CRLF is found
NULL if CRLF is not found.
--*/
// VIRTUAL
char * CLIENT_CONNECTION::IsLineComplete(IN OUT const char * InputBuffer, IN DWORD cbRecvd)
{
register DWORD Loop = 0;
_ASSERT(InputBuffer != NULL);
//we need at least 2 bytes to find a
//CRLF pair
if( cbRecvd < 2)
return NULL;
//we are going to start at the 2nd byte
//looking for the LF, then look backwards
//for the CR
Loop = 1;
while (Loop < cbRecvd)
{
if(InputBuffer[Loop] == LF)
{
if(InputBuffer[Loop - 1] == CR)
return (char *) &InputBuffer[Loop - 1];
else
{
//skip 2 bytes since we saw a LF
//without a CR.
Loop += 2;
}
}
else if(InputBuffer[Loop] == CR)
{
//we saw a CR, so increment out
//loop variable by one so that
//we can catch the LF on the next
//go around
Loop += 1;
}
else
{
//This character is neither a CR
//or a LF, so we can increment by
//2
Loop += 2;
}
}
//didn't find a CRLF pair
return NULL;
}
/*++
Description:
VIRTUAL Method that MUST be defined by the derived class
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.
--*/
// VIRTUAL
BOOL CLIENT_CONNECTION::ProcessClient( IN DWORD cbWritten, IN DWORD dwCompletionStatus, IN OUT OVERLAPPED * lpo)
{
return ( TRUE);
} // CLIENT_CONNECTION::ProcessClient()
/*++
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.
--*/
// VIRTUAL
BOOL CLIENT_CONNECTION::ReadFile(
IN LPVOID pBuffer,
IN DWORD cbSize /* = MAX_READ_BUFF_SIZE */
)
{
_ASSERT(pBuffer != NULL);
_ASSERT(cbSize > 0);
ZeroMemory(&m_Overlapped, sizeof(OVERLAPPED));
return AtqReadFile(m_pAtqContext, // Atq context
pBuffer, // Buffer
cbSize, // BytesToRead
&m_Overlapped) ;
}
/*++
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.
--*/
// VIRTUAL
BOOL CLIENT_CONNECTION::WriteFile( IN LPVOID pBuffer, IN DWORD cbSize )
{
BOOL fReturn = TRUE;
int BytesAlreadySent = 0;
DWORD BytesSent;
DWORD dwError = NO_ERROR;
DWORD cTimesBlocked = 0;
DWORD dwSleepTime = 1000;
_ASSERT(pBuffer != NULL);
for (BytesSent = 0; BytesSent < cbSize; BytesSent += BytesAlreadySent)
{
BytesAlreadySent = send(m_sClient,
(const char FAR *) pBuffer + BytesSent,
(int) (cbSize - BytesSent),
0);
if (BytesAlreadySent == SOCKET_ERROR)
{
//The above send will fail with WSAEWOULDBLOCK when
//the TCP buffer is full... this can easily happen for blob
//protocol sinks. The correct thing to do is rely on memory
//instead of TCP buffers to store pending sends, but the
//low impact work-around is to sleep after we would block
dwError = GetLastError();
if ((WSAEWOULDBLOCK == dwError) && (cTimesBlocked < 500))
{
SetLastError(NO_ERROR);
cTimesBlocked++;
BytesAlreadySent = 0;
Sleep(dwSleepTime);
dwSleepTime += dwSleepTime;
continue;
}
fReturn = FALSE;
break;
}
}
return fReturn;
}
BOOL CLIENT_CONNECTION::WriteSocket( IN SOCKET Sock, IN LPVOID pBuffer, IN DWORD cbSize )
{
BOOL fReturn = TRUE;
int BytesAlreadySent = 0;
DWORD BytesSent;
_ASSERT(pBuffer != NULL);
for (BytesSent = 0; BytesSent < cbSize; BytesSent += BytesAlreadySent)
{
BytesAlreadySent = send(Sock,
(const char FAR *) pBuffer + BytesSent,
(int) (cbSize - BytesSent),
0);
if (BytesAlreadySent == SOCKET_ERROR)
{
fReturn = FALSE;
break;
}
}
return fReturn;
}
/*++
Writes contents from given buffer using ATQ.
(thin wrapper for ATQ call and managing references)
Arguments:
Pov pointer to OVERLAPPED structure describing the write
Returns:
TRUE on success and FALSE on a failure.
--*/
// VIRTUAL
BOOL CLIENT_CONNECTION::WriteFile(
IN LPVOID lpvBuffer,
IN DWORD cbSize,
IN OVERLAPPED *lpo)
{
BOOL fReturn = TRUE;
_ASSERT(lpo != NULL);
_ASSERT(lpvBuffer != NULL);
_ASSERT(cbSize != 0);
fReturn = AtqWriteFile(m_pAtqContext, lpvBuffer, cbSize, lpo);
return fReturn;
}
/*++
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 CLIENT_CONNECTION::TransmitFile(
IN HANDLE hFile,
IN LARGE_INTEGER &liSize,
IN LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers
)
{
_ASSERT(hFile != INVALID_HANDLE_VALUE);
_ASSERT(liSize.QuadPart > 0);
return AtqTransmitFile(
m_pAtqContext, // Atq context
hFile, // file data comes from
(DWORD) liSize.LowPart, // Bytes To Send
lpTransmitBuffers, // header/tail buffers
0 // Flags
);
}
//+------------------------------------------------------------
//
// Function: CLIENT_CONNECTION::PostCompletionStatus
//
// Synopsis: Wrapper around atq for posting completion status
//
// Arguments:
// dwBytes: The number of bytes to indicate in the completion status
//
// Returns:
// TRUE: Success
// FALSE: Failure
//
// History:
// jstamerj 1998/11/03 20:16:17: Created.
//
//-------------------------------------------------------------
BOOL CLIENT_CONNECTION::PostCompletionStatus(
IN DWORD dwBytes)
{
return AtqPostCompletionStatus(
m_pAtqContext,
dwBytes);
}
/*++
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.
--*/
// VIRTUAL
BOOL CLIENT_CONNECTION::StartSession( void)
{
return TRUE;
}
/*++
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 CLIENT_CONNECTION::ReceiveRequest(IN DWORD cbWritten, OUT LPBOOL pfFullRequestRecvd)
{
return ( TRUE);
}
/*++
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
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.
by the REQUEST object. But the disconnection occurs immediately; Hence
the REQUEST object should send synchronous error messages.
Returns:
None
--*/
VOID CLIENT_CONNECTION::DisconnectClient(IN DWORD dwErrorCode)
{
SOCKET hSocket;
hSocket = (SOCKET)InterlockedExchangePointer( (PVOID *)&m_sClient, (PVOID) INVALID_SOCKET );
if ( hSocket != INVALID_SOCKET )
{
if ( QueryAtqContext() != NULL )
{
AtqCloseSocket(QueryAtqContext() , (dwErrorCode == NO_ERROR));
}
else
{
ShutAndCloseSocket( m_sClient );
}
}
}
/*++
Description:
returns the client user name
VIRTUAL function so apps can override its return value
Arguments:
void
Returns:
returns ptr to user name
--*/
LPCTSTR CLIENT_CONNECTION::QueryClientUserName( VOID )
{
return m_pchLocalHostName;
}
//
// Private Functions
//
# if DBG
// VIRTUAL
VOID CLIENT_CONNECTION::Print( VOID) const
{
return;
} // ICLIENT_CONNECTION::Print()
# endif // DBG
/*++
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 ShutAndCloseSocket( IN SOCKET sock)
{
INT serr = 0;
//
// Shut the socket. ( Assumes this to be a TCP socket.)
// Prevent future sends from occuring. hence 2nd param is "1"
//
if( sock != INVALID_SOCKET )
{
if ( shutdown( sock, 1) == SOCKET_ERROR)
serr = WSAGetLastError();
closesocket( sock);
}
return ( serr);
} // ShutAndCloseSocket()
/*++
This function canonicalizes the path, taking into account the current
user's current directory value.
Arguments:
pszDest string that will on return contain the complete
canonicalized path. This buffer will be of size
specified in *lpdwSize.
lpdwSize Contains the size of the buffer pszDest on entry.
On return contains the number of bytes written
into the buffer or number of bytes required.
pszSearchPath pointer to string containing the path to be converted.
IF NULL, use the current directory only
Returns:
Win32 Error Code - NO_ERROR on success
MuraliK 24-Apr-1995 Created.
--*/
BOOL
ResolveVirtualRoot(
OUT CHAR * pszDest,
IN OUT LPDWORD lpdwSize,
IN OUT CHAR * pszSearchPath,
OUT HANDLE * phToken /* = NULL */
)
{
TraceFunctEnter("ResolveVirtualRoot");
_ASSERT(pszDest != NULL);
_ASSERT(lpdwSize != NULL);
_ASSERT(pszSearchPath != NULL);
//
// Now we have the complete symbolic path to the target file.
// Translate it into the absolute path
//
#if 0
if (!TsLookupVirtualRoot(g_pTsvcInfo->GetTsvcCache(), // TSvcCache
pszSearchPath, // pszRoot
pszDest, // pszDirectory
lpdwSize, // lpcbSize
NULL, // lpdwAccessMask
NULL, // pcchDirRoot
NULL, // pcchVroot
phToken, // phImpersonationToken
NULL, // pszAddress
NULL // lpdwFileSystem
))
{
ErrorTrace(NULL, "TsLookupVirtualRoot failed looking for %s: %d", pszSearchPath, GetLastError());
TraceFunctLeave();
return FALSE;
}
#endif
TraceFunctLeave();
return TRUE;
}
/************************ End of File ***********************/