Fix NOMINMAX usage
[amule.git] / src / ServerConnect.cpp
blobdb8113a10368fd08c80f45c422654b5d9c2e6967
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.
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.
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.
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
26 #include "ServerConnect.h" // Interface declarations.
28 #include <protocol/Protocols.h>
29 #include <protocol/ed2k/ClientSoftware.h> // Sometimes we reply with TCP packets.
30 #include <tags/ClientTags.h>
31 #include <common/ClientVersion.h>
32 #include <common/EventIDs.h>
34 #include "SearchList.h" // Needed for CSearchList
35 #include "ServerUDPSocket.h" // Needed for CServerUDPSocket
36 #include "SharedFileList.h" // Needed for CSharedFileList
37 #include "Packet.h" // Needed for CTag
38 #include "MemFile.h" // Needed for CMemFile
39 #include "ServerSocket.h" // Needed for CServerSocket
40 #include "ListenSocket.h" // Needed for CListenSocket
41 #include "Server.h" // Needed for CServer
42 #include "amule.h" // Needed for theApp
43 #include "ServerList.h" // Needed for CServerList
44 #include "Preferences.h" // Needed for CPreferences
45 #include "Statistics.h" // Needed for theStats
46 #include "Logger.h"
47 #include "GuiEvents.h" // Needed for Notify_*
48 #include "IPFilter.h" // Needed for theApp->ipfilter->IsReady()
49 #include <common/Format.h>
52 //#define DEBUG_CLIENT_PROTOCOL
55 void CServerConnect::TryAnotherConnectionrequest()
57 if (m_recurseTryAnotherConnectionrequest) {
58 return;
60 if ( connectionattemps.size() < (unsigned)(( thePrefs::IsSafeServerConnectEnabled()) ? 1 : 2) ) {
62 CServer* next_server = used_list->GetNextServer(m_bTryObfuscated);
64 if ( thePrefs::AutoConnectStaticOnly() ) {
65 while (next_server && !next_server->IsStaticMember()) {
66 next_server = used_list->GetNextServer(m_bTryObfuscated);
70 if (!next_server) {
71 if ( connectionattemps.empty() ) {
72 m_recurseTryAnotherConnectionrequest = true;
73 if (m_bTryObfuscated && !thePrefs::IsClientCryptLayerRequired()){
74 AddLogLineC(_("Failed to connect to all obfuscated servers listed. Making another pass without obfuscation."));
75 // try all servers on the non-obfuscated port next
76 m_bTryObfuscated = false;
77 ConnectToAnyServer( false, true);
78 } else {
79 AddLogLineC(_("Failed to connect to all servers listed. Making another pass."));
80 ConnectToAnyServer( false );
82 m_recurseTryAnotherConnectionrequest = false;
84 return;
87 ConnectToServer(next_server, true, !m_bTryObfuscated);
91 void CServerConnect::ConnectToAnyServer(bool prioSort, bool bNoCrypt)
93 if (!thePrefs::GetNetworkED2K()){
94 AddLogLineC(_("eD2k network disabled on preferences, not connecting."));
95 return;
97 if (!theApp->ipfilter->IsReady()) {
98 // do it later when ipfilter is up
99 theApp->ipfilter->ConnectToAnyServerWhenReady();
100 return;
103 StopConnectionTry();
104 Disconnect();
105 connecting = true;
106 singleconnecting = false;
107 m_bTryObfuscated = thePrefs::IsServerCryptLayerTCPRequested() && !bNoCrypt;
109 // Barry - Only auto-connect to static server option
110 if (thePrefs::AutoConnectStaticOnly()) {
111 bool anystatic = false;
112 CServer *next_server;
113 used_list->ResetServerPos();
114 while ((next_server = used_list->GetNextServer(false)) != NULL) {
115 if (next_server->IsStaticMember()) {
116 anystatic = true;
117 break;
120 if (!anystatic) {
121 connecting = false;
122 AddLogLineC(_("No valid servers to which to connect found in server list"));
123 return;
127 if ( thePrefs::Score() && prioSort ) {
128 used_list->Sort();
131 used_list->ResetServerPos();
133 if (used_list->GetServerCount()==0 ) {
134 connecting = false;
135 AddLogLineC(_("No valid servers to which to connect found in server list"));
136 return;
139 theApp->listensocket->Process();
141 TryAnotherConnectionrequest();
145 void CServerConnect::ConnectToServer(CServer* server, bool multiconnect, bool bNoCrypt)
147 if (!thePrefs::GetNetworkED2K()){
148 AddLogLineC(_("eD2k network disabled on preferences, not connecting."));
149 return;
152 if (!multiconnect) {
153 StopConnectionTry();
154 Disconnect();
156 connecting = true;
157 singleconnecting = !multiconnect;
159 CServerSocket* newsocket = new CServerSocket(this, thePrefs::GetProxyData());
160 m_lstOpenSockets.push_back(newsocket);
161 newsocket->ConnectToServer(server, bNoCrypt);
163 connectionattemps[GetTickCount()] = newsocket;
167 void CServerConnect::StopConnectionTry()
169 connectionattemps.clear();
170 connecting = false;
171 singleconnecting = false;
173 if (m_idRetryTimer.IsRunning())
175 m_idRetryTimer.Stop();
178 // close all currenty opened sockets except the one which is connected to our current server
179 for(SocketsList::iterator it = m_lstOpenSockets.begin(); it != m_lstOpenSockets.end(); ) {
180 CServerSocket *pSck = *it++;
181 if (pSck == connectedsocket) // don't destroy socket which is connected to server
182 continue;
183 if (pSck->m_bIsDeleting == false) // don't destroy socket if it is going to destroy itself later on
184 DestroySocket(pSck);
188 void CServerConnect::ConnectionEstablished(CServerSocket* sender)
190 if (connecting == false)
192 // we are already connected to another server
193 DestroySocket(sender);
194 return;
197 if (sender->GetConnectionState() == CS_WAITFORLOGIN) {
198 AddLogLineN(CFormat( _("Connected to %s (%s:%i)") )
199 % sender->cur_server->GetListName()
200 % sender->cur_server->GetFullIP()
201 % sender->cur_server->GetPort() );
203 //send loginpacket
204 CServer* update = theApp->serverlist->GetServerByAddress( sender->cur_server->GetAddress(), sender->cur_server->GetPort() );
205 if (update){
206 update->ResetFailedCount();
207 Notify_ServerRefresh( update );
210 CMemFile data(256);
211 data.WriteHash(thePrefs::GetUserHash());
212 // Why pass an ID, if we are loggin in?
213 data.WriteUInt32(GetClientID());
214 data.WriteUInt16(thePrefs::GetPort());
215 data.WriteUInt32(4); // tagcount
217 // Kry - Server doesn't support VBT tags afaik.
218 // Not to mention we don't know its flags yet
220 CTagString tagname(CT_NAME,thePrefs::GetUserNick());
221 tagname.WriteTagToFile(&data);
223 CTagInt32 tagversion(CT_VERSION,EDONKEYVERSION);
224 tagversion.WriteTagToFile(&data);
226 uint32 dwCryptFlags = 0;
228 if (thePrefs::IsClientCryptLayerSupported()) {
229 dwCryptFlags |= SRVCAP_SUPPORTCRYPT;
232 if (thePrefs::IsClientCryptLayerRequested()) {
233 dwCryptFlags |= SRVCAP_REQUESTCRYPT;
236 if (thePrefs::IsClientCryptLayerRequired()) {
237 dwCryptFlags |= SRVCAP_REQUIRECRYPT;
240 // FLAGS for server connection
241 CTagInt32 tagflags(CT_SERVER_FLAGS, CAPABLE_ZLIB
242 | CAPABLE_AUXPORT
243 | CAPABLE_NEWTAGS
244 | CAPABLE_UNICODE
245 | CAPABLE_LARGEFILES
246 | dwCryptFlags
249 tagflags.WriteTagToFile(&data);
251 // eMule Version (14-Mar-2004: requested by lugdunummaster (need for LowID clients which have no chance
252 // to send an Hello packet to the server during the callback test))
253 CTagInt32 tagMuleVersion(CT_EMULE_VERSION,
254 (SO_AMULE << 24) |
255 make_full_ed2k_version(VERSION_MJR, VERSION_MIN, VERSION_UPDATE)
257 tagMuleVersion.WriteTagToFile(&data);
259 CPacket* packet = new CPacket(data, OP_EDONKEYPROT, OP_LOGINREQUEST);
260 #ifdef DEBUG_CLIENT_PROTOCOL
261 AddLogLineC(wxT("Client: OP_LOGINREQUEST"));
262 AddLogLineC(wxString(wxT(" Hash : ")) << thePrefs::GetUserHash().Encode());
263 AddLogLineC(wxString(wxT(" ClientID : ")) << GetClientID());
264 AddLogLineC(wxString(wxT(" Port : ")) << thePrefs::GetPort());
265 AddLogLineC(wxString(wxT(" User Nick: ")) << thePrefs::GetUserNick());
266 AddLogLineC(wxString(wxT(" Edonkey : ")) << EDONKEYVERSION);
267 #endif
268 theStats::AddUpOverheadServer(packet->GetPacketSize());
269 SendPacket(packet, true, sender);
270 } else if (sender->GetConnectionState() == CS_CONNECTED){
271 theStats::AddReconnect();
272 theStats::GetServerConnectTimer()->ResetTimer();
273 connected = true;
274 AddLogLineC(CFormat( _("Connection established on: %s") ) % sender->cur_server->GetListName());
275 connectedsocket = sender;
277 StopConnectionTry();
279 CServer* update = theApp->serverlist->GetServerByAddress(connectedsocket->cur_server->GetAddress(),sender->cur_server->GetPort());
280 if ( update ) {
281 Notify_ServerHighlight(update, true);
284 theApp->sharedfiles->ClearED2KPublishInfo();
286 Notify_ServerRemoveDead();
288 // tecxx 1609 2002 - serverlist update
289 if (thePrefs::AddServersFromServer()) {
290 CPacket* packet = new CPacket(OP_GETSERVERLIST, 0, OP_EDONKEYPROT);
291 theStats::AddUpOverheadServer(packet->GetPacketSize());
292 SendPacket(packet, true);
293 #ifdef DEBUG_CLIENT_PROTOCOL
294 AddLogLineC(wxT("Client: OP_GETSERVERLIST"));
295 #endif
299 theApp->ShowConnectionState();
303 bool CServerConnect::SendPacket(CPacket* packet,bool delpacket, CServerSocket* to)
305 if (!to) {
306 if (connected) {
307 connectedsocket->SendPacket(packet, delpacket, true);
308 return true;
309 } else {
310 if ( delpacket ) {
311 delete packet;
314 return false;
316 } else {
317 to->SendPacket(packet, delpacket, true);
318 return true;
323 bool CServerConnect::SendUDPPacket(CPacket* packet, CServer* host, bool delpacket, bool rawpacket, uint16 port_offset)
325 if (connected) {
326 serverudpsocket->SendPacket(packet, host, delpacket, rawpacket, port_offset);
327 } else if (delpacket) {
328 delete packet;
331 return true;
335 void CServerConnect::ConnectionFailed(CServerSocket* sender)
337 if (connecting == false && sender != connectedsocket)
339 // just return, cleanup is done by the socket itself
340 return;
342 //messages
343 CServer* pServer = theApp->serverlist->GetServerByAddress(sender->cur_server->GetAddress(), sender->cur_server->GetPort());
344 switch (sender->GetConnectionState()){
345 case CS_FATALERROR:
346 AddLogLineC(_("Fatal Error while trying to connect. Internet connection might be down"));
347 break;
348 case CS_DISCONNECTED:
349 theApp->sharedfiles->ClearED2KPublishInfo();
350 AddLogLineN(CFormat( _("Lost connection to %s (%s:%i)") )
351 % sender->cur_server->GetListName()
352 % sender->cur_server->GetFullIP()
353 % sender->cur_server->GetPort() );
355 if (pServer){
356 Notify_ServerHighlight(pServer, false);
358 break;
359 case CS_SERVERDEAD:
360 AddLogLineN(CFormat( _("%s (%s:%i) appears to be dead.") )
361 % sender->cur_server->GetListName()
362 % sender->cur_server->GetFullIP()
363 % sender->cur_server->GetPort() );
365 if (pServer) {
366 pServer->AddFailedCount();
367 Notify_ServerRefresh( pServer );
369 break;
370 case CS_ERROR:
371 break;
372 case CS_SERVERFULL:
373 AddLogLineN(CFormat( _("%s (%s:%i) appears to be full.") )
374 % sender->cur_server->GetListName()
375 % sender->cur_server->GetFullIP()
376 % sender->cur_server->GetPort() );
378 break;
379 case CS_NOTCONNECTED:;
380 break;
383 // IMPORTANT: mark this socket not to be deleted in StopConnectionTry(),
384 // because it will delete itself after this function!
385 sender->m_bIsDeleting = true;
387 switch (sender->GetConnectionState()) {
388 case CS_FATALERROR:{
389 bool autoretry= !singleconnecting;
390 StopConnectionTry();
391 if ((thePrefs::Reconnect()) && (autoretry) && (!m_idRetryTimer.IsRunning())) {
392 AddLogLineN(CFormat(wxPLURAL("Automatic connection to server will retry in %d second", "Automatic connection to server will retry in %d seconds", CS_RETRYCONNECTTIME)) % CS_RETRYCONNECTTIME);
393 m_idRetryTimer.Start(1000*CS_RETRYCONNECTTIME);
395 break;
397 case CS_DISCONNECTED:{
398 theApp->sharedfiles->ClearED2KPublishInfo();
399 connected = false;
400 Notify_ServerHighlight(sender->cur_server,false);
401 if (connectedsocket) {
402 connectedsocket->Close();
404 connectedsocket = NULL;
405 theApp->searchlist->StopSearch(true);
406 Notify_SearchCancel();
407 theStats::GetServerConnectTimer()->StopTimer();
408 if (thePrefs::Reconnect() && !connecting){
409 ConnectToAnyServer();
412 AddLogLineC(_("Connection lost") );
413 break;
415 case CS_ERROR:
416 case CS_NOTCONNECTED:{
417 if (!connecting)
418 break;
419 AddLogLineN(CFormat( _("Connecting to %s (%s:%i) failed.") )
420 % sender->info
421 % sender->cur_server->GetFullIP()
422 % sender->cur_server->GetPort() );
424 case CS_SERVERDEAD:
425 case CS_SERVERFULL:{
426 if (!connecting) {
427 break;
429 if (singleconnecting) {
430 if (pServer && sender->IsServerCryptEnabledConnection() && !thePrefs::IsClientCryptLayerRequired()){
431 // try reconnecting without obfuscation
432 ConnectToServer(pServer, false, true);
433 break;
436 StopConnectionTry();
437 break;
440 ServerSocketMap::iterator it = connectionattemps.begin();
441 while ( it != connectionattemps.end() ){
442 if ( it->second == sender ) {
443 connectionattemps.erase( it );
444 break;
446 ++it;
448 TryAnotherConnectionrequest();
451 theApp->ShowConnectionState();
454 void CServerConnect::CheckForTimeout()
456 uint32 dwCurTick = GetTickCount();
458 ServerSocketMap::iterator it = connectionattemps.begin();
459 while ( it != connectionattemps.end() ){
460 if ( !it->second ) {
461 AddLogLineN(_("ERROR: Socket invalid at timeout check"));
462 connectionattemps.erase( it );
463 return;
466 if ( dwCurTick - it->first > CONSERVTIMEOUT) {
467 uint32 key = it->first;
468 CServerSocket* value = it->second;
469 ++it;
470 if (!value->IsSolving()) {
471 AddLogLineN(CFormat( _("Connection attempt to %s (%s:%i) timed out.") )
472 % value->info
473 % value->cur_server->GetFullIP()
474 % value->cur_server->GetPort() );
476 connectionattemps.erase( key );
478 TryAnotherConnectionrequest();
479 DestroySocket( value );
481 } else {
482 ++it;
488 bool CServerConnect::Disconnect()
490 if (connected && connectedsocket) {
491 theApp->sharedfiles->ClearED2KPublishInfo();
493 connected = false;
495 CServer* update = theApp->serverlist->GetServerByAddress(
496 connectedsocket->cur_server->GetAddress(),
497 connectedsocket->cur_server->GetPort());
498 Notify_ServerHighlight(update, false);
499 theApp->SetPublicIP(0);
500 DestroySocket(connectedsocket);
501 connectedsocket = NULL;
502 theApp->ShowConnectionState();
503 theStats::GetServerConnectTimer()->StopTimer();
504 return true;
505 } else {
506 return false;
511 CServerConnect::CServerConnect(CServerList* in_serverlist, amuleIPV4Address &address)
512 : m_idRetryTimer(theApp,ID_SERVER_RETRY_TIMER_EVENT)
514 connectedsocket = NULL;
515 used_list = in_serverlist;
516 max_simcons = (thePrefs::IsSafeServerConnectEnabled()) ? 1 : 2;
517 connecting = false;
518 connected = false;
519 clientid = 0;
520 singleconnecting = false;
521 m_recurseTryAnotherConnectionrequest = false;
523 // initalize socket for udp packets
524 if (thePrefs::GetNetworkED2K()) {
525 serverudpsocket = new CServerUDPSocket(address, thePrefs::GetProxyData());
526 } else {
527 serverudpsocket = NULL;
532 CServerConnect::~CServerConnect()
534 m_idRetryTimer.Stop();
535 // stop all connections
536 StopConnectionTry();
537 // close connected socket, if any
538 DestroySocket(connectedsocket);
539 connectedsocket = NULL;
540 // close udp socket
541 delete serverudpsocket;
545 CServer* CServerConnect::GetCurrentServer()
547 if (IsConnected() && connectedsocket) {
548 return connectedsocket->cur_server;
550 return NULL;
554 void CServerConnect::SetClientID(uint32 newid)
556 clientid = newid;
558 if (!::IsLowID(newid)) {
559 theApp->SetPublicIP(newid);
564 void CServerConnect::DestroySocket(CServerSocket* pSck)
566 if (pSck == NULL) {
567 return;
569 m_lstOpenSockets.remove(pSck);
570 pSck->Destroy();
574 bool CServerConnect::IsLocalServer(uint32 dwIP, uint16 nPort)
576 if (IsConnected()){
577 if (connectedsocket->cur_server->GetIP() == dwIP && connectedsocket->cur_server->GetPort() == nPort)
578 return true;
580 return false;
584 void CServerConnect::KeepConnectionAlive()
586 uint32 dwServerKeepAliveTimeout = thePrefs::GetServerKeepAliveTimeout();
587 if (dwServerKeepAliveTimeout && connected && connectedsocket &&
588 connectedsocket->connectionstate == CS_CONNECTED &&
589 GetTickCount() - connectedsocket->GetLastTransmission() >= dwServerKeepAliveTimeout) {
590 // "Ping" the server if the TCP connection was not used for the specified interval with
591 // an empty publish files packet -> recommended by lugdunummaster himself!
593 CMemFile files(4);
594 files.WriteUInt32(0); //nFiles
596 CPacket* packet = new CPacket(files, OP_EDONKEYPROT, OP_OFFERFILES);
597 #ifdef DEBUG_CLIENT_PROTOCOL
598 AddLogLineC(wxT("Client: OP_OFFERFILES"));
599 #endif
600 // compress packet
601 // - this kind of data is highly compressable (N * (1 MD4 and at least 3 string meta data tags and 1 integer meta data tag))
602 // - the min. amount of data needed for one published file is ~100 bytes
603 // - this function is called once when connecting to a server and when a file becomes shareable - so, it's called rarely.
604 // - if the compressed size is still >= the original size, we send the uncompressed packet
605 // therefor we always try to compress the packet
606 theStats::AddUpOverheadServer(packet->GetPacketSize());
607 connectedsocket->SendPacket(packet,true);
609 AddDebugLogLineN(logServer, wxT("Refreshing server connection"));
613 // true if the IP is one of a server which we currently try to connect to
614 bool CServerConnect::AwaitingTestFromIP(uint32 dwIP)
616 ServerSocketMap::iterator it = connectionattemps.begin();
618 while (it != connectionattemps.end()) {
619 const CServerSocket* serversocket = it->second;
620 if (serversocket && (serversocket->GetConnectionState() == CS_WAITFORLOGIN) &&
621 (serversocket->GetServerIP() == dwIP)) {
622 return true;
624 ++it;
627 return false;
631 bool CServerConnect::IsConnectedObfuscated() const
633 return connectedsocket != NULL && connectedsocket->IsObfusicating();
637 void CServerConnect::OnServerHostnameResolved(void* socket, uint32 ip)
639 // The socket object might have been deleted while we waited for the hostname
640 // to be resolved, so we check if it's still among the open sockets.
641 SocketsList::iterator it = std::find(m_lstOpenSockets.begin(), m_lstOpenSockets.end(), socket);
642 if (it != m_lstOpenSockets.end()) {
643 (*it)->OnHostnameResolved(ip);
644 } else {
645 AddLogLineNS(_("Received late result of DNS lookup, discarding."));
650 // File_checked_for_headers