mirror of https://github.com/tongzx/nt5src
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.
745 lines
17 KiB
745 lines
17 KiB
/**********************************************************************/
|
|
/** 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
|
|
|