/************************************************************** SocketOnConnected(SOCKET s, const char *lpszName) If the security is enabled then perform te SSL neogotiation otherwise just return a success and let the plain text FTP handles the comunication To let the server know that we are looking for SSL or TLS we need to send the following command FEAT The Feature negotiation mechanism for the File Transfer Protocol To ask the server for SSL or TLS negotiation we will send the AUTH command. A parameter for the AUTH command to indicate that TLS is required. It is recommended that 'TLS', 'TLS-C', 'SSL' and 'TLS-P' are acceptable, and mean the following :- 'TLS' or 'TLS-C' - the TLS protocol or the SSL protocol will be negotiated on the control connection. The default protection setting for the Data connection is 'Clear'. 'SSL' or 'TLS-P' - the TLS protocol or the SSL protocol will be negotiated on the control connection. The default protection setting for the Data connection is 'Private'. This is primarily for backward compatibility. Notice that we will first send a TLS P to select The highest implementation. the server might response with the response 503 unknown security mechanism if this happened we will issue an Auth SSL command. Param: SOCKET s - The newly created socket lpszName - apointer to the host name we are attempting to connect to Return: UTE_NO_RESPONSE - Server did not response to our command UTE_CONNECT_FAIL_NO_SSL_SUPPORT - Server does not support SSL. UTE_CONNECT_FAILED - The connection have failed security errors - This function may fail with other error UTE_LOAD_SECURITY_LIBRARIES_FAILED UTE_OUT_OF_MEMORY UTE_FAILED_TO_GET_SECURITY_STREAM_SIZE UTE_OUT_OF_MEMORY UTE_FAILED_TO_QUERY_CERTIFICATE UTE_NULL_PARAM UTE_PARAMETER_INVALID_VALUE UTE_FAILED_TO_GET_CERTIFICATE_CHAIN UTE_FAILED_TO_VERIFY_CERTIFICATE_CHAIN UTE_FAILED_TO_VERIFY_CERTIFICATE_TRUST UTE_POP3_TLS_NOT_SUPPORTED - server does not support the STLS command **************************************************************/ int CUT_POP3Client::SocketOnConnected(SOCKET s, const char *lpszName){ int rt = UTE_SUCCESS; #ifdef CUT_SECURE_SOCKET BOOL bSecFlag = GetSecurityEnabled(); if(bSecFlag) { // disable the secure comunication so we can send the plain data SetSecurityEnabled(FALSE); // get server response if( !GetResponseCode(m_nPOP3TimeOut)) rt = UTE_CONNECT_FAILED; else{ Send("STLS\r\n"); // get response if( GetResponseCode(m_nPOP3TimeOut)) { // reset back the security so we can proceed with negotiation SetSecurityEnabled(bSecFlag); rt = CUT_SecureSocketClient::SocketOnConnected(s, lpszName); } else rt = UTE_POP3_TLS_NOT_SUPPORTED; } return rt; } else { return UTE_SUCCESS; } #else // v4.2 non-secure builds result in unref'd param warns VC6 UNREFERENCED_PARAMETER(s); UNREFERENCED_PARAMETER(lpszName); // v4.2 unreachable code in secure build return OnError(rt); // v4.22 check this - was retuning UTE_SUCCESS #endif }
/******************************** OnConnect This function is called when a new connection comes in. This routine processes all commands from the connection and only returns when the connection is finished Params none Return none Revisions: Added string length limitations as enforced by RFC 821 There are several objects that have required minimum maximum sizes. That is, every implementation must be able to receive objects of at least these sizes, but must not send objects larger than these sizes. **************************************************** * * * TO THE MAXIMUM EXTENT POSSIBLE, IMPLEMENTATION * * TECHNIQUES WHICH IMPOSE NO LIMITS ON THE LENGTH * * OF THESE OBJECTS SHOULD BE USED. * * * **************************************************** user The maximum total length of a user name is 64 characters. domain The maximum total length of a domain name or number is 64 characters. path The maximum total length of a reverse-path or forward-path is 256 characters (including the punctuation and element separators). command line The maximum total length of a command line including the command word and the <CRLF> is 512 characters. reply line The maximum total length of a reply line including the reply code and the <CRLF> is 512 characters. recipients buffer The maximum total number of recipients that must be buffered is 100 recipients. *********************************/ void CUT_SMTPThread::OnConnect() { char szMailFrom[WSS_BUFFER_SIZE + 1]; char szHeloName[WSS_BUFFER_SIZE + 1]; char szBuffer[WSS_LINE_BUFFER_SIZE + 1]; char szRcpt[WSS_LINE_BUFFER_SIZE + 1]; int nLength = 0; int nNumBadCommands = 0; int bQuit = FALSE; SMTPCommandID nLastCommand = CMD_UNKNOWN_SMTP; SMTPCommandID nCommand = CMD_UNKNOWN_SMTP; CUT_MailServer *ptrMailServer = ((CUT_SMTPServer *)m_winsockclass_this)->m_ptrMailServer; CUT_DataManager *ptrDataManager = ptrMailServer->m_ptrDataManager; CUT_StringList m_listRcpt; BOOL greeted = FALSE; time_t ltime; char szReceivedHeader[2*WSS_LINE_BUFFER_SIZE]; if(ptrMailServer->GetSMTPServer()->GetShutDownFlag()) return; time( <ime ); ZeroMemory( szBuffer, sizeof(szBuffer)); _snprintf(szBuffer,sizeof(szBuffer)-1, "**** Client [%s] connected at (%s) ***** ", GetClientAddress(), (const char *)ctime( <ime )); CUT_StrMethods::RemoveCRLF (szBuffer); ptrMailServer->OnStatus(szBuffer); #ifdef CUT_SECURE_SOCKET BOOL bHandshakeDone = FALSE; // Disable security while connecting BOOL bSecureFlag = GetSecurityEnabled(); if(!ptrMailServer->GetSMTPServer()->m_bImmediateNegotiation) SetSecurityEnabled(FALSE); else bHandshakeDone = TRUE; #endif // Send inital message ZeroMemory( szBuffer, sizeof(szBuffer)); _snprintf(szBuffer,sizeof(szBuffer)-1,"220 %s Service Ready. Ultimate TCP/IP Enterprise Edition\r\n",ptrMailServer->Sys_GetLocalName(0)); Send(szBuffer); // Main message pump while(bQuit == FALSE) { if(ptrMailServer->GetSMTPServer()->GetShutDownFlag()) { ZeroMemory( szBuffer, sizeof(szBuffer)); _snprintf(szBuffer,sizeof(szBuffer)-1,"421 %s Service not available, closing transmission channel\r\n",ptrMailServer->Sys_GetLocalName(0)); Send(szBuffer); break; } ptrMailServer->OnStatus("NEW Command:"); if (nNumBadCommands > 10) break; // Receive a line from the client ZeroMemory( szBuffer, sizeof(szBuffer)); nLength = ReceiveLine(szBuffer, WSS_LINE_BUFFER_SIZE); // Exit on an error if(nLength <= 0) break; // command line // The maximum total length of a command line including the // command word and the <CRLF> is 512 characters. if(nLength > 512) { Send("500 Line too long.\r\n"); ptrMailServer->OnStatus("500 Line too long."); continue; } CUT_StrMethods::RemoveCRLF(szBuffer); ptrMailServer->OnStatus(szBuffer); // Store the last command nLastCommand = nCommand; // Get the command that was sent nCommand = GetCommand(szBuffer); #ifdef CUT_SECURE_SOCKET // Only this commands allowed without establishing secure connection if( nCommand != CMD_SMTPNOOP && nCommand != CMD_SMTPSTARTTLS && nCommand != CMD_SMTPEHLO && nCommand != CMD_SMTPQUIT) { if(!bHandshakeDone && bSecureFlag ) { if(!OnNonSecureConnection(GetClientAddress())) { Send("530 Must issue a STARTTLS command first\r\n"); ptrMailServer->OnStatus("Must issue a STARTTLS command first"); continue; } } } #endif // Check to see if it is a command that can run without logging in switch(nCommand) { case CMD_SMTPHELO: { // Get the param *szHeloName = NULL; //\b Backspace \f Formfeed \n New line \r Carriage return \t Horizontal tab \v // CUT_StrMethods::ParseString(szBuffer," \t\b\v\f",1, szHeloName,sizeof(szHeloName)-1); // the domain name must be sorter than // The maximum total length of a domain name or number is 64 // characters // see rfc 821 section // 4.5.3. SIZES if (szHeloName == 0 || strlen(szHeloName) < 2 ) { Send("501 Syntax Error In Parameters Or Arguments.\r\n"); nNumBadCommands++; ptrMailServer->OnStatus("501 Syntax Error In Parameters Or Arguments."); nCommand = nLastCommand ; continue; } CUT_StrMethods::RemoveCRLF (szHeloName); CUT_StrMethods::RemoveSpaces (szHeloName); if (szHeloName == 0 || strlen(szHeloName) > 64 ) { Send("501 Syntax Error In Parameters Or Arguments.\r\n"); ptrMailServer->OnStatus("501 Syntax Error In Parameters Or Arguments"); nNumBadCommands++; nCommand = nLastCommand ; continue; } // Send acknowlegment time( <ime ); char szTimeStr[29]; ZeroMemory( szTimeStr, 29 ); // The string result produced by ctime contains exactly 26 characters and has the form: // Wed Jan 02 02:03:55 1980\n\0 // So we need 29 _snprintf(szTimeStr,sizeof(szTimeStr)-1,"at %s", (const char *)ctime( <ime )); // A 24-hour clock is used. // All fields have a constant width. // The newline character ('\n') and the null character // ('\0') occupy the last two positions of the string. CUT_StrMethods::RemoveCRLF (szTimeStr); // ok you may wonder what was that about //I found out that ctime adds a \n to the end // so I wanted to remove any \r or \n // the add a new one ZeroMemory( szReceivedHeader, sizeof(szReceivedHeader)-1 ); _snprintf(szReceivedHeader,sizeof(szReceivedHeader)-1,"Received: from %s [%s];\r\n\tby %s %s;\r\n\twith Codepro Mail Server (R2hhemkgVyBOb3YgMDYA);\r\n\tPowered by Ultimate TCP-IP v4.2 Enterprise Edition (www.theultimatetoolbox.com).\r\n", szHeloName, GetClientAddress(), ptrMailServer->Sys_GetLocalName(0), szTimeStr); ZeroMemory( szBuffer, sizeof(szBuffer)); _snprintf(szBuffer,sizeof(szBuffer)-1,"250 %s %s [%s] \r\n",ptrMailServer->Sys_GetLocalName(0), szHeloName, GetClientAddress()); greeted = TRUE; Send(szBuffer); ptrMailServer->OnStatus(szBuffer); continue; } case CMD_SMTPHELP: OnHelpCommand(); continue; case CMD_SMTPQUIT: Send("221 Goodbye\r\n"); ptrMailServer->OnStatus("QUIT"); bQuit = TRUE; break; case CMD_SMTPNOOP: Send("250 Requested mail action ok, completed\r\n"); continue; case CMD_SMTPRSET: m_listRcpt.ClearList(); Send("250 Ok\r\n"); continue; case CMD_SMTPEHLO: { // Get the param // Get the param *szHeloName = NULL; //\b Backspace \f Formfeed \n New line \r Carriage return \t Horizontal tab \v // CUT_StrMethods::ParseString(szBuffer," \t\b\v\f",1, szHeloName,sizeof(szHeloName)-1); // the domain name must be sorter than // The maximum total length of a domain name or number is 64 // characters // see rfc 821 section // 4.5.3. SIZES if (szHeloName == 0 || strlen(szHeloName) < 2 ) { Send("501 Syntax Error In Parameters Or Arguments.\r\n"); nNumBadCommands++; ptrMailServer->OnStatus("501 Syntax Error In Parameters Or Arguments."); nCommand = nLastCommand ; continue; } CUT_StrMethods::RemoveCRLF (szHeloName); CUT_StrMethods::RemoveSpaces (szHeloName); if (szHeloName == 0 || strlen(szHeloName) > 64 ) { Send("501 Syntax Error In Parameters Or Arguments.\r\n"); ptrMailServer->OnStatus("501 Syntax Error In Parameters Or Arguments"); nNumBadCommands++; nCommand = nLastCommand ; continue; } char szTimeStr[29]; ZeroMemory( szTimeStr, sizeof(szTimeStr)); // Send acknowlegment time( <ime ); _snprintf(szTimeStr,sizeof(szTimeStr)-1,"at %s", (const char *)ctime( <ime )); // A 24-hour clock is used. // All fields have a constant width. // The newline character ('\n') and the null character // ('\0') occupy the last two positions of the string. CUT_StrMethods::RemoveCRLF (szTimeStr); // ok you may wonder what was that about //I found out that ctime adds a \n to the end // so I wanted to remove any \r or \n // the add a new one ZeroMemory( szReceivedHeader, sizeof(szReceivedHeader)-1 ); _snprintf(szReceivedHeader,sizeof(szReceivedHeader)-1,"Received: from %s [%s];\r\n\tby %s %s;\r\n\twith Ultimate TCP/IP Mail Server ;\r\n\tPowered by Ultimate TCP/IP v4.2 Enterprise Edition (www.theultimatetoolbox.com).\r\n", szHeloName, GetClientAddress(), ptrMailServer->Sys_GetLocalName(0), szTimeStr); // Send acknowlegment #ifdef CUT_SECURE_SOCKET ZeroMemory( szBuffer, sizeof(szBuffer)); _snprintf(szBuffer,sizeof(szBuffer)-1,"250-%s %s [%s] \r\n250 STARTTLS\r\n",ptrMailServer->Sys_GetLocalName(0), szHeloName, GetClientAddress()); #else ZeroMemory( szBuffer, sizeof(szBuffer)); _snprintf(szBuffer,sizeof(szBuffer)-1,"250 %s %s [%s] \r\n",ptrMailServer->Sys_GetLocalName(0), szHeloName, GetClientAddress()); #endif greeted = TRUE; Send(szBuffer); ptrMailServer->OnStatus(szBuffer); continue; } case CMD_SMTPSTARTTLS: #ifdef CUT_SECURE_SOCKET if(bSecureFlag) { // Already done if(bHandshakeDone) { Send("502 Command not permitted when TLS active\r\n"); continue; } // Send an OK command Send("220 Ready to start TLS\r\n"); // Set security flag SetSecurityEnabled(TRUE); // Start negotiation int nResult = CUT_SecureSocket::SocketOnConnected(m_clientSocket, ""); if (nResult != UTE_SUCCESS) { SetSecurityEnabled(FALSE); Send("554 The SSL negotiation failed during the handshake"); bQuit = TRUE; continue; } else { bHandshakeDone = TRUE; continue; } } else Send("454 TLS not available due to temporary reason\r\n"); #else Send("454 TLS not available due to temporary reason\r\n"); #endif continue; case CMD_SMTPMAIL: { if (!greeted) { Send("503 Bad sequence of commands. Nice People Say Helo first\r\n"); continue; } // char firstString[WSS_LINE_BUFFER_SIZE]; // Special case "MAIL FROM: <>" if (strstr(szBuffer,"<>")) { strcpy(szMailFrom,szHeloName); } else { // Get number of components int counter = 0; int piecesCount= 0; piecesCount = CUT_StrMethods::GetParseStringPieces (szBuffer, "\t\b\v\f: ,\r\n"); for (; counter < piecesCount;counter ++) { if ( CUT_StrMethods::ParseString (szBuffer,"\t\b\v\f: ,\r\n",counter, firstString,sizeof(firstString)) == UTE_SUCCESS) { // does it include an @ sign // then there should be 2 peices if ( CUT_StrMethods::GetParseStringPieces (firstString, "@") > 1){ // now remove the > and < signs from the email // this function call should pass even if the line does not include // a <> tag CUT_StrMethods::ParseString (firstString,"<>",0, szMailFrom,sizeof(szMailFrom)); break; } } } // if we have reached the number of peices // then the from is not found // then we should let the client know of the error if (counter == piecesCount) { Send("501 Syntax Error In Parameters Or Arguments.\r\n"); nCommand = nLastCommand ; continue; } } m_listRcpt.ClearList(); // Send acknowlegment ZeroMemory( szBuffer, sizeof(szBuffer)); _snprintf(szBuffer,sizeof(szBuffer)-1,"250 OK it is from %s\r\n",szMailFrom); Send(szBuffer); ptrMailServer->OnStatus(szBuffer); continue; } case CMD_SMTPRCPT: { if (!greeted) { Send("503 Bad sequence of commands. Nice People Say Helo first\r\n"); ptrMailServer->OnStatus("503 Bad sequence of commands. Nice People Say Helo first");; continue; } if(nLastCommand != CMD_SMTPRCPT && nLastCommand != CMD_SMTPMAIL) { Send("503 Bad Sequence Of Commands.\r\n"); ptrMailServer->OnStatus("503 Bad sequence of commands.");; nCommand = nLastCommand ; continue; } // char firstString[WSS_LINE_BUFFER_SIZE]; // Get number of components int counter = 0; int piecesCount= 0; piecesCount = CUT_StrMethods::GetParseStringPieces (szBuffer, "\t\b\v\f: ,\r\n"); for (; counter < piecesCount;counter ++) { if ( CUT_StrMethods::ParseString (szBuffer,"\t\b\v\f: ,\r\n",counter, firstString,sizeof(firstString)) == UTE_SUCCESS) { // does it include an @ sign // then there should be 2 peices if ( CUT_StrMethods::GetParseStringPieces (firstString, "@") > 1){ // now remove the > and < signs from the email address // this function call should pass even if the line does not include // a <> tag CUT_StrMethods::ParseString (firstString,"<>",0, szRcpt,sizeof(szRcpt)); break; } } } // if we have reached the number of peices // then the to is not found // then we should let the client know of the error if (counter == piecesCount) { Send("501 Syntax Error In Parameters Or Arguments.\r\n"); ptrMailServer->OnStatus("501 Syntax Error In Parameters Or Arguments..");; nCommand = nLastCommand ; continue; } // recipients buffer // The maximum total number of recipients that must be // buffered is 100 recipients. if (m_listRcpt.GetCount () >= 100) { Send("552 Too many recipients.\r\n"); ptrMailServer->OnStatus("552 Too many recipients."); nCommand = nLastCommand ; continue; } // Add the name to the rcpt list // GetRelay here if (!CheckRelay(szMailFrom,szRcpt,szHeloName)) { Send("550 Relaying not allowed .\r\n"); ptrMailServer->OnStatus("550 Relaying not allowed."); nNumBadCommands++; nCommand = nLastCommand ; continue; } m_listRcpt.AddString(szRcpt); // Send acknowlegment Send("250 OK it is for "); Send(szRcpt); Send(" \r\n"); ZeroMemory( szBuffer, sizeof(szBuffer)); _snprintf(szBuffer,sizeof(szBuffer)-1,"Ok so it is for %s" ,szRcpt); ptrMailServer->OnStatus(szBuffer); continue; } case CMD_SMTPDATA: { if(nLastCommand != CMD_SMTPRCPT) { Send("503 Bad Sequence Of Commands.\r\n"); ptrMailServer->OnStatus("503 Bad Sequence Of Commands"); nNumBadCommands++; continue; } Send("354 Start mail input, end with <CRLF>.<CRLF>\r\n"); long fileHandle = ptrDataManager->Que_CreateFile(); if(INVALID_HANDLE_VALUE == (void*)(ULONG_PTR)fileHandle) { bQuit = TRUE; // Hard kill - read what's left of transaction and discard... ZeroMemory( szBuffer, sizeof(szBuffer)); while(ReceiveLine(szBuffer,sizeof(szBuffer)-1)); Send("451 Requested action aborted - error in processing\r\n"); ptrMailServer->OnStatus("SMTPDATA error - Requested action aborted "); break; } BOOLEAN deleteFlag = FALSE; // Write the message header ptrDataManager->Que_WriteFileHeader(fileHandle,m_listRcpt.GetString(0L),szMailFrom,0,0); // Read lines in until <CRLF>.<CRLF> and save it // Check header portion for message id - if none, // add one. int len; BOOL IdOk = FALSE; BOOL EndOfHeaderSeen = FALSE; // the return path is the first thing to be written ptrDataManager->Que_WriteFile(fileHandle,(LPBYTE)szReceivedHeader,(int)strlen(szReceivedHeader)); for(;;) { if(ptrMailServer->GetSMTPServer()->GetShutDownFlag()) { Send("421 Service not available, closing transmission channel\r\n"); ptrMailServer->OnStatus("421 Service not available, closing transmission channel"); deleteFlag = TRUE; break; } ZeroMemory( szBuffer, sizeof(szBuffer)-1); len = ReceiveLine(szBuffer,sizeof(szBuffer)-1); if(len <= 0) { Send("554 Transaction failed\r\n"); ptrMailServer->OnStatus("SMTPDATA error 554 Transaction failed"); deleteFlag = TRUE; break; } // make sure we have enough chars if (szBuffer[0] != 0 && strlen(szBuffer) > 11) { if(!EndOfHeaderSeen) { if(_strnicmp(szBuffer, "Message-Id:",11) == 0) IdOk = TRUE; } } if(len < 3) { EndOfHeaderSeen = TRUE; // Did this message have a valid ID? if(!IdOk) { char szMsgID[WSS_LINE_BUFFER_SIZE + 1]; // Create a unique ID and write to the file! ptrMailServer->BuildUniqueID(szMsgID, sizeof(szMsgID)-1); ptrDataManager->Que_WriteFile(fileHandle,(LPBYTE)"Message-ID: ",12); strcat(szMsgID, "\r\n"); ptrDataManager->Que_WriteFile(fileHandle,(LPBYTE)szMsgID, (int)strlen(szMsgID)); IdOk = TRUE; } } ptrDataManager->Que_WriteFile(fileHandle,(LPBYTE)szBuffer,len); if(len == 3) { if(szBuffer[0]=='.' && szBuffer[1]=='\r' && szBuffer[2]=='\n') { Send("250 Mail Received OK\r\n"); break; } } } ptrMailServer->OnStatus("Data finished"); // Make sure that the server is not relaying messages to itself, // it is possible for the server to relay a message to itself // when the destination of the email contains a domain name // that resolves to the same IP as the server but the names // are different. // v4.2 using AC here - names now _TCHAR") if ( strcmp( AC(ptrMailServer->Sys_GetLocalName(0)), szHeloName ) == 0 ) { // Bad message found, receive it but also delete it from the queue // to prevent further resending of the message. deleteFlag = TRUE; } // Get the number of rcpt to lines // Copy the original file for each rcpt in the rcpt list if(!deleteFlag) for(int loop = 1;loop < m_listRcpt.GetCount(); loop++) // bypass first - it's done ptrDataManager->Que_CarbonCopyFile(fileHandle,m_listRcpt.GetString(loop)); // Close the file ptrDataManager->Que_CloseFile(fileHandle,deleteFlag); continue; } default: Send("502 Command not implemented\r\n"); // Break after 5 unreconized commands nNumBadCommands ++; if(nNumBadCommands == 5) { bQuit = TRUE; break; } } } CloseConnection(); }
const char *Frame::ToInfoString(char *aBuf, uint16_t aSize) const { uint8_t type, commandId; Address src, dst; const char *typeStr; char stringBuffer[10]; char srcStringBuffer[Address::kAddressStringSize]; char dstStringBuffer[Address::kAddressStringSize]; type = GetType(); switch (type) { case kFcfFrameBeacon: typeStr = "Beacon"; break; case kFcfFrameData: typeStr = "Data"; break; case kFcfFrameAck: typeStr = "Ack"; break; case kFcfFrameMacCmd: if (GetCommandId(commandId) != OT_ERROR_NONE) { commandId = 0xff; } switch (commandId) { case kMacCmdDataRequest: typeStr = "Cmd(DataReq)"; break; case kMacCmdBeaconRequest: typeStr = "Cmd(BeaconReq)"; break; default: snprintf(stringBuffer, sizeof(stringBuffer), "Cmd(%d)", commandId); typeStr = stringBuffer; break; } break; default: snprintf(stringBuffer, sizeof(stringBuffer), "%d", type); typeStr = stringBuffer; break; } GetSrcAddr(src); GetDstAddr(dst); snprintf(aBuf, aSize, "len:%d, seqnum:%d, type:%s, src:%s, dst:%s, sec:%s, ackreq:%s", GetLength(), GetSequence(), typeStr, src.ToString(srcStringBuffer, sizeof(srcStringBuffer)), dst.ToString(dstStringBuffer, sizeof(dstStringBuffer)), GetSecurityEnabled() ? "yes" : "no", GetAckRequest() ? "yes" : "no"); return aBuf; }