📄 serverlist.cpp
字号:
//this file is part of eMule
//Copyright (C)2002 Merkur ( devs@emule-project.net / http://www.emule-project.net )
//
//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., 675 Mass Ave, Cambridge, MA 02139, USA.
#include "stdafx.h"
#include <io.h>
#include "emule.h"
#include "ServerList.h"
#include "SafeFile.h"
#include "Exceptions.h"
#include "OtherFunctions.h"
#include "IPFilter.h"
#include "LastCommonRouteFinder.h"
#include "Statistics.h"
#include "DownloadQueue.h"
#include "Preferences.h"
#include "Opcodes.h"
#include "Server.h"
#include "Sockets.h"
#include "Packets.h"
#include "emuledlg.h"
#include "HttpDownloadDlg.h"
#include "ServerWnd.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
#define SERVER_MET_FILENAME _T("server.met")
CServerList::CServerList()
{
servercount = 0;
version = 0;
serverpos = 0;
searchserverpos = 0;
statserverpos = 0;
delservercount = 0;
m_nLastSaved = ::GetTickCount();
}
CServerList::~CServerList()
{
SaveServermetToFile();
for (POSITION pos = list.GetHeadPosition(); pos != NULL; )
delete list.GetNext(pos);
}
void CServerList::AutoUpdate()
{
if (thePrefs.adresses_list.IsEmpty()){
AfxMessageBox(GetResString(IDS_ERR_EMPTYADRESSESDAT),MB_ICONASTERISK);
return;
}
bool bDownloaded=false;
CString servermetdownload;
CString servermetbackup;
CString servermet;
CString strURLToDownload;
servermetdownload.Format(_T("%sserver_met.download"), thePrefs.GetConfigDir());
servermetbackup.Format(_T("%sserver_met.old"), thePrefs.GetConfigDir());
servermet.Format(_T("%s") SERVER_MET_FILENAME, thePrefs.GetConfigDir());
_tremove(servermetbackup);
_tremove(servermetdownload);
_trename(servermet,servermetbackup);
POSITION Pos = thePrefs.adresses_list.GetHeadPosition();
while (!bDownloaded && Pos != NULL){
CHttpDownloadDlg dlgDownload;
dlgDownload.m_strTitle = GetResString(IDS_HTTP_CAPTION);
strURLToDownload = thePrefs.adresses_list.GetNext(Pos);
dlgDownload.m_sURLToDownload = strURLToDownload.GetBuffer();
dlgDownload.m_sFileToDownloadInto = servermetdownload;
if (dlgDownload.DoModal() == IDOK){
bDownloaded=true;
}
else{
AddLogLine(true,GetResString(IDS_ERR_FAILEDDOWNLOADMET), strURLToDownload.GetBuffer());
}
}
if (bDownloaded){
_trename(servermet, servermetdownload);
_trename(servermetbackup, servermet);
}
else{
_tremove(servermet);
_trename(servermetbackup, servermet);
}
}
bool CServerList::Init()
{
// auto update the list by using an url
if (thePrefs.AutoServerlist())
AutoUpdate();
// Load Metfile
CString strPath;
strPath.Format(_T("%s") SERVER_MET_FILENAME, thePrefs.GetConfigDir());
bool bRes = AddServermetToList(strPath, false);
if (thePrefs.AutoServerlist()){
strPath.Format(_T("%sserver_met.download"), thePrefs.GetConfigDir());
bool bRes2 = AddServermetToList(strPath);
if( !bRes && bRes2 )
bRes = true;
}
// insert static servers from textfile
strPath.Format(_T("%sstaticservers.dat"), thePrefs.GetConfigDir());
AddServersFromTextFile(strPath);
// ZZ:UploadSpeedSense -->
theApp.serverlist->GiveServersForTraceRoute();
// ZZ:UploadSpeedSense <--
return bRes;
}
bool CServerList::AddServermetToList(const CString& strFile, bool merge)
{
if (!merge)
{
theApp.emuledlg->serverwnd->serverlistctrl.DeleteAllItems();
RemoveAllServers();
}
CSafeBufferedFile servermet;
CFileException fexp;
if (!servermet.Open(strFile,CFile::modeRead|CFile::osSequentialScan|CFile::typeBinary|CFile::shareDenyWrite, &fexp)){
if(!merge){
CString strError(GetResString(IDS_ERR_LOADSERVERMET));
TCHAR szError[MAX_CFEXP_ERRORMSG];
if (fexp.GetErrorMessage(szError,ARRSIZE(szError))){
strError += _T(" - ");
strError += szError;
}
AddLogLine(false, _T("%s"), strError);
}
return false;
}
setvbuf(servermet.m_pStream, NULL, _IOFBF, 16384);
try{
version = servermet.ReadUInt8();
if (version != 0xE0 && version != MET_HEADER){
servermet.Close();
AddLogLine(false,GetResString(IDS_ERR_BADSERVERMETVERSION),version);
return false;
}
theApp.emuledlg->serverwnd->serverlistctrl.Hide();
theApp.emuledlg->serverwnd->serverlistctrl.SetRedraw(FALSE);
UINT fservercount = servermet.ReadUInt32();
ServerMet_Struct sbuffer;
UINT iAddCount = 0;
for (UINT j = 0; j < fservercount; j++)
{
// get server
servermet.Read(&sbuffer,sizeof(ServerMet_Struct));
CServer* newserver = new CServer(&sbuffer);
//add tags
for (UINT i = 0; i < sbuffer.tagcount; i++)
newserver->AddTagFromFile(&servermet);
// set listname for server
if (newserver->GetListName().IsEmpty()){
CString listname;
listname.Format(_T("Server %s"), newserver->GetAddress());
newserver->SetListName(listname);
}
if (!theApp.emuledlg->serverwnd->serverlistctrl.AddServer(newserver,true) ){
CServer* update = theApp.serverlist->GetServerByAddress(newserver->GetAddress(), newserver->GetPort());
if(update){
update->SetListName(newserver->GetListName());
update->SetDescription(newserver->GetDescription());
theApp.emuledlg->serverwnd->serverlistctrl.RefreshServer(update);
}
delete newserver;
}
else
iAddCount++;
}
if (!merge)
AddLogLine(true,GetResString(IDS_SERVERSFOUND),fservercount);
else
AddLogLine(true,GetResString(IDS_SERVERSADDED), iAddCount, fservercount-iAddCount);
servermet.Close();
}
catch(CFileException* error){
if (thePrefs.GetVerbose())
{
if (error->m_cause == CFileException::endOfFile){
AddDebugLogLine(true,GetResString(IDS_ERR_BADSERVERLIST));
}
else{
TCHAR buffer[MAX_CFEXP_ERRORMSG];
error->GetErrorMessage(buffer, ARRSIZE(buffer));
AddDebugLogLine(true,GetResString(IDS_ERR_FILEERROR_SERVERMET),buffer);
}
}
error->Delete();
}
theApp.emuledlg->serverwnd->serverlistctrl.SetRedraw(TRUE);
theApp.emuledlg->serverwnd->serverlistctrl.Visable();
return true;
}
bool CServerList::AddServer(CServer* in_server)
{
if (!IsGoodServerIP(in_server)){ // check for 0-IP, localhost and optionally for LAN addresses
if (thePrefs.GetLogFilteredIPs())
AddDebugLogLine(false, _T("Ignored server (IP=%s)"), ipstr(in_server->GetIP()));
return false;
}
if (thePrefs.FilterServerByIP()){
if (in_server->HasDynIP())
return false;
if (theApp.ipfilter->IsFiltered(in_server->GetIP())){
if (thePrefs.GetLogFilteredIPs())
AddDebugLogLine(false, _T("Ignored server (IP=%s) - IP filter (%s)"), ipstr(in_server->GetIP()), theApp.ipfilter->GetLastHit());
return false;
}
}
CServer* test_server = GetServerByAddress(in_server->GetAddress(), in_server->GetPort());
if (test_server){
test_server->ResetFailedCount();
theApp.emuledlg->serverwnd->serverlistctrl.RefreshServer( test_server );
return false;
}
list.AddTail(in_server);
return true;
}
// ZZ:UploadSpeedSense -->
bool CServerList::GiveServersForTraceRoute()
{
return theApp.lastCommonRouteFinder->AddHostsToCheck(list);
}
// ZZ:UploadSpeedSense <--
void CServerList::ServerStats()
{
// Update the server list even if we are connected to Kademlia only. The idea is for both networks to keep
// each other up to date.. Kad network can get you back into the ED2K network.. And the ED2K network can get
// you back into the Kad network..
if (theApp.IsConnected() && theApp.serverconnect->IsUDPSocketAvailable() && list.GetCount() > 0)
{
CServer* ping_server = GetNextStatServer();
if( !ping_server )
return;
uint32 tNow = (uint32)time(NULL);
const CServer* test = ping_server;
while(ping_server->GetLastPingedTime() != 0 && (tNow - ping_server->GetLastPingedTime()) < UDPSERVSTATREASKTIME){
ping_server = GetNextStatServer();
if( ping_server == test )
return;
}
if (ping_server->GetFailedCount() >= thePrefs.GetDeadserverRetries()){
theApp.emuledlg->serverwnd->serverlistctrl.RemoveServer(ping_server);
return;
}
Packet* packet = new Packet( OP_GLOBSERVSTATREQ, 4 );
srand(tNow);
uint32 uChallenge = 0x55AA0000 + GetRandomUInt16();
ping_server->SetChallenge(uChallenge);
PokeUInt32(packet->pBuffer, uChallenge);
ping_server->SetLastPinged( ::GetTickCount() );
ping_server->SetLastPingedTime(tNow);
ping_server->AddFailedCount();
theApp.emuledlg->serverwnd->serverlistctrl.RefreshServer( ping_server );
if (thePrefs.GetDebugServerUDPLevel() > 0)
Debug(_T(">>> Sending OP__GlobServStatReq to %s:%u\n"), ping_server->GetAddress(), ping_server->GetPort());
theStats.AddUpDataOverheadServer(packet->size);
theApp.serverconnect->SendUDPPacket( packet, ping_server, true );
ping_server->SetLastDescPingedCount(false);
if(ping_server->GetLastDescPingedCount() < 2){
// eserver 16.45+ supports a new OP_SERVER_DESC_RES answer, if the OP_SERVER_DESC_REQ contains a uint32
// challenge, the server returns additional info with OP_SERVER_DESC_RES. To properly distinguish the
// old and new OP_SERVER_DESC_RES answer, the challenge has to be selected carefully. The first 2 bytes
// of the challenge (in network byte order) MUST NOT be a valid string-len-int16!
packet = new Packet(OP_SERVER_DESC_REQ,4);
uint32 uDescReqChallenge = ((uint32)GetRandomUInt16() << 16) + INV_SERV_DESC_LEN; // 0xF0FF = an 'invalid' string length.
ping_server->SetDescReqChallenge(uDescReqChallenge);
PokeUInt32(packet->pBuffer, uDescReqChallenge);
theStats.AddUpDataOverheadServer(packet->size);
if (thePrefs.GetDebugServerUDPLevel() > 0)
Debug(_T(">>> Sending OP__ServDescReq to %s:%u, challenge %08x\n"), ping_server->GetAddress(), ping_server->GetPort(), uDescReqChallenge);
theApp.serverconnect->SendUDPPacket( packet, ping_server, true );
}
else{
ping_server->SetLastDescPingedCount(true);
}
}
}
bool CServerList::IsGoodServerIP(const CServer* in_server) const
{
if (in_server->HasDynIP())
return true;
return IsGoodIPPort(in_server->GetIP(), in_server->GetPort());
}
void CServerList::RemoveServer(const CServer* out_server)
{
for(POSITION pos = list.GetHeadPosition(); pos != NULL; )
{
POSITION pos2 = pos;
const CServer* test_server = list.GetNext(pos);
if (test_server == out_server){
if (theApp.downloadqueue->cur_udpserver == out_server)
theApp.downloadqueue->cur_udpserver = NULL;
list.RemoveAt(pos2);
delservercount++;
delete test_server;
return;
}
}
}
void CServerList::RemoveAllServers()
{
for (POSITION pos = list.GetHeadPosition(); pos != NULL; ){
delete list.GetNext(pos);
delservercount++;
}
list.RemoveAll();
}
void CServerList::GetStatus(uint32& total, uint32& failed,
uint32& user, uint32& file, uint32& lowiduser,
uint32& totaluser, uint32& totalfile,
float& occ) const
{
total = list.GetCount();
failed = 0;
user = 0;
file = 0;
totaluser = 0;
totalfile = 0;
occ = 0;
lowiduser = 0;
uint32 maxuserknownmax = 0;
uint32 totaluserknownmax = 0;
for (POSITION pos = list.GetHeadPosition(); pos != 0; ){
const CServer* curr = list.GetNext(pos);
if (curr->GetFailedCount()){
failed++;
}
else{
user += curr->GetUsers();
file += curr->GetFiles();
lowiduser += curr->GetLowIDUsers();
}
totaluser += curr->GetUsers();
totalfile += curr->GetFiles();
if (curr->GetMaxUsers()){
totaluserknownmax += curr->GetUsers(); // total users on servers with known maximum
maxuserknownmax += curr->GetMaxUsers();
}
}
if (maxuserknownmax > 0)
occ = (float)(totaluserknownmax * 100) / maxuserknownmax;
}
void CServerList::GetAvgFile(uint32& average) const
{
//Since there is no real way to know how many files are in the kad network,
//I figure to try to use the ED2K network stats to find how many files the
//average user shares..
uint32 totaluser = 0;
uint32 totalfile = 0;
for (POSITION pos = list.GetHeadPosition(); pos != 0; ){
const CServer* curr = list.GetNext(pos);
//If this server has reported Users/Files and doesn't limit it's files too much
//use this in the calculation..
if( curr->GetUsers() && curr->GetFiles() && curr->GetSoftFiles() > 1000 )
{
totaluser += curr->GetUsers();
totalfile += curr->GetFiles();
}
}
//If the user count is a little low, don't send back a average..
//I added 50 to the count as many servers do not allow a large amount of files to be shared..
//Therefore the extimate here will be lower then the actual.
//I would love to add a way for the client to send some statistics back so we could see the real
//values here..
if ( totaluser > 500000 )
average = (totalfile/totaluser)+50;
else
average = 0;
}
void CServerList::GetUserFileStatus(uint32& user, uint32& file) const
{
user = 0;
file = 0;
for (POSITION pos = list.GetHeadPosition(); pos != 0; ){
const CServer* curr = list.GetNext(pos);
if( !curr->GetFailedCount() ){
user += curr->GetUsers();
file += curr->GetFiles();
}
}
}
void CServerList::MoveServerDown(const CServer* aServer)
{
POSITION pos1, pos2;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -