1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
10 //
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
24 //
25 
26 #include <wx/wx.h>
27 
28 #include "ServerList.h"			// Interface declarations.
29 
30 #include <protocol/Protocols.h>
31 #include <protocol/ed2k/Constants.h>
32 #include <common/DataFileVersion.h>
33 #include <tags/ServerTags.h>
34 
35 #include <wx/txtstrm.h>
36 #include <wx/wfstream.h>
37 #include <wx/url.h>			// Needed for wxURL
38 #include <wx/tokenzr.h>
39 
40 #include "DownloadQueue.h"		// Needed for CDownloadQueue
41 #include "ServerConnect.h"		// Needed for CServerConnect
42 #include "Server.h"			// Needed for CServer and SRV_PR_*
43 #include "OtherStructs.h"		// Needed for ServerMet_Struct
44 #include "CFile.h"			// Needed for CFile
45 #include "HTTPDownload.h"		// Needed for HTTPThread
46 #include "Preferences.h"		// Needed for thePrefs
47 #include "amule.h"			// Needed for theApp
48 #include "Statistics.h"			// Needed for theStats
49 #include "Packet.h"			// Neeed for CPacket
50 #include "Logger.h"
51 #include "ScopedPtr.h"
52 #include <common/Format.h>
53 #include "IPFilter.h"
54 #include <common/FileFunctions.h>	// Needed for UnpackArchive
55 #include <common/TextFile.h>	// Needed for CTextFile
56 
CServerList()57 CServerList::CServerList()
58 {
59 	m_serverpos = m_servers.end();
60 	m_statserverpos = m_servers.end();
61 	m_nLastED2KServerLinkCheck = ::GetTickCount();
62 	m_initialized = false;
63 }
64 
65 
Init()66 bool CServerList::Init()
67 {
68 	// Load Metfile
69 	bool bRes = LoadServerMet(CPath(thePrefs::GetConfigDir() + wxT("server.met")));
70 
71 	// insert static servers from textfile
72 	m_staticServersConfig = thePrefs::GetConfigDir() + wxT("staticservers.dat");
73 	LoadStaticServers();
74 
75 	// Send the auto-update of server.met via HTTPThread requests
76 	current_url_index = 0;
77 	if ( thePrefs::AutoServerlist()) {
78 		AutoUpdate();
79 	}
80 
81 	m_initialized = true;
82 	return bRes;
83 }
84 
85 
LoadServerMet(const CPath & path)86 bool CServerList::LoadServerMet(const CPath& path)
87 {
88 	AddLogLineN(CFormat(_("Loading server.met file: %s")) % path);
89 
90 	bool merge = !m_servers.empty();
91 
92 	if (!path.FileExists()) {
93 		AddLogLineN(_("Server.met file not found!"));
94 		return false;
95 	}
96 
97 	// Try to unpack the file, might be an archive
98 	const wxChar* mets[] = { wxT("server.met"), NULL };
99 	// Try to unpack the file, might be an archive
100 	if (UnpackArchive(path, mets).second != EFT_Met) {
101 		AddLogLineC(CFormat(_("Failed to load server.met file '%s', unknown format encountered.")) % path);
102 		return false;
103 	}
104 
105 	CFile servermet(path, CFile::read);
106 	if ( !servermet.IsOpened() ){
107 		AddLogLineN(_("Failed to open server.met!") );
108 		return false;
109 	}
110 
111 
112 	try {
113 		Notify_ServerFreeze();
114 
115 		uint8_t version = servermet.ReadUInt8();
116 
117 		if (version != 0xE0 && version != MET_HEADER) {
118 			AddLogLineC(CFormat(_("Server.met file corrupt, found invalid versiontag: 0x%x, size %i")) % version % sizeof(version));
119 			Notify_ServerThaw();
120 			return false;
121 		}
122 
123 		uint32 fservercount = servermet.ReadUInt32();
124 
125 		ServerMet_Struct sbuffer;
126 		uint32 iAddCount = 0;
127 
128 		for ( uint32 j = 0; j < fservercount; ++j ) {
129 			sbuffer.ip		= servermet.ReadUInt32();
130 			sbuffer.port		= servermet.ReadUInt16();
131 			sbuffer.tagcount	= servermet.ReadUInt32();
132 
133 			CServer* newserver = new CServer(&sbuffer);
134 
135 			// Load tags
136 			for ( uint32 i = 0; i < sbuffer.tagcount; ++i ) {
137 				newserver->AddTagFromFile(&servermet);
138 			}
139 
140 			// Server priorities are not in sorted order
141 			// High = 1, Low = 2, Normal = 0, so we have to check
142 			// in a less logical fashion.
143 			int priority = newserver->GetPreferences();
144 			if (priority < SRV_PR_MIN || priority > SRV_PR_MAX) {
145 				newserver->SetPreference(SRV_PR_NORMAL);
146 			}
147 
148 			// set listname for server
149 			if ( newserver->GetListName().IsEmpty() ) {
150 				newserver->SetListName(wxT("Server ") +newserver->GetAddress());
151 			}
152 
153 
154 			if ( !theApp->AddServer(newserver) ) {
155 				CServer* update = GetServerByAddress(newserver->GetAddress(), newserver->GetPort());
156 				if(update) {
157 					update->SetListName( newserver->GetListName());
158 					if(!newserver->GetDescription().IsEmpty()) {
159 						update->SetDescription( newserver->GetDescription());
160 					}
161 					Notify_ServerRefresh(update);
162 				}
163 				delete newserver;
164 			} else {
165 				++iAddCount;
166 			}
167 
168 		}
169 
170 		Notify_ServerThaw();
171 
172 		// cppcheck-suppress duplicateBranch
173 		if (!merge) {
174 			AddLogLineC(CFormat(wxPLURAL("%i server in server.met found", "%i servers in server.met found", fservercount)) % fservercount);
175 		} else {
176 			AddLogLineC(CFormat(wxPLURAL("%d server added", "%d servers added", iAddCount)) % iAddCount);
177 		}
178 	} catch (const CInvalidPacket& err) {
179 		AddLogLineC(_("Error: the file 'server.met' is corrupted: ") + err.what());
180 		Notify_ServerThaw();
181 		return false;
182 	} catch (const CSafeIOException& err) {
183 		AddLogLineC(_("IO error while reading 'server.met': ") + err.what());
184 		Notify_ServerThaw();
185 		return false;
186 	}
187 
188 	return true;
189 }
190 
191 
AddServer(CServer * in_server,bool fromUser)192 bool CServerList::AddServer(CServer* in_server, bool fromUser)
193 {
194 	if ( !in_server->GetPort() ) {
195 		if ( fromUser ) {
196 			AddLogLineC(CFormat( _("Server not added: [%s:%d] does not specify a valid port.") )
197 					% in_server->GetAddress()
198 					% in_server->GetPort()
199 			);
200 		}
201 
202 		return false;
203 	} else if (
204 				!in_server->HasDynIP() &&
205 				(
206 					!IsGoodIP( in_server->GetIP(), thePrefs::FilterLanIPs() ) ||
207 					(	// don't test for filtered while ipfilter is still loading,
208 						// it will be filtered when filter loading is finished
209 						theApp->ipfilter->IsReady()
210 						&& theApp->ipfilter->IsFiltered(in_server->GetIP(), true))
211 				)
212 	          ) {
213 		if ( fromUser ) {
214 			AddLogLineC(CFormat( _("Server not added: The IP of [%s:%d] is filtered or invalid.") )
215 					% in_server->GetAddress()
216 					% in_server->GetPort()
217 			);
218 		}
219 
220 		return false;
221 	}
222 
223 	CServer* test_server = GetServerByAddress(in_server->GetAddress(), in_server->GetPort());
224 	// Avoid duplicate (dynIP) servers: If the server which is to be added, is a dynIP-server
225 	// but we don't know yet it's DN, we need to search for an already available server with
226 	// that IP.
227 	if (test_server == NULL && in_server->GetIP() != 0) {
228 		test_server = GetServerByIPTCP(in_server->GetIP(), in_server->GetPort());
229 	}
230 
231 	if (test_server) {
232 		if ( fromUser ) {
233 			AddLogLineC(CFormat( _("Server not added: Server with matching IP:Port [%s:%d] found in list.") )
234 					% in_server->GetAddress()
235 					% in_server->GetPort()
236 			);
237 		}
238 
239 		test_server->ResetFailedCount();
240 		Notify_ServerRefresh( test_server );
241 
242 		return false;
243 	}
244 
245 	theStats::AddServer();
246 
247 	m_servers.push_back(in_server);
248 	NotifyObservers( EventType( EventType::INSERTED, in_server ) );
249 
250 	if ( fromUser ) {
251 		AddLogLineC(CFormat( _("Server added: Server at [%s:%d] using the name '%s'.") )
252 				% in_server->GetAddress()
253 				% in_server->GetPort()
254 				% in_server->GetListName()
255 		);
256 	}
257 
258 
259 	return true;
260 }
261 
262 
ServerStats()263 void CServerList::ServerStats()
264 {
265 	uint32 tNow = ::GetTickCount();
266 
267 	if (theApp->IsConnectedED2K() && !m_servers.empty()) {
268 		CServer* ping_server = GetNextStatServer();
269 		CServer* test = ping_server;
270 		if (!ping_server) {
271 			return;
272 		}
273 
274 		while (ping_server->GetLastPingedTime() && (tNow - ping_server->GetLastPingedTime()) < UDPSERVSTATREASKTIME) {
275 			ping_server = GetNextStatServer();
276 			if (ping_server == test) {
277 				return;
278 			}
279 		}
280 
281 		if (ping_server->GetFailedCount() >= thePrefs::GetDeadserverRetries() && thePrefs::DeadServer() && !ping_server->IsStaticMember()) {
282 			RemoveServer(ping_server);
283 			return;
284 		}
285 
286 		srand((unsigned)time(NULL));
287 		ping_server->SetRealLastPingedTime(tNow); // this is not used to calcualte the next ping, but only to ensure a minimum delay for premature pings
288 		if (!ping_server->GetCryptPingReplyPending() && (!ping_server->GetLastPingedTime() || (tNow - ping_server->GetLastPingedTime()) >= UDPSERVSTATREASKTIME) && theApp->GetPublicIP() && thePrefs::IsServerCryptLayerUDPEnabled()) {
289 			// We try a obfsucation ping first and wait 20 seconds for an answer
290 			// if it doesn't get responsed, we don't count it as error but continue with a normal ping
291 			ping_server->SetCryptPingReplyPending(true);
292 			uint32 nPacketLen = 4 + (uint8)(rand() % 16); // max padding 16 bytes
293 			CScopedArray<uint8_t> pRawPacket(nPacketLen);
294 			uint32 dwChallenge = (rand() << 17) | (rand() << 2) | (rand() & 0x03);
295 			if (dwChallenge == 0) {
296 				dwChallenge++;
297 			}
298 
299 			memcpy(pRawPacket.get(), &dwChallenge, sizeof(uint32));
300 			for (uint32 i = 4; i < nPacketLen; i++) { // fillng up the remaining bytes with random data
301 				pRawPacket[i] = (uint8)rand();
302 			}
303 
304 			ping_server->SetChallenge(dwChallenge);
305 			ping_server->SetLastPinged(tNow);
306 			ping_server->SetLastPingedTime((tNow - (uint32)UDPSERVSTATREASKTIME) + 20); // give it 20 seconds to respond
307 
308 			AddDebugLogLineN(logServerUDP, CFormat(wxT(">> Sending OP__GlobServStatReq (obfuscated) to server %s:%u")) % ping_server->GetAddress() % ping_server->GetPort());
309 
310 			CPacket* packet = new CPacket(pRawPacket[1], nPacketLen - 2, pRawPacket[0]);
311 			packet->CopyToDataBuffer(0, pRawPacket.get() + 2, nPacketLen - 2);
312 
313 			theStats::AddUpOverheadServer(packet->GetPacketSize());
314 			theApp->serverconnect->SendUDPPacket(packet, ping_server, true, true /*raw packet*/, 12 /* Port offset is 12 for obfuscated encryption*/);
315 		} else if (ping_server->GetCryptPingReplyPending() || theApp->GetPublicIP() == 0 || !thePrefs::IsServerCryptLayerUDPEnabled()){
316 			// our obfsucation ping request was not answered, so probably the server doesn'T supports obfuscation
317 			// continue with a normal request
318 			if (ping_server->GetCryptPingReplyPending() && thePrefs::IsServerCryptLayerUDPEnabled()) {
319 				AddDebugLogLineN(logServerUDP, wxT("CryptPing failed for server ") + ping_server->GetListName());
320 			} else if (thePrefs::IsServerCryptLayerUDPEnabled()) {
321 				AddDebugLogLineN(logServerUDP, wxT("CryptPing skipped because our public IP is unknown for server ") + ping_server->GetListName());
322 			}
323 
324 			ping_server->SetCryptPingReplyPending(false);
325 
326 			CPacket* packet = new CPacket(OP_GLOBSERVSTATREQ, 4, OP_EDONKEYPROT);
327 			uint32 challenge = 0x55AA0000 + (uint16)rand();
328 			ping_server->SetChallenge(challenge);
329 			packet->CopyUInt32ToDataBuffer(challenge);
330 			ping_server->SetLastPinged(tNow);
331 			ping_server->SetLastPingedTime(tNow - (rand() % HR2S(1)));
332 			ping_server->AddFailedCount();
333 			Notify_ServerRefresh(ping_server);
334 			theStats::AddUpOverheadServer(packet->GetPacketSize());
335 			theApp->serverconnect->SendUDPPacket(packet, ping_server, true);
336 		} else {
337 			wxFAIL;
338 		}
339 	}
340 }
341 
342 
RemoveServer(CServer * in_server)343 void CServerList::RemoveServer(CServer* in_server)
344 {
345 	if (in_server == theApp->serverconnect->GetCurrentServer()) {
346 		theApp->ShowAlert(_("You are connected to the server you are trying to delete. please disconnect first."), _("Info"), wxOK);
347 	} else {
348 		CInternalList::iterator it = std::find(m_servers.begin(), m_servers.end(), in_server);
349 		if ( it != m_servers.end() ) {
350 			if (theApp->downloadqueue->GetUDPServer() == in_server) {
351 				theApp->downloadqueue->SetUDPServer( 0 );
352 			}
353 
354 			NotifyObservers( EventType( EventType::REMOVED, in_server ) );
355 
356 			if (m_serverpos == it) {
357 				++m_serverpos;
358 			}
359 			if (m_statserverpos == it) {
360 				++m_statserverpos;
361 			}
362 			m_servers.erase(it);
363 			theStats::DeleteServer();
364 
365 			Notify_ServerRemove(in_server);
366 			delete in_server;
367 		}
368 	}
369 }
370 
371 
RemoveAllServers()372 void CServerList::RemoveAllServers()
373 {
374 	NotifyObservers( EventType( EventType::CLEARED ) );
375 
376 	theStats::DeleteAllServers();
377 	// no connection, safely remove all servers
378 	while ( !m_servers.empty() ) {
379 		delete m_servers.back();
380 		m_servers.pop_back();
381 	}
382 	m_serverpos = m_servers.end();
383 	m_statserverpos = m_servers.end();
384 }
385 
386 
GetStatus(uint32 & failed,uint32 & user,uint32 & file,uint32 & tuser,uint32 & tfile,float & occ)387 void CServerList::GetStatus(uint32 &failed, uint32 &user, uint32 &file, uint32 &tuser, uint32 &tfile,float &occ)
388 {
389 	failed = 0;
390 	user = 0;
391 	file = 0;
392 	tuser=0;
393 	tfile = 0;
394 	occ=0;
395 	uint32 maxusers=0;
396 	uint32 tuserk = 0;
397 
398 	for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it) {
399 		const CServer* const curr = *it;
400 		if( curr->GetFailedCount() ) {
401 			++failed;
402 		} else {
403 			user += curr->GetUsers();
404 			file += curr->GetFiles();
405 		}
406 		tuser += curr->GetUsers();
407 		tfile += curr->GetFiles();
408 
409 		if (curr->GetMaxUsers()) {
410 			tuserk += curr->GetUsers(); // total users on servers with known maximum
411 			maxusers+=curr->GetMaxUsers();
412 		}
413 	}
414 	if (maxusers>0) {
415 		occ=(float)(tuserk*100)/maxusers;
416 	}
417 }
418 
419 
GetUserFileStatus(uint32 & user,uint32 & file)420 void CServerList::GetUserFileStatus(uint32 &user, uint32 &file)
421 {
422 	user = 0;
423 	file = 0;
424 	for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it) {
425 		const CServer* const curr = *it;
426 		if( !curr->GetFailedCount() ) {
427 			user += curr->GetUsers();
428 			file += curr->GetFiles();
429 		}
430 	}
431 }
432 
433 
~CServerList()434 CServerList::~CServerList()
435 {
436 	if (thePrefs::GetNetworkED2K() && IsInitialized()) {
437 		SaveServerMet();
438 	}
439 	while ( !m_servers.empty() ) {
440 		delete m_servers.back();
441 		m_servers.pop_back();
442 	}
443 }
444 
445 
LoadStaticServers()446 void CServerList::LoadStaticServers()
447 {
448 	if ( !CPath::FileExists(m_staticServersConfig) ) {
449 		return;
450 	}
451 
452 	wxFileInputStream stream(m_staticServersConfig);
453 	wxTextInputStream f(stream);
454 
455 	while ( !stream.Eof() ) {
456 		wxString line = f.ReadLine();
457 
458 		// Skip comments
459 		if (line.IsEmpty() || line.GetChar(0) == '#' || line.GetChar(0) == '/') {
460 			continue;
461 		}
462 
463 		wxStringTokenizer tokens( line, wxT(",") );
464 
465 		if ( tokens.CountTokens() != 3 ) {
466 			continue;
467 		}
468 
469 
470 		// format is host:port,priority,Name
471 		wxString addy = tokens.GetNextToken().Strip( wxString::both );
472 		wxString prio = tokens.GetNextToken().Strip( wxString::both );
473 		wxString name = tokens.GetNextToken().Strip( wxString::both );
474 
475 		wxString host = addy.BeforeFirst( wxT(':') );
476 		wxString port = addy.AfterFirst( wxT(':') );
477 
478 
479 		int priority = StrToLong( prio );
480 		if (priority < SRV_PR_MIN || priority > SRV_PR_MAX) {
481 			priority = SRV_PR_NORMAL;
482 		}
483 
484 
485 		// We need a valid name for the list
486 		if ( name.IsEmpty() ) {
487 			name = addy;
488 		}
489 
490 
491 		// create server object and add it to the list
492 		CServer* server = new CServer( StrToLong( port ), host );
493 
494 		server->SetListName( name );
495 		server->SetIsStaticMember( true );
496 		server->SetPreference( priority );
497 
498 
499 		// Try to add the server to the list
500 		if ( !theApp->AddServer( server ) ) {
501 			delete server;
502 			CServer* existing = GetServerByAddress( host, StrToULong( port ) );
503 			if ( existing) {
504 				existing->SetListName( name );
505 				existing->SetIsStaticMember( true );
506 				existing->SetPreference( priority );
507 
508 				Notify_ServerRefresh( existing );
509 			}
510 		}
511 	}
512 }
513 
SaveStaticServers()514 void CServerList::SaveStaticServers()
515 {
516 	CTextFile file;
517 	if (!file.Open(m_staticServersConfig, CTextFile::write)) {
518 		AddLogLineN(CFormat( _("Failed to open '%s'") ) % m_staticServersConfig );
519 		return;
520 	}
521 
522 	for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it) {
523 		const CServer* server = *it;
524 
525 		if (server->IsStaticMember()) {
526 			file.WriteLine(CFormat(wxT("%s:%u,%u,%s"))
527 				% server->GetAddress() % server->GetPort()
528 				% server->GetPreferences() % server->GetListName());
529 		}
530 	}
531 
532 	file.Close();
533 }
534 
535 struct ServerPriorityComparator {
536 	// Return true iff lhs should strictly appear earlier in the list than rhs.
537 	// In this case, we want higher priority servers to appear earlier.
operator ()ServerPriorityComparator538 	bool operator()(const CServer* lhs, const CServer* rhs) {
539 		wxASSERT
540 			(
541 			rhs->GetPreferences() == SRV_PR_LOW		||
542 			rhs->GetPreferences() == SRV_PR_NORMAL	||
543 			rhs->GetPreferences() == SRV_PR_HIGH
544 			);
545 		switch (lhs->GetPreferences()) {
546 			case SRV_PR_LOW:
547 				return false;
548 			case SRV_PR_NORMAL:
549 				return rhs->GetPreferences() == SRV_PR_LOW;
550 			case SRV_PR_HIGH:
551 				return rhs->GetPreferences() != SRV_PR_HIGH;
552 			default:
553 				wxFAIL;
554 				return false;
555 		}
556 	}
557 };
558 
Sort()559 void CServerList::Sort()
560 {
561 	m_servers.sort(ServerPriorityComparator());
562 	// Once the list has been sorted, it doesn't really make sense to continue
563 	// traversing the new order from the old position.  Plus, there's a bug in
564 	// version of libstdc++ before gcc4 such that iterators that were equal to
565 	// end() were left dangling.
566 	m_serverpos = m_servers.begin();
567 	m_statserverpos = m_servers.begin();
568 }
569 
570 
GetNextServer(bool bOnlyObfuscated)571 CServer* CServerList::GetNextServer(bool bOnlyObfuscated)
572 {
573 	while (bOnlyObfuscated && (m_serverpos != m_servers.end()) && !((*m_serverpos)->SupportsObfuscationTCP() || (*m_serverpos)->SupportsObfuscationUDP())) {
574 		wxASSERT(*m_serverpos != NULL);
575 		++m_serverpos;
576 	}
577 
578 	if (m_serverpos == m_servers.end()) {
579 		return 0;
580 	} else {
581 		if (*m_serverpos) {
582 			return *m_serverpos++;
583 		} else {
584 			return 0;
585 		}
586 	}
587 }
588 
589 
GetNextStatServer()590 CServer* CServerList::GetNextStatServer()
591 {
592 	if (m_statserverpos == m_servers.end()) {
593 		m_statserverpos = m_servers.begin();
594 	}
595 
596 	if (m_statserverpos == m_servers.end()) {
597 		return 0;
598 	} else {
599 		wxASSERT(*m_statserverpos != NULL);
600 		return *m_statserverpos++;
601 	}
602 }
603 
604 
GetServerByAddress(const wxString & address,uint16 port) const605 CServer* CServerList::GetServerByAddress(const wxString& address, uint16 port) const
606 {
607 	for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it) {
608 		CServer* const s = *it;
609 		if (port == s->GetPort() && s->GetAddress() == address) {
610 			return s;
611 		}
612 	}
613 	return NULL;
614 }
615 
616 
GetServerByIP(uint32 nIP) const617 CServer* CServerList::GetServerByIP(uint32 nIP) const
618 {
619 	for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it){
620         CServer* const s = *it;
621 		if (s->GetIP() == nIP)
622 			return s;
623 	}
624 	return NULL;
625 }
626 
627 
GetServerByIPTCP(uint32 nIP,uint16 nPort) const628 CServer* CServerList::GetServerByIPTCP(uint32 nIP, uint16 nPort) const
629 {
630 	for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it){
631         CServer* const s = *it;
632 		if (s->GetIP() == nIP && s->GetPort() == nPort)
633 			return s;
634 	}
635 	return NULL;
636 }
637 
638 
GetServerByIPUDP(uint32 nIP,uint16 nUDPPort,bool bObfuscationPorts) const639 CServer* CServerList::GetServerByIPUDP(uint32 nIP, uint16 nUDPPort, bool bObfuscationPorts) const
640 {
641 	for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it){
642         CServer* const s =*it;
643 		if (s->GetIP() == nIP
644 			&& (s->GetPort() == nUDPPort-4
645 				|| (bObfuscationPorts && s->GetObfuscationPortUDP() == nUDPPort)
646 				|| s->GetPort() == nUDPPort - 12))
647 			return s;
648 	}
649 	return NULL;
650 }
651 
652 
GetServerByECID(uint32 ecid) const653 CServer* CServerList::GetServerByECID(uint32 ecid) const
654 {
655 	for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it){
656         CServer* const s = *it;
657 		if (s->ECID() == ecid) {
658 			return s;
659 		}
660 	}
661 	return NULL;
662 }
663 
664 
SetStaticServer(CServer * server,bool isStatic)665 void CServerList::SetStaticServer(CServer* server, bool isStatic)
666 {
667 	server->SetIsStaticMember(isStatic);
668 	Notify_ServerRefresh(server);
669 	SaveStaticServers();
670 }
671 
672 
SetServerPrio(CServer * server,uint32 prio)673 void CServerList::SetServerPrio(CServer* server, uint32 prio)
674 {
675 	server->SetPreference(prio);
676 	Notify_ServerRefresh(server);
677 }
678 
679 
SaveServerMet()680 bool CServerList::SaveServerMet()
681 {
682 	CPath curservermet = CPath(thePrefs::GetConfigDir() + wxT("server.met"));
683 
684 	CFile servermet(curservermet, CFile::write_safe);
685 	if (!servermet.IsOpened()) {
686 		AddLogLineN(_("Failed to save server.met!"));
687 		return false;
688 	}
689 
690 	try {
691 		servermet.WriteUInt8(0xE0);
692 		servermet.WriteUInt32( m_servers.size() );
693 
694 		for ( CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it) {
695 			const CServer* const server = *it;
696 
697 			uint16 tagcount = 12;
698 			if (!server->GetListName().IsEmpty()) {
699 				++tagcount;
700 			}
701 			if (!server->GetDynIP().IsEmpty()) {
702 				++tagcount;
703 			}
704 			if (!server->GetDescription().IsEmpty()) {
705 				++tagcount;
706 			}
707 			if (server->GetConnPort() != server->GetPort()) {
708 				++tagcount;
709 			}
710 
711 			// For unicoded name, description, and dynip
712 			if ( !server->GetListName().IsEmpty() ) {
713 				++tagcount;
714 			}
715 			if ( !server->GetDynIP().IsEmpty() ) {
716 				++tagcount;
717 			}
718 			if ( !server->GetDescription().IsEmpty() ) {
719 				++tagcount;
720 			}
721 			if (!server->GetVersion().IsEmpty()) {
722 				++tagcount;
723 			}
724 
725 			if (server->GetServerKeyUDP(true)) {
726 				++tagcount;
727 			}
728 
729 			if (server->GetServerKeyUDPIP()) {
730 				++tagcount;
731 			}
732 
733 			if (server->GetObfuscationPortTCP()) {
734 				++tagcount;
735 			}
736 
737 			if (server->GetObfuscationPortUDP()) {
738 				++tagcount;
739 			}
740 
741 			servermet.WriteUInt32(server->GetIP());
742 			servermet.WriteUInt16(server->GetPort());
743 			servermet.WriteUInt32(tagcount);
744 
745 			if ( !server->GetListName().IsEmpty() ) {
746 				// This is BOM to keep eMule compatibility
747 				CTagString( ST_SERVERNAME, server->GetListName()).WriteTagToFile( &servermet,  utf8strOptBOM);
748 				CTagString( ST_SERVERNAME, server->GetListName()).WriteTagToFile( &servermet );
749 			}
750 
751 			if ( !server->GetDynIP().IsEmpty() ) {
752 				// This is BOM to keep eMule compatibility
753 				CTagString( ST_DYNIP, server->GetDynIP()).WriteTagToFile( &servermet, utf8strOptBOM );
754 				CTagString( ST_DYNIP, server->GetDynIP()).WriteTagToFile( &servermet );
755 			}
756 
757 			if ( !server->GetDescription().IsEmpty() ) {
758 				// This is BOM to keep eMule compatibility
759 				CTagString( ST_DESCRIPTION, server->GetDescription()).WriteTagToFile( &servermet, utf8strOptBOM );
760 				CTagString( ST_DESCRIPTION, server->GetDescription()).WriteTagToFile( &servermet );
761 			}
762 
763 			if ( server->GetConnPort() != server->GetPort() ) {
764 				CTagString( ST_AUXPORTSLIST,	server->GetAuxPortsList()	).WriteTagToFile( &servermet );
765 			}
766 
767 			CTagInt32( ST_FAIL,       server->GetFailedCount()   ).WriteTagToFile( &servermet );
768 			CTagInt32( ST_PREFERENCE, server->GetPreferences()   ).WriteTagToFile( &servermet );
769 			CTagInt32( wxT("users"),  server->GetUsers()         ).WriteTagToFile( &servermet );
770 			CTagInt32( wxT("files"),  server->GetFiles()         ).WriteTagToFile( &servermet );
771 			CTagInt32( ST_PING,       server->GetPing()          ).WriteTagToFile( &servermet );
772 			CTagInt32( ST_LASTPING,   server->GetLastPingedTime()).WriteTagToFile( &servermet );
773 			CTagInt32( ST_MAXUSERS,   server->GetMaxUsers()      ).WriteTagToFile( &servermet );
774 			CTagInt32( ST_SOFTFILES,  server->GetSoftFiles()     ).WriteTagToFile( &servermet );
775 			CTagInt32( ST_HARDFILES,  server->GetHardFiles()     ).WriteTagToFile( &servermet );
776 			if (!server->GetVersion().IsEmpty()){
777 				CTagString( ST_VERSION,	server->GetVersion() ).WriteTagToFile( &servermet, utf8strOptBOM );
778 				CTagString( ST_VERSION,	server->GetVersion() ).WriteTagToFile( &servermet );
779 			}
780 			CTagInt32( ST_UDPFLAGS,   server->GetUDPFlags()      ).WriteTagToFile( &servermet );
781 			CTagInt32( ST_LOWIDUSERS, server->GetLowIDUsers()    ).WriteTagToFile( &servermet );
782 
783 			if (server->GetServerKeyUDP(true)) {
784 				CTagInt32(ST_UDPKEY, server->GetServerKeyUDP(true)).WriteTagToFile( &servermet );
785 			}
786 
787 			if (server->GetServerKeyUDPIP()) {
788 				CTagInt32(ST_UDPKEYIP, server->GetServerKeyUDPIP()).WriteTagToFile( &servermet );
789 			}
790 
791 			if (server->GetObfuscationPortTCP()) {
792 				CTagInt16(ST_TCPPORTOBFUSCATION, server->GetObfuscationPortTCP()).WriteTagToFile( &servermet );
793 			}
794 
795 			if (server->GetObfuscationPortUDP()) {
796 				CTagInt16(ST_UDPPORTOBFUSCATION, server->GetObfuscationPortUDP()).WriteTagToFile( &servermet );
797 			}
798 
799 		}
800 		// Now server.met.new is ready to be closed and renamed to server.met.
801 		// But first rename existing server.met to server.met.bak (replacing old .bak file).
802 		const CPath oldservermet = CPath(thePrefs::GetConfigDir() + wxT("server.met.bak"));
803 		if (curservermet.FileExists()) {
804 			CPath::RenameFile(curservermet, oldservermet, true);
805 		}
806 
807 		servermet.Close();
808 
809 	} catch (const CIOFailureException& e) {
810 		AddLogLineC(wxT("IO failure while writing 'server.met': ") + e.what());
811 		return false;
812 	}
813 
814 	return true;
815 }
816 
817 
RemoveDeadServers()818 void CServerList::RemoveDeadServers()
819 {
820 	if ( thePrefs::DeadServer() ) {
821 		for ( CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ) {
822 			CServer* server = *it++;
823 			if ( server->GetFailedCount() > thePrefs::GetDeadserverRetries() && !server->IsStaticMember()) {
824 				RemoveServer(server);
825 			}
826 		}
827 	}
828 }
829 
UpdateServerMetFromURL(const wxString & strURL)830 void CServerList::UpdateServerMetFromURL(const wxString& strURL)
831 {
832 	if (strURL.Find(wxT("://")) == -1) {
833 		AddLogLineC(_("Invalid URL"));
834 		return;
835 	}
836 	m_URLUpdate = strURL;
837 	wxString strTempFilename(thePrefs::GetConfigDir() + wxT("server.met.download"));
838 	CHTTPDownloadThread *downloader = new CHTTPDownloadThread(strURL, strTempFilename, thePrefs::GetConfigDir() + wxT("server.met"), HTTP_ServerMet, false, false);
839 	downloader->Create();
840 	downloader->Run();
841 }
842 
843 
DownloadFinished(uint32 result)844 bool CServerList::DownloadFinished(uint32 result)
845 {
846 	bool ret = false;
847 	if(result == HTTP_Success) {
848 		const CPath tempFilename = CPath(thePrefs::GetConfigDir() + wxT("server.met.download"));
849 
850 		// curl succeeded. proceed with server.met loading
851 		LoadServerMet(tempFilename);
852 		SaveServerMet();
853 
854 		// So, file is loaded and merged, and also saved
855 		CPath::RemoveFile(tempFilename);
856 		AddLogLineN(CFormat(_("Finished downloading the server list from %s")) % m_URLUpdate);
857 		ret = true;
858 	// cppcheck-suppress duplicateBranch
859 	} else if (result == HTTP_Skipped) {
860 		AddLogLineN(CFormat(_("Skipped download of %s, because requested file is not newer.")) % wxT("server.met"));
861 	} else {
862 		AddLogLineC(CFormat(_("Failed to download %s from %s")) % wxT("server.met") % m_URLUpdate);
863 	}
864 	return ret;
865 }
866 
867 
AutoUpdate()868 void CServerList::AutoUpdate()
869 {
870 
871 	uint8 url_count = theApp->glob_prefs->adresses_list.GetCount();
872 
873 	if (!url_count) {
874 		AddLogLineC(_("No server list address entry in 'addresses.dat' found. Please paste a valid server list address into this file in order to auto-update your server list"));
875 		return;
876 	}
877 	// Do current URL. Callback function will take care of the others.
878 	while ( current_url_index < url_count ) {
879 		wxString URI = theApp->glob_prefs->adresses_list[current_url_index];
880 		// We use wxURL to validate the URI
881 		if ( wxURL( URI ).GetError() == wxURL_NOERR ) {
882 			// Ok, got a valid URI
883 			m_URLUpdate = URI;
884 			wxString strTempFilename =
885 				thePrefs::GetConfigDir() + wxT("server_auto.met");
886 			AddLogLineC(CFormat(
887 				_("Start downloading server list from %s")) % URI);
888 			CHTTPDownloadThread *downloader = new CHTTPDownloadThread(
889 				URI, strTempFilename, thePrefs::GetConfigDir() + wxT("server.met"), HTTP_ServerMetAuto, false, false);
890 			downloader->Create();
891 			downloader->Run();
892 
893 			return;
894 		} else {
895 			AddLogLineC(CFormat(
896 				_("WARNING: invalid URL specified for auto-updating of servers: %s") ) % URI);
897 		}
898 		current_url_index++;
899 	}
900 	AddLogLineC(_("No valid server.met auto-download url on addresses.dat"));
901 }
902 
903 
AutoDownloadFinished(uint32 result)904 void CServerList::AutoDownloadFinished(uint32 result)
905 {
906 	if (result == HTTP_Success) {
907 		CPath tempFilename = CPath(thePrefs::GetConfigDir() + wxT("server_auto.met"));
908 
909 		// curl succeeded. proceed with server.met loading
910 		LoadServerMet(tempFilename);
911 		SaveServerMet();
912 
913 		// So, file is loaded and merged, and also saved
914 		CPath::RemoveFile(tempFilename);
915 	} else {
916 		AddLogLineC(CFormat(_("Failed to download the server list from %s") ) % m_URLUpdate);
917 	}
918 
919 	++current_url_index;
920 
921 	if (current_url_index < theApp->glob_prefs->adresses_list.GetCount()) {
922 		// Next!
923 		AutoUpdate();
924 	}
925 }
926 
927 
ObserverAdded(ObserverType * o)928 void CServerList::ObserverAdded( ObserverType* o )
929 {
930 	CObservableQueue<CServer*>::ObserverAdded( o );
931 
932 	EventType::ValueList ilist;
933 	ilist.reserve( m_servers.size() );
934 	ilist.assign( m_servers.begin(), m_servers.end() );
935 
936 	NotifyObservers( EventType( EventType::INITIAL, &ilist ), o );
937 }
938 
939 
GetAvgFile() const940 uint32 CServerList::GetAvgFile() const
941 {
942 	//Since there is no real way to know how many files are in the kad network,
943 	//I figure to try to use the ED2K network stats to find how many files the
944 	//average user shares..
945 	uint32 totaluser = 0;
946 	uint32 totalfile = 0;
947 	for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it){
948 		const CServer* const curr = *it;
949 		//If this server has reported Users/Files and doesn't limit it's files too much
950 		//use this in the calculation..
951 		if( curr->GetUsers() && curr->GetFiles() && curr->GetSoftFiles() > 1000 ) {
952 			totaluser += curr->GetUsers();
953 			totalfile += curr->GetFiles();
954 		}
955 	}
956 	//If the user count is a little low, don't send back a average..
957 	//I added 50 to the count as many servers do not allow a large amount of files to be shared..
958 	//Therefore the estimate here will be lower then the actual.
959 	//I would love to add a way for the client to send some statistics back so we could see the real
960 	//values here..
961 	if ( totaluser > 500000 ) {
962 		return (totalfile/totaluser)+50;
963 	} else {
964 		return 0;
965 	}
966 }
967 
968 
CopySnapshot() const969 std::vector<const CServer*> CServerList::CopySnapshot() const
970 {
971 	std::vector<const CServer*> result;
972 	result.reserve(m_servers.size());
973 	result.assign(m_servers.begin(), m_servers.end());
974 	return result;
975 }
976 
977 
FilterServers()978 void CServerList::FilterServers()
979 {
980 	CInternalList::iterator it = m_servers.begin();
981 	while (it != m_servers.end()) {
982 		CServer* server = *it++;
983 
984 		if (server->HasDynIP()) {
985 			continue;
986 		}
987 
988 		if (theApp->ipfilter->IsFiltered(server->GetIP(), true)) {
989 			if (server == theApp->serverconnect->GetCurrentServer()) {
990 				AddLogLineC(_("Local server is filtered by the IPFilters, reconnecting to a different server!"));
991 				theApp->serverconnect->Disconnect();
992 				RemoveServer(server);
993 				theApp->serverconnect->ConnectToAnyServer();
994 			} else {
995 				RemoveServer(server);
996 			}
997 		}
998 	}
999 }
1000 
CheckForExpiredUDPKeys()1001 void CServerList::CheckForExpiredUDPKeys() {
1002 
1003 	if (!thePrefs::IsServerCryptLayerUDPEnabled()) {
1004 		return;
1005 	}
1006 
1007 	uint32 cKeysTotal = 0;
1008 	uint32 cKeysExpired = 0;
1009 	uint32 cPingDelayed = 0;
1010 	const uint32 dwIP = theApp->GetPublicIP();
1011 	const uint32 tNow = ::GetTickCount();
1012 	wxASSERT( dwIP != 0 );
1013 
1014 	for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it) {
1015         CServer* pServer = *it;
1016 		if (pServer->SupportsObfuscationUDP() && pServer->GetServerKeyUDP(true) != 0 && pServer->GetServerKeyUDPIP() != dwIP){
1017 			cKeysTotal++;
1018 			cKeysExpired++;
1019 			if (tNow - pServer->GetRealLastPingedTime() < UDPSERVSTATMINREASKTIME){
1020 				cPingDelayed++;
1021 				// next ping: Now + (MinimumDelay - already elapsed time)
1022 				pServer->SetLastPingedTime((tNow - (uint32)UDPSERVSTATREASKTIME) + (UDPSERVSTATMINREASKTIME - (tNow - pServer->GetRealLastPingedTime())));
1023 			} else {
1024 				pServer->SetLastPingedTime(0);
1025 			}
1026 		} else if (pServer->SupportsObfuscationUDP() && pServer->GetServerKeyUDP(false) != 0) {
1027 			cKeysTotal++;
1028 		}
1029 	}
1030 }
1031 // File_checked_for_headers
1032