📄 connectsocket.cpp
字号:
/********************************************************************/
/* */
/* CONNECTSOCKET.CPP */
/* */
/* Implementation of the Connect Socket. */
/* This class is a part of the CConnectThread which handles */
/* socket connections. Incomming data is processed in OnReceive */
/* */
/* Programmed by Pablo van der Meer */
/* Based partially on and inspired by FileZilla Server. */
/* */
/* http://www.pablovandermeer.nl */
/* */
/* Last updated: 15 july 2002 */
/* */
/********************************************************************/
#include "stdafx.h"
#include "FTPServerApp.h"
#include "FTPServer.h"
#include "ConnectSocket.h"
#include "ConnectThread.h"
#include "ApplicationDlg.h"
#include "DataSocket.h"
extern CFTPServer theServer;
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/********************************************************************/
/* */
/* Function name : CConnectSocket::CConnectSocket */
/* Description : Constructor */
/* */
/********************************************************************/
CConnectSocket::CConnectSocket()
{
m_bLoggedon = FALSE;
m_bRenameFile = FALSE;
m_pDataSocket = NULL;
m_strRemoteHost = "";
m_nRemotePort = -1;
m_dwRestartOffset = 0;
m_bPassiveMode = FALSE;
}
/********************************************************************/
/* */
/* Function name : CConnectSocket::~CConnectSocket */
/* Description : Destructor */
/* */
/********************************************************************/
CConnectSocket::~CConnectSocket()
{
DestroyDataConnection();
// tell our thread we have been closed
AfxGetThread()->PostThreadMessage(WM_QUIT,0,0);
TRACE0("CConnectSocket destroyed.\n");
}
// Do not edit the following lines, which are needed by ClassWizard.
#if 0
BEGIN_MESSAGE_MAP(CConnectSocket, CSocket)
//{{AFX_MSG_MAP(CConnectSocket)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
#endif // 0
/********************************************************************/
/* */
/* Function name : OnClose */
/* Description : Send WM_QUIT message to the thread containing */
/* the socket to shutdown once the connection is */
/* closed. */
/* */
/********************************************************************/
void CConnectSocket::OnClose(int nErrorCode)
{
Close();
// destroy connection
m_pThread->PostThreadMessage(WM_THREADMSG, 1, 0);
TRACE("CConnectSocket() OnClose()\n");
CSocket::OnClose(nErrorCode);
}
#define BUFFERSIZE 4096
/********************************************************************/
/* */
/* Function name : OnReceive */
/* Description : Called by the framework to notify this socket */
/* that there is data in the buffer. */
/* */
/********************************************************************/
void CConnectSocket::OnReceive(int nErrorCode)
{
TCHAR buff[BUFFERSIZE];
int nRead = Receive(buff, BUFFERSIZE);
switch (nRead)
{
case 0:
Close();
break;
case SOCKET_ERROR:
if (GetLastError() != WSAEWOULDBLOCK)
{
TCHAR szError[256];
wsprintf(szError, "OnReceive error: %d", GetLastError());
AfxMessageBox (szError);
}
break;
default:
if (nRead != SOCKET_ERROR && nRead != 0)
{
((CConnectThread *)AfxGetThread())->IncReceivedBytes(nRead);
// terminate the string
buff[nRead] = 0;
m_RxBuffer += CString(buff);
GetRxLine();
}
break;
}
CSocket::OnReceive(nErrorCode);
}
/********************************************************************/
/* */
/* Function name: GetRxCommand */
/* Description : Get command from receiver buffer. */
/* */
/********************************************************************/
BOOL CConnectSocket::GetRxCommand(CString &strCommand, CString &strArguments)
{
if (!m_strCommands.IsEmpty())
{
CString strBuff = m_strCommands.RemoveHead();
int nIndex = strBuff.Find(" ");
if (nIndex != -1)
{
CString strPassword = strBuff;
strPassword.MakeUpper();
// make password invisible
if (strPassword.Left(5) == "PASS ")
{
for (int i=5; i < strPassword.GetLength(); i++)
{
strPassword.SetAt(i, '*');
}
FireStatusMessage(strPassword, 1);
}
else
{
FireStatusMessage(strBuff, 1);
}
strCommand = strBuff.Left(nIndex);
strArguments = strBuff.Mid(nIndex+1);
}
else
{
FireStatusMessage(strBuff, 1);
strCommand = strBuff;
}
if (strCommand != "")
{
strCommand.MakeUpper();
// who screwed up ???
if (strCommand.Right(4) == "ABOR")
{
strCommand = "ABOR";
}
TRACE2("COMMAND: %s, ARGS: %s\n", strCommand, strArguments);
return TRUE;
}
}
return FALSE;
}
/********************************************************************/
/* */
/* Function name: GetRxLine */
/* Description : Parse complete command line */
/* */
/********************************************************************/
void CConnectSocket::GetRxLine()
{
CString strTemp;
int nIndex;
while(!m_RxBuffer.IsEmpty())
{
nIndex = m_RxBuffer.Find("\r\n");
if (nIndex != -1)
{
strTemp = m_RxBuffer.Left(nIndex);
m_RxBuffer = m_RxBuffer.Mid(nIndex + 2);
if (!strTemp.IsEmpty())
{
m_strCommands.AddTail(strTemp);
// parse and execute command
ParseCommand();
}
}
else
break;
}
}
/********************************************************************/
/* */
/* Function name: OnConnect */
/* Description : Called by the framework to notify this connecting */
/* socket that its connection attempt is completed. */
/* */
/********************************************************************/
void CConnectSocket::OnConnect(int nErrorCode)
{
CSocket::OnConnect(nErrorCode);
}
/********************************************************************/
/* */
/* Function name: HasConnectionDropped */
/* Description : Check if connection has been dropped. */
/* Used to detect if client has crashed. */
/* */
/********************************************************************/
BOOL CConnectSocket::HasConnectionDropped(void)
{
BOOL bConnDropped = FALSE;
INT iRet = 0;
BOOL bOK = TRUE;
if (m_hSocket == INVALID_SOCKET)
return TRUE;
struct timeval timeout = { 0, 0 };
fd_set readSocketSet;
FD_ZERO(&readSocketSet);
FD_SET(m_hSocket, &readSocketSet);
iRet = ::select(0, &readSocketSet, NULL, NULL, &timeout);
bOK = (iRet > 0);
if(bOK)
{
bOK = FD_ISSET(m_hSocket, &readSocketSet);
}
if(bOK)
{
CHAR szBuffer[1] = "";
iRet = ::recv(m_hSocket, szBuffer, 1, MSG_PEEK);
bOK = (iRet > 0);
if(!bOK)
{
INT iError = ::WSAGetLastError();
bConnDropped = (( iError == WSAENETRESET) ||
(iError == WSAECONNABORTED) ||
(iError == WSAECONNRESET) ||
(iError == WSAEINVAL) ||
(iRet == 0));
}
}
return(bConnDropped);
}
/********************************************************************/
/* */
/* Function name: SendResponse */
/* Description : Send response to client. */
/* */
/********************************************************************/
BOOL CConnectSocket::SendResponse(LPCTSTR pstrFormat, ...)
{
CString str;
// format arguments and put them in CString
va_list args;
va_start(args, pstrFormat);
str.FormatV(pstrFormat, args);
// is connection still active ?
if (HasConnectionDropped())
{
FireStatusMessage("Could not send reply, disconnected.", 2);
Close();
// tell our thread we have been closed
// destroy connection
m_pThread->PostThreadMessage(WM_THREADMSG, 1, 0);
return FALSE;
}
int nBytes = CSocket::Send(str + "\r\n", str.GetLength()+2);
if (nBytes == SOCKET_ERROR)
{
Close();
FireStatusMessage("Could not send reply, disconnected.", 2);
// tell our thread we have been closed
m_pThread->PostThreadMessage(WM_THREADMSG, 1, 0);
return FALSE;
}
FireStatusMessage(str, 2);
((CConnectThread *)AfxGetThread())->IncSentBytes(nBytes);
return TRUE;
}
/********************************************************************/
/* */
/* Function name: ParseCommand */
/* Description : Parse and execute command from client. */
/* */
/* Based on code provided by FileZilla Server. */
/* http://sourceforge.net/projects/filezilla */
/* */
/********************************************************************/
void CConnectSocket::ParseCommand()
{
static CFTPCommand commandList[] =
{
{TOK_ABOR, "ABOR", FALSE, "Abort transfer: ABOR"},
{TOK_BYE, "BYE", FALSE, "Logout or break the connection: BYE"},
{TOK_CDUP, "CDUP", FALSE, "Change to parent directory: CDUP"},
{TOK_CWD, "CWD", TRUE, "Change working directory: CWD [directory-name]"},
{TOK_DELE, "DELE", TRUE , "Delete file: DELE file-name"},
{TOK_DIR, "DIR", FALSE, "Get directory listing: DIR [path-name]"},
{TOK_HELP, "HELP", FALSE, "Show help: HELP [command]"},
{TOK_LIST, "LIST", FALSE, "Get directory listing: LIST [path-name]"},
{TOK_MKD, "MKD", TRUE, "Make directory: MKD path-name"},
{TOK_NOOP, "NOOP", FALSE, "Do nothing: NOOP"},
{TOK_PASS, "PASS", TRUE, "Supply a user password: PASS password"},
{TOK_PASV, "PASV", FALSE, "Set server in passive mode: PASV"},
{TOK_PORT, "PORT", TRUE, "Specify the client port number: PORT a0,a1,a2,a3,a4,a5"},
{TOK_PWD, "PWD", FALSE, "Get current directory: PWD"},
{TOK_QUIT, "QUIT", FALSE, "Logout or break the connection: QUIT"},
{TOK_REST, "REST", TRUE, "Set restart transfer marker: REST marker"},
{TOK_RETR, "RETR", TRUE, "Get file: RETR file-name"},
{TOK_RMD, "RMD", TRUE, "Remove directory: RMD path-name"},
{TOK_RNFR, "RNFR", TRUE, "Specify old path name of file to be renamed: RNFR file-name"},
{TOK_RNTO, "RNTO", TRUE, "Specify new path name of file to be renamed: RNTO file-name"},
{TOK_SIZE, "SIZE", TRUE, "Get filesize: SIZE file-name"},
{TOK_STOR, "STOR", TRUE, "Store file: STOR file-name"},
{TOK_SYST, "SYST", FALSE, "Get operating system type: SYST"},
{TOK_TYPE, "TYPE", TRUE, "Set filetype: TYPE [A | I]"},
{TOK_USER, "USER", TRUE, "Supply a username: USER username"},
{TOK_ERROR, "", FALSE, ""},
};
// parse command
CString strCommand, strArguments;
if (!GetRxCommand(strCommand, strArguments))
{
return;
}
int nCommand;
// find command in command list
for (nCommand = TOK_ABOR; nCommand < TOK_ERROR; nCommand++)
{
// found command ?
if (strCommand == commandList[nCommand].m_pszName)
{
// did we expect an argument ?
if (commandList[nCommand].m_bHasArguments && (strArguments.IsEmpty()))
{
SendResponse("501 Syntax error: Invalid number of parameters.");
return;
}
break;
}
}
if (nCommand == TOK_ERROR)
{
// command is not in our list
SendResponse("501 Syntax error: Command not understood.");
return;
}
// no commands are excepted before successfull logged on
if ((nCommand != TOK_USER && nCommand != TOK_PASS) && !m_bLoggedon)
{
SendResponse("530 Please login with USER and PASS.");
return;
}
// proces command
switch(nCommand)
{
// specify username
case TOK_USER:
{
strArguments.MakeLower();
m_bLoggedon = FALSE;
m_strUserName = strArguments;
CString strPeerAddress;
UINT nPeerPort;
GetPeerName(strPeerAddress, nPeerPort);
// tell FTP server a new user has connected
CConnectThread *pThread = (CConnectThread *)m_pThread;
((CFTPServer *)pThread->m_pWndServer)->m_pEventSink->OnFTPUserConnected(m_pThread->m_nThreadID, m_strUserName, strPeerAddress);
SendResponse("331 User name ok, need password.");
}
break;
// specify password
case TOK_PASS:
{
// already logged on ?
if (m_bLoggedon)
{
SendResponse("503 Login with USER first.");
}
else
{
// now we have user name and password, attempt to login the client
CUser user;
// check username
if (theServer.m_UserManager.GetUser(m_strUserName, user))
{
// check password
if ((!user.m_strPassword.Compare(strArguments) || user.m_strPassword.IsEmpty()) && !user.m_bAccountDisabled)
{
// set home directory of user
m_strCurrentDir = "/";
// succesfully logged on
m_bLoggedon = TRUE;
SendResponse("230 User successfully logged in.");
break;
}
}
SendResponse("530 Not logged in, user or password incorrect!");
}
}
break;
// change transfer type
case TOK_TYPE:
{
// let's pretend we did something...
SendResponse("200 Type set to %s", strArguments);
}
break;
// print current directory
case TOK_PWD:
{
SendResponse("257 \"%s\" is current directory.", m_strCurrentDir);
}
break;
// change to parent directory
case TOK_CDUP:
strArguments = "..";
// change working directory
case TOK_CWD:
{
// try to change to specified directory
int nResult = theServer.m_UserManager.ChangeDirectory(m_strUserName, m_strCurrentDir, strArguments);
switch(nResult)
{
case 0:
SendResponse("250 CWD command successful. \"%s\" is current directory.", m_strCurrentDir);
break;
case 1:
SendResponse("550 CWD command failed. \"%s\": Permission denied.", strArguments);
break;
default:
SendResponse("550 CWD command failed. \"%s\": Directory not found.", strArguments);
break;
}
}
break;
// specify IP and port (PORT a1,a2,a3,a4,p1,p2) -> IP address a1.a2.a3.a4, port p1*256+p2.
case TOK_PORT:
{
CString strSub;
int nCount=0;
while (AfxExtractSubString(strSub, strArguments, nCount++, ','))
{
switch(nCount)
{
case 1: // a1
m_strRemoteHost = strSub;
m_strRemoteHost += ".";
break;
case 2: // a2
m_strRemoteHost += strSub;
m_strRemoteHost += ".";
break;
case 3: // a3
m_strRemoteHost += strSub;
m_strRemoteHost += ".";
break;
case 4: // a4
m_strRemoteHost += strSub;
break;
case 5: // p1
m_nRemotePort = 256*atoi(strSub);
break;
case 6: // p2
m_nRemotePort += atoi(strSub);
break;
}
}
m_bPassiveMode = FALSE;
SendResponse("200 Port command successful.");
break;
}
// switch to passive mode
case TOK_PASV:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -