Upstream tarball 9574
[amule.git] / src / ServerConnect.cpp
blobd0cb3aff4418a9d28d6ee83d027a093076a01a9f
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 <common/Format.h>
51 //#define DEBUG_CLIENT_PROTOCOL
54 void CServerConnect::TryAnotherConnectionrequest()
56 if ( connectionattemps.size() < (unsigned)(( thePrefs::IsSafeServerConnectEnabled()) ? 1 : 2) ) {
58 CServer* next_server = used_list->GetNextServer(m_bTryObfuscated);
60 if ( thePrefs::AutoConnectStaticOnly() ) {
61 while (next_server && !next_server->IsStaticMember()) {
62 next_server = used_list->GetNextServer(m_bTryObfuscated);
66 if (!next_server) {
67 if ( connectionattemps.empty() ) {
68 if (m_bTryObfuscated && !thePrefs::IsClientCryptLayerRequired()){
69 AddLogLineM(true, _("Failed to connect to all obfuscated servers listed. Making another pass without obfuscation."));
70 // try all servers on the non-obfuscated port next
71 m_bTryObfuscated = false;
72 ConnectToAnyServer( false, true);
73 } else {
74 AddLogLineM(true, _("Failed to connect to all servers listed. Making another pass."));
75 ConnectToAnyServer( false );
78 return;
81 ConnectToServer(next_server, true, !m_bTryObfuscated);
85 void CServerConnect::ConnectToAnyServer(bool prioSort, bool bNoCrypt)
87 if (!thePrefs::GetNetworkED2K()){
88 AddLogLineM(true,_("eD2k network disabled on preferences, not connecting."));
89 return;
92 StopConnectionTry();
93 Disconnect();
94 connecting = true;
95 singleconnecting = false;
96 m_bTryObfuscated = thePrefs::IsServerCryptLayerTCPRequested() && !bNoCrypt;
98 // Barry - Only auto-connect to static server option
99 if (thePrefs::AutoConnectStaticOnly()) {
100 bool anystatic = false;
101 CServer *next_server;
102 used_list->ResetServerPos();
103 while ((next_server = used_list->GetNextServer(false)) != NULL) {
104 if (next_server->IsStaticMember()) {
105 anystatic = true;
106 break;
109 if (!anystatic) {
110 connecting = false;
111 AddLogLineM(true,_("No valid servers to which to connect found in server list"));
112 return;
116 if ( thePrefs::Score() && prioSort ) {
117 used_list->Sort();
120 used_list->ResetServerPos();
122 if (used_list->GetServerCount()==0 ) {
123 connecting = false;
124 AddLogLineM(true,_("No valid servers to which to connect found in server list"));
125 return;
128 theApp->listensocket->Process();
130 TryAnotherConnectionrequest();
134 void CServerConnect::ConnectToServer(CServer* server, bool multiconnect, bool bNoCrypt)
136 if (!thePrefs::GetNetworkED2K()){
137 AddLogLineM(true,_("eD2k network disabled on preferences, not connecting."));
138 return;
141 if (!multiconnect) {
142 StopConnectionTry();
143 Disconnect();
145 connecting = true;
146 singleconnecting = !multiconnect;
148 CServerSocket* newsocket = new CServerSocket(this, thePrefs::GetProxyData());
149 m_lstOpenSockets.push_back(newsocket);
150 newsocket->ConnectToServer(server, bNoCrypt);
152 connectionattemps[GetTickCount()] = newsocket;
156 void CServerConnect::StopConnectionTry()
158 connectionattemps.clear();
159 connecting = false;
160 singleconnecting = false;
162 if (m_idRetryTimer.IsRunning())
164 m_idRetryTimer.Stop();
167 // close all currenty opened sockets except the one which is connected to our current server
168 for(SocketsList::iterator it = m_lstOpenSockets.begin(); it != m_lstOpenSockets.end(); ) {
169 CServerSocket *pSck = *it++;
170 if (pSck == connectedsocket) // don't destroy socket which is connected to server
171 continue;
172 if (pSck->m_bIsDeleting == false) // don't destroy socket if it is going to destroy itself later on
173 DestroySocket(pSck);
177 void CServerConnect::ConnectionEstablished(CServerSocket* sender)
179 if (connecting == false)
181 // we are already connected to another server
182 DestroySocket(sender);
183 return;
186 if (sender->GetConnectionState() == CS_WAITFORLOGIN) {
187 AddLogLineM(false, CFormat( _("Connected to %s (%s:%i)") )
188 % sender->cur_server->GetListName()
189 % sender->cur_server->GetFullIP()
190 % sender->cur_server->GetPort() );
192 //send loginpacket
193 CServer* update = theApp->serverlist->GetServerByAddress( sender->cur_server->GetAddress(), sender->cur_server->GetPort() );
194 if (update){
195 update->ResetFailedCount();
196 Notify_ServerRefresh( update );
199 CMemFile data(256);
200 data.WriteHash(thePrefs::GetUserHash());
201 // Why pass an ID, if we are loggin in?
202 data.WriteUInt32(GetClientID());
203 data.WriteUInt16(thePrefs::GetPort());
204 data.WriteUInt32(4); // tagcount
206 // Kry - Server doesn't support VBT tags afaik.
207 // Not to mention we don't know its flags yet
209 CTagString tagname(CT_NAME,thePrefs::GetUserNick());
210 tagname.WriteTagToFile(&data);
212 CTagInt32 tagversion(CT_VERSION,EDONKEYVERSION);
213 tagversion.WriteTagToFile(&data);
215 uint32 dwCryptFlags = 0;
217 if (thePrefs::IsClientCryptLayerSupported()) {
218 dwCryptFlags |= SRVCAP_SUPPORTCRYPT;
221 if (thePrefs::IsClientCryptLayerRequested()) {
222 dwCryptFlags |= SRVCAP_REQUESTCRYPT;
225 if (thePrefs::IsClientCryptLayerRequired()) {
226 dwCryptFlags |= SRVCAP_REQUIRECRYPT;
229 // FLAGS for server connection
230 CTagInt32 tagflags(CT_SERVER_FLAGS, CAPABLE_ZLIB
231 | CAPABLE_AUXPORT
232 | CAPABLE_NEWTAGS
233 | CAPABLE_UNICODE
234 | CAPABLE_LARGEFILES
235 | dwCryptFlags
238 tagflags.WriteTagToFile(&data);
240 // eMule Version (14-Mar-2004: requested by lugdunummaster (need for LowID clients which have no chance
241 // to send an Hello packet to the server during the callback test))
242 CTagInt32 tagMuleVersion(CT_EMULE_VERSION,
243 (SO_AMULE << 24) |
244 make_full_ed2k_version(VERSION_MJR, VERSION_MIN, VERSION_UPDATE)
246 tagMuleVersion.WriteTagToFile(&data);
248 CPacket* packet = new CPacket(data, OP_EDONKEYPROT, OP_LOGINREQUEST);
249 #ifdef DEBUG_CLIENT_PROTOCOL
250 AddLogLineM(true,wxT("Client: OP_LOGINREQUEST"));
251 AddLogLineM(true,wxString(wxT(" Hash : ")) << thePrefs::GetUserHash().Encode());
252 AddLogLineM(true,wxString(wxT(" ClientID : ")) << GetClientID());
253 AddLogLineM(true,wxString(wxT(" Port : ")) << thePrefs::GetPort());
254 AddLogLineM(true,wxString(wxT(" User Nick: ")) << thePrefs::GetUserNick());
255 AddLogLineM(true,wxString(wxT(" Edonkey : ")) << EDONKEYVERSION);
256 #endif
257 theStats::AddUpOverheadServer(packet->GetPacketSize());
258 SendPacket(packet, true, sender);
259 } else if (sender->GetConnectionState() == CS_CONNECTED){
260 theStats::AddReconnect();
261 theStats::GetServerConnectTimer()->ResetTimer();
262 connected = true;
263 AddLogLineM(true, CFormat( _("Connection established on: %s") ) % sender->cur_server->GetListName());
264 connectedsocket = sender;
266 StopConnectionTry();
268 CServer* update = theApp->serverlist->GetServerByAddress(connectedsocket->cur_server->GetAddress(),sender->cur_server->GetPort());
269 if ( update ) {
270 Notify_ServerHighlight(update, true);
273 theApp->sharedfiles->ClearED2KPublishInfo();
275 Notify_ServerRemoveDead();
277 // tecxx 1609 2002 - serverlist update
278 if (thePrefs::AddServersFromServer()) {
279 CPacket* packet = new CPacket(OP_GETSERVERLIST, 0, OP_EDONKEYPROT);
280 theStats::AddUpOverheadServer(packet->GetPacketSize());
281 SendPacket(packet, true);
282 #ifdef DEBUG_CLIENT_PROTOCOL
283 AddLogLineM(true,wxT("Client: OP_GETSERVERLIST"));
284 #endif
288 theApp->ShowConnectionState();
292 bool CServerConnect::SendPacket(CPacket* packet,bool delpacket, CServerSocket* to)
294 if (!to) {
295 if (connected) {
296 connectedsocket->SendPacket(packet, delpacket, true);
297 return true;
298 } else {
299 if ( delpacket ) {
300 delete packet;
303 return false;
305 } else {
306 to->SendPacket(packet, delpacket, true);
307 return true;
312 bool CServerConnect::SendUDPPacket(CPacket* packet, CServer* host, bool delpacket, bool rawpacket, uint16 port_offset)
314 if (connected) {
315 serverudpsocket->SendPacket(packet, host, delpacket, rawpacket, port_offset);
316 } else if (delpacket) {
317 delete packet;
320 return true;
324 void CServerConnect::ConnectionFailed(CServerSocket* sender)
326 if (connecting == false && sender != connectedsocket)
328 // just return, cleanup is done by the socket itself
329 return;
331 //messages
332 CServer* pServer = theApp->serverlist->GetServerByAddress(sender->cur_server->GetAddress(), sender->cur_server->GetPort());
333 switch (sender->GetConnectionState()){
334 case CS_FATALERROR:
335 AddLogLineM(true, _("Fatal Error while trying to connect. Internet connection might be down"));
336 break;
337 case CS_DISCONNECTED:
338 theApp->sharedfiles->ClearED2KPublishInfo();
339 AddLogLineM(false,CFormat( _("Lost connection to %s (%s:%i)") )
340 % sender->cur_server->GetListName()
341 % sender->cur_server->GetFullIP()
342 % sender->cur_server->GetPort() );
344 if (pServer){
345 Notify_ServerHighlight(pServer, false);
347 break;
348 case CS_SERVERDEAD:
349 AddLogLineM(false, CFormat( _("%s (%s:%i) appears to be dead.") )
350 % sender->cur_server->GetListName()
351 % sender->cur_server->GetFullIP()
352 % sender->cur_server->GetPort() );
354 if (pServer) {
355 pServer->AddFailedCount();
356 Notify_ServerRefresh( pServer );
358 break;
359 case CS_ERROR:
360 break;
361 case CS_SERVERFULL:
362 AddLogLineM(false, CFormat( _("%s (%s:%i) appears to be full.") )
363 % sender->cur_server->GetListName()
364 % sender->cur_server->GetFullIP()
365 % sender->cur_server->GetPort() );
367 break;
368 case CS_NOTCONNECTED:;
369 break;
372 // IMPORTANT: mark this socket not to be deleted in StopConnectionTry(),
373 // because it will delete itself after this function!
374 sender->m_bIsDeleting = true;
376 switch (sender->GetConnectionState()) {
377 case CS_FATALERROR:{
378 bool autoretry= !singleconnecting;
379 StopConnectionTry();
380 if ((thePrefs::Reconnect()) && (autoretry) && (!m_idRetryTimer.IsRunning())){
381 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));
382 m_idRetryTimer.Start(1000*CS_RETRYCONNECTTIME);
384 break;
386 case CS_DISCONNECTED:{
387 theApp->sharedfiles->ClearED2KPublishInfo();
388 connected = false;
389 Notify_ServerHighlight(sender->cur_server,false);
390 if (connectedsocket) {
391 connectedsocket->Close();
393 connectedsocket = NULL;
394 theApp->searchlist->StopGlobalSearch();
395 Notify_SearchCancel();
396 theStats::GetServerConnectTimer()->StopTimer();
397 if (thePrefs::Reconnect() && !connecting){
398 ConnectToAnyServer();
401 AddLogLineM( true, _("Connection lost") );
402 break;
404 case CS_ERROR:
405 case CS_NOTCONNECTED:{
406 if (!connecting)
407 break;
408 AddLogLineM(false, CFormat( _("Connecting to %s (%s:%i) failed.") )
409 % sender->info
410 % sender->cur_server->GetFullIP()
411 % sender->cur_server->GetPort() );
413 case CS_SERVERDEAD:
414 case CS_SERVERFULL:{
415 if (!connecting) {
416 break;
418 if (singleconnecting) {
419 if (pServer && sender->IsServerCryptEnabledConnection() && !thePrefs::IsClientCryptLayerRequired()){
420 // try reconnecting without obfuscation
421 ConnectToServer(pServer, false, true);
422 break;
425 StopConnectionTry();
426 break;
429 ServerSocketMap::iterator it = connectionattemps.begin();
430 while ( it != connectionattemps.end() ){
431 if ( it->second == sender ) {
432 connectionattemps.erase( it );
433 break;
435 ++it;
437 TryAnotherConnectionrequest();
440 theApp->ShowConnectionState();
443 void CServerConnect::CheckForTimeout()
445 uint32 dwCurTick = GetTickCount();
447 ServerSocketMap::iterator it = connectionattemps.begin();
448 while ( it != connectionattemps.end() ){
449 if ( !it->second ) {
450 AddLogLineM(false, _("ERROR: Socket invalid at timeout check"));
451 connectionattemps.erase( it );
452 return;
455 if ( dwCurTick - it->first > CONSERVTIMEOUT) {
456 uint32 key = it->first;
457 CServerSocket* value = it->second;
458 ++it;
459 if (!value->IsSolving()) {
460 AddLogLineM(false, CFormat( _("Connection attempt to %s (%s:%i) timed out.") )
461 % value->info
462 % value->cur_server->GetFullIP()
463 % value->cur_server->GetPort() );
465 connectionattemps.erase( key );
467 TryAnotherConnectionrequest();
468 DestroySocket( value );
470 } else {
471 ++it;
477 bool CServerConnect::Disconnect()
479 if (connected && connectedsocket) {
480 theApp->sharedfiles->ClearED2KPublishInfo();
482 connected = false;
484 CServer* update = theApp->serverlist->GetServerByAddress(
485 connectedsocket->cur_server->GetAddress(),
486 connectedsocket->cur_server->GetPort());
487 Notify_ServerHighlight(update, false);
488 theApp->SetPublicIP(0);
489 DestroySocket(connectedsocket);
490 connectedsocket = NULL;
491 theApp->ShowConnectionState();
492 theStats::GetServerConnectTimer()->StopTimer();
493 return true;
494 } else {
495 return false;
500 CServerConnect::CServerConnect(CServerList* in_serverlist, amuleIPV4Address &address)
501 : m_idRetryTimer(theApp,ID_SERVER_RETRY_TIMER_EVENT)
503 connectedsocket = NULL;
504 used_list = in_serverlist;
505 max_simcons = (thePrefs::IsSafeServerConnectEnabled()) ? 1 : 2;
506 connecting = false;
507 connected = false;
508 clientid = 0;
509 singleconnecting = false;
511 // initalize socket for udp packets
512 if (thePrefs::GetNetworkED2K()) {
513 serverudpsocket = new CServerUDPSocket(address, thePrefs::GetProxyData());
514 } else {
515 serverudpsocket = NULL;
520 CServerConnect::~CServerConnect()
522 m_idRetryTimer.Stop();
523 // stop all connections
524 StopConnectionTry();
525 // close connected socket, if any
526 DestroySocket(connectedsocket);
527 connectedsocket = NULL;
528 // close udp socket
529 delete serverudpsocket;
533 CServer* CServerConnect::GetCurrentServer()
535 if (IsConnected() && connectedsocket) {
536 return connectedsocket->cur_server;
538 return NULL;
542 void CServerConnect::SetClientID(uint32 newid)
544 clientid = newid;
546 if (!::IsLowID(newid)) {
547 theApp->SetPublicIP(newid);
552 void CServerConnect::DestroySocket(CServerSocket* pSck)
554 if (pSck == NULL) {
555 return;
557 m_lstOpenSockets.remove(pSck);
558 pSck->Destroy();
562 bool CServerConnect::IsLocalServer(uint32 dwIP, uint16 nPort)
564 if (IsConnected()){
565 if (connectedsocket->cur_server->GetIP() == dwIP && connectedsocket->cur_server->GetPort() == nPort)
566 return true;
568 return false;
572 void CServerConnect::KeepConnectionAlive()
574 uint32 dwServerKeepAliveTimeout = thePrefs::GetServerKeepAliveTimeout();
575 if (dwServerKeepAliveTimeout && connected && connectedsocket &&
576 connectedsocket->connectionstate == CS_CONNECTED &&
577 GetTickCount() - connectedsocket->GetLastTransmission() >= dwServerKeepAliveTimeout) {
578 // "Ping" the server if the TCP connection was not used for the specified interval with
579 // an empty publish files packet -> recommended by lugdunummaster himself!
581 CMemFile files(4);
582 files.WriteUInt32(0); //nFiles
584 CPacket* packet = new CPacket(files, OP_EDONKEYPROT, OP_OFFERFILES);
585 #ifdef DEBUG_CLIENT_PROTOCOL
586 AddLogLineM(true,wxT("Client: OP_OFFERFILES"));
587 #endif
588 // compress packet
589 // - this kind of data is highly compressable (N * (1 MD4 and at least 3 string meta data tags and 1 integer meta data tag))
590 // - the min. amount of data needed for one published file is ~100 bytes
591 // - this function is called once when connecting to a server and when a file becomes shareable - so, it's called rarely.
592 // - if the compressed size is still >= the original size, we send the uncompressed packet
593 // therefor we always try to compress the packet
594 theStats::AddUpOverheadServer(packet->GetPacketSize());
595 connectedsocket->SendPacket(packet,true);
597 AddDebugLogLineM(false, logServer, wxT("Refreshing server connection"));
601 // true if the IP is one of a server which we currently try to connect to
602 bool CServerConnect::AwaitingTestFromIP(uint32 dwIP)
604 ServerSocketMap::iterator it = connectionattemps.begin();
606 while (it != connectionattemps.end()) {
607 const CServerSocket* serversocket = it->second;
608 if (serversocket && (serversocket->GetConnectionState() == CS_WAITFORLOGIN) &&
609 (serversocket->GetServerIP() == dwIP)) {
610 return true;
612 ++it;
615 return false;
619 bool CServerConnect::IsConnectedObfuscated() const
621 return connectedsocket != NULL && connectedsocket->IsObfusicating();
625 void CServerConnect::OnServerHostnameResolved(void* socket, uint32 ip)
627 // The socket object might have been deleted while we waited for the hostname
628 // to be resolved, so we check if it's still among the open sockets.
629 SocketsList::iterator it = std::find(m_lstOpenSockets.begin(), m_lstOpenSockets.end(), socket);
630 if (it != m_lstOpenSockets.end()) {
631 (*it)->OnHostnameResolved(ip);
632 } else {
633 AddLogLineNS(_("Received late result of DNS lookup, discarding."));
638 // File_checked_for_headers