Fix for libraries being compiled unconditionally
[amule.git] / src / ServerConnect.cpp
blob12279caf58d3c19ed32740212880feeb3e737b28
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2008 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.
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
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 ( connectionattemps.size() < (unsigned)(( thePrefs::IsSafeServerConnectEnabled()) ? 1 : 2) ) {
59 CServer* next_server = used_list->GetNextServer(m_bTryObfuscated);
61 if ( thePrefs::AutoConnectStaticOnly() ) {
62 while (next_server && !next_server->IsStaticMember()) {
63 next_server = used_list->GetNextServer(m_bTryObfuscated);
67 if (!next_server) {
68 if ( connectionattemps.empty() ) {
69 if (m_bTryObfuscated && !thePrefs::IsClientCryptLayerRequired()){
70 AddLogLineM(true, _("Failed to connect to all obfuscated servers listed. Making another pass without obfuscation."));
71 // try all servers on the non-obfuscated port next
72 m_bTryObfuscated = false;
73 ConnectToAnyServer( false, true);
74 } else {
75 AddLogLineM(true, _("Failed to connect to all servers listed. Making another pass."));
76 ConnectToAnyServer( false );
79 return;
82 ConnectToServer(next_server, true, !m_bTryObfuscated);
86 void CServerConnect::ConnectToAnyServer(bool prioSort, bool bNoCrypt)
88 if (!thePrefs::GetNetworkED2K()){
89 AddLogLineM(true,_("eD2k network disabled on preferences, not connecting."));
90 return;
92 if (!theApp->ipfilter->IsReady()) {
93 // do it later when ipfilter is up
94 theApp->ipfilter->ConnectToAnyServerWhenReady();
95 return;
98 StopConnectionTry();
99 Disconnect();
100 connecting = true;
101 singleconnecting = false;
102 m_bTryObfuscated = thePrefs::IsServerCryptLayerTCPRequested() && !bNoCrypt;
104 // Barry - Only auto-connect to static server option
105 if (thePrefs::AutoConnectStaticOnly()) {
106 bool anystatic = false;
107 CServer *next_server;
108 used_list->ResetServerPos();
109 while ((next_server = used_list->GetNextServer(false)) != NULL) {
110 if (next_server->IsStaticMember()) {
111 anystatic = true;
112 break;
115 if (!anystatic) {
116 connecting = false;
117 AddLogLineM(true,_("No valid servers to which to connect found in server list"));
118 return;
122 if ( thePrefs::Score() && prioSort ) {
123 used_list->Sort();
126 used_list->ResetServerPos();
128 if (used_list->GetServerCount()==0 ) {
129 connecting = false;
130 AddLogLineM(true,_("No valid servers to which to connect found in server list"));
131 return;
134 theApp->listensocket->Process();
136 TryAnotherConnectionrequest();
140 void CServerConnect::ConnectToServer(CServer* server, bool multiconnect, bool bNoCrypt)
142 if (!thePrefs::GetNetworkED2K()){
143 AddLogLineM(true,_("eD2k network disabled on preferences, not connecting."));
144 return;
147 if (!multiconnect) {
148 StopConnectionTry();
149 Disconnect();
151 connecting = true;
152 singleconnecting = !multiconnect;
154 CServerSocket* newsocket = new CServerSocket(this, thePrefs::GetProxyData());
155 m_lstOpenSockets.push_back(newsocket);
156 newsocket->ConnectToServer(server, bNoCrypt);
158 connectionattemps[GetTickCount()] = newsocket;
162 void CServerConnect::StopConnectionTry()
164 connectionattemps.clear();
165 connecting = false;
166 singleconnecting = false;
168 if (m_idRetryTimer.IsRunning())
170 m_idRetryTimer.Stop();
173 // close all currenty opened sockets except the one which is connected to our current server
174 for(SocketsList::iterator it = m_lstOpenSockets.begin(); it != m_lstOpenSockets.end(); ) {
175 CServerSocket *pSck = *it++;
176 if (pSck == connectedsocket) // don't destroy socket which is connected to server
177 continue;
178 if (pSck->m_bIsDeleting == false) // don't destroy socket if it is going to destroy itself later on
179 DestroySocket(pSck);
183 void CServerConnect::ConnectionEstablished(CServerSocket* sender)
185 if (connecting == false)
187 // we are already connected to another server
188 DestroySocket(sender);
189 return;
192 if (sender->GetConnectionState() == CS_WAITFORLOGIN) {
193 AddLogLineM(false, CFormat( _("Connected to %s (%s:%i)") )
194 % sender->cur_server->GetListName()
195 % sender->cur_server->GetFullIP()
196 % sender->cur_server->GetPort() );
198 //send loginpacket
199 CServer* update = theApp->serverlist->GetServerByAddress( sender->cur_server->GetAddress(), sender->cur_server->GetPort() );
200 if (update){
201 update->ResetFailedCount();
202 Notify_ServerRefresh( update );
205 CMemFile data(256);
206 data.WriteHash(thePrefs::GetUserHash());
207 // Why pass an ID, if we are loggin in?
208 data.WriteUInt32(GetClientID());
209 data.WriteUInt16(thePrefs::GetPort());
210 data.WriteUInt32(4); // tagcount
212 // Kry - Server doesn't support VBT tags afaik.
213 // Not to mention we don't know its flags yet
215 CTagString tagname(CT_NAME,thePrefs::GetUserNick());
216 tagname.WriteTagToFile(&data);
218 CTagInt32 tagversion(CT_VERSION,EDONKEYVERSION);
219 tagversion.WriteTagToFile(&data);
221 uint32 dwCryptFlags = 0;
223 if (thePrefs::IsClientCryptLayerSupported()) {
224 dwCryptFlags |= SRVCAP_SUPPORTCRYPT;
227 if (thePrefs::IsClientCryptLayerRequested()) {
228 dwCryptFlags |= SRVCAP_REQUESTCRYPT;
231 if (thePrefs::IsClientCryptLayerRequired()) {
232 dwCryptFlags |= SRVCAP_REQUIRECRYPT;
235 // FLAGS for server connection
236 CTagInt32 tagflags(CT_SERVER_FLAGS, CAPABLE_ZLIB
237 | CAPABLE_AUXPORT
238 | CAPABLE_NEWTAGS
239 | CAPABLE_UNICODE
240 | CAPABLE_LARGEFILES
241 | dwCryptFlags
244 tagflags.WriteTagToFile(&data);
246 // eMule Version (14-Mar-2004: requested by lugdunummaster (need for LowID clients which have no chance
247 // to send an Hello packet to the server during the callback test))
248 CTagInt32 tagMuleVersion(CT_EMULE_VERSION,
249 (SO_AMULE << 24) |
250 make_full_ed2k_version(VERSION_MJR, VERSION_MIN, VERSION_UPDATE)
252 tagMuleVersion.WriteTagToFile(&data);
254 CPacket* packet = new CPacket(data, OP_EDONKEYPROT, OP_LOGINREQUEST);
255 #ifdef DEBUG_CLIENT_PROTOCOL
256 AddLogLineM(true,wxT("Client: OP_LOGINREQUEST"));
257 AddLogLineM(true,wxString(wxT(" Hash : ")) << thePrefs::GetUserHash().Encode());
258 AddLogLineM(true,wxString(wxT(" ClientID : ")) << GetClientID());
259 AddLogLineM(true,wxString(wxT(" Port : ")) << thePrefs::GetPort());
260 AddLogLineM(true,wxString(wxT(" User Nick: ")) << thePrefs::GetUserNick());
261 AddLogLineM(true,wxString(wxT(" Edonkey : ")) << EDONKEYVERSION);
262 #endif
263 theStats::AddUpOverheadServer(packet->GetPacketSize());
264 SendPacket(packet, true, sender);
265 } else if (sender->GetConnectionState() == CS_CONNECTED){
266 theStats::AddReconnect();
267 theStats::GetServerConnectTimer()->ResetTimer();
268 connected = true;
269 AddLogLineM(true, CFormat( _("Connection established on: %s") ) % sender->cur_server->GetListName());
270 connectedsocket = sender;
272 StopConnectionTry();
274 CServer* update = theApp->serverlist->GetServerByAddress(connectedsocket->cur_server->GetAddress(),sender->cur_server->GetPort());
275 if ( update ) {
276 Notify_ServerHighlight(update, true);
279 theApp->sharedfiles->ClearED2KPublishInfo();
281 Notify_ServerRemoveDead();
283 // tecxx 1609 2002 - serverlist update
284 if (thePrefs::AddServersFromServer()) {
285 CPacket* packet = new CPacket(OP_GETSERVERLIST, 0, OP_EDONKEYPROT);
286 theStats::AddUpOverheadServer(packet->GetPacketSize());
287 SendPacket(packet, true);
288 #ifdef DEBUG_CLIENT_PROTOCOL
289 AddLogLineM(true,wxT("Client: OP_GETSERVERLIST"));
290 #endif
294 theApp->ShowConnectionState();
298 bool CServerConnect::SendPacket(CPacket* packet,bool delpacket, CServerSocket* to)
300 if (!to) {
301 if (connected) {
302 connectedsocket->SendPacket(packet, delpacket, true);
303 return true;
304 } else {
305 if ( delpacket ) {
306 delete packet;
309 return false;
311 } else {
312 to->SendPacket(packet, delpacket, true);
313 return true;
318 bool CServerConnect::SendUDPPacket(CPacket* packet, CServer* host, bool delpacket, bool rawpacket, uint16 port_offset)
320 if (connected) {
321 serverudpsocket->SendPacket(packet, host, delpacket, rawpacket, port_offset);
322 } else if (delpacket) {
323 delete packet;
326 return true;
330 void CServerConnect::ConnectionFailed(CServerSocket* sender)
332 if (connecting == false && sender != connectedsocket)
334 // just return, cleanup is done by the socket itself
335 return;
337 //messages
338 CServer* pServer = theApp->serverlist->GetServerByAddress(sender->cur_server->GetAddress(), sender->cur_server->GetPort());
339 switch (sender->GetConnectionState()){
340 case CS_FATALERROR:
341 AddLogLineM(true, _("Fatal Error while trying to connect. Internet connection might be down"));
342 break;
343 case CS_DISCONNECTED:
344 theApp->sharedfiles->ClearED2KPublishInfo();
345 AddLogLineM(false,CFormat( _("Lost connection to %s (%s:%i)") )
346 % sender->cur_server->GetListName()
347 % sender->cur_server->GetFullIP()
348 % sender->cur_server->GetPort() );
350 if (pServer){
351 Notify_ServerHighlight(pServer, false);
353 break;
354 case CS_SERVERDEAD:
355 AddLogLineM(false, CFormat( _("%s (%s:%i) appears to be dead.") )
356 % sender->cur_server->GetListName()
357 % sender->cur_server->GetFullIP()
358 % sender->cur_server->GetPort() );
360 if (pServer) {
361 pServer->AddFailedCount();
362 Notify_ServerRefresh( pServer );
364 break;
365 case CS_ERROR:
366 break;
367 case CS_SERVERFULL:
368 AddLogLineM(false, CFormat( _("%s (%s:%i) appears to be full.") )
369 % sender->cur_server->GetListName()
370 % sender->cur_server->GetFullIP()
371 % sender->cur_server->GetPort() );
373 break;
374 case CS_NOTCONNECTED:;
375 break;
378 // IMPORTANT: mark this socket not to be deleted in StopConnectionTry(),
379 // because it will delete itself after this function!
380 sender->m_bIsDeleting = true;
382 switch (sender->GetConnectionState()) {
383 case CS_FATALERROR:{
384 bool autoretry= !singleconnecting;
385 StopConnectionTry();
386 if ((thePrefs::Reconnect()) && (autoretry) && (!m_idRetryTimer.IsRunning())){
387 AddLogLineM(false, wxString::Format(wxPLURAL("Automatic connection to server will retry in %d second", "Automatic connection to server will retry in %d seconds", CS_RETRYCONNECTTIME), CS_RETRYCONNECTTIME));
388 m_idRetryTimer.Start(1000*CS_RETRYCONNECTTIME);
390 break;
392 case CS_DISCONNECTED:{
393 theApp->sharedfiles->ClearED2KPublishInfo();
394 connected = false;
395 Notify_ServerHighlight(sender->cur_server,false);
396 if (connectedsocket) {
397 connectedsocket->Close();
399 connectedsocket = NULL;
400 theApp->searchlist->StopGlobalSearch();
401 Notify_SearchCancel();
402 theStats::GetServerConnectTimer()->StopTimer();
403 if (thePrefs::Reconnect() && !connecting){
404 ConnectToAnyServer();
407 AddLogLineM( true, _("Connection lost") );
408 break;
410 case CS_ERROR:
411 case CS_NOTCONNECTED:{
412 if (!connecting)
413 break;
414 AddLogLineM(false, CFormat( _("Connecting to %s (%s:%i) failed.") )
415 % sender->info
416 % sender->cur_server->GetFullIP()
417 % sender->cur_server->GetPort() );
419 case CS_SERVERDEAD:
420 case CS_SERVERFULL:{
421 if (!connecting) {
422 break;
424 if (singleconnecting) {
425 if (pServer && sender->IsServerCryptEnabledConnection() && !thePrefs::IsClientCryptLayerRequired()){
426 // try reconnecting without obfuscation
427 ConnectToServer(pServer, false, true);
428 break;
431 StopConnectionTry();
432 break;
435 ServerSocketMap::iterator it = connectionattemps.begin();
436 while ( it != connectionattemps.end() ){
437 if ( it->second == sender ) {
438 connectionattemps.erase( it );
439 break;
441 ++it;
443 TryAnotherConnectionrequest();
446 theApp->ShowConnectionState();
449 void CServerConnect::CheckForTimeout()
451 uint32 dwCurTick = GetTickCount();
453 ServerSocketMap::iterator it = connectionattemps.begin();
454 while ( it != connectionattemps.end() ){
455 if ( !it->second ) {
456 AddLogLineM(false, _("ERROR: Socket invalid at timeout check"));
457 connectionattemps.erase( it );
458 return;
461 if ( dwCurTick - it->first > CONSERVTIMEOUT) {
462 uint32 key = it->first;
463 CServerSocket* value = it->second;
464 ++it;
465 if (!value->IsSolving()) {
466 AddLogLineM(false, CFormat( _("Connection attempt to %s (%s:%i) timed out.") )
467 % value->info
468 % value->cur_server->GetFullIP()
469 % value->cur_server->GetPort() );
471 connectionattemps.erase( key );
473 TryAnotherConnectionrequest();
474 DestroySocket( value );
476 } else {
477 ++it;
483 bool CServerConnect::Disconnect()
485 if (connected && connectedsocket) {
486 theApp->sharedfiles->ClearED2KPublishInfo();
488 connected = false;
490 CServer* update = theApp->serverlist->GetServerByAddress(
491 connectedsocket->cur_server->GetAddress(),
492 connectedsocket->cur_server->GetPort());
493 Notify_ServerHighlight(update, false);
494 theApp->SetPublicIP(0);
495 DestroySocket(connectedsocket);
496 connectedsocket = NULL;
497 theApp->ShowConnectionState();
498 theStats::GetServerConnectTimer()->StopTimer();
499 return true;
500 } else {
501 return false;
506 CServerConnect::CServerConnect(CServerList* in_serverlist, amuleIPV4Address &address)
507 : m_idRetryTimer(theApp,ID_SERVER_RETRY_TIMER_EVENT)
509 connectedsocket = NULL;
510 used_list = in_serverlist;
511 max_simcons = (thePrefs::IsSafeServerConnectEnabled()) ? 1 : 2;
512 connecting = false;
513 connected = false;
514 clientid = 0;
515 singleconnecting = false;
517 // initalize socket for udp packets
518 if (thePrefs::GetNetworkED2K()) {
519 serverudpsocket = new CServerUDPSocket(address, thePrefs::GetProxyData());
520 } else {
521 serverudpsocket = NULL;
526 CServerConnect::~CServerConnect()
528 m_idRetryTimer.Stop();
529 // stop all connections
530 StopConnectionTry();
531 // close connected socket, if any
532 DestroySocket(connectedsocket);
533 connectedsocket = NULL;
534 // close udp socket
535 delete serverudpsocket;
539 CServer* CServerConnect::GetCurrentServer()
541 if (IsConnected() && connectedsocket) {
542 return connectedsocket->cur_server;
544 return NULL;
548 void CServerConnect::SetClientID(uint32 newid)
550 clientid = newid;
552 if (!::IsLowID(newid)) {
553 theApp->SetPublicIP(newid);
558 void CServerConnect::DestroySocket(CServerSocket* pSck)
560 if (pSck == NULL) {
561 return;
563 m_lstOpenSockets.remove(pSck);
564 pSck->Destroy();
568 bool CServerConnect::IsLocalServer(uint32 dwIP, uint16 nPort)
570 if (IsConnected()){
571 if (connectedsocket->cur_server->GetIP() == dwIP && connectedsocket->cur_server->GetPort() == nPort)
572 return true;
574 return false;
578 void CServerConnect::KeepConnectionAlive()
580 uint32 dwServerKeepAliveTimeout = thePrefs::GetServerKeepAliveTimeout();
581 if (dwServerKeepAliveTimeout && connected && connectedsocket &&
582 connectedsocket->connectionstate == CS_CONNECTED &&
583 GetTickCount() - connectedsocket->GetLastTransmission() >= dwServerKeepAliveTimeout) {
584 // "Ping" the server if the TCP connection was not used for the specified interval with
585 // an empty publish files packet -> recommended by lugdunummaster himself!
587 CMemFile files(4);
588 files.WriteUInt32(0); //nFiles
590 CPacket* packet = new CPacket(files, OP_EDONKEYPROT, OP_OFFERFILES);
591 #ifdef DEBUG_CLIENT_PROTOCOL
592 AddLogLineM(true,wxT("Client: OP_OFFERFILES"));
593 #endif
594 // compress packet
595 // - this kind of data is highly compressable (N * (1 MD4 and at least 3 string meta data tags and 1 integer meta data tag))
596 // - the min. amount of data needed for one published file is ~100 bytes
597 // - this function is called once when connecting to a server and when a file becomes shareable - so, it's called rarely.
598 // - if the compressed size is still >= the original size, we send the uncompressed packet
599 // therefor we always try to compress the packet
600 theStats::AddUpOverheadServer(packet->GetPacketSize());
601 connectedsocket->SendPacket(packet,true);
603 AddDebugLogLineM(false, logServer, wxT("Refreshing server connection"));
607 // true if the IP is one of a server which we currently try to connect to
608 bool CServerConnect::AwaitingTestFromIP(uint32 dwIP)
610 ServerSocketMap::iterator it = connectionattemps.begin();
612 while (it != connectionattemps.end()) {
613 const CServerSocket* serversocket = it->second;
614 if (serversocket && (serversocket->GetConnectionState() == CS_WAITFORLOGIN) &&
615 (serversocket->GetServerIP() == dwIP)) {
616 return true;
618 ++it;
621 return false;
625 bool CServerConnect::IsConnectedObfuscated() const
627 return connectedsocket != NULL && connectedsocket->IsObfusicating();
631 void CServerConnect::OnServerHostnameResolved(void* socket, uint32 ip)
633 // The socket object might have been deleted while we waited for the hostname
634 // to be resolved, so we check if it's still among the open sockets.
635 SocketsList::iterator it = std::find(m_lstOpenSockets.begin(), m_lstOpenSockets.end(), socket);
636 if (it != m_lstOpenSockets.end()) {
637 (*it)->OnHostnameResolved(ip);
638 } else {
639 AddLogLineNS(_("Received late result of DNS lookup, discarding."));
644 // File_checked_for_headers