746 lines
17 KiB
C++
746 lines
17 KiB
C++
/**********************************************************************/
|
||
/** Microsoft Windows NT **/
|
||
/** Copyright(c) Microsoft Corp., 1994 **/
|
||
/**********************************************************************/
|
||
|
||
/*
|
||
connect.cxx
|
||
|
||
This module contains the connection accept routine called by the connection
|
||
thread.
|
||
|
||
|
||
FILE HISTORY:
|
||
Johnl 08-Aug-1994 Lifted from FTP server
|
||
|
||
*/
|
||
|
||
#define INCL_INETSRV_INCS
|
||
#include "smtpinc.h"
|
||
#include <rdns.hxx>
|
||
#include "smtpcli.hxx"
|
||
|
||
|
||
|
||
//
|
||
// Private prototypes.
|
||
//
|
||
|
||
VOID
|
||
SmtpCompletion(
|
||
PVOID pvContext,
|
||
DWORD cbWritten,
|
||
DWORD dwCompletionStatus,
|
||
OVERLAPPED * lpo
|
||
);
|
||
|
||
SOCKERR CloseSocket( SOCKET sock );
|
||
|
||
BOOL
|
||
SendError(
|
||
SOCKET socket,
|
||
DWORD ids
|
||
);
|
||
|
||
//
|
||
// Private functions.
|
||
//
|
||
|
||
/*++
|
||
This function dereferences User data and kills the UserData object if the
|
||
reference count hits 0. Before killing the user data, it also removes
|
||
the connection from the list of active connections.
|
||
|
||
--*/
|
||
VOID DereferenceUserDataAndKill(IN OUT CLIENT_CONNECTION * pUserData)
|
||
{
|
||
pUserData->DisconnectClient();
|
||
((SMTP_CONNECTION*)pUserData)->QuerySmtpInstance()->RemoveConnection(pUserData);
|
||
delete pUserData;
|
||
|
||
} // DereferenceUserDataAndKill()
|
||
|
||
void HandleErrorCondition(SOCKET sock, SMTP_SERVER_INSTANCE *pInstance, DWORD dwError, PSOCKADDR_IN pSockAddr)
|
||
{
|
||
char ErrorBuf[4 *MAX_PATH];
|
||
const CHAR * apszSubStrings[2];
|
||
CHAR pchAddr1[50] = "";
|
||
CHAR pchAddr2[50] = "";
|
||
DWORD SendSize;
|
||
|
||
_ASSERT(pSockAddr != NULL);
|
||
_ASSERT(pInstance != NULL);
|
||
_ASSERT(sock != INVALID_SOCKET);
|
||
|
||
SendSize = wsprintf (ErrorBuf, "%d-%s %s\r\n",
|
||
SMTP_RESP_SRV_UNAVAIL,
|
||
pInstance->GetConnectResponse(),
|
||
SMTP_SRV_UNAVAIL_MSG);
|
||
|
||
CLIENT_CONNECTION::WriteSocket(sock, ErrorBuf, SendSize );
|
||
|
||
if(dwError == ERROR_REMOTE_SESSION_LIMIT_EXCEEDED)
|
||
{
|
||
_itoa(pInstance->QueryInstanceId(), pchAddr1, 10);
|
||
InetNtoa(pSockAddr->sin_addr, pchAddr2 );
|
||
|
||
apszSubStrings[0] = pchAddr1;
|
||
apszSubStrings[1] = pchAddr2;
|
||
|
||
SmtpLogEvent(SMTP_MAX_CONNECTION_REACHED,
|
||
2,
|
||
apszSubStrings,
|
||
NO_ERROR );
|
||
}
|
||
}
|
||
|
||
BOOL VerifyClient(SMTP_CONNECTION * pNewClient, PSOCKADDR_IN psockAddrRemote)
|
||
{
|
||
AC_RESULT acIpAccess;
|
||
BOOL fNeedDnsCheck = FALSE;
|
||
BOOL fRet = TRUE;
|
||
struct hostent* pH = NULL;
|
||
|
||
pNewClient->QueryAccessCheck()->BindAddr( (PSOCKADDR)psockAddrRemote );
|
||
if ( pNewClient->BindInstanceAccessCheck() )
|
||
{
|
||
acIpAccess = pNewClient->QueryAccessCheck()->CheckIpAccess( &fNeedDnsCheck);
|
||
if ( (acIpAccess == AC_IN_DENY_LIST) ||
|
||
((acIpAccess == AC_NOT_IN_GRANT_LIST) && !fNeedDnsCheck) )
|
||
{
|
||
fRet = FALSE;
|
||
}
|
||
else if (fNeedDnsCheck)
|
||
{
|
||
pH = gethostbyaddr( (char*)(&((PSOCKADDR_IN)psockAddrRemote)->sin_addr),
|
||
4, PF_INET );
|
||
if(pH != NULL)
|
||
{
|
||
acIpAccess = pNewClient->QueryAccessCheck()->CheckName(pH->h_name);
|
||
}
|
||
else
|
||
{
|
||
acIpAccess = AC_IN_DENY_LIST;
|
||
}
|
||
}
|
||
|
||
if ( (acIpAccess == AC_IN_DENY_LIST) ||
|
||
(acIpAccess == AC_NOT_IN_GRANT_LIST))
|
||
{
|
||
fRet = FALSE;
|
||
}
|
||
|
||
pNewClient->UnbindInstanceAccessCheck();
|
||
}
|
||
|
||
|
||
if(!fRet)
|
||
{
|
||
SetLastError(ERROR_ACCESS_DENIED);
|
||
}
|
||
|
||
return fRet;
|
||
}
|
||
|
||
BOOL
|
||
ProcessNewClient(
|
||
IN SOCKET sNew,
|
||
IN PVOID EndpointObject,
|
||
IN SMTP_SERVER_INSTANCE *pInstance,
|
||
IN BOOL fMaxConnExceeded,
|
||
IN PSOCKADDR_IN psockAddrRemote,
|
||
IN PSOCKADDR_IN psockAddrLocal = NULL,
|
||
IN PATQ_CONTEXT patqContext = NULL,
|
||
IN PVOID pvBuff = NULL,
|
||
IN DWORD cbWritten = 0,
|
||
OUT LPBOOL pfAtqToBeFreed = NULL
|
||
)
|
||
{
|
||
SMTP_CONNECTION * pNewClient = NULL;
|
||
DWORD err = NO_ERROR;
|
||
BOOL fReturn = FALSE;
|
||
BOOL fMaxExceeded = FALSE;
|
||
DBG_CODE( CHAR pchAddr[32];);
|
||
BOOL fSockToBeFreed = TRUE;
|
||
BOOL fDereferenceInstance = FALSE;
|
||
CLIENT_CONN_PARAMS clientParams;
|
||
|
||
TraceFunctEnterEx((LPARAM) NULL, "ProcessNewClient");
|
||
|
||
DBG_CODE( InetNtoa( psockAddrRemote->sin_addr, pchAddr));
|
||
|
||
if ( pfAtqToBeFreed != NULL)
|
||
{
|
||
*pfAtqToBeFreed = TRUE;
|
||
}
|
||
|
||
clientParams.sClient = sNew;
|
||
clientParams.pAtqContext = patqContext;
|
||
clientParams.pAddrLocal = (PSOCKADDR) psockAddrLocal;
|
||
clientParams.pAddrRemote = (PSOCKADDR)psockAddrRemote;
|
||
clientParams.pvInitialBuff = pvBuff;
|
||
clientParams.cbInitialBuff = cbWritten ;
|
||
clientParams.pEndpoint = (PIIS_ENDPOINT)EndpointObject;
|
||
|
||
if( pInstance && (pInstance->IsShuttingDown() || (pInstance->QueryServerState( ) != MD_SERVER_STATE_STARTED)))
|
||
{
|
||
DBGPRINTF((DBG_CONTEXT," Service instance is shutting down\n"));
|
||
}
|
||
else if ( !fMaxConnExceeded)
|
||
{
|
||
|
||
// DBGPRINTF((DBG_CONTEXT,"Getting a connection object\n"));
|
||
|
||
pNewClient = (SMTP_CONNECTION *) pInstance->CreateNewConnection( &clientParams);
|
||
|
||
if(pNewClient)
|
||
{
|
||
if(!VerifyClient(pNewClient, psockAddrRemote))
|
||
{
|
||
DereferenceUserDataAndKill(pNewClient);
|
||
pNewClient = NULL;
|
||
fSockToBeFreed = FALSE;
|
||
fDereferenceInstance = FALSE;
|
||
SetLastError(ERROR_ACCESS_DENIED);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
err = ERROR_REMOTE_SESSION_LIMIT_EXCEEDED;
|
||
SetLastError(err);
|
||
}
|
||
|
||
if( pNewClient != NULL)
|
||
{
|
||
|
||
//DBGPRINTF((DBG_CONTEXT,"New connection object is non-null\n"));
|
||
|
||
//
|
||
// Start off processing this client connection.
|
||
//
|
||
// Once we make a reset call, the USER_DATA object is created
|
||
// with the socket and atq context.
|
||
// From now on USER_DATA will take care of freeing
|
||
// ATQ context & socket
|
||
//
|
||
|
||
fSockToBeFreed = FALSE;
|
||
|
||
//
|
||
// At this point we have the context for the AcceptExed socket.
|
||
// Set the context in the AtqContext if need be.
|
||
//
|
||
|
||
if ( patqContext != NULL)
|
||
{
|
||
//DBGPRINTF((DBG_CONTEXT,"AtqContext is not NULL\n"));
|
||
|
||
|
||
//
|
||
// Associate client connection object with this control socket
|
||
// handle for future completions.
|
||
//
|
||
|
||
AtqContextSetInfo(patqContext,
|
||
ATQ_INFO_COMPLETION_CONTEXT,
|
||
(UINT_PTR) pNewClient);
|
||
}
|
||
else
|
||
{
|
||
//DBGPRINTF((DBG_CONTEXT,"AtqContext is NULL\n"));
|
||
|
||
if(!pNewClient->AddToAtqHandles((HANDLE) sNew,EndpointObject, pInstance->QueryConnectionTimeout(),
|
||
SmtpCompletion))
|
||
{
|
||
err = GetLastError();
|
||
DBGPRINTF((DBG_CONTEXT,"AddToAtqHandles() failed- err= %d\n", err));
|
||
DebugTrace((LPARAM) NULL, "pNewClient->AddToAtqHandles failed- err = %d", err);
|
||
|
||
DereferenceUserDataAndKill(pNewClient);
|
||
fDereferenceInstance = FALSE;
|
||
fSockToBeFreed = FALSE;
|
||
pNewClient = NULL;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
err = GetLastError();
|
||
|
||
if(err != ERROR_ACCESS_DENIED)
|
||
fDereferenceInstance = TRUE;
|
||
}
|
||
|
||
if ( (pNewClient == NULL) || (err != NO_ERROR) )
|
||
{
|
||
|
||
// DBGPRINTF((DBG_CONTEXT,"New connection object is NULL\n"));
|
||
|
||
//
|
||
// Failed to allocate new connection
|
||
// Reasons:
|
||
// 1) Max connections might have been exceeded.
|
||
// 2) Not enough memory is available.
|
||
//
|
||
// handle the failures and notify client.
|
||
//
|
||
|
||
HandleErrorCondition(sNew, pInstance, err, psockAddrRemote);
|
||
|
||
}
|
||
else
|
||
{
|
||
|
||
//DBGPRINTF((DBG_CONTEXT,"Calling StartSession()\n"));
|
||
|
||
if(!pNewClient->StartSession())
|
||
{
|
||
err = GetLastError();
|
||
DBGPRINTF((DBG_CONTEXT,"StartSession() failed - err= %d\n", err));
|
||
DebugTrace((LPARAM) NULL, "pNewClient->StartSession() failed- err = %d", err);
|
||
|
||
DereferenceUserDataAndKill(pNewClient);
|
||
pNewClient = NULL;
|
||
fSockToBeFreed = FALSE;
|
||
fDereferenceInstance = FALSE;
|
||
}
|
||
else
|
||
{
|
||
fReturn = TRUE;
|
||
}
|
||
}
|
||
|
||
|
||
if ( fSockToBeFreed )
|
||
{
|
||
if ( patqContext != NULL)
|
||
{
|
||
// ensure that socket is shut down.
|
||
DBG_REQUIRE( AtqCloseSocket( patqContext, TRUE));
|
||
}
|
||
else
|
||
{
|
||
|
||
CloseSocket( sNew);
|
||
}
|
||
}
|
||
|
||
|
||
if ( pfAtqToBeFreed != NULL)
|
||
{
|
||
|
||
*pfAtqToBeFreed = fSockToBeFreed;
|
||
}
|
||
|
||
if (pInstance && fDereferenceInstance )
|
||
{
|
||
pInstance->DecrementCurrentConnections();
|
||
pInstance->Dereference();
|
||
}
|
||
|
||
TraceFunctLeaveEx((LPARAM) NULL);
|
||
return (fReturn);
|
||
|
||
} // ProcessNewClient()
|
||
|
||
/*******************************************************************
|
||
|
||
NAME: SmtpOnConnect
|
||
|
||
SYNOPSIS: Handles the incoming connection indication from the
|
||
connection thread
|
||
|
||
|
||
ENTRY: sNew - New client socket
|
||
|
||
HISTORY:
|
||
KeithMo 09-Mar-1993 Created.
|
||
Johnl 02-Aug-1994 Reworked from FTP server
|
||
|
||
********************************************************************/
|
||
|
||
VOID SmtpOnConnect( IN SOCKET sNew,
|
||
IN SOCKADDR_IN * psockaddr, //Should be SOCKADDR *
|
||
IN PVOID pEndpointContext,
|
||
IN PVOID pEndpointObject )
|
||
{
|
||
|
||
PIIS_ENDPOINT pEndpoint = (PIIS_ENDPOINT)pEndpointContext;
|
||
INT cbAddr = sizeof( sockaddr );
|
||
SOCKADDR_IN sockaddr;
|
||
SMTP_SERVER_INSTANCE *pInstance;
|
||
BOOL fProcessed;
|
||
BOOL fMaxConnExceeded;
|
||
|
||
DBG_ASSERT( sNew != INVALID_SOCKET );
|
||
DBG_ASSERT( psockaddr != NULL );
|
||
|
||
if ( g_pInetSvc->QueryCurrentServiceState() != SERVICE_RUNNING )
|
||
{
|
||
|
||
DBGPRINTF((DBG_CONTEXT,"Connection attempt on inactive service\n"));
|
||
|
||
goto error_exit;
|
||
|
||
}
|
||
|
||
if ( getsockname( sNew, (PSOCKADDR) &sockaddr, &cbAddr ) != 0 )
|
||
{
|
||
goto error_exit;
|
||
}
|
||
|
||
//
|
||
// Find Instance
|
||
//
|
||
pInstance = (SMTP_SERVER_INSTANCE *)
|
||
((PIIS_ENDPOINT)pEndpointContext)->FindAndReferenceInstance(
|
||
(LPCSTR)NULL,
|
||
sockaddr.sin_addr.s_addr,
|
||
&fMaxConnExceeded);
|
||
|
||
if ( pInstance == NULL )
|
||
{
|
||
|
||
//
|
||
// Site is not permitted to access this server.
|
||
// Dont establish this connection. We should send a message.
|
||
//
|
||
|
||
goto error_exit;
|
||
}
|
||
|
||
fProcessed = ProcessNewClient( sNew,
|
||
pEndpointObject,
|
||
pInstance,
|
||
fMaxConnExceeded,
|
||
psockaddr);
|
||
|
||
if ( fProcessed)
|
||
{
|
||
//StatCheckAndSetMaxConnections();
|
||
}
|
||
|
||
return;
|
||
|
||
error_exit:
|
||
|
||
CloseSocket( sNew );
|
||
|
||
return;
|
||
|
||
} // SmtpOnConnect
|
||
|
||
|
||
|
||
VOID
|
||
SmtpOnConnectEx(
|
||
VOID * patqContext,
|
||
DWORD cbWritten,
|
||
DWORD err,
|
||
OVERLAPPED * lpo
|
||
)
|
||
{
|
||
BOOL fAllowConnection = FALSE;
|
||
PVOID pvBuff = NULL;
|
||
PSOCKADDR_IN psockAddrLocal = NULL;
|
||
PSOCKADDR_IN psockAddrRemote = NULL;
|
||
SOCKET sNew;
|
||
PIIS_ENDPOINT pEndpoint;
|
||
PSMTP_SERVER_INSTANCE pInstance;
|
||
BOOL fProcessed = FALSE;
|
||
BOOL fAtqContextToBeFreed = TRUE;
|
||
BOOL fMaxConnExceeded;
|
||
|
||
if ( err || !lpo || g_IsShuttingDown)
|
||
{
|
||
if(g_IsShuttingDown)
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"[SmtpOnConnectEx] Completion failed because of shutdown %d, Atq context %lx\n",
|
||
err,
|
||
patqContext ));
|
||
|
||
}
|
||
else
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"[SmtpOnConnectEx] Completion failed with error %d, Atq context %lx\n",
|
||
err,
|
||
patqContext ));
|
||
}
|
||
|
||
goto exit;
|
||
}
|
||
|
||
|
||
//
|
||
// Get AcceptEx parameters
|
||
//
|
||
|
||
AtqGetAcceptExAddrs( (PATQ_CONTEXT) patqContext,
|
||
&sNew,
|
||
&pvBuff,
|
||
(PVOID*)&pEndpoint,
|
||
(PSOCKADDR *) &psockAddrLocal,
|
||
(PSOCKADDR *) &psockAddrRemote );
|
||
|
||
|
||
if ( g_pInetSvc->QueryCurrentServiceState() != SERVICE_RUNNING )
|
||
{
|
||
|
||
DBGPRINTF((DBG_CONTEXT,"Connection attempt on inactive service\n"));
|
||
|
||
goto exit ;
|
||
}
|
||
|
||
//
|
||
// Find Instance
|
||
//
|
||
|
||
pInstance = (SMTP_SERVER_INSTANCE *)
|
||
((PIIS_ENDPOINT)pEndpoint)->FindAndReferenceInstance(
|
||
(LPCSTR)NULL,
|
||
psockAddrLocal->sin_addr.s_addr,
|
||
&fMaxConnExceeded
|
||
);
|
||
|
||
if(pInstance == NULL)
|
||
{
|
||
//
|
||
// Site is not permitted to access this server.
|
||
// Dont establish this connection. We should send a message.
|
||
//
|
||
|
||
// DBGPRINTF((DBG_CONTEXT,
|
||
// "Unable to find instance [err %d]\n",GetLastError()));
|
||
goto exit;
|
||
}
|
||
|
||
|
||
//
|
||
// Set the timeout for future IOs on this context
|
||
//
|
||
|
||
AtqContextSetInfo( (PATQ_CONTEXT) patqContext,
|
||
ATQ_INFO_TIMEOUT,
|
||
(UINT_PTR) pInstance->QueryConnectionTimeout());
|
||
|
||
|
||
fProcessed = ProcessNewClient( sNew,
|
||
pEndpoint,
|
||
pInstance,
|
||
fMaxConnExceeded,
|
||
psockAddrRemote,
|
||
psockAddrLocal,
|
||
(PATQ_CONTEXT ) patqContext,
|
||
pvBuff,
|
||
cbWritten,
|
||
&fAtqContextToBeFreed);
|
||
|
||
|
||
exit:
|
||
|
||
if ( !fProcessed && fAtqContextToBeFreed )
|
||
{
|
||
|
||
// DBGPRINTF((DBG_CONTEXT,
|
||
// "ProcessNewClient returned false\n"));
|
||
|
||
//
|
||
// We failed to process this connection. Free up resources properly
|
||
//
|
||
|
||
DBG_REQUIRE( AtqCloseSocket( (PATQ_CONTEXT )patqContext, FALSE));
|
||
AtqFreeContext( (PATQ_CONTEXT ) patqContext, TRUE );
|
||
}
|
||
|
||
return;
|
||
|
||
} // SmtpOnConnectEx
|
||
|
||
|
||
|
||
#if 0
|
||
BOOL
|
||
SendError(
|
||
SOCKET socket,
|
||
DWORD ids
|
||
)
|
||
{
|
||
STR strResponse;
|
||
|
||
if ( !strResponse.Resize( 512 ) ||
|
||
!HTTP_REQ_BASE::BuildExtendedStatus( &strResponse,
|
||
HT_FORBIDDEN,
|
||
NO_ERROR,
|
||
ids ))
|
||
{
|
||
DBGPRINTF((DBG_CONTEXT,
|
||
"[SendError] Failed to build status (error %d)\n",
|
||
GetLastError()));
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Do a synchronous send
|
||
//
|
||
|
||
send( socket,
|
||
strResponse.QueryStr(),
|
||
strResponse.QueryCB(),
|
||
0 );
|
||
|
||
return TRUE ;
|
||
} // SendError
|
||
#endif
|
||
|
||
/*******************************************************************
|
||
|
||
NAME: CloseSocket
|
||
|
||
SYNOPSIS: Closes the specified socket. This is just a thin
|
||
wrapper around the "real" closesocket() API.
|
||
|
||
ENTRY: sock - The socket to close.
|
||
|
||
RETURNS: SOCKERR - 0 if successful, !0 if not.
|
||
|
||
HISTORY:
|
||
KeithMo 26-Apr-1993 Created.
|
||
|
||
********************************************************************/
|
||
SOCKERR CloseSocket( SOCKET sock )
|
||
{
|
||
SOCKERR serr = 0;
|
||
|
||
//
|
||
// Close the socket.
|
||
//
|
||
|
||
#if 0
|
||
shutdown( sock, 1 ); // Davidtr sez not needed
|
||
#endif
|
||
|
||
if( closesocket( sock ) != 0 )
|
||
{
|
||
serr = WSAGetLastError();
|
||
}
|
||
|
||
if( serr == 0 )
|
||
{
|
||
// DBGPRINTF(( DBG_CONTEXT,
|
||
// "closed socket %d\n",
|
||
// sock ));
|
||
}
|
||
else
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"cannot close socket %d, error %d\n",
|
||
sock,
|
||
serr ));
|
||
}
|
||
|
||
return serr;
|
||
|
||
} // CloseSocket
|
||
|
||
/*++
|
||
|
||
Description:
|
||
|
||
Handles a completed IO.
|
||
|
||
Arguments:
|
||
|
||
pvContext: the context pointer specified in the initial IO
|
||
cbWritten: the number of bytes sent
|
||
dwCompletionStatus: the status of the completion (usually NO_ERROR)
|
||
lpo: the overlapped structure associated with the IO
|
||
|
||
Returns:
|
||
|
||
nothing.
|
||
|
||
--*/
|
||
VOID
|
||
SmtpCompletion(
|
||
PVOID pvContext,
|
||
DWORD cbWritten,
|
||
DWORD dwCompletionStatus,
|
||
OVERLAPPED * lpo
|
||
)
|
||
{
|
||
BOOL WasProcessed;
|
||
SMTP_CONNECTION *pCC = (SMTP_CONNECTION *) pvContext;
|
||
|
||
_ASSERT(pCC);
|
||
_ASSERT(pCC->IsValid());
|
||
_ASSERT(pCC->QuerySmtpInstance() != NULL);
|
||
|
||
//
|
||
// if we could not process a command, or we were
|
||
// told to destroy this object, close the connection.
|
||
//
|
||
WasProcessed = pCC->ProcessClient(cbWritten, dwCompletionStatus, lpo);
|
||
}
|
||
|
||
/*++
|
||
|
||
Description:
|
||
|
||
Handles a completed IO.
|
||
|
||
Arguments:
|
||
|
||
pvContext: the context pointer specified in the initial IO
|
||
cbWritten: the number of bytes sent
|
||
dwCompletionStatus: the status of the completion (usually NO_ERROR)
|
||
lpo: the overlapped structure associated with the IO
|
||
|
||
Returns:
|
||
|
||
nothing.
|
||
|
||
--*/
|
||
VOID
|
||
SmtpCompletionFIO(
|
||
PFIO_CONTEXT pFIOContext,
|
||
FH_OVERLAPPED *pOverlapped,
|
||
DWORD cbWritten,
|
||
DWORD dwCompletionStatus)
|
||
{
|
||
BOOL WasProcessed;
|
||
SMTP_CONNECTION *pCC = (SMTP_CONNECTION *) (((SERVEREVENT_OVERLAPPED *) pOverlapped)->ThisPtr);
|
||
|
||
_ASSERT(pCC);
|
||
_ASSERT(pCC->IsValid());
|
||
_ASSERT(pCC->QuerySmtpInstance() != NULL);
|
||
|
||
//
|
||
// if we could not process a command, or we were
|
||
// told to destroy this object, close the connection.
|
||
//
|
||
WasProcessed = pCC->ProcessClient(cbWritten, dwCompletionStatus, (OVERLAPPED *) pOverlapped);
|
||
}
|
||
|
||
#if 0
|
||
VOID
|
||
ServerEventCompletion(
|
||
PVOID pvContext,
|
||
DWORD cbWritten,
|
||
DWORD dwCompletionStatus,
|
||
OVERLAPPED * lpo
|
||
)
|
||
{
|
||
SERVEREVENT_OVERLAPPED * Ov = (SERVEREVENT_OVERLAPPED *) lpo;
|
||
|
||
_ASSERT(pvContext);
|
||
|
||
Ov->Overlapped.pfnCompletion(Ov->ThisPtr, cbWritten, dwCompletionStatus, lpo);
|
||
|
||
}
|
||
#endif
|
||
|