954 lines
25 KiB
C++
954 lines
25 KiB
C++
|
// 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.
|
|||
|
|
|||
|
// ServerThread.cpp: Implementierungsdatei
|
|||
|
//
|
|||
|
|
|||
|
#include "stdafx.h"
|
|||
|
#include "iputils.h"
|
|||
|
#include "ServerThread.h"
|
|||
|
#include "ControlSocket.h"
|
|||
|
#include "transfersocket.h"
|
|||
|
#include "Options.h"
|
|||
|
#include "version.h"
|
|||
|
#include "Permissions.h"
|
|||
|
#include "ExternalIpCheck.h"
|
|||
|
#include "autobanmanager.h"
|
|||
|
#include "hash_thread.h"
|
|||
|
|
|||
|
#ifdef _DEBUG
|
|||
|
#undef THIS_FILE
|
|||
|
static char THIS_FILE[] = __FILE__;
|
|||
|
#endif
|
|||
|
|
|||
|
std::map<int, t_socketdata> CServerThread::m_userids;
|
|||
|
CCriticalSectionWrapper CServerThread::m_GlobalThreadsync;
|
|||
|
std::map<CStdString, int> CServerThread::m_userIPs;
|
|||
|
std::list<CServerThread*> CServerThread::m_sInstanceList;
|
|||
|
std::map<CStdString, int> CServerThread::m_antiHammerInfo;
|
|||
|
CHashThread* CServerThread::m_hashThread = 0;
|
|||
|
|
|||
|
/////////////////////////////////////////////////////////////////////////////
|
|||
|
// CServerThread
|
|||
|
|
|||
|
CServerThread::CServerThread(int nNotificationMessageId)
|
|||
|
{
|
|||
|
m_nNotificationMessageId = nNotificationMessageId;
|
|||
|
m_pOptions = 0;
|
|||
|
m_pAutoBanManager = 0;
|
|||
|
}
|
|||
|
|
|||
|
CServerThread::~CServerThread()
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
BOOL CServerThread::InitInstance()
|
|||
|
{
|
|||
|
BOOL res = TRUE;
|
|||
|
WSADATA wsaData;
|
|||
|
|
|||
|
WORD wVersionRequested = MAKEWORD(1, 1);
|
|||
|
int nResult = WSAStartup(wVersionRequested, &wsaData);
|
|||
|
if (nResult != 0)
|
|||
|
res=FALSE;
|
|||
|
else if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
|
|||
|
{
|
|||
|
WSACleanup();
|
|||
|
res=FALSE;
|
|||
|
}
|
|||
|
|
|||
|
m_timerid = SetTimer(0, 0, 1000, 0);
|
|||
|
m_nRateTimer = SetTimer(0, 0, 100, 0);
|
|||
|
|
|||
|
// Reduce anti hammer value twice an hour
|
|||
|
m_antiHammerTimer = SetTimer(0, 0, 1800 * 1000, 0);
|
|||
|
|
|||
|
m_bQuit = FALSE;
|
|||
|
m_nRecvCount = 0;
|
|||
|
m_nSendCount = 0;
|
|||
|
m_pOptions = new COptions;
|
|||
|
m_pAutoBanManager = new CAutoBanManager(m_pOptions);
|
|||
|
m_pPermissions = new CPermissions;
|
|||
|
|
|||
|
EnterCritSection(m_GlobalThreadsync);
|
|||
|
if (m_sInstanceList.empty())
|
|||
|
m_bIsMaster = TRUE;
|
|||
|
else
|
|||
|
m_bIsMaster = FALSE;
|
|||
|
m_sInstanceList.push_back(this);
|
|||
|
LeaveCritSection(m_GlobalThreadsync);
|
|||
|
|
|||
|
m_nLoopCount = 0;
|
|||
|
|
|||
|
EnterCritSection(m_threadsync);
|
|||
|
if (!m_bIsMaster)
|
|||
|
m_pExternalIpCheck = NULL;
|
|||
|
else
|
|||
|
{
|
|||
|
m_pExternalIpCheck = new CExternalIpCheck(this);
|
|||
|
m_hashThread = new CHashThread();
|
|||
|
}
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
|
|||
|
m_throttled = 0;
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
DWORD CServerThread::ExitInstance()
|
|||
|
{
|
|||
|
ASSERT(m_pPermissions);
|
|||
|
delete m_pPermissions;
|
|||
|
m_pPermissions=0;
|
|||
|
delete m_pAutoBanManager;
|
|||
|
m_pAutoBanManager = 0;
|
|||
|
delete m_pOptions;
|
|||
|
m_pOptions=0;
|
|||
|
KillTimer(0, m_timerid);
|
|||
|
KillTimer(0, m_nRateTimer);
|
|||
|
WSACleanup();
|
|||
|
m_hashThread->Stop(this);
|
|||
|
|
|||
|
if (m_bIsMaster)
|
|||
|
{
|
|||
|
EnterCritSection(m_threadsync);
|
|||
|
delete m_pExternalIpCheck;
|
|||
|
m_pExternalIpCheck = NULL;
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
}
|
|||
|
|
|||
|
EnterCritSection(m_GlobalThreadsync);
|
|||
|
m_sInstanceList.remove(this);
|
|||
|
if (!m_sInstanceList.empty())
|
|||
|
m_sInstanceList.front()->m_bIsMaster = TRUE;
|
|||
|
else
|
|||
|
{
|
|||
|
delete m_hashThread;
|
|||
|
m_hashThread = 0;
|
|||
|
}
|
|||
|
LeaveCritSection(m_GlobalThreadsync);
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
/////////////////////////////////////////////////////////////////////////////
|
|||
|
// Behandlungsroutinen f<>r Nachrichten CServerThread
|
|||
|
|
|||
|
const int CServerThread::GetNumConnections()
|
|||
|
{
|
|||
|
EnterCritSection(m_threadsync);
|
|||
|
int num = m_LocalUserIDs.size();
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
return num;
|
|||
|
}
|
|||
|
|
|||
|
void CServerThread::AddSocket(SOCKET sockethandle, bool ssl)
|
|||
|
{
|
|||
|
PostThreadMessage(WM_FILEZILLA_THREADMSG, ssl ? FTM_NEWSOCKET_SSL : FTM_NEWSOCKET, (LPARAM)sockethandle);
|
|||
|
}
|
|||
|
|
|||
|
#define IDMAX 1000000000
|
|||
|
int CServerThread::CalcUserID()
|
|||
|
{
|
|||
|
if (m_userids.size() >= IDMAX)
|
|||
|
return -1;
|
|||
|
static int curid=0;
|
|||
|
curid++;
|
|||
|
if (curid==IDMAX)
|
|||
|
curid=1;
|
|||
|
while (m_userids.find(curid) != m_userids.end())
|
|||
|
{
|
|||
|
curid++;
|
|||
|
if (curid == IDMAX)
|
|||
|
curid=1;
|
|||
|
}
|
|||
|
return curid;
|
|||
|
}
|
|||
|
|
|||
|
void CServerThread::AddNewSocket(SOCKET sockethandle, bool ssl)
|
|||
|
{
|
|||
|
CControlSocket *socket = new CControlSocket(this);
|
|||
|
if (!socket->Attach(sockethandle))
|
|||
|
{
|
|||
|
socket->SendStatus(_T("Failed to attach socket."), 1);
|
|||
|
closesocket(sockethandle);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
CStdString ip;
|
|||
|
UINT port = 0;
|
|||
|
if (socket->GetPeerName(ip, port))
|
|||
|
{
|
|||
|
if (socket->GetFamily() == AF_INET6)
|
|||
|
ip = GetIPV6ShortForm(ip);
|
|||
|
socket->m_RemoteIP = ip;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
socket->m_RemoteIP = _T("ip unknown");
|
|||
|
socket->m_userid = 0;
|
|||
|
socket->SendStatus(_T("Can't get remote IP, disconnected"), 1);
|
|||
|
socket->Close();
|
|||
|
delete socket;
|
|||
|
return;
|
|||
|
}
|
|||
|
EnterCritSection(m_GlobalThreadsync);
|
|||
|
int userid = CalcUserID();
|
|||
|
if (userid == -1)
|
|||
|
{
|
|||
|
LeaveCritSection(m_GlobalThreadsync);
|
|||
|
socket->m_userid = 0;
|
|||
|
socket->SendStatus(_T("Refusing connection, server too busy!"), 1);
|
|||
|
socket->Send(_T("421 Server too busy, closing connection. Please retry later!"));
|
|||
|
socket->Close();
|
|||
|
delete socket;
|
|||
|
return;
|
|||
|
}
|
|||
|
socket->m_userid = userid;
|
|||
|
t_socketdata data;
|
|||
|
data.pSocket = socket;
|
|||
|
data.pThread = this;
|
|||
|
m_userids[userid] = data;
|
|||
|
|
|||
|
// Check if remote IP is blocked due to hammering
|
|||
|
std::map<CStdString, int>::const_iterator iter = m_antiHammerInfo.find(ip);
|
|||
|
if (iter != m_antiHammerInfo.end() && iter->second > 10)
|
|||
|
socket->AntiHammerIncrease(25); // ~6 secs delay
|
|||
|
LeaveCritSection(m_GlobalThreadsync);
|
|||
|
EnterCritSection(m_threadsync);
|
|||
|
m_LocalUserIDs[userid] = socket;
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
|
|||
|
t_connectiondata_add *conndata = new t_connectiondata_add;
|
|||
|
t_connop *op = new t_connop;
|
|||
|
op->data = conndata;
|
|||
|
op->op = USERCONTROL_CONNOP_ADD;
|
|||
|
op->userid = userid;
|
|||
|
conndata->pThread = this;
|
|||
|
|
|||
|
conndata->port = port;
|
|||
|
_tcscpy(conndata->ip, socket->m_RemoteIP);
|
|||
|
|
|||
|
SendNotification(FSM_CONNECTIONDATA, (LPARAM)op);
|
|||
|
|
|||
|
if (ssl && !socket->InitImplicitSsl())
|
|||
|
return;
|
|||
|
|
|||
|
socket->AsyncSelect(FD_READ|FD_WRITE|FD_CLOSE);
|
|||
|
socket->SendStatus(_T("Connected, sending welcome message..."), 0);
|
|||
|
|
|||
|
CStdString msg;
|
|||
|
if (m_pOptions->GetOptionVal(OPTION_ENABLE_HASH))
|
|||
|
msg = _T("EXPERIMANTAL BUILD\nNOT FOR PRODUCTION USE\n\nImplementing draft-bryan-ftp-hash-06");
|
|||
|
else
|
|||
|
msg = m_pOptions->GetOption(OPTION_WELCOMEMESSAGE);
|
|||
|
if (m_RawWelcomeMessage != msg)
|
|||
|
{
|
|||
|
m_RawWelcomeMessage = msg;
|
|||
|
m_ParsedWelcomeMessage.clear();
|
|||
|
|
|||
|
msg.Replace(_T("%%"), _T("\001"));
|
|||
|
msg.Replace(_T("%v"), GetVersionString());
|
|||
|
msg.Replace(_T("\001"), _T("%"));
|
|||
|
|
|||
|
ASSERT(msg != _T(""));
|
|||
|
int oldpos = 0;
|
|||
|
msg.Replace(_T("\r\n"), _T("\n"));
|
|||
|
int pos = msg.Find(_T("\n"));
|
|||
|
CStdString line;
|
|||
|
while (pos != -1)
|
|||
|
{
|
|||
|
ASSERT(pos);
|
|||
|
m_ParsedWelcomeMessage.push_back(_T("220-") + msg.Mid(oldpos, pos-oldpos) );
|
|||
|
oldpos = pos + 1;
|
|||
|
pos = msg.Find(_T("\n"), oldpos);
|
|||
|
}
|
|||
|
|
|||
|
line = msg.Mid(oldpos);
|
|||
|
if (line != _T(""))
|
|||
|
m_ParsedWelcomeMessage.push_back(_T("220 ") + line);
|
|||
|
else
|
|||
|
{
|
|||
|
m_ParsedWelcomeMessage.back()[3] = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
bool hideStatus = m_pOptions->GetOptionVal(OPTION_WELCOMEMESSAGE_HIDE) != 0;
|
|||
|
ASSERT(!m_ParsedWelcomeMessage.empty());
|
|||
|
for (std::list<CStdString>::iterator iter = m_ParsedWelcomeMessage.begin(); iter != m_ParsedWelcomeMessage.end(); iter++)
|
|||
|
if (!socket->Send(*iter, !hideStatus))
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
int CServerThread::OnThreadMessage(UINT Msg, WPARAM wParam, LPARAM lParam)
|
|||
|
{
|
|||
|
if (Msg == WM_FILEZILLA_THREADMSG)
|
|||
|
{
|
|||
|
if (wParam == FTM_NEWSOCKET) //Add a new socket to this thread
|
|||
|
AddNewSocket((SOCKET)lParam, false);
|
|||
|
else if (wParam == FTM_NEWSOCKET_SSL) //Add a new socket to this thread
|
|||
|
AddNewSocket((SOCKET)lParam, true);
|
|||
|
else if (wParam==FTM_DELSOCKET) //Remove a socket from this thread
|
|||
|
{
|
|||
|
CControlSocket *socket=GetControlSocket(lParam);
|
|||
|
EnterCritSection(m_threadsync);
|
|||
|
if (m_LocalUserIDs.find(lParam)!=m_LocalUserIDs.end())
|
|||
|
m_LocalUserIDs.erase(m_LocalUserIDs.find(lParam));
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
if (socket)
|
|||
|
{
|
|||
|
socket->Close();
|
|||
|
EnterCritSection(m_GlobalThreadsync);
|
|||
|
if (m_userids.find(lParam)!=m_userids.end())
|
|||
|
m_userids.erase(m_userids.find(lParam));
|
|||
|
LeaveCritSection(m_GlobalThreadsync);
|
|||
|
delete socket;
|
|||
|
}
|
|||
|
EnterCritSection(m_threadsync);
|
|||
|
if (m_bQuit)
|
|||
|
{
|
|||
|
int count=m_LocalUserIDs.size();
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
if (!count)
|
|||
|
SendNotification(FSM_THREADCANQUIT, (LPARAM)this);
|
|||
|
}
|
|||
|
else
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
}
|
|||
|
else if (wParam==FTM_COMMAND)
|
|||
|
{ //Process a command sent from a client
|
|||
|
CControlSocket *socket=GetControlSocket(lParam);
|
|||
|
if (socket)
|
|||
|
socket->ParseCommand();
|
|||
|
}
|
|||
|
else if (wParam==FTM_TRANSFERMSG)
|
|||
|
{
|
|||
|
CControlSocket *socket=GetControlSocket(lParam);
|
|||
|
if (socket)
|
|||
|
socket->ProcessTransferMsg();
|
|||
|
}
|
|||
|
else if (wParam==FTM_GOOFFLINE)
|
|||
|
{
|
|||
|
EnterCritSection(m_threadsync);
|
|||
|
m_bQuit = TRUE;
|
|||
|
int count = m_LocalUserIDs.size();
|
|||
|
if (!count)
|
|||
|
{
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
SendNotification(FSM_THREADCANQUIT, (LPARAM)this);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
if (lParam==2)
|
|||
|
{
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
for (std::map<int, CControlSocket *>::iterator iter=m_LocalUserIDs.begin(); iter!=m_LocalUserIDs.end(); iter++)
|
|||
|
{
|
|||
|
switch (lParam)
|
|||
|
{
|
|||
|
case 2:
|
|||
|
iter->second->WaitGoOffline(false);
|
|||
|
break;
|
|||
|
case 0:
|
|||
|
default:
|
|||
|
iter->second->ForceClose(0);
|
|||
|
break;
|
|||
|
case 1:
|
|||
|
iter->second->WaitGoOffline(true);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
}
|
|||
|
else if (wParam == FTM_CONTROL)
|
|||
|
ProcessControlMessage((t_controlmessage *)lParam);
|
|||
|
else if (wParam == FTM_HASHRESULT)
|
|||
|
{
|
|||
|
CHashThread::_algorithm alg;
|
|||
|
CStdString hash;
|
|||
|
CStdString file;
|
|||
|
int hash_res = GetHashThread().GetResult(lParam, alg, hash, file);
|
|||
|
EnterCritSection(m_threadsync);
|
|||
|
|
|||
|
for (std::map<int, CControlSocket *>::iterator iter = m_LocalUserIDs.begin(); iter != m_LocalUserIDs.end(); iter++)
|
|||
|
{
|
|||
|
iter->second->ProcessHashResult(lParam, hash_res, alg, hash, file);
|
|||
|
}
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
}
|
|||
|
}
|
|||
|
else if (Msg == WM_TIMER)
|
|||
|
OnTimer(wParam, lParam);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
void CServerThread::OnTimer(WPARAM wParam,LPARAM lParam)
|
|||
|
{
|
|||
|
if (wParam == m_timerid)
|
|||
|
{
|
|||
|
EnterCritSection(m_threadsync);
|
|||
|
|
|||
|
/*
|
|||
|
* Check timeouts and collect transfer file offsets.
|
|||
|
* Do both in the same loop to save performance.
|
|||
|
*/
|
|||
|
|
|||
|
/*
|
|||
|
* Maximum memory required for file offsets:
|
|||
|
* 2 unused prefix bytes, will be filled by CServer,
|
|||
|
* This avoids buffer copying.
|
|||
|
* For each connection 4 bytes for the userid
|
|||
|
* and 8 for the offset.
|
|||
|
* We do not need to store the number of elements, this
|
|||
|
* information can be calculated from the length if neccessary.
|
|||
|
*/
|
|||
|
int bufferLen = 2 + m_LocalUserIDs.size() * 12;
|
|||
|
unsigned char* buffer = new unsigned char[bufferLen];
|
|||
|
unsigned char* p = buffer + 2;
|
|||
|
for (std::map<int, CControlSocket *>::iterator iter = m_LocalUserIDs.begin(); iter != m_LocalUserIDs.end(); iter++)
|
|||
|
{
|
|||
|
CControlSocket* pSocket = iter->second;
|
|||
|
CTransferSocket* pTransferSocket = pSocket->GetTransferSocket();
|
|||
|
if (pTransferSocket && pTransferSocket->WasActiveSinceCheck())
|
|||
|
{
|
|||
|
memcpy(p, &iter->first, 4);
|
|||
|
p += 4;
|
|||
|
__int64 offset = pTransferSocket->GetCurrentFileOffset();
|
|||
|
memcpy(p, &offset, 8);
|
|||
|
p += 8;
|
|||
|
}
|
|||
|
iter->second->CheckForTimeout();
|
|||
|
}
|
|||
|
|
|||
|
if ((p - buffer) <= 2)
|
|||
|
delete [] buffer;
|
|||
|
else
|
|||
|
{
|
|||
|
t_connectiondata_transferoffsets* conndata = new t_connectiondata_transferoffsets;
|
|||
|
conndata->pData = buffer;
|
|||
|
conndata->len = p - buffer;
|
|||
|
t_connop* op = new t_connop;
|
|||
|
op->data = conndata;
|
|||
|
op->op = USERCONTROL_CONNOP_TRANSFEROFFSETS;
|
|||
|
SendNotification(FSM_CONNECTIONDATA, (LPARAM)op);
|
|||
|
}
|
|||
|
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
|
|||
|
// Check if master thread has changed
|
|||
|
if (m_bIsMaster && !m_pExternalIpCheck)
|
|||
|
{
|
|||
|
EnterCritSection(m_threadsync);
|
|||
|
m_pExternalIpCheck = new CExternalIpCheck(this);
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
}
|
|||
|
}
|
|||
|
else if (wParam==m_nRateTimer)
|
|||
|
{
|
|||
|
if (m_nSendCount)
|
|||
|
{
|
|||
|
SendNotification(FSM_SEND, m_nSendCount);
|
|||
|
m_nSendCount = 0;
|
|||
|
}
|
|||
|
if (m_nRecvCount)
|
|||
|
{
|
|||
|
SendNotification(FSM_RECV, m_nRecvCount);
|
|||
|
m_nRecvCount = 0;
|
|||
|
}
|
|||
|
|
|||
|
if (m_bIsMaster)
|
|||
|
{
|
|||
|
EnterCritSection(m_GlobalThreadsync);
|
|||
|
|
|||
|
std::list<CServerThread *>::iterator iter;
|
|||
|
|
|||
|
//Only update the speed limits from the rule set every 2 seconds to improve performance
|
|||
|
if (!m_nLoopCount)
|
|||
|
{
|
|||
|
m_lastLimits[download] = m_pOptions->GetCurrentSpeedLimit(download);
|
|||
|
m_lastLimits[upload] = m_pOptions->GetCurrentSpeedLimit(upload);
|
|||
|
}
|
|||
|
++m_nLoopCount %= 20;
|
|||
|
|
|||
|
// Gather transfer statistics if a speed limit is set
|
|||
|
if (m_lastLimits[download] != -1 || m_lastLimits[upload] != -1)
|
|||
|
for (iter = m_sInstanceList.begin(); iter != m_sInstanceList.end(); iter++)
|
|||
|
{
|
|||
|
CServerThread *pThread = *iter;
|
|||
|
EnterCritSection(pThread->m_threadsync);
|
|||
|
pThread->GatherTransferedBytes();
|
|||
|
LeaveCritSection(pThread->m_threadsync);
|
|||
|
}
|
|||
|
|
|||
|
for (int i = 0; i < 2; i++)
|
|||
|
{
|
|||
|
int limit = m_lastLimits[i];
|
|||
|
|
|||
|
if (limit == -1)
|
|||
|
{
|
|||
|
for (iter = m_sInstanceList.begin(); iter != m_sInstanceList.end(); iter++)
|
|||
|
{
|
|||
|
CServerThread *pThread = *iter;
|
|||
|
EnterCritSection(pThread->m_threadsync);
|
|||
|
pThread->m_SlQuotas[i].nBytesAllowedToTransfer = -1;
|
|||
|
pThread->m_SlQuotas[i].nTransferred = 0;
|
|||
|
LeaveCritSection(pThread->m_threadsync);
|
|||
|
}
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
limit *= 100;
|
|||
|
|
|||
|
int nRemaining = limit;
|
|||
|
int nThreadLimit = limit / m_sInstanceList.size();
|
|||
|
|
|||
|
std::list<CServerThread *> fullUsageList;
|
|||
|
|
|||
|
for (iter = m_sInstanceList.begin(); iter != m_sInstanceList.end(); iter++)
|
|||
|
{
|
|||
|
CServerThread *pThread = *iter;
|
|||
|
EnterCritSection(pThread->m_threadsync);
|
|||
|
int r = pThread->m_SlQuotas[i].nBytesAllowedToTransfer - pThread->m_SlQuotas[i].nTransferred;
|
|||
|
if ( r > 0 && pThread->m_SlQuotas[i].nBytesAllowedToTransfer <= nThreadLimit)
|
|||
|
{
|
|||
|
pThread->m_SlQuotas[i].nBytesAllowedToTransfer = nThreadLimit;
|
|||
|
nRemaining -= pThread->m_SlQuotas[i].nTransferred;
|
|||
|
pThread->m_SlQuotas[i].nTransferred = 0;
|
|||
|
}
|
|||
|
else if (r > 0 && pThread->m_SlQuotas[i].nTransferred < nThreadLimit)
|
|||
|
{
|
|||
|
pThread->m_SlQuotas[i].nBytesAllowedToTransfer = nThreadLimit;
|
|||
|
nRemaining -= pThread->m_SlQuotas[i].nTransferred;
|
|||
|
pThread->m_SlQuotas[i].nTransferred = 0;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
fullUsageList.push_back(pThread);
|
|||
|
// Don't unlock thread here, do it later
|
|||
|
continue;
|
|||
|
}
|
|||
|
LeaveCritSection(pThread->m_threadsync);
|
|||
|
}
|
|||
|
|
|||
|
// fullUsageList now contains all threads which did use up its assigned quota
|
|||
|
std::list<CServerThread *> fullUsageList2;
|
|||
|
if (!fullUsageList.empty())
|
|||
|
{
|
|||
|
nThreadLimit = nRemaining / fullUsageList.size();
|
|||
|
for (iter = fullUsageList.begin(); iter != fullUsageList.end(); iter++)
|
|||
|
{
|
|||
|
CServerThread *pThread = *iter;
|
|||
|
|
|||
|
// Thread has already been locked
|
|||
|
int r = pThread->m_SlQuotas[i].nBytesAllowedToTransfer - pThread->m_SlQuotas[i].nTransferred;
|
|||
|
if (r > 0)
|
|||
|
{
|
|||
|
if (pThread->m_SlQuotas[i].nTransferred > nThreadLimit)
|
|||
|
pThread->m_SlQuotas[i].nBytesAllowedToTransfer = nThreadLimit;
|
|||
|
else
|
|||
|
pThread->m_SlQuotas[i].nBytesAllowedToTransfer = pThread->m_SlQuotas[i].nTransferred;
|
|||
|
pThread->m_SlQuotas[i].nTransferred = 0;
|
|||
|
nRemaining -= pThread->m_SlQuotas[i].nBytesAllowedToTransfer;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
fullUsageList2.push_back(pThread);
|
|||
|
// Don't unlock thread here either, do it later
|
|||
|
continue;
|
|||
|
}
|
|||
|
LeaveCritSection(pThread->m_threadsync);
|
|||
|
}
|
|||
|
|
|||
|
if (!fullUsageList2.empty())
|
|||
|
{
|
|||
|
nThreadLimit = nRemaining / fullUsageList2.size();
|
|||
|
for (iter = fullUsageList2.begin(); iter != fullUsageList2.end(); iter++)
|
|||
|
{
|
|||
|
CServerThread *pThread = *iter;
|
|||
|
pThread->m_SlQuotas[i].nTransferred = 0;
|
|||
|
pThread->m_SlQuotas[i].nBytesAllowedToTransfer = nThreadLimit;
|
|||
|
|
|||
|
// Finally unlock threads
|
|||
|
LeaveCritSection(pThread->m_threadsync);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
LeaveCritSection(m_GlobalThreadsync);
|
|||
|
}
|
|||
|
ProcessNewSlQuota();
|
|||
|
}
|
|||
|
else if (m_pExternalIpCheck && wParam == m_pExternalIpCheck->GetTimerID())
|
|||
|
{
|
|||
|
EnterCritSection(m_threadsync);
|
|||
|
m_pExternalIpCheck->OnTimer();
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
}
|
|||
|
else if (wParam == m_antiHammerTimer && m_bIsMaster)
|
|||
|
AntiHammerDecrease();
|
|||
|
}
|
|||
|
|
|||
|
const int CServerThread::GetGlobalNumConnections()
|
|||
|
{
|
|||
|
EnterCritSection(m_GlobalThreadsync);
|
|||
|
int num=m_userids.size();
|
|||
|
LeaveCritSection(m_GlobalThreadsync);
|
|||
|
return num;
|
|||
|
}
|
|||
|
|
|||
|
CControlSocket * CServerThread::GetControlSocket(int userid)
|
|||
|
{
|
|||
|
CControlSocket *ret=0;
|
|||
|
EnterCritSection(m_threadsync);
|
|||
|
std::map<int, CControlSocket *>::iterator iter=m_LocalUserIDs.find(userid);
|
|||
|
if (iter!=m_LocalUserIDs.end())
|
|||
|
ret=iter->second;
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
return ret;
|
|||
|
}
|
|||
|
|
|||
|
void CServerThread::ProcessControlMessage(t_controlmessage *msg)
|
|||
|
{
|
|||
|
if (msg->command == USERCONTROL_KICK)
|
|||
|
{
|
|||
|
CControlSocket *socket = GetControlSocket(msg->socketid);
|
|||
|
if (socket)
|
|||
|
socket->ForceClose(4);
|
|||
|
}
|
|||
|
delete msg;
|
|||
|
}
|
|||
|
|
|||
|
BOOL CServerThread::IsReady()
|
|||
|
{
|
|||
|
return !m_bQuit;
|
|||
|
}
|
|||
|
|
|||
|
int CServerThread::GetIpCount(const CStdString &ip) const
|
|||
|
{
|
|||
|
int count=0;
|
|||
|
EnterCritSection(m_GlobalThreadsync);
|
|||
|
std::map<CStdString, int>::iterator iter=m_userIPs.find(ip);
|
|||
|
if (iter!=m_userIPs.end())
|
|||
|
count=iter->second;
|
|||
|
LeaveCritSection(m_GlobalThreadsync);
|
|||
|
return count;
|
|||
|
}
|
|||
|
|
|||
|
void CServerThread::IncIpCount(const CStdString &ip)
|
|||
|
{
|
|||
|
int count;
|
|||
|
EnterCritSection(m_GlobalThreadsync);
|
|||
|
std::map<CStdString, int>::iterator iter=m_userIPs.find(ip);
|
|||
|
if (iter!=m_userIPs.end())
|
|||
|
count=iter->second++;
|
|||
|
else
|
|||
|
m_userIPs[ip]=1;
|
|||
|
LeaveCritSection(m_GlobalThreadsync);
|
|||
|
}
|
|||
|
|
|||
|
void CServerThread::DecIpCount(const CStdString &ip)
|
|||
|
{
|
|||
|
EnterCritSection(m_GlobalThreadsync);
|
|||
|
std::map<CStdString, int>::iterator iter=m_userIPs.find(ip);
|
|||
|
ASSERT(iter!=m_userIPs.end());
|
|||
|
if (iter==m_userIPs.end())
|
|||
|
{
|
|||
|
LeaveCritSection(m_GlobalThreadsync);
|
|||
|
return;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ASSERT(iter->second);
|
|||
|
if (iter->second)
|
|||
|
iter->second--;
|
|||
|
}
|
|||
|
LeaveCritSection(m_GlobalThreadsync);
|
|||
|
}
|
|||
|
|
|||
|
void CServerThread::IncSendCount(int count)
|
|||
|
{
|
|||
|
m_nSendCount += count;
|
|||
|
}
|
|||
|
|
|||
|
void CServerThread::IncRecvCount(int count)
|
|||
|
{
|
|||
|
m_nRecvCount += count;
|
|||
|
}
|
|||
|
|
|||
|
void CServerThread::ProcessNewSlQuota()
|
|||
|
{
|
|||
|
EnterCritSection(m_threadsync);
|
|||
|
|
|||
|
std::map<int, CControlSocket *>::iterator iter;
|
|||
|
|
|||
|
for (int i = 0; i < 2; i++)
|
|||
|
{
|
|||
|
if (m_SlQuotas[i].nBytesAllowedToTransfer == -1)
|
|||
|
{
|
|||
|
for (iter = m_LocalUserIDs.begin(); iter != m_LocalUserIDs.end(); iter++)
|
|||
|
{
|
|||
|
CControlSocket *pControlSocket = iter->second;
|
|||
|
pControlSocket->m_SlQuotas[i].nBytesAllowedToTransfer = -1;
|
|||
|
pControlSocket->m_SlQuotas[i].nTransferred = 0;
|
|||
|
}
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
int nRemaining = m_SlQuotas[i].nBytesAllowedToTransfer;
|
|||
|
int nThreadLimit = nRemaining / m_sInstanceList.size();
|
|||
|
|
|||
|
std::list<CControlSocket *> fullUsageList;
|
|||
|
|
|||
|
for (iter = m_LocalUserIDs.begin(); iter != m_LocalUserIDs.end(); iter++)
|
|||
|
{
|
|||
|
CControlSocket *pControlSocket = iter->second;
|
|||
|
int r = pControlSocket->m_SlQuotas[i].nBytesAllowedToTransfer - pControlSocket->m_SlQuotas[i].nTransferred;
|
|||
|
if (pControlSocket->m_SlQuotas[i].nBytesAllowedToTransfer == -1)
|
|||
|
{
|
|||
|
pControlSocket->m_SlQuotas[i].nBytesAllowedToTransfer = nThreadLimit;
|
|||
|
pControlSocket->m_SlQuotas[i].nTransferred = 0;
|
|||
|
}
|
|||
|
else if (r > 0 && pControlSocket->m_SlQuotas[i].nBytesAllowedToTransfer <= nThreadLimit)
|
|||
|
{
|
|||
|
pControlSocket->m_SlQuotas[i].nBytesAllowedToTransfer = nThreadLimit;
|
|||
|
nRemaining -= pControlSocket->m_SlQuotas[i].nTransferred;
|
|||
|
pControlSocket->m_SlQuotas[i].nTransferred = 0;
|
|||
|
}
|
|||
|
else if (r > 0 && pControlSocket->m_SlQuotas[i].nTransferred < nThreadLimit)
|
|||
|
{
|
|||
|
pControlSocket->m_SlQuotas[i].nBytesAllowedToTransfer = nThreadLimit;
|
|||
|
nRemaining -= pControlSocket->m_SlQuotas[i].nTransferred;
|
|||
|
pControlSocket->m_SlQuotas[i].nTransferred = 0;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
fullUsageList.push_back(pControlSocket);
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
std::list<CControlSocket *> fullUsageList2;
|
|||
|
if (!fullUsageList.empty())
|
|||
|
{
|
|||
|
std::list<CControlSocket *>::iterator iter;
|
|||
|
|
|||
|
nThreadLimit = nRemaining / fullUsageList.size();
|
|||
|
for (iter = fullUsageList.begin(); iter != fullUsageList.end(); iter++)
|
|||
|
{
|
|||
|
CControlSocket *pControlSocket = *iter;
|
|||
|
int r = pControlSocket->m_SlQuotas[i].nBytesAllowedToTransfer - pControlSocket->m_SlQuotas[i].nTransferred;
|
|||
|
if (r)
|
|||
|
{
|
|||
|
if (pControlSocket->m_SlQuotas[i].nTransferred > nThreadLimit)
|
|||
|
pControlSocket->m_SlQuotas[i].nBytesAllowedToTransfer = nThreadLimit;
|
|||
|
else
|
|||
|
pControlSocket->m_SlQuotas[i].nBytesAllowedToTransfer = pControlSocket->m_SlQuotas[i].nTransferred;
|
|||
|
pControlSocket->m_SlQuotas[i].nTransferred = 0;
|
|||
|
nRemaining -= pControlSocket->m_SlQuotas[i].nBytesAllowedToTransfer;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
fullUsageList2.push_back(pControlSocket);
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!fullUsageList2.empty())
|
|||
|
{
|
|||
|
nThreadLimit = nRemaining / fullUsageList2.size();
|
|||
|
for (iter = fullUsageList2.begin(); iter != fullUsageList2.end(); iter++)
|
|||
|
{
|
|||
|
CControlSocket *pControlSocket = *iter;
|
|||
|
pControlSocket->m_SlQuotas[i].nTransferred = 0;
|
|||
|
pControlSocket->m_SlQuotas[i].nBytesAllowedToTransfer = nThreadLimit;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
for (iter = m_LocalUserIDs.begin(); iter != m_LocalUserIDs.end(); iter++)
|
|||
|
{
|
|||
|
CControlSocket *pControlSocket = iter->second;
|
|||
|
pControlSocket->Continue();
|
|||
|
}
|
|||
|
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
}
|
|||
|
|
|||
|
void CServerThread::GatherTransferedBytes()
|
|||
|
{
|
|||
|
EnterCritSection(m_threadsync);
|
|||
|
for (std::map<int, CControlSocket *>::iterator iter = m_LocalUserIDs.begin(); iter != m_LocalUserIDs.end(); iter++)
|
|||
|
{
|
|||
|
for (int i = 0; i < 2; i++)
|
|||
|
{
|
|||
|
if (iter->second->m_SlQuotas[i].nBytesAllowedToTransfer != -1)
|
|||
|
if (iter->second->m_SlQuotas[i].bBypassed)
|
|||
|
iter->second->m_SlQuotas[i].nTransferred = 0;
|
|||
|
else
|
|||
|
m_SlQuotas[i].nTransferred += iter->second->m_SlQuotas[i].nTransferred;
|
|||
|
|
|||
|
iter->second->m_SlQuotas[i].bBypassed = false;
|
|||
|
}
|
|||
|
}
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
}
|
|||
|
|
|||
|
CStdString CServerThread::GetExternalIP(const CStdString& localIP)
|
|||
|
{
|
|||
|
CStdString ip;
|
|||
|
EnterCritSection(m_threadsync);
|
|||
|
if (m_pExternalIpCheck)
|
|||
|
{
|
|||
|
ip = m_pExternalIpCheck->GetIP(localIP);
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
EnterCritSection(m_GlobalThreadsync);
|
|||
|
CServerThread *pThread = m_sInstanceList.front();
|
|||
|
EnterCritSection(pThread->m_threadsync);
|
|||
|
if (pThread != this && pThread->m_pExternalIpCheck)
|
|||
|
ip = pThread->m_pExternalIpCheck->GetIP(localIP);
|
|||
|
LeaveCritSection(pThread->m_threadsync);
|
|||
|
LeaveCritSection(m_GlobalThreadsync);
|
|||
|
}
|
|||
|
|
|||
|
return ip;
|
|||
|
}
|
|||
|
|
|||
|
void CServerThread::ExternalIPFailed()
|
|||
|
{
|
|||
|
CStdString ip;
|
|||
|
EnterCritSection(m_threadsync);
|
|||
|
if (m_pExternalIpCheck)
|
|||
|
{
|
|||
|
m_pExternalIpCheck->TriggerUpdate();
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
EnterCritSection(m_GlobalThreadsync);
|
|||
|
CServerThread *pThread = m_sInstanceList.front();
|
|||
|
EnterCritSection(pThread->m_threadsync);
|
|||
|
if (pThread != this && pThread->m_pExternalIpCheck)
|
|||
|
pThread->m_pExternalIpCheck->TriggerUpdate();
|
|||
|
LeaveCritSection(pThread->m_threadsync);
|
|||
|
LeaveCritSection(m_GlobalThreadsync);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void CServerThread::SendNotification(WPARAM wParam, LPARAM lParam)
|
|||
|
{
|
|||
|
EnterCritSection(m_threadsync);
|
|||
|
t_Notification notification;
|
|||
|
notification.wParam = wParam;
|
|||
|
notification.lParam = lParam;
|
|||
|
|
|||
|
if (m_pendingNotifications.empty())
|
|||
|
PostMessage(hMainWnd, m_nNotificationMessageId, 0, 0);
|
|||
|
|
|||
|
m_pendingNotifications.push_back(notification);
|
|||
|
|
|||
|
// Check if main thread can't handle number of notifications fast enough, throttle thread if neccessary
|
|||
|
if (m_pendingNotifications.size() > 200 && m_throttled < 3)
|
|||
|
{
|
|||
|
SetPriority(THREAD_PRIORITY_IDLE);
|
|||
|
m_throttled = 3;
|
|||
|
}
|
|||
|
else if (m_pendingNotifications.size() > 150 && m_throttled < 2)
|
|||
|
{
|
|||
|
SetPriority(THREAD_PRIORITY_LOWEST);
|
|||
|
m_throttled = 2;
|
|||
|
}
|
|||
|
else if (m_pendingNotifications.size() > 100 && !m_throttled)
|
|||
|
{
|
|||
|
SetPriority(THREAD_PRIORITY_BELOW_NORMAL);
|
|||
|
m_throttled = 1;
|
|||
|
}
|
|||
|
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
}
|
|||
|
|
|||
|
void CServerThread::GetNotifications(std::list<CServerThread::t_Notification>& list)
|
|||
|
{
|
|||
|
EnterCritSection(m_threadsync);
|
|||
|
|
|||
|
m_pendingNotifications.swap(list);
|
|||
|
|
|||
|
if (m_throttled)
|
|||
|
SetPriority(THREAD_PRIORITY_NORMAL);
|
|||
|
|
|||
|
LeaveCritSection(m_threadsync);
|
|||
|
}
|
|||
|
|
|||
|
void CServerThread::AntiHammerIncrease(const CStdString& ip)
|
|||
|
{
|
|||
|
EnterCritSection(m_GlobalThreadsync);
|
|||
|
|
|||
|
std::map<CStdString, int>::iterator iter = m_antiHammerInfo.find(ip);
|
|||
|
if (iter != m_antiHammerInfo.end())
|
|||
|
{
|
|||
|
if (iter->second < 20)
|
|||
|
iter->second++;
|
|||
|
LeaveCritSection(m_GlobalThreadsync);
|
|||
|
return;
|
|||
|
}
|
|||
|
if (m_antiHammerInfo.size() >= 1000)
|
|||
|
{
|
|||
|
std::map<CStdString, int>::iterator best = m_antiHammerInfo.begin();
|
|||
|
for (iter = m_antiHammerInfo.begin(); iter != m_antiHammerInfo.end(); iter++)
|
|||
|
{
|
|||
|
if (iter->second < best->second)
|
|||
|
best = iter;
|
|||
|
}
|
|||
|
m_antiHammerInfo.erase(best);
|
|||
|
}
|
|||
|
m_antiHammerInfo.insert(std::make_pair(ip, 1));
|
|||
|
|
|||
|
LeaveCritSection(m_GlobalThreadsync);
|
|||
|
}
|
|||
|
|
|||
|
void CServerThread::AntiHammerDecrease()
|
|||
|
{
|
|||
|
EnterCritSection(m_GlobalThreadsync);
|
|||
|
|
|||
|
std::map<CStdString, int>::iterator iter = m_antiHammerInfo.begin();
|
|||
|
while (iter != m_antiHammerInfo.end())
|
|||
|
{
|
|||
|
if (iter->second > 1)
|
|||
|
{
|
|||
|
--(iter->second);
|
|||
|
++iter;
|
|||
|
}
|
|||
|
else
|
|||
|
m_antiHammerInfo.erase(iter++);
|
|||
|
}
|
|||
|
|
|||
|
LeaveCritSection(m_GlobalThreadsync);
|
|||
|
}
|
|||
|
|
|||
|
CHashThread& CServerThread::GetHashThread()
|
|||
|
{
|
|||
|
return *m_hashThread;
|
|||
|
}
|