📄 ftp-server-code.txt
字号:
}
//not root dir, we can convert it to dos driver
else
{
BOOL bNotFound = FALSE;
BOOL bIsDir = FALSE;
char dosWildCard[MAX_PATH];
char dosFullPath[MAX_PATH];
if( unixFullPath[0] == '/' && strlen(unixFullPath)==2 )
{
bIsDir = TRUE;
sprintf(dosWildCard, "%c:\\*.*", unixFullPath[1]);
}
else if( UnixPathToDosPath(unixFullPath, dosFullPath)
&& ExistFileOrDirectory(dosFullPath) )
{
if( ExistFile(dosFullPath) ) //it's a file
strcpy(dosWildCard, dosFullPath);
else //it's a directory
{
bIsDir = TRUE;
sprintf(dosWildCard, "%s\\*.*", dosFullPath);
}
}
else
bNotFound = TRUE;
if( !bNotFound )
{
WIN32_FIND_DATA findData;
HANDLE hFind = FindFirstFile(dosWildCard, &findData);
if( hFind != INVALID_HANDLE_VALUE )
{
do
{
//preparation
const static char* monthStr[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
char item[MAX_PATH + 100];
FILETIME lft;
SYSTEMTIME lt;
FileTimeToLocalFileTime(&findData.ftLastWriteTime, &lft);
FileTimeToSystemTime(&lft, <);
if( bNameList )
sprintf(item, "%s\r\n", findData.cFileName);
else
{
if( (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY )
{
sprintf(item, "drwxrwxrwx 1 owner group %12d %s %02d %2d:%02d %s\r\n",
findData.nFileSizeLow, monthStr[lt.wMonth-1], lt.wDay, lt.wHour, lt.wMinute, findData.cFileName);
}
else
{
sprintf(item, "-rwxrwxrwx 1 owner group %12d %s %02d %2d:%02d %s\r\n",
findData.nFileSizeLow, monthStr[lt.wMonth-1], lt.wDay, lt.wHour, lt.wMinute, findData.cFileName);
}
}
fwrite(item, 1, strlen(item), fp);
}while( FindNextFile(hFind, &findData) );
FindClose(hFind);
}
}
return !bNotFound;
}
}
class CFtpServer
{
public:
CFtpServer(SOCKET sockCtrl, BOOL* pbShouldExit);
~CFtpServer(){};
void Run();
BOOL OnCommand(const char* command);
private:
void CFtpServer::ClearDataSockInfo();
SOCKET CFtpServer::GetDataSocket();
private:
FTP_CONTROL_BLOCK m_block;
SOCKET m_sockDataListen;
enum DATA_SOCKET_STATUS{DATA_SOCK_NONE, DATA_SOCK_PORT, DATA_SOCK_PASV} m_dataSockStatus;
sockaddr_in m_clientAddr;
SOCKET m_sockCtrl;
BOOL *m_pbShouldExit;
CCollectLine m_lineMaker;
enum FTP_SERVER_STATUS { SERVER_NEED_USER, SERVER_NEED_PASS, SERVER_NEED_ANY,
SERVER_NEED_RNTO} m_status;
char m_szUnixWorkDir[MAX_PATH];
char m_szUnixRnfrName[MAX_PATH];
};
CFtpServer::CFtpServer(SOCKET sockCtrl, BOOL *pbShouldExit)
:m_sockCtrl(sockCtrl), m_pbShouldExit(pbShouldExit), m_lineMaker(5000), m_status(SERVER_NEED_USER)
{
//init m_block
m_block.sockCtrl = sockCtrl;
m_block.sockData = INVALID_SOCKET;
strcpy(m_block.filename, "");
m_block.translateType = TYPE_RETR;
m_block.dwStartPos = 0;
m_block.bDeleteFileAfterCompletion = TRUE;
m_block.bAbort = FALSE;
m_block.bThreadExited = TRUE;
//init m_dataSockStatus
m_sockDataListen = INVALID_SOCKET;
m_dataSockStatus = DATA_SOCK_NONE;
memset(&m_clientAddr, 0, sizeof(m_clientAddr));
//init work directory
strcpy(m_szUnixWorkDir, "/");
memset(m_szUnixRnfrName, 0, sizeof(m_szUnixRnfrName));
}
void CFtpServer::ClearDataSockInfo()
{
if( m_block.sockData != INVALID_SOCKET )
{
shutdown(m_block.sockData, SD_BOTH);
closesocket(m_block.sockData);
m_block.sockData = INVALID_SOCKET;
}
memset(&m_clientAddr, 0, sizeof(m_clientAddr));
if( m_sockDataListen != INVALID_SOCKET )
{
closesocket(m_sockDataListen);
m_sockDataListen = INVALID_SOCKET;
}
m_dataSockStatus = DATA_SOCK_NONE;
}
SOCKET CFtpServer::GetDataSocket()
{
char szError[MAX_SOCKET_ERROR_STRING];
if( m_dataSockStatus == DATA_SOCK_PORT )
{
SOCKET sockData = socket(AF_INET, SOCK_STREAM, 0);
if( sockData != INVALID_SOCKET )
{
if( connect(sockData, (sockaddr*)&m_clientAddr, sizeof(m_clientAddr)) == 0 )
return sockData;
debug_printf("GetDataSocket():connect():error code %d\r\n", WSAGetLastError());
closesocket(sockData);
}
m_dataSockStatus = DATA_SOCK_NONE;
return INVALID_SOCKET;
}
else if( m_dataSockStatus == DATA_SOCK_PASV )
{
if( m_sockDataListen != INVALID_SOCKET )
{
SOCKET sockData = BlockingAccept(m_sockDataListen, m_pbShouldExit, szError);
closesocket(m_sockDataListen);
m_sockDataListen = INVALID_SOCKET;
m_dataSockStatus = DATA_SOCK_NONE;
return sockData;
}
else
return INVALID_SOCKET;
}
else
return INVALID_SOCKET;
}
BOOL CFtpServer::OnCommand(const char* command)
{
debug_printf("%s\r\n", command);
char line[FTP_MAX_LINE_BUFFER];
char szError[MAX_SOCKET_ERROR_STRING];
BOOL bReplyHasSend = FALSE;
BOOL bShouldExit = FALSE;
BOOL bCommandNotSupport = FALSE;
sprintf(line, "500 command not supported\r\n");
if( m_status == SERVER_NEED_ANY )
{
//access control commands
if( memicmp(command, "CWD", 3) == 0 )
{
char param[MAX_PATH];
BOOL bFailed = TRUE;
for(int i=3; ' ' == command[i]; i++)NULL; //skip white space
strcpy(param, command+i);
if( strlen(param)>=2 && param[strlen(param)-1] == '/' )
param[strlen(param)-1] = 0;
char unixFullPath[MAX_PATH], dosFullPath[MAX_PATH];
if( GetFullUnixPath(param, m_szUnixWorkDir, unixFullPath) )
{
int length = strlen(unixFullPath);
if( unixFullPath[0] == '/' && ( length == 1 || length == 2 ) )
{
bFailed = FALSE;
sprintf(line, "250 work dir changed to %s\r\n", unixFullPath);
}
else
{
if( UnixPathToDosPath( unixFullPath, dosFullPath)
&& ExistDirectory(dosFullPath) )
{
bFailed = FALSE;
sprintf(line, "250 work dir changed to %s\r\n", unixFullPath);
}
}
}
if( bFailed )
sprintf(line, "550 CWD ( %s ) failed\r\n", param);
else
strcpy(m_szUnixWorkDir, unixFullPath);
}
else if( memicmp(command, "CDUP", 4) == 0 )
{
char parentPath[MAX_PATH];
if( GetParentUnixPath(m_szUnixWorkDir, parentPath) )
{
strcpy(m_szUnixWorkDir, parentPath);
sprintf(line, "250 work dir changed to %s\r\n", m_szUnixWorkDir);
}
else
sprintf(line, "550 parent path cannot be found\r\n");
}
else if( memicmp(command, "REIN", 4) == 0 )
{
m_status = SERVER_NEED_USER;
sprintf(line, "220 reintialize succeed\r\n");
}
else if( memicmp(command, "QUIT", 4) == 0 )
{
sprintf(line, "221\r\n");
bShouldExit = TRUE;
}
//parameter commands
else if( memicmp(command, "PORT", 4) == 0 )
{
ClearDataSockInfo();
int b1,b2,b3,b4,p1,p2;
int cursor = 4;
BOOL bGotAddr = FALSE;
while( command[cursor] != 0 )
{
if( sscanf(command + cursor, "%d,%d,%d,%d,%d,%d", &b1, &b2, &b3, &b4, &p1, &p2) == 6 )
{
bGotAddr = TRUE;
m_clientAddr.sin_family = AF_INET;
m_clientAddr.sin_port = htons((unsigned short)(p1 * 256 + p2));
m_clientAddr.sin_addr.S_un.S_un_b.s_b1 = b1;
m_clientAddr.sin_addr.S_un.S_un_b.s_b2 = b2;
m_clientAddr.sin_addr.S_un.S_un_b.s_b3 = b3;
m_clientAddr.sin_addr.S_un.S_un_b.s_b4 = b4;
m_dataSockStatus = DATA_SOCK_PORT;
sprintf(line, "200 port (%d,%d,%d,%d,%d,%d) command successful\r\n", b1, b2, b3, b4, p1, p2);
break;
}
else
cursor++;
}
if( !bGotAddr )
sprintf(line, "500 cannot extract address and port info from %s\r\n", command);
}
else if( memicmp(command, "PASV", 4) == 0 )
{
ClearDataSockInfo();
BOOL bPasvSucceed = FALSE;
sockaddr_in host;
int namelen = sizeof(host);
getsockname(m_sockCtrl, (sockaddr*)&host, &namelen);
m_sockDataListen = socket(AF_INET, SOCK_STREAM, 0);
if( m_sockDataListen != INVALID_SOCKET )
{
//select port by socket implement
host.sin_port = 0;
if( bind(m_sockDataListen, (sockaddr*)&host, namelen) != SOCKET_ERROR )
{
if( getsockname(m_sockDataListen, (sockaddr*)&host, &namelen) != SOCKET_ERROR
&& listen(m_sockDataListen, SOMAXCONN) != SOCKET_ERROR )
{
m_dataSockStatus = DATA_SOCK_PASV;
bPasvSucceed = TRUE;
sprintf(line, "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
host.sin_addr.S_un.S_un_b.s_b1,
host.sin_addr.S_un.S_un_b.s_b2,
host.sin_addr.S_un.S_un_b.s_b3,
host.sin_addr.S_un.S_un_b.s_b4,
ntohs(host.sin_port) / 0x100,
ntohs(host.sin_port) % 0x100 );
}
}
}
if( !bPasvSucceed )
{
closesocket(m_sockDataListen);
m_sockDataListen = INVALID_SOCKET;
sprintf(line, "421 cannot open a listen socket\r\n");
}
}
else if( memicmp(command, "TYPE", 4) == 0 )
{
sprintf(line, "200 %s ok\r\n", command);
}
else if( memicmp(command, "STRU", 4) == 0 )
{
sprintf(line, "200 %s ok\r\n", command);
}
else if( memicmp(command, "MODE", 4) == 0 )
{
sprintf(line, "200 %s ok\r\n", command);
}
//service commands
else if( memicmp(command, "RETR", 4) == 0
|| memicmp(command, "STOR", 4) == 0
|| memicmp(command, "APPE", 4) == 0 )
{
BOOL bShouldStartTransfer = FALSE;
for(int i=4; ' ' == command[i]; i++)NULL; //skip white space
char unixFullPath[MAX_PATH], dosFullPath[MAX_PATH];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -