Server : Apache/2.4.43 (Win64) OpenSSL/1.1.1g PHP/7.4.6 System : Windows NT USER-PC 6.1 build 7601 (Windows 7 Professional Edition Service Pack 1) AMD64 User : User ( 0) PHP Version : 7.4.6 Disable Function : NONE Directory : C:/xampp/FileZillaFTP/source/ |
// FileZilla Server - a Windows ftp server // Copyright (C) 2002-2004 - Tim Kosse <tim.kosse@gmx.de> // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // Server.cpp: Implementierung der Klasse CServer. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "Server.h" #include "Options.h" #include "ServerThread.h" #include "ListenSocket.h" #include "AdminListenSocket.h" #include "AdminInterface.h" #include "AdminSocket.h" #include "Permissions.h" #include "FileLogger.h" #include "version.h" #include "defs.h" #include "iputils.h" #include "autobanmanager.h" #ifndef MB_SERVICE_NOTIFICATION #define MB_SERVICE_NOTIFICATION 0x00040000L #endif ////////////////////////////////////////////////////////////////////// // Konstruktion/Destruktion ////////////////////////////////////////////////////////////////////// CServer::CServer() { m_hWnd=0; m_pOptions = NULL; m_pAdminInterface = new CAdminInterface(this); m_nServerState = 0; m_bQuit = FALSE; m_nSendCount = m_nRecvCount = 0; m_nTimerID = 0; m_nBanTimerID = 0; // Since this thread has to handle notifications, increase it's priority SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); m_pAutoBanManager = 0; } CServer::~CServer() { for (std::list<CListenSocket*>::iterator iter = m_ListenSocketList.begin(); iter != m_ListenSocketList.end(); iter++) delete *iter; m_ListenSocketList.clear(); for (std::list<CAdminListenSocket*>::iterator adminIter = m_AdminListenSocketList.begin(); adminIter != m_AdminListenSocketList.end(); adminIter++) delete *adminIter; m_AdminListenSocketList.clear(); delete m_pAdminInterface; delete m_pAutoBanManager; delete m_pFileLogger; delete m_pOptions; //Destroy window if (m_hWnd) { hMainWnd = 0; DestroyWindow(m_hWnd); m_hWnd = 0; } } bool CServer::Create() { //Create window WNDCLASSEX wndclass; wndclass.cbSize=sizeof wndclass; wndclass.style=0; wndclass.lpfnWndProc=WindowProc; wndclass.cbClsExtra=0; wndclass.cbWndExtra=0; wndclass.hInstance=GetModuleHandle(0); wndclass.hIcon=0; wndclass.hCursor=0; wndclass.hbrBackground=0; wndclass.lpszMenuName=0; wndclass.lpszClassName=_T("FileZilla Server Helper Window"); wndclass.hIconSm=0; RegisterClassEx(&wndclass); m_hWnd = CreateWindow(_T("FileZilla Server Helper Window"), _T("FileZilla Server Helper Window"), 0, 0, 0, 0, 0, 0, 0, 0, GetModuleHandle(0)); if (!m_hWnd) return false; SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG)this); hMainWnd = m_hWnd; m_pOptions = new COptions; m_pFileLogger = new CFileLogger(m_pOptions); m_pAutoBanManager = new CAutoBanManager(m_pOptions); //Create the threads int num = (int)m_pOptions->GetOptionVal(OPTION_THREADNUM); for (int i = 0; i < num; i++) { int index = GetNextThreadNotificationID(); CServerThread *pThread = new CServerThread(WM_FILEZILLA_SERVERMSG + index); m_ThreadNotificationIDs[index] = pThread; if (pThread->Create(THREAD_PRIORITY_NORMAL, CREATE_SUSPENDED)) { pThread->ResumeThread(); m_ThreadArray.push_back(pThread); } } m_pFileLogger->Log(GetVersionString() + _T(" started")); m_pFileLogger->Log(_T("Initializing Server.")); m_nTimerID = SetTimer(m_hWnd, 1234, 10000, NULL); ASSERT(m_nTimerID); m_nBanTimerID = SetTimer(m_hWnd, 1235, 60000, NULL); ASSERT(m_nBanTimerID); if (CreateListenSocket()) { m_nServerState = STATE_ONLINE; ShowStatus(_T("Server online."), 0); } else ShowStatus(_T("Server not online."), 1); CreateAdminListenSocket(); return true; } HWND CServer::GetHwnd() { return m_hWnd; } LRESULT CALLBACK CServer::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { CServer *pServer=(CServer *)GetWindowLongPtr(hWnd, GWLP_USERDATA); if (message == WM_CLOSE) { pServer->OnClose(); return 0; } else if (hWnd && message == WM_DESTROY) { ASSERT( hWnd == pServer->m_hWnd); HANDLE *handle = new HANDLE[pServer->m_ThreadArray.size()]; unsigned int i = 0; std::list<CServerThread *>::iterator iter; for (iter = pServer->m_ThreadArray.begin(); iter != pServer->m_ThreadArray.end(); iter++, i++) { handle[i]=(*iter)->m_hThread; (*iter)->PostThreadMessage(WM_QUIT, 0, 0); } for (i=0; i<pServer->m_ThreadArray.size(); i++) { int res=WaitForSingleObject(handle[i],INFINITE); if (res==WAIT_FAILED) res=GetLastError(); } delete [] handle; handle = new HANDLE[pServer->m_ClosedThreads.size()]; i=0; for (iter = pServer->m_ClosedThreads.begin(); iter != pServer->m_ClosedThreads.end(); iter++, i++) { handle[i]=(*iter)->m_hThread; (*iter)->PostThreadMessage(WM_QUIT, 0, 0); } for (i = 0; i < pServer->m_ClosedThreads.size(); i++) { int res=WaitForSingleObject(handle[i],INFINITE); if (res==WAIT_FAILED) res=GetLastError(); } delete [] handle; for (std::list<CAdminListenSocket*>::iterator iter2 = pServer->m_AdminListenSocketList.begin(); iter2!=pServer->m_AdminListenSocketList.end(); iter2++) { (*iter2)->Close(); delete *iter2; } pServer->m_AdminListenSocketList.clear(); delete pServer->m_pAdminInterface; pServer->m_pAdminInterface = NULL; delete pServer->m_pOptions; pServer->m_pOptions = NULL; if (pServer->m_nTimerID) { KillTimer(pServer->m_hWnd, pServer->m_nTimerID); pServer->m_nTimerID = 0; } PostQuitMessage(0); return 0; } else if (message == WM_TIMER) pServer->OnTimer(wParam); else if (message == WM_FILEZILLA_RELOADCONFIG) { COptions options; options.ReloadConfig(); CPermissions perm; perm.ReloadConfig(); } else if (message >= WM_FILEZILLA_SERVERMSG) { UINT index = message - WM_FILEZILLA_SERVERMSG; if (index >= pServer->m_ThreadNotificationIDs.size()) return 0; CServerThread *pThread = pServer->m_ThreadNotificationIDs[index]; if (pThread) { std::list<CServerThread::t_Notification> notifications; pThread->GetNotifications(notifications); for (std::list<CServerThread::t_Notification>::const_iterator iter = notifications.begin(); iter != notifications.end(); iter++) if (pServer->OnServerMessage(pThread, iter->wParam, iter->lParam) != 0) break; } return 0; } return ::DefWindowProc(hWnd, message, wParam, lParam); } LRESULT CServer::OnServerMessage(CServerThread* pThread, WPARAM wParam, LPARAM lParam) { if (wParam == FSM_STATUSMESSAGE) { t_statusmsg *msg = reinterpret_cast<t_statusmsg *>(lParam); if (!msg) return 0; CStdString str; FILETIME fFileTime; SystemTimeToFileTime(&msg->time, &fFileTime); str.Format(_T("(%06d)- %s (%s)> %s"), msg->userid, (LPCTSTR)msg->user, (LPCTSTR)msg->ip, (LPCTSTR)msg->status); ShowStatus(fFileTime.dwHighDateTime, fFileTime.dwLowDateTime, str, msg->type); delete [] msg->status; delete [] msg->user; delete msg; } else if (wParam == FSM_CONNECTIONDATA) { t_connop *pConnOp = reinterpret_cast<t_connop*>(lParam); if (!pConnOp) return 0; int len; unsigned char *buffer; switch (pConnOp->op) { case USERCONTROL_CONNOP_ADD: { t_connectiondata_add* pData = (t_connectiondata_add*)pConnOp->data; t_connectiondata data; data.userid = pConnOp->userid; data.pThread = pThread; _tcsncpy(data.ip, pData->ip, 40); data.port = pData->port; data.transferMode = 0; data.currentOffset = 0; data.totalSize = -1; m_UsersList[pConnOp->userid] = data; char* utf8 = ConvToNetwork(pData->ip); int iplen = strlen(utf8); len = 2 + 4 + 2 + iplen + 4; buffer = new unsigned char[len]; buffer[2 + 4] = iplen / 256; buffer[2 + 4 + 1] = iplen % 256; memcpy(buffer + 2 + 4 + 2, utf8, iplen); delete [] utf8; memcpy(buffer + 2 + 4 + 2 + iplen, &pData->port, 4); delete pData; } break; case USERCONTROL_CONNOP_CHANGEUSER: { t_connectiondata_changeuser* pData = (t_connectiondata_changeuser*)pConnOp->data; m_UsersList[pConnOp->userid].user = pData->user; char* utf8 = ConvToNetwork(pData->user); if (!utf8) { buffer = new unsigned char[2 + 2 + 4 + 2]; buffer[2 + 4] = 0; buffer[2 + 4 + 1] = 0; len = 8; } else { int userlen = strlen(utf8); len = 2 + 4 + 2 + userlen; buffer = new unsigned char[len]; buffer[2 + 4] = userlen / 256; buffer[2 + 4 + 1] = userlen % 256; memcpy(buffer + 2 + 4 + 2, utf8, userlen); delete [] utf8; } delete pData; } break; case USERCONTROL_CONNOP_REMOVE: { std::map<int, t_connectiondata>::iterator iter = m_UsersList.find(pConnOp->userid); if (iter != m_UsersList.end()) m_UsersList.erase(iter); len = 6; buffer = new unsigned char[len]; } break; case USERCONTROL_CONNOP_TRANSFERINIT: { t_connectiondata_transferinfo* pData = (t_connectiondata_transferinfo*)pConnOp->data; t_connectiondata& data = m_UsersList[pConnOp->userid]; data.transferMode = pData->transferMode; data.physicalFile = pData->physicalFile; data.logicalFile = pData->logicalFile; data.currentOffset = pData->startOffset; data.totalSize = pData->totalSize; if (data.transferMode) { char* physicalFile = ConvToNetwork(pData->physicalFile); int physicalFileLen = physicalFile ? strlen(physicalFile) : 0; char* logicalFile = ConvToNetwork(pData->logicalFile); int logicalFileLen = logicalFile ? strlen(logicalFile) : 0; len = 2 + 4 + 1 + 2 + physicalFileLen + 2 + logicalFileLen; if (data.currentOffset != 0) len += 8; if (data.totalSize != -1) len += 8; buffer = new unsigned char[len]; unsigned char *p = buffer + 6; *p = data.transferMode; // Bit 5 and 6 indicate presence of currentOffset and totalSize. if (data.currentOffset != 0) *p |= 0x20; if (data.totalSize != -1) *p |= 0x40; p++; *p++ = physicalFileLen / 256; *p++ = physicalFileLen % 256; if (physicalFile) { memcpy(p, physicalFile, physicalFileLen); delete [] physicalFile; } p += physicalFileLen; *p++ = logicalFileLen / 256; *p++ = logicalFileLen % 256; if (logicalFile) { memcpy(p, logicalFile, logicalFileLen); delete [] logicalFile; } p += logicalFileLen; if (data.currentOffset != 0) { memcpy(p, &data.currentOffset, 8); p += 8; } if (data.totalSize != -1) { memcpy(p, &data.totalSize, 8); p += 8; } } else { len = 2 + 4 + 1; buffer = new unsigned char[len]; buffer[2 + 4] = 0; } delete pData; } break; case USERCONTROL_CONNOP_TRANSFEROFFSETS: { t_connectiondata_transferoffsets* pData = (t_connectiondata_transferoffsets*)pConnOp->data; buffer = pData->pData; len = pData->len; unsigned char* p = buffer + 2; int* userid; __int64* offset; while ((p - buffer + 12) <= len) { userid = (int*)p; offset = (__int64*)(p + 4); t_connectiondata& data = m_UsersList[*userid]; data.currentOffset = *offset; p += 12; } delete pData; } break; } buffer[0] = USERCONTROL_CONNOP; buffer[1] = pConnOp->op; if (pConnOp->op != USERCONTROL_CONNOP_TRANSFEROFFSETS) memcpy(buffer + 2, &pConnOp->userid, 4); m_pAdminInterface->SendCommand(2, 3, buffer, len); delete [] buffer; delete pConnOp; } else if (wParam == FSM_THREADCANQUIT) { std::list<CServerThread *>::iterator iter; for (iter = m_ThreadArray.begin(); iter != m_ThreadArray.end(); iter++) { if (*iter == pThread) { HANDLE handle=pThread->m_hThread; pThread->PostThreadMessage(WM_QUIT, 0, 0); int res=WaitForSingleObject(handle, INFINITE); if (res==WAIT_FAILED) res=GetLastError(); m_ThreadArray.erase(iter); FreeThreadNotificationID(pThread); if (!m_ThreadArray.size() && !m_ClosedThreads.size()) { m_nServerState &= ~(STATE_ONLINE | STATE_MASK_GOOFFLINE); SendState(); if (!m_bQuit) ShowStatus(_T("Server offline."), 1); else { hMainWnd = 0; DestroyWindow(m_hWnd); m_hWnd = 0; } } return -1; } } for (iter = m_ClosedThreads.begin(); iter != m_ClosedThreads.end(); iter++) { if (*iter == pThread) { HANDLE handle = pThread->m_hThread; pThread->PostThreadMessage(WM_QUIT, 0, 0); int res = WaitForSingleObject(handle, INFINITE); if (res == WAIT_FAILED) res = GetLastError(); m_ClosedThreads.erase(iter); FreeThreadNotificationID(pThread); if (!m_ThreadArray.size() && !m_ClosedThreads.size()) { m_nServerState &= ~(STATE_ONLINE | STATE_MASK_GOOFFLINE); SendState(); if (!m_bQuit) ShowStatus(_T("Server offline."), 1); else { hMainWnd = 0; DestroyWindow(m_hWnd); m_hWnd = 0; } } return -1; } } } else if (wParam == FSM_SEND) { char buffer[5]; buffer[0] = 1; memcpy(buffer+1, &lParam, 4); m_pAdminInterface->SendCommand(2, 7, buffer, 5); m_nSendCount += lParam; } else if (wParam == FSM_RECV) { char buffer[5]; buffer[0] = 0; memcpy(buffer+1, &lParam, 4); m_pAdminInterface->SendCommand(2, 7, buffer, 5); m_nRecvCount += lParam; } return 0; } void CServer::OnClose() { for (std::list<CListenSocket*>::iterator listIter = m_ListenSocketList.begin(); listIter != m_ListenSocketList.end(); listIter++) { (*listIter)->Close(); delete *listIter; } m_ListenSocketList.clear(); m_bQuit = TRUE; if (!m_ThreadArray.size() && !m_ClosedThreads.size()) { hMainWnd = 0; DestroyWindow(m_hWnd); m_hWnd = 0; return; } std::list<CServerThread *>::iterator iter; for (iter = m_ThreadArray.begin(); iter != m_ThreadArray.end(); iter++) { VERIFY((*iter)->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_GOOFFLINE, 0)); } for (iter = m_ClosedThreads.begin(); iter != m_ClosedThreads.end(); iter++) { VERIFY((*iter)->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_GOOFFLINE, 0)); } } BOOL CServer::ProcessCommand(CAdminSocket *pAdminSocket, int nID, unsigned char *pData, int nDataLength) { switch (nID) { case 2: if (!nDataLength) { unsigned char buffer[2]; buffer[0] = m_nServerState / 256; buffer[1] = m_nServerState % 256; pAdminSocket->SendCommand(1, 2, buffer, 2); } else if (nDataLength == 2) { ToggleActive(*pData * 256 + pData[1]); unsigned char buffer[2]; buffer[0] = m_nServerState / 256; buffer[1] = m_nServerState % 256; pAdminSocket->SendCommand(1, 2, buffer, 2); } else pAdminSocket->SendCommand(1, 1, "\001Protocol error: Unexpected data length", strlen("\001Protocol error: Unexpected data length") + 1); break; case 3: if (!nDataLength) pAdminSocket->SendCommand(1, 1, "\001Protocol error: Unexpected data length", strlen("\001Protocol error: Unexpected data length") + 1); else if (*pData == USERCONTROL_GETLIST) { int len = 3; std::map<int, t_connectiondata>::iterator iter; for (iter = m_UsersList.begin(); iter != m_UsersList.end(); iter++) { const t_connectiondata& data = iter->second; char* ip = ConvToNetwork(data.ip); char* user = ConvToNetwork(data.user); len += 4 + (ip ? strlen(ip) : 0) + 2 + 4 + (user ? strlen(user) : 0) + 2 + 1; delete [] ip; delete [] user; if (data.transferMode) { char* physicalFile = ConvToNetwork(data.physicalFile); char* logicalFile = ConvToNetwork(data.logicalFile); len += 2 + (physicalFile ? strlen(physicalFile) : 0) + 2 + (logicalFile ? strlen(logicalFile) : 0); delete [] physicalFile; delete [] logicalFile; if (data.currentOffset != 0) len += 8; if (data.totalSize != -1) len += 8; } } unsigned char *buffer = new unsigned char[len]; buffer[0] = USERCONTROL_GETLIST; buffer[1] = m_UsersList.size() / 256; buffer[2] = m_UsersList.size() % 256; unsigned char *p = buffer + 3; for (iter = m_UsersList.begin(); iter != m_UsersList.end(); iter++) { const t_connectiondata& data = iter->second; char* ip = ConvToNetwork(data.ip); char* user = ConvToNetwork(data.user); int ipLen = ip ? strlen(ip) : 0; int userLen = user ? strlen(user) : 0; memcpy(p, &data.userid, 4); p+=4; *p++ = ipLen / 256; *p++ = ipLen % 256; if (ip) { memcpy(p, ip, ipLen); p += ipLen; delete [] ip; } memcpy(p, &data.port, 4); p += 4; *p++ = userLen / 256; *p++ = userLen % 256; if (user) { memcpy(p, user, userLen); p += userLen; delete [] user; } *p = data.transferMode; if (data.transferMode) { // Bit 5 and 6 indicate presence of currentOffset and totalSize. if (data.currentOffset != 0) *p |= 0x20; if (data.totalSize != -1) *p |= 0x40; p++; char* physicalFile = ConvToNetwork(data.physicalFile); if (physicalFile) { int physicalLen = strlen(physicalFile); *p++ = physicalLen / 256; *p++ = physicalLen % 256; memcpy(p, physicalFile, physicalLen); delete [] physicalFile; p += physicalLen; } else { *p++ = 0; *p++ = 0; } char* logicalFile = ConvToNetwork(data.logicalFile); if (logicalFile) { int logicalLen = strlen(logicalFile); *p++ = logicalLen / 256; *p++ = logicalLen % 256; memcpy(p, logicalFile, logicalLen); delete [] logicalFile; p += logicalLen; } else { *p++ = 0; *p++ = 0; } if (data.currentOffset != 0) { memcpy(p, &data.currentOffset, 8); p += 8; } if (data.totalSize != -1) { memcpy(p, &data.totalSize, 8); p += 8; } } else p++; } m_pAdminInterface->SendCommand(1, 3, buffer, len); delete [] buffer; } else if (*pData == USERCONTROL_KICK || *pData == USERCONTROL_BAN) { if (nDataLength != 5) pAdminSocket->SendCommand(1, 1, "\001Protocol error: Unexpected data length", strlen("\001Protocol error: Unexpected data length")+1); else { int nUserID; memcpy(&nUserID, pData+1, 4); std::map<int, t_connectiondata>::iterator iter = m_UsersList.find(nUserID); if (iter!=m_UsersList.end()) { if (*pData == USERCONTROL_BAN) { // Get the list of IP filter rules. CStdString ips = m_pOptions->GetOption(OPTION_IPFILTER_DISALLOWED); if (ips != _T("")) ips += _T(" "); USES_CONVERSION; int pos = ips.Find(' '); while (pos != -1) { CStdString blockedIP = ips.Left(pos); ips = ips.Mid(pos + 1); pos = ips.Find(' '); if (MatchesFilter(blockedIP, iter->second.ip)) break; } if (pos == -1) { ips = m_pOptions->GetOption(OPTION_IPFILTER_DISALLOWED); if (ips != _T("")) ips += _T(" "); ips += iter->second.ip; m_pOptions->SetOption(OPTION_IPFILTER_DISALLOWED, ips); } } t_controlmessage *msg=new t_controlmessage; msg->command=USERCONTROL_KICK; msg->socketid=nUserID; iter->second.pThread->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_CONTROL, (LPARAM)msg); char buffer[2]; buffer[0] = *pData; buffer[1] = 0; pAdminSocket->SendCommand(1, 3, &buffer, 2); } else { char buffer[2]; buffer[0] = USERCONTROL_KICK; buffer[1] = 1; pAdminSocket->SendCommand(1, 3, &buffer, 2); } } } else pAdminSocket->SendCommand(1, 1, "\001Protocol error: Invalid data", strlen("\001Protocol error: Invalid data")+1); break; case 5: if (!nDataLength) { char *pBuffer = NULL; DWORD nBufferLength = 0; if (m_pOptions && m_pOptions->GetAsCommand(&pBuffer, &nBufferLength)) { pAdminSocket->SendCommand(1, 5, pBuffer, nBufferLength); delete [] pBuffer; } } else if (m_pOptions) { if (nDataLength < 2) pAdminSocket->SendCommand(1, 1, "\001Protocol error: Unexpected data length", strlen("\001Protocol error: Unexpected data length")+1); else { CStdString listenPorts = m_pOptions->GetOption(OPTION_SERVERPORT); CStdString listenPortsSsl = m_pOptions->GetOption(OPTION_SSLPORTS); bool enableSsl = m_pOptions->GetOptionVal(OPTION_ENABLESSL) != 0; int nAdminListenPort = (int)m_pOptions->GetOptionVal(OPTION_ADMINPORT); CStdString adminIpBindings = m_pOptions->GetOption(OPTION_ADMINIPBINDINGS); CStdString peerIP; UINT port = 0; bool bLocal = false; if (!pAdminSocket->GetPeerName(peerIP, port)) return FALSE; else bLocal = IsLocalhost(peerIP); if (!m_pOptions->ParseOptionsCommand(pData, nDataLength, bLocal)) { pAdminSocket->SendCommand(1, 1, "\001Protocol error: Invalid data, could not import settings.", strlen("\001Protocol error: Invalid data, could not import settings.")+1); char buffer = 1; pAdminSocket->SendCommand(1, 5, &buffer, 1); break; } char buffer = 0; pAdminSocket->SendCommand(1, 5, &buffer, 1); unsigned int threadnum = (int)m_pOptions->GetOptionVal(OPTION_THREADNUM); if (m_nServerState & STATE_ONLINE) { if (threadnum > m_ThreadArray.size()) { int newthreads = threadnum - m_ThreadArray.size(); for (int i = 0; i < newthreads; i++) { int index = GetNextThreadNotificationID(); CServerThread *pThread = new CServerThread(WM_FILEZILLA_SERVERMSG + index); m_ThreadNotificationIDs[index] = pThread; if (pThread->Create(THREAD_PRIORITY_NORMAL, CREATE_SUSPENDED)) { pThread->ResumeThread(); m_ThreadArray.push_back(pThread); } } CStdString str; str.Format(_T("Number of threads increased to %d."), threadnum); ShowStatus(str, 0); } else if (threadnum < m_ThreadArray.size()) { CStdString str; str.Format(_T("Decreasing number of threads to %d."), threadnum); ShowStatus(str, 0); unsigned int i=0; std::list<CServerThread *> newList; for (std::list<CServerThread *>::iterator iter=m_ThreadArray.begin(); iter!=m_ThreadArray.end(); iter++,i++) if (i>=threadnum) { (*iter)->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_GOOFFLINE, 2); m_ClosedThreads.push_back(*iter); } else newList.push_back(*iter); m_ThreadArray.clear(); m_ThreadArray = newList; } } if (listenPorts != m_pOptions->GetOption(OPTION_SERVERPORT) || enableSsl != (m_pOptions->GetOptionVal(OPTION_ENABLESSL) != 0) || (m_pOptions->GetOptionVal(OPTION_ENABLESSL) && listenPortsSsl != m_pOptions->GetOption(OPTION_SSLPORTS))) { if (!m_ListenSocketList.empty()) { ShowStatus(_T("Closing all listening sockets"), 0); for (std::list<CListenSocket*>::iterator listIter = m_ListenSocketList.begin(); listIter != m_ListenSocketList.end(); listIter++) { (*listIter)->Close(); delete *listIter; } m_ListenSocketList.clear(); if (!CreateListenSocket()) { ShowStatus(_T("Failed to create a listen socket on any of the specified ports. Server is not online!"), 1); m_nServerState &= ~STATE_ONLINE; } else { ShowStatus(_T("Listen socket port changed"), 0); if (!(m_nServerState & STATE_MASK_GOOFFLINE)) m_nServerState |= STATE_ONLINE; } SendState(); } } if (nAdminListenPort != m_pOptions->GetOptionVal(OPTION_ADMINPORT) || adminIpBindings!=m_pOptions->GetOption(OPTION_ADMINIPBINDINGS)) { if (nAdminListenPort == m_pOptions->GetOptionVal(OPTION_ADMINPORT)) { for (std::list<CAdminListenSocket*>::iterator iter = m_AdminListenSocketList.begin(); iter!=m_AdminListenSocketList.end(); iter++) { (*iter)->Close(); delete *iter; } m_AdminListenSocketList.clear(); } CAdminListenSocket *pSocket = new CAdminListenSocket(m_pAdminInterface); if (!pSocket->Create((int)m_pOptions->GetOptionVal(OPTION_ADMINPORT), SOCK_STREAM, FD_ACCEPT, (m_pOptions->GetOption(OPTION_ADMINIPBINDINGS) != _T("*")) ? _T("127.0.0.1") : NULL)) { delete pSocket; CStdString str; str.Format(_T("Failed to change admin listen port to %I64d."), m_pOptions->GetOptionVal(OPTION_ADMINPORT)); m_pOptions->SetOption(OPTION_ADMINPORT, nAdminListenPort); ShowStatus(str, 1); } else { pSocket->Listen(); for (std::list<CAdminListenSocket*>::iterator iter = m_AdminListenSocketList.begin(); iter!=m_AdminListenSocketList.end(); iter++) { (*iter)->Close(); delete *iter; } m_AdminListenSocketList.clear(); m_AdminListenSocketList.push_back(pSocket); if (nAdminListenPort != m_pOptions->GetOptionVal(OPTION_ADMINPORT)) { CStdString str; str.Format(_T("Admin listen port changed to %I64d."), m_pOptions->GetOptionVal(OPTION_ADMINPORT)); ShowStatus(str, 0); } if (m_pOptions->GetOption(OPTION_ADMINIPBINDINGS) != _T("*")) { BOOL bError = FALSE; CStdString str = _T("Failed to bind the admin interface to the following IPs:"); CStdString ipBindings = m_pOptions->GetOption(OPTION_ADMINIPBINDINGS); if (ipBindings != _T("")) ipBindings += _T(" "); while (ipBindings != _T("")) { int pos = ipBindings.Find(' '); if (pos == -1) break; CStdString ip = ipBindings.Left(pos); ipBindings = ipBindings.Mid(pos+1); CAdminListenSocket *pAdminListenSocket = new CAdminListenSocket(m_pAdminInterface); int family; if (ip.Find(':') != -1) family = AF_INET6; else family = AF_INET; if (!pAdminListenSocket->Create((int)m_pOptions->GetOptionVal(OPTION_ADMINPORT), SOCK_STREAM, FD_ACCEPT, ip, family) || !pAdminListenSocket->Listen()) { bError = TRUE; str += _T(" ") + ip; delete pAdminListenSocket; } else m_AdminListenSocketList.push_back(pAdminListenSocket); } if (bError) ShowStatus(str, 1); } if (adminIpBindings != m_pOptions->GetOption(OPTION_ADMINIPBINDINGS)) ShowStatus(_T("Admin interface IP bindings changed"), 0); } } } } break; case 6: if (!nDataLength) { char *pBuffer = NULL; DWORD nBufferLength = 0; CPermissions permissions; permissions.GetAsCommand(&pBuffer, &nBufferLength); pAdminSocket->SendCommand(1, 6, pBuffer, nBufferLength); delete [] pBuffer; } else { if (nDataLength < 2) pAdminSocket->SendCommand(1, 1, "\001Protocol error: Unexpected data length", strlen("\001Protocol error: Unexpected data length")+1); else { CPermissions permissions; if (!permissions.ParseUsersCommand(pData, nDataLength)) { pAdminSocket->SendCommand(1, 1, "\001Protocol error: Invalid data, could not import account settings.", strlen("\001Protocol error: Invalid data, could not import account settings.")+1); char buffer = 1; pAdminSocket->SendCommand(1, 6, &buffer, 1); break; } char buffer = 0; pAdminSocket->SendCommand(1, 6, &buffer, 1); } } break; case 8: pAdminSocket->SendCommand(1, 8, NULL, 0); break; default: { CStdStringA str; str.Format("\001Protocol error: Unknown command (%d).", nID); pAdminSocket->SendCommand(1, 1, str.c_str(), str.GetLength()); } break; } return TRUE; } BOOL CServer::ToggleActive(int nServerState) { if (nServerState & STATE_GOOFFLINE_NOW) { if (m_nServerState & STATE_MASK_GOOFFLINE) return FALSE; for (std::list<CListenSocket*>::iterator listenIter = m_ListenSocketList.begin(); listenIter != m_ListenSocketList.end(); listenIter++) { (*listenIter)->Close(); delete *listenIter; } m_ListenSocketList.clear(); for (std::list<CServerThread *>::iterator iter = m_ClosedThreads.begin(); iter != m_ClosedThreads.end(); iter++) (*iter)->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_GOOFFLINE, 0); for (std::list<CServerThread *>::iterator iter = m_ThreadArray.begin(); iter != m_ThreadArray.end(); iter++) { (*iter)->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_GOOFFLINE, 0); m_ClosedThreads.push_back(*iter); } m_ThreadArray.clear(); m_nServerState &= ~STATE_ONLINE; if (!m_ClosedThreads.empty()) m_nServerState |= STATE_GOOFFLINE_NOW; } else if (nServerState & STATE_GOOFFLINE_LOGOUT) { if (m_nServerState & STATE_MASK_GOOFFLINE) return FALSE; for (std::list<CListenSocket*>::iterator listenIter = m_ListenSocketList.begin(); listenIter != m_ListenSocketList.end(); listenIter++) { (*listenIter)->Close(); delete *listenIter; } m_ListenSocketList.clear(); for (std::list<CServerThread *>::iterator iter = m_ClosedThreads.begin(); iter != m_ClosedThreads.end(); iter++) (*iter)->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_GOOFFLINE, 2); for (std::list<CServerThread *>::iterator iter=m_ThreadArray.begin(); iter!=m_ThreadArray.end(); iter++) { (*iter)->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_GOOFFLINE, 2); m_ClosedThreads.push_back(*iter); } m_ThreadArray.clear(); m_nServerState &= ~STATE_ONLINE; if (!m_ClosedThreads.empty()) m_nServerState = m_nServerState |= STATE_GOOFFLINE_LOGOUT; } else if (nServerState & STATE_GOOFFLINE_WAITTRANSFER) { if (m_nServerState & STATE_MASK_GOOFFLINE) return FALSE; for (std::list<CListenSocket*>::iterator listenIter = m_ListenSocketList.begin(); listenIter != m_ListenSocketList.end(); listenIter++) { (*listenIter)->Close(); delete *listenIter; } m_ListenSocketList.clear(); for (std::list<CServerThread *>::iterator iter = m_ClosedThreads.begin(); iter != m_ClosedThreads.end(); iter++) (*iter)->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_GOOFFLINE, 1); for (std::list<CServerThread *>::iterator iter=m_ThreadArray.begin(); iter!=m_ThreadArray.end(); iter++) { (*iter)->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_GOOFFLINE, 1); m_ClosedThreads.push_back(*iter); } m_ThreadArray.clear(); m_nServerState &= ~STATE_ONLINE; if (!m_ClosedThreads.empty()) m_nServerState = m_nServerState |= STATE_GOOFFLINE_WAITTRANSFER; } else if (nServerState & STATE_ONLINE) { if (m_ListenSocketList.empty()) { if (!CreateListenSocket()) { ShowStatus(_T("Failed to create a listen socket on any of the specified ports. Server is not online!"), 1); return true; } } if (!m_ListenSocketList.empty()) { ShowStatus(_T("Server online"), 0); int i = 0; int num = (m_pOptions ? (int)m_pOptions->GetOptionVal(OPTION_THREADNUM) : 2); //Recreate the threads for (i = m_ThreadArray.size(); i < num; i++) { int index = GetNextThreadNotificationID(); CServerThread *pThread = new CServerThread(WM_FILEZILLA_SERVERMSG + index); m_ThreadNotificationIDs[index] = pThread; if (pThread->Create(THREAD_PRIORITY_NORMAL, CREATE_SUSPENDED)) { m_ThreadArray.push_back(pThread); pThread->ResumeThread(); } } } for (std::list<CListenSocket *>::iterator iter = m_ListenSocketList.begin(); iter != m_ListenSocketList.end(); iter++) (*iter)->m_bLocked = nServerState & STATE_LOCKED; // Set closing threads to "wait until logout" mode. for (std::list<CServerThread *>::iterator iter = m_ClosedThreads.begin(); iter != m_ClosedThreads.end(); iter++) (*iter)->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_GOOFFLINE, 2); m_nServerState = (m_ListenSocketList.empty() ? 0 : STATE_ONLINE) + (nServerState & STATE_LOCKED); } SendState(); return TRUE; } void CServer::ShowStatus(LPCTSTR msg, int nType) { char* utf8 = ConvToNetwork(msg); if (!utf8) return; char *pBuffer = new char[strlen(utf8) + 1]; *pBuffer = nType; memcpy(pBuffer + 1, utf8, strlen(utf8)); if (m_pAdminInterface) m_pAdminInterface->SendCommand(2, 1, pBuffer, strlen(utf8) + 1); delete [] pBuffer; delete [] utf8; if (m_pFileLogger) m_pFileLogger->Log(msg); } void CServer::ShowStatus(DWORD eventDateHigh, DWORD eventDateLow, LPCTSTR msg, int nType) { char* utf8 = ConvToNetwork(msg); if (!utf8) return; if (!*utf8) { delete [] utf8; return; } unsigned int utf8len = strlen(utf8); char *pBuffer = new char[utf8len + 1 + 8]; *pBuffer = nType; memcpy(pBuffer + 1, &eventDateHigh, 8); memcpy(pBuffer + 5, &eventDateLow, 4); memcpy(pBuffer + 1 + 8, utf8, utf8len); if (m_pAdminInterface) m_pAdminInterface->SendCommand(2, 4, pBuffer, utf8len + 1 + 8); delete [] pBuffer; delete [] utf8; //Log string if (m_pFileLogger) { FILETIME fFileTime; SYSTEMTIME sFileTime; fFileTime.dwHighDateTime = eventDateHigh; fFileTime.dwLowDateTime = eventDateLow; FileTimeToSystemTime(&fFileTime, &sFileTime); TCHAR text[80]; if (!GetDateFormat( LOCALE_USER_DEFAULT, // locale for which date is to be formatted DATE_SHORTDATE, // flags specifying function options &sFileTime, // date to be formatted 0, // date format string text, // buffer for storing formatted string 80 // size of buffer )) return; CStdString text2 = _T(" "); text2 += text; if (!GetTimeFormat( LOCALE_USER_DEFAULT, // locale for which date is to be formatted TIME_FORCE24HOURFORMAT, // flags specifying function options &sFileTime, // date to be formatted 0, // date format string text, // buffer for storing formatted string 80 // size of buffer )) return; text2 += _T(" "); text2 += text; CStdString str = msg; int pos = str.Find(_T("-")); if (pos!=-1) { str.Insert(pos, text2 + _T(" ")); } m_pFileLogger->Log(str); } } void CServer::OnTimer(UINT nIDEvent) { if (nIDEvent == m_nBanTimerID) { if (m_pAutoBanManager) m_pAutoBanManager->PurgeOutdated(); return; } m_pAdminInterface->CheckForTimeout(); m_pFileLogger->CheckLogFile(); } int CServer::DoCreateAdminListenSocket(UINT port, LPCTSTR addr, int family) { CAdminListenSocket *pAdminListenSocket = new CAdminListenSocket(m_pAdminInterface); if (!pAdminListenSocket->Create(port, SOCK_STREAM, FD_ACCEPT, addr, family)) { delete pAdminListenSocket; return 0; } if (!pAdminListenSocket->Listen()) { delete pAdminListenSocket; return 0; } if (!port) { CStdString ip; BOOL bResult = pAdminListenSocket->GetSockName(ip, port); if (!bResult) { delete pAdminListenSocket; return 0; } } m_AdminListenSocketList.push_back(pAdminListenSocket); return port; } BOOL CServer::CreateAdminListenSocket() { CStdString ipBindings = (m_pOptions ? m_pOptions->GetOption(OPTION_ADMINIPBINDINGS) : _T("")); int nAdminPort = (m_pOptions ? (int)m_pOptions->GetOptionVal(OPTION_ADMINPORT) : 14147); CStdString error; if (!DoCreateAdminListenSocket(nAdminPort, (ipBindings != _T("*")) ? _T("127.0.0.1") : NULL, AF_INET)) { int p = DoCreateAdminListenSocket(nAdminPort, _T("127.0.0.1"), AF_INET); if (!p) { CStdString str; str.Format(_T("Failed to create listen socket for admin interface on port %d for IPv4, the IPv4 admin interface has been disabled."), nAdminPort); ShowStatus(str, 1); error += _T("\n") + str; } else { CStdString str; str.Format(_T("Failed to create listen socket for admin interface on port %d for IPv4, for this session the IPv4 admin interface is available on port %u."), p); ShowStatus(str, 1); error += _T("\n") + str; } } if (!m_pOptions->GetOptionVal(OPTION_DISABLE_IPV6)) { if (!DoCreateAdminListenSocket(nAdminPort, (ipBindings != _T("*")) ? _T("::1") : NULL, AF_INET6)) { int p = DoCreateAdminListenSocket(nAdminPort, _T("127.0.0.1"), AF_INET6); if (!p) { CStdString str; str.Format(_T("Failed to create listen socket for admin interface on port %d for IPv6, the IPv6 admin interface has been disabled."), nAdminPort); ShowStatus(str, 1); error += _T("\n") + str; } else { CStdString str; str.Format(_T("Failed to create listen socket for admin interface on port %d for IPv6, for this session the IPv6 admin interface is available on port %u."), p); ShowStatus(str, 1); error += _T("\n") + str; } } } if (ipBindings != _T("*")) { if (ipBindings != _T("")) ipBindings += _T(" "); while (ipBindings != _T("")) { int pos = ipBindings.Find(' '); if (pos == -1) break; CStdString ip = ipBindings.Left(pos); ipBindings = ipBindings.Mid(pos+1); CAdminListenSocket *pAdminListenSocket = new CAdminListenSocket(m_pAdminInterface); int family; if (ip.Find(':') != -1) family = AF_INET6; else family = AF_INET; if (!pAdminListenSocket->Create(nAdminPort, SOCK_STREAM, FD_ACCEPT, ip, family) || !pAdminListenSocket->Listen()) { delete pAdminListenSocket; error += _T("\n") + ip; } else m_AdminListenSocketList.push_back(pAdminListenSocket); } } /* Disabled since appareantly MessageBox freezes the server on Windows Server 2003. if (!error.IsEmpty()) { error = _T("Failed to bind the admin interface to the following addresses:") + error; MessageBox(0, error, _T("FileZilla Server Error"), MB_ICONEXCLAMATION | MB_SERVICE_NOTIFICATION); }*/ return !m_AdminListenSocketList.empty(); } BOOL CServer::CreateListenSocket() { CStdString ports = (m_pOptions ? m_pOptions->GetOption(OPTION_SERVERPORT) : _T("21")); bool ssl = false; if (ports == _T("") && m_pOptions && m_pOptions->GetOptionVal(OPTION_ENABLESSL)) { ports = m_pOptions->GetOption(OPTION_SSLPORTS); ssl = true; } if (ports == _T("")) { ShowStatus(_T("No listen ports set in settings"), 1); return false; } ports += _T(" "); int pos = ports.Find(' '); while (pos > 0) { CStdString ipBindings = (m_pOptions ? m_pOptions->GetOption(OPTION_IPBINDINGS) : _T("*")); if (ipBindings == _T("")) ipBindings = _T("*"); int nPort = _ttoi(ports.Left(pos)); ports = ports.Mid(pos + 1); pos = ports.Find(' '); CStdString str; str.Format(_T("Creating listen socket on port %d..."), nPort); ShowStatus(str, 0); if (ipBindings == _T("*")) { CListenSocket *pListenSocket = new CListenSocket(this, ssl); pListenSocket->m_pThreadList = &m_ThreadArray; if (!pListenSocket->Create(nPort, SOCK_STREAM, FD_ACCEPT, NULL, AF_INET) || !pListenSocket->Listen()) { delete pListenSocket; pListenSocket = NULL; str.Format(_T("Failed to create listen socket on port %d for IPv4"), nPort); ShowStatus(str, 1); } else m_ListenSocketList.push_back(pListenSocket); if (!m_pOptions->GetOptionVal(OPTION_DISABLE_IPV6)) { CListenSocket *pListenSocket = new CListenSocket(this, ssl); pListenSocket->m_pThreadList = &m_ThreadArray; if (!pListenSocket->Create(nPort, SOCK_STREAM, FD_ACCEPT, NULL, AF_INET6) || !pListenSocket->Listen()) { delete pListenSocket; pListenSocket = NULL; str.Format(_T("Failed to create listen socket on port %d for IPv6"), nPort); ShowStatus(str, 1); } else m_ListenSocketList.push_back(pListenSocket); } } else { BOOL bError = FALSE; CStdString str; str.Format(_T("Failed to bind the listen socket on port %d to the following IPs:"), nPort); ipBindings += _T(" "); while (ipBindings != _T("")) { int pos = ipBindings.Find(' '); if (pos == -1) break; CStdString ip = ipBindings.Left(pos); ipBindings = ipBindings.Mid(pos + 1); CListenSocket *pListenSocket = new CListenSocket(this, ssl); pListenSocket->m_pThreadList = &m_ThreadArray; int family; if (ip.Find(':') != -1) family = AF_INET6; else family = AF_INET; if (!pListenSocket->Create(nPort, SOCK_STREAM, FD_ACCEPT, ip, family) || !pListenSocket->Listen()) { delete pListenSocket; bError = TRUE; str += _T(" ") + ip; } else m_ListenSocketList.push_back(pListenSocket); } if (bError) ShowStatus(str, 1); } if (pos < 1 && !ssl && m_pOptions && m_pOptions->GetOptionVal(OPTION_ENABLESSL)) { // Now create the ssl ports ports = m_pOptions->GetOption(OPTION_SSLPORTS); ports += _T(" "); pos = ports.Find(' '); ssl = true; } } return !m_ListenSocketList.empty(); } unsigned int CServer::GetNextThreadNotificationID() { for (unsigned int i = 0; i < m_ThreadNotificationIDs.size(); i++) if (!m_ThreadNotificationIDs[i]) return i; m_ThreadNotificationIDs.push_back(0); return m_ThreadNotificationIDs.size() - 1; } void CServer::FreeThreadNotificationID(CServerThread *pThread) { for (unsigned int i = 0; i < m_ThreadNotificationIDs.size(); i++) if (m_ThreadNotificationIDs[i] == pThread) { m_ThreadNotificationIDs[i] = 0; break; } } void CServer::SendState() { if (!m_pAdminInterface) return; // Send state to interface unsigned char buffer[2]; buffer[0] = m_nServerState / 256; buffer[1] = m_nServerState % 256; m_pAdminInterface->SendCommand(2, 2, buffer, 2); }