/************************************************************************************************ Copyright (c) 2001 Microsoft Corporation File Name: Pop3Context.cpp Abstract: Implementation of the POP3_CONTEXT Class Notes: History: 08/01/2001 Created by Hao Yu (haoyu) ************************************************************************************************/ #include #include #include #include #ifdef ROCKALL3 void * __cdecl operator new(size_t cb) { void *const pv = g_RockallHeap.New(cb); return pv; } void __cdecl operator delete(void *pv) { g_RockallHeap.Delete(pv); } #endif POP3_CONTEXT::POP3_CONTEXT() { Reset(); } POP3_CONTEXT::~POP3_CONTEXT() { } void POP3_CONTEXT::Reset() { m_dwCurrentState=INIT_STATE; m_bFileTransmitPending=FALSE; m_bCommandComplete=TRUE; m_dwCommandSize=0; m_wszUserName[0]=0; m_szPassword[0]=0; m_szDomainName[0]=0; m_szCommandBuffer[0]=0; m_cPswdSize=0; m_dwAuthStatus=0; m_dwFailedAuthCount=0; m_AuthServer.Cleanup(); } void POP3_CONTEXT::TimeOut(IO_CONTEXT *pIoContext) { TerminateConnection(pIoContext); } void POP3_CONTEXT::ProcessRequest(IO_CONTEXT *pIoContext,OVERLAPPED *pOverlapped,DWORD dwBytesRcvd) { POP3_CMD CurrentCmd=CMD_UNKNOWN; char szGreetingBuffer[MAX_PATH*2]; LONG lTotalMsgSize, lMsgCnt; char *pEndOfCmd=NULL; if( ( NULL == pIoContext) || ( NULL == pIoContext->m_hAsyncIO) ) { //This is a rare shutdown case. //IO completion received after socket is shut down. if(NULL!=pIoContext) { //Signal that the IO Context should be deleted/reused pIoContext->m_ConType = DELETE_PENDING; } return; } ASSERT( LOCKED_TO_PROCESS_POP3_CMD == pIoContext->m_lLock); m_pIoContext=pIoContext; // BLaPorte - I moved transmit completion handling here to avoid confusion. // Check if this should be signal of TransmitFile completion if(m_bFileTransmitPending) { m_bFileTransmitPending=FALSE; //Here we calculate the perf on message size downloaded g_PerfCounters.AddPerfCntr(e_gcBytesTransmitted, dwBytesRcvd); g_PerfCounters.AddPerfCntr(e_gcBytesTransmitRate, dwBytesRcvd); WaitForCommand(); return; } if(INIT_STATE == m_dwCurrentState) { if(SERVICE_RUNNING!=g_dwServerStatus) { //Reject the connection SendResponse(RESP_SERVER_NOT_AVAILABLE); TerminateConnection(pIoContext); return; } if( 0 > _snwprintf(m_wszGreeting, sizeof(m_wszGreeting)/sizeof(WCHAR), L"<%u@%s>", GetTickCount(), g_wszComputerName)) { //Make sure length of is less than MAX_PATH m_wszGreeting[sizeof(m_wszGreeting)/sizeof(WCHAR)-1]=0; m_wszGreeting[sizeof(m_wszGreeting)/sizeof(WCHAR)-2]=L'>'; } if(L'\0'!=g_wszGreeting[0]) { _snprintf(szGreetingBuffer, sizeof(szGreetingBuffer)/sizeof(char), RESP_SERVER_READY, g_wszGreeting, m_wszGreeting); } else { _snprintf(szGreetingBuffer, sizeof(szGreetingBuffer)/sizeof(char), RESP_SERVER_READY, RESP_SERVER_GREETING, m_wszGreeting); } //Make sure the NULL is there szGreetingBuffer[sizeof(szGreetingBuffer)/sizeof(char)-1]=0; m_dwCurrentState=AUTH_STATE; g_PerfCounters.IncPerfCntr(e_gcAuthStateCnt); SendResponse(szGreetingBuffer); if(0==dwBytesRcvd) { WaitForCommand(); return; } } // BLaPorte - oversized/undersized data should be rejected immediately. if(dwBytesRcvd >= POP3_REQUEST_BUF_SIZE || dwBytesRcvd == 0) { //Error this command is too big or is nil //Consider this is an attack or // termination of a connection unexpectedly. TerminateConnection(pIoContext); return; } if( g_SocketPool.IsMaxSocketUsed() ) //Possible DoS Attack situation { DWORD dwTime=GetTickCount(); if(AUTH_STATE==m_dwCurrentState) { //The connection is not authenticated for twice the shorted timeout if(dwTime>m_pIoContext->m_dwConnectionTime+SHORTENED_TIMEOUT) //The connection is not authenticated { TerminateConnection(pIoContext); return; //The connection will be terminated } } } // BLaPorte - moved the counter increment here since we do it in either case. g_PerfCounters.AddPerfCntr(e_gcBytesReceived, dwBytesRcvd); g_PerfCounters.AddPerfCntr(e_gcBytesReceiveRate, dwBytesRcvd); if(m_bCommandComplete) { m_dwCommandSize=dwBytesRcvd; memcpy(m_szCommandBuffer, m_pIoContext->m_Buffer,dwBytesRcvd); } else { if(m_dwCommandSize+dwBytesRcvd >= POP3_REQUEST_BUF_SIZE) { //Error this command is too big! //Consider this is an attack. TerminateConnection(pIoContext); return; } memcpy(m_szCommandBuffer+m_dwCommandSize, m_pIoContext->m_Buffer,dwBytesRcvd); m_dwCommandSize+=dwBytesRcvd; } m_szCommandBuffer[m_dwCommandSize]='\0'; pEndOfCmd=strstr(m_szCommandBuffer,"\r\n"); if(NULL == pEndOfCmd) { m_bCommandComplete=FALSE; WaitForCommand(); return; } else { m_bCommandComplete=TRUE; *pEndOfCmd='\0'; //Cut the \r\n m_dwCommandSize-=2; } if(m_dwAuthStatus!=1) { CurrentCmd=ParseCommand(); if(CMD_UNKNOWN == CurrentCmd) { // Count the bad commands? SendResponse(RESP_UNKNOWN_COMMAND); if(!g_SocketPool.IsMaxSocketUsed() ) { WaitForCommand(); } else { TerminateConnection(pIoContext); } return; } } else { CurrentCmd=CMD_AUTH; } if(AUTH_STATE == m_dwCurrentState) { if(!ProcessAuthStateCommands(CurrentCmd,m_dwCommandSize)) { TerminateConnection(pIoContext); } } else // TRANS_STATE == m_dwCurrentState { if(dwBytesRcvd == 0) { //Connection terminated TerminateConnection(pIoContext); } if(!ProcessTransStateCommands(CurrentCmd, m_dwCommandSize)) { TerminateConnection(pIoContext); } } } void POP3_CONTEXT::WaitForCommand() { int iRet; DWORD cbRevd=0; DWORD Flags=0; ASSERT( NULL != m_pIoContext); ASSERT( NULL != m_pIoContext->m_hAsyncIO); if(NULL == m_pIoContext->m_hAsyncIO) { TerminateConnection(m_pIoContext); return; } WSABUF wsaBuf={POP3_REQUEST_BUF_SIZE, m_pIoContext->m_Buffer}; iRet=WSARecv(m_pIoContext->m_hAsyncIO, &wsaBuf, 1, &cbRevd, &Flags, &(m_pIoContext->m_Overlapped), NULL); if(SOCKET_ERROR == iRet ) { iRet=WSAGetLastError(); if(iRet != ERROR_IO_PENDING ) { //Problem with this connection //We terminate the connection TerminateConnection(m_pIoContext); } } } void POP3_CONTEXT::TerminateConnection(PIO_CONTEXT pIoContext) { SOCKET hSocket; if(NULL!=pIoContext) { if(pIoContext->m_ConType != DELETE_PENDING) { m_MailBox.QuitAndClose(); hSocket=(SOCKET)InterlockedExchange((LPLONG)( &(pIoContext->m_hAsyncIO)), NULL); if(NULL != hSocket ) { closesocket(hSocket); g_SocketPool.DecrementTotalSocketCount(); switch (m_dwCurrentState) { case UPDATE_STATE: break; case TRANS_STATE: g_PerfCounters.DecPerfCntr(e_gcTransStateCnt); break; case AUTH_STATE: g_PerfCounters.DecPerfCntr(e_gcAuthStateCnt); break; } g_PerfCounters.DecPerfCntr(e_gcConnectedSocketCnt); } if(!m_bFileTransmitPending) { pIoContext->m_ConType = DELETE_PENDING; } } } } POP3_CMD POP3_CONTEXT::ParseCommand() { int i=0; if(strlen(m_szCommandBuffer) < COMMAND_SIZE-1) { return CMD_UNKNOWN; } //Check if any invalid characters for(i=0; i< m_dwCommandSize; i++) { if(!isprint(m_szCommandBuffer[i])) { return CMD_UNKNOWN; } } for(i=0; i< CMD_UNKNOWN; i++) { if( 0 == _strnicmp(m_szCommandBuffer, cszCommands[i], ciCommandSize[i]) ) { return (POP3_CMD)i; } } return CMD_UNKNOWN; } BOOL POP3_CONTEXT::ProcessAuthStateCommands(POP3_CMD CurrentCmd, DWORD dwBytesRcvd) { BOOL bRetVal=FALSE; char szBuf[POP3_RESPONSE_BUF_SIZE]; char szUserName[POP3_MAX_ADDRESS_LENGTH]; BSTR bstrUserName=NULL; szBuf[POP3_RESPONSE_BUF_SIZE-1]='\0'; // BLaPorte - wszPassword could potentially be used to concatenate 2 strings of length MAX_PATH + change. WCHAR wszPassword[2*MAX_PATH+32]; HRESULT hr=E_FAIL; VARIANT vPassword; VariantInit(&vPassword); switch (CurrentCmd) { case CMD_USER: if( (m_szCommandBuffer[COMMAND_SIZE]!=' ') && (m_szCommandBuffer[COMMAND_SIZE]!='\0') ) { SendResponse(RESP_UNKNOWN_COMMAND); } else if(g_dwRequireSPA) { SendResponse(RESP_SPA_REQUIRED); bRetVal=TRUE; } else if(m_wszUserName[0] != 0 ) { SendResponse(RESP_CMD_NOT_SUPPORTED); } else { if(GetNextStringParameter( &(m_szCommandBuffer[COMMAND_SIZE]), szUserName, sizeof(szUserName)/sizeof(char))) { AnsiToUnicode(szUserName, -1, m_wszUserName, sizeof(m_wszUserName)/sizeof(WCHAR)); SendResponse(RESP_OK); bRetVal=TRUE; } else { SendResponse(RESP_CMD_NOT_VALID); } } if(! g_SocketPool.IsMaxSocketUsed() ) { bRetVal=TRUE; } break; case CMD_PASS: // // BLaPorte - make sure the password is cleared from the receive buffer. // SecureZeroMemory(m_pIoContext->m_Buffer,sizeof(m_pIoContext->m_Buffer)); if(m_wszUserName[0] == 0) { SendResponse(RESP_CMD_NOT_SUPPORTED); if(! g_SocketPool.IsMaxSocketUsed() ) { bRetVal=TRUE; } break; } else //USER command alread issued { if( (m_dwCommandSize == COMMAND_SIZE ) || ((m_dwCommandSize == COMMAND_SIZE +1) && (' '==m_szCommandBuffer[COMMAND_SIZE] )) ) { //No password m_cPswdSize=0; } else { m_cPswdSize=m_dwCommandSize-COMMAND_SIZE-1; if( (m_cPswdSize >= sizeof(m_szPassword)/sizeof(char)) || (' '!=m_szCommandBuffer[COMMAND_SIZE]) ) { SendResponse(RESP_CMD_NOT_VALID); m_wszUserName[0] = 0; // // BLaPorte - Zero out command buffer so cleartext password isn't // lying around in memory. // SecureZeroMemory(m_szCommandBuffer,m_dwCommandSize); if(! g_SocketPool.IsMaxSocketUsed() ) { bRetVal=TRUE; } break; } strncpy(m_szPassword, &(m_szCommandBuffer[COMMAND_SIZE+1]), sizeof(m_szPassword)/sizeof(char)-1); m_szPassword[sizeof(m_szPassword)/sizeof(char)-1]=0; SecureZeroMemory(m_szCommandBuffer,m_dwCommandSize); } } //Do Authentication here bstrUserName=SysAllocString(m_wszUserName); AnsiToUnicode(m_szPassword, -1, wszPassword, sizeof(wszPassword)/sizeof(WCHAR)); // // BLaPorte - clear the password. // SecureZeroMemory(m_szPassword,sizeof(m_szPassword)); vPassword.vt=VT_BSTR; if(0==m_cPswdSize) { vPassword.bstrVal=NULL; } else { vPassword.bstrVal=SysAllocString(wszPassword); SecureZeroMemory(wszPassword,sizeof(wszPassword)); } if(NULL != bstrUserName) { if(S_OK == ( hr= g_pAuthMethod->Authenticate(bstrUserName, vPassword))) { bRetVal=TRUE; } else if(E_ACCESSDENIED == hr ) { g_EventLogger.LogEvent(LOGTYPE_ERR_WARNING, POP3SVR_MAILROOT_ACCESS_DENIED, m_wszUserName, 1); } } SysFreeString(bstrUserName); if(NULL != vPassword.bstrVal) { SecureZeroMemory(vPassword.bstrVal,SysStringByteLen(vPassword.bstrVal)); SysFreeString(vPassword.bstrVal); } //Open the mailbox if(bRetVal) { bRetVal=m_MailBox.OpenMailBox(m_wszUserName); } if (bRetVal) { bRetVal=m_MailBox.LockMailBox(); if(bRetVal) { bRetVal=m_MailBox.EnumerateMailBox(g_dwMaxMsgPerDnld); if(!bRetVal) { m_MailBox.UnlockMailBox(); } } } else { //Open mailbox failed if(ERROR_ACCESS_DENIED==GetLastError()) { //Log the event g_EventLogger.LogEvent(LOGTYPE_ERR_WARNING, POP3SVR_MAILROOT_ACCESS_DENIED, m_wszUserName, 1); } } if (!bRetVal) { g_PerfCounters.IncPerfCntr(e_gcFailedLogonCnt); m_dwFailedAuthCount++; if( MAX_FAILED_AUTH<=m_dwFailedAuthCount ) { g_EventLogger.LogEvent(LOGTYPE_ERR_WARNING, POP3SVR_MAX_LOGON_FAILURES, m_wszUserName, 1); } else { if(! g_SocketPool.IsMaxSocketUsed() ) { bRetVal=TRUE; //Don't disconnect } } SendResponse(RESP_ACCOUNT_ERROR); m_wszUserName[0] = 0; } else { m_dwCurrentState=TRANS_STATE; g_PerfCounters.DecPerfCntr(e_gcAuthStateCnt); g_PerfCounters.IncPerfCntr(e_gcTransStateCnt); SendResponse(RESP_AUTH_DONE); } break; case CMD_APOP: char *pPswd; // // BLaPorte - Clear the receive buffer. // SecureZeroMemory(m_pIoContext->m_Buffer,sizeof(m_pIoContext->m_Buffer)); pPswd=strchr( &(m_szCommandBuffer[COMMAND_SIZE+1]), ' '); if(NULL == pPswd) { SecureZeroMemory(m_szCommandBuffer,m_dwCommandSize); SendResponse(RESP_ACCOUNT_ERROR); if(! g_SocketPool.IsMaxSocketUsed() ) { bRetVal=TRUE; } break; } *pPswd='\0'; strncpy(szUserName, &(m_szCommandBuffer[COMMAND_SIZE+1]), sizeof(szUserName)/sizeof(char)-1); szUserName[sizeof(szUserName)/sizeof(char)-1]=0; pPswd++; strncpy(m_szPassword, pPswd, sizeof(m_szPassword)/sizeof(char)-1); m_szPassword[sizeof(m_szPassword)/sizeof(char)-1]=0; SecureZeroMemory(m_szCommandBuffer,m_dwCommandSize); if(strlen(m_szPassword) != MD5_HASH_SIZE ) { SecureZeroMemory(m_szPassword,sizeof(m_szPassword)); SendResponse(RESP_ACCOUNT_ERROR); if(! g_SocketPool.IsMaxSocketUsed() ) { bRetVal=TRUE; } break; } //Do the authentication AnsiToUnicode(szUserName, -1,m_wszUserName, sizeof(m_wszUserName)/sizeof(WCHAR)); bstrUserName=SysAllocString(m_wszUserName); AnsiToUnicode(m_szPassword, -1, wszPassword, sizeof(wszPassword)/sizeof(WCHAR)); SecureZeroMemory(m_szPassword,sizeof(m_szPassword)); wcscat(wszPassword, m_wszGreeting); vPassword.vt=VT_BSTR; vPassword.bstrVal=SysAllocString(wszPassword); SecureZeroMemory(wszPassword,sizeof(wszPassword)); if(NULL != bstrUserName && NULL != vPassword.bstrVal) { if(S_OK == (hr= g_pAuthMethod->Authenticate(bstrUserName, vPassword))) { bRetVal=TRUE; } else if(E_ACCESSDENIED == hr ) { g_EventLogger.LogEvent(LOGTYPE_ERR_WARNING, POP3SVR_MAILROOT_ACCESS_DENIED, m_wszUserName, 1); } } SysFreeString(bstrUserName); SecureZeroMemory(vPassword.bstrVal,SysStringByteLen(vPassword.bstrVal)); SysFreeString(vPassword.bstrVal); //Open the mailbox if(bRetVal) { bRetVal=m_MailBox.OpenMailBox(m_wszUserName); } if (bRetVal) { bRetVal=m_MailBox.LockMailBox(); if(bRetVal) { bRetVal=m_MailBox.EnumerateMailBox(g_dwMaxMsgPerDnld); if(!bRetVal) { m_MailBox.UnlockMailBox(); } } } else { //Open mailbox failed if(ERROR_ACCESS_DENIED==GetLastError()) { //Log the event g_EventLogger.LogEvent(LOGTYPE_ERR_WARNING, POP3SVR_MAILROOT_ACCESS_DENIED, m_wszUserName, 1); } } if (!bRetVal) { g_PerfCounters.IncPerfCntr(e_gcFailedLogonCnt); m_dwFailedAuthCount++; if( MAX_FAILED_AUTH<=m_dwFailedAuthCount ) { g_EventLogger.LogEvent(LOGTYPE_ERR_WARNING, POP3SVR_MAX_LOGON_FAILURES, m_wszUserName, 1); } else { if(! g_SocketPool.IsMaxSocketUsed() ) { bRetVal=TRUE; //Don't disconnect } } SendResponse(RESP_ACCOUNT_ERROR); } else { m_dwCurrentState=TRANS_STATE; g_PerfCounters.DecPerfCntr(e_gcAuthStateCnt); g_PerfCounters.IncPerfCntr(e_gcTransStateCnt); SendResponse(RESP_AUTH_DONE); } break; case CMD_AUTH: if(0==m_dwAuthStatus) { //First time AUTH command //Only when AD/Local SAM Auth is used, //we support NTLM if(AUTH_OTHER==g_dwAuthMethod) { SendResponse(RESP_CMD_NOT_SUPPORTED); bRetVal=TRUE; } else { if(IsEndOfCommand(&(m_szCommandBuffer[COMMAND_SIZE]))) { SendResponse(RESP_AUTH_METHODS); bRetVal=TRUE; } else { szBuf[0]='\0'; if((GetNextStringParameter( &(m_szCommandBuffer[COMMAND_SIZE]), szBuf, POP3_RESPONSE_BUF_SIZE) ) && (0==_stricmp(szBuf, SZ_NTLM)) ) { SendResponse(RESP_OK); m_dwAuthStatus=1; bRetVal=TRUE; } else { SendResponse(RESP_CMD_NOT_VALID); } } } } else // This is specific to auth protocol { char OutBuf[AUTH_BUF_SIZE]; DWORD dwOutBufSize=AUTH_BUF_SIZE; SecureZeroMemory(OutBuf, AUTH_BUF_SIZE); hr=m_AuthServer.HandShake((LPBYTE)m_szCommandBuffer, dwBytesRcvd, (LPBYTE)OutBuf, &dwOutBufSize); if(S_FALSE==hr) { SendResponse(OutBuf); bRetVal=TRUE; } else if(S_OK==hr) { m_dwAuthStatus=0; //Authentication Done! if(S_OK==m_AuthServer.GetUserName(m_wszUserName)) { bRetVal=TRUE; } else { bRetVal=FALSE; } //Now open the mailbox if(bRetVal) { bRetVal=m_MailBox.OpenMailBox(m_wszUserName); } if (bRetVal) { bRetVal=m_MailBox.LockMailBox(); if(bRetVal) { bRetVal=m_MailBox.EnumerateMailBox(g_dwMaxMsgPerDnld); if(!bRetVal) { m_MailBox.UnlockMailBox(); } } } else { //Open mailbox failed if(ERROR_ACCESS_DENIED==GetLastError()) { //Log the event g_EventLogger.LogEvent(LOGTYPE_ERR_WARNING, POP3SVR_MAILROOT_ACCESS_DENIED, m_wszUserName, 1); } } if (!bRetVal) { m_dwAuthStatus=0; m_AuthServer.Cleanup(); g_PerfCounters.IncPerfCntr(e_gcFailedLogonCnt); m_dwFailedAuthCount++; SendResponse(RESP_ACCOUNT_ERROR); if( (MAX_FAILED_AUTH>m_dwFailedAuthCount) && (! g_SocketPool.IsMaxSocketUsed() ) ) { bRetVal=TRUE; //Don't disconnect } } else { m_dwCurrentState=TRANS_STATE; g_PerfCounters.DecPerfCntr(e_gcAuthStateCnt); g_PerfCounters.IncPerfCntr(e_gcTransStateCnt); SendResponse(RESP_AUTH_DONE); bRetVal=TRUE; } } else // Failed Auth { m_dwAuthStatus=0; m_AuthServer.Cleanup(); g_PerfCounters.IncPerfCntr(e_gcFailedLogonCnt); m_dwFailedAuthCount++; SendResponse(RESP_CMD_NOT_VALID); if( (MAX_FAILED_AUTH>m_dwFailedAuthCount) && (! g_SocketPool.IsMaxSocketUsed() ) ) { bRetVal=TRUE; //Don't disconnect } } } break; case CMD_QUIT: if(IsEndOfCommand(&(m_szCommandBuffer[COMMAND_SIZE]))) { g_PerfCounters.DecPerfCntr(e_gcAuthStateCnt); m_dwCurrentState=UPDATE_STATE; if(L'\0'!=g_wszGreeting[0]) { _snprintf(szBuf, POP3_RESPONSE_BUF_SIZE-1, RESP_SERVER_QUIT, g_wszGreeting, m_wszGreeting); } else { _snprintf(szBuf, POP3_RESPONSE_BUF_SIZE-1, RESP_SERVER_QUIT, RESP_SERVER_GREETING, m_wszGreeting); } szBuf[POP3_RESPONSE_BUF_SIZE-1]=0; SendResponse(szBuf); } else { SendResponse(RESP_CMD_NOT_VALID); if(! g_SocketPool.IsMaxSocketUsed() ) { bRetVal=TRUE; } } break; case CMD_STAT: case CMD_LIST: case CMD_RETR: case CMD_DELE: case CMD_UIDL: case CMD_RSET: case CMD_TOP: SendResponse(RESP_CMD_NOT_SUPPORTED); if(! g_SocketPool.IsMaxSocketUsed() ) { bRetVal=TRUE; } break; default: //CountUnknownCommand? SendResponse(RESP_UNKNOWN_COMMAND); if(! g_SocketPool.IsMaxSocketUsed() ) { bRetVal=TRUE;// Still allow client to send another command } } if(bRetVal) { WaitForCommand(); } return bRetVal; } BOOL POP3_CONTEXT::ProcessTransStateCommands(POP3_CMD CurrentCmd, DWORD dwBytesRcvd) { char szReBuf[POP3_RESPONSE_BUF_SIZE]; char szReHelpBuf[POP3_RESPONSE_BUF_SIZE]; DWORD dwLen=0; DWORD dwCurLen; BOOL bRetVal=TRUE; int iArg=-1; int iArg2=-1; int iMailCount=0; DWORD dwResult; char *pCur=NULL; //The buffer will always be NULL terminated szReBuf[POP3_RESPONSE_BUF_SIZE-1]='\0'; switch (CurrentCmd) { case CMD_STAT:if(!IsEndOfCommand(&(m_szCommandBuffer[COMMAND_SIZE]))) { SendResponse(RESP_CMD_NOT_VALID); } else if(0 >_snprintf(szReBuf, POP3_RESPONSE_BUF_SIZE-1, "+OK %d %d\r\n", m_MailBox.GetCurrentMailCount(), m_MailBox.GetTotalSize())) { //This should not happen //EventLog?? SendResponse(RESP_SERVER_ERROR); } else { szReBuf[POP3_RESPONSE_BUF_SIZE-1]=0; SendResponse(szReBuf); } WaitForCommand(); break; case CMD_LIST:pCur=&(m_szCommandBuffer[COMMAND_SIZE]); if(!IsEndOfCommand(pCur)) { if( (GetNextNumParameter(&pCur, &iArg)) && (IsEndOfCommand(pCur)) ) { dwResult=m_MailBox.ListMail(iArg-1, szReBuf, sizeof(szReBuf)/sizeof(char)); SendResponse(dwResult, szReBuf); } else { SendResponse(RESP_CMD_NOT_VALID); } } else { if(0> _snprintf(szReBuf, POP3_RESPONSE_BUF_SIZE-1, "+OK %d messages (%d octets)\r\n", m_MailBox.GetCurrentMailCount(), m_MailBox.GetTotalSize())) { //This should not happen //EventLog?? SendResponse(RESP_SERVER_ERROR); } else { iMailCount=m_MailBox.GetMailCount(); dwLen=strlen(szReBuf); for(iArg=0; iArg _snprintf(szReBuf, POP3_RESPONSE_BUF_SIZE-1, "+OK %d messages (%d octets)\r\n", m_MailBox.GetCurrentMailCount(), m_MailBox.GetTotalSize())) { //This should not happen //EventLog?? SendResponse(RESP_SERVER_ERROR); } else { iMailCount=m_MailBox.GetMailCount(); dwLen=strlen(szReBuf); for(iArg=0; iArgm_hAsyncIO!=NULL); if(SOCKET_ERROR == send(m_pIoContext->m_hAsyncIO, szBuf, strlen(szBuf), 0)) { iErr=WSAGetLastError(); //Can not send through the socket //The connection will be terminated later //in the WaitForCommand call. } } void POP3_CONTEXT::SendResponse(DWORD dwResult, char *szBuf) { char szResp[POP3_RESPONSE_BUF_SIZE]; if(ERROR_SUCCESS == dwResult) { if( 0 > _snprintf(szResp, POP3_RESPONSE_BUF_SIZE-1, "+OK %s", szBuf)) { SendResponse(RESP_SERVER_ERROR); } else { szResp[POP3_RESPONSE_BUF_SIZE-1]=0; SendResponse(szResp); } } else { SendResponse(RESP_INVALID_MAIL_NUMBER); } } BOOL POP3_CONTEXT::GetNextStringParameter(char *szInput, char *szOutput, DWORD dwOutputSize) { ASSERT(szInput!=NULL); ASSERT(szOutput!=NULL); //Must have at lease one space if(!isspace(*szInput)) { return FALSE; } do { szInput++; }while(isspace(*szInput)); if('\0'==*szInput) { return FALSE; } // // BLaPorte - added output size parameter to prevent buffer overflow. // if (strlen(szInput) >= dwOutputSize) { return FALSE; } strcpy(szOutput,szInput); return TRUE; } BOOL POP3_CONTEXT::GetNextNumParameter(char **pszInput, int *piOutput) { ASSERT(pszInput!=NULL); ASSERT(*pszInput!=NULL); char *szInput=*pszInput; char *szEndInput=NULL; if(!isspace(*szInput)) { return FALSE; } do { szInput++; }while(isspace(*szInput)); szEndInput=szInput; if(!isdigit(*szEndInput)) { return FALSE; } do { szEndInput++; }while(isdigit(*szEndInput)); if((szEndInput-szInput) > MAX_INT_LEN ) { return FALSE; } *piOutput=atoi(szInput); *pszInput=szEndInput; return TRUE; } BOOL POP3_CONTEXT::IsEndOfCommand(char *szInput) { ASSERT(szInput!=NULL); while(isspace(*szInput)) { szInput++; } if('\0'==*szInput) { return TRUE; } else { return FALSE; } } BOOL POP3_CONTEXT::Unauthenticated() { return ( m_dwCurrentState==AUTH_STATE ); }