2 // This file is part of the aMule Project.
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 )
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
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
27 #include "ServerUDPSocket.h" // Interface declarations.
29 #include <protocol/Protocols.h>
30 #include <common/EventIDs.h>
31 #include <tags/ServerTags.h>
33 #include "Packet.h" // Needed for CPacket
34 #include "PartFile.h" // Needed for CPartFile
35 #include "SearchList.h" // Needed for CSearchList
36 #include "MemFile.h" // Needed for CMemFile
37 #include "DownloadQueue.h" // Needed for CDownloadQueue
38 #include "ServerList.h" // Needed for CServerList
39 #include "Server.h" // Needed for CServer
40 #include "amule.h" // Needed for theApp
41 #include "AsyncDNS.h" // Needed for CAsyncDNS
42 #include "Statistics.h" // Needed for theStats
44 #include <common/Format.h>
45 #include "updownclient.h" // Needed for SF_REMOTE_SERVER
46 #include "GuiEvents.h" // Needed for Notify_*
47 #include "Preferences.h"
48 #include "EncryptedDatagramSocket.h"
49 #include "RandomFunctions.h"
50 #include "ServerConnect.h"
56 CServerUDPSocket::CServerUDPSocket(amuleIPV4Address
&address
, const CProxyData
*ProxyData
)
57 : CMuleUDPSocket(wxT("Server UDP-Socket"), ID_SERVERUDPSOCKET_EVENT
, address
, ProxyData
)
63 void CServerUDPSocket::OnPacketReceived(uint32 serverip
, uint16 serverport
, byte
* buffer
, size_t length
)
65 wxCHECK_RET(length
>= 2, wxT("Invalid packet."));
67 size_t nPayLoadLen
= length
;
68 byte
* pBuffer
= buffer
;
69 CServer
* pServer
= theApp
->serverlist
->GetServerByIPUDP(serverip
, serverport
, true);
70 if (pServer
&& thePrefs::IsServerCryptLayerUDPEnabled() &&
71 ((pServer
->GetServerKeyUDP() != 0 && pServer
->SupportsObfuscationUDP()) || (pServer
->GetCryptPingReplyPending() && pServer
->GetChallenge() != 0)))
75 if (pServer
->GetCryptPingReplyPending() && pServer
->GetChallenge() != 0 /* && pServer->GetPort() == ntohs(sockAddr.sin_port) - 12 */) {
76 dwKey
= pServer
->GetChallenge();
78 dwKey
= pServer
->GetServerKeyUDP();
81 wxASSERT( dwKey
!= 0 );
82 nPayLoadLen
= CEncryptedDatagramSocket::DecryptReceivedServer(buffer
, length
, &pBuffer
, dwKey
, serverip
);
83 if (nPayLoadLen
== length
) {
84 AddDebugLogLineM( false, logServerUDP
,CFormat(wxT("Expected encrypted packet, but received unencrytped from server %s, UDPKey %u, Challenge: %u")) % pServer
->GetListName() % pServer
->GetServerKeyUDP() % pServer
->GetChallenge());
86 AddDebugLogLineM( false, logServerUDP
,CFormat(wxT("Received encrypted packet from server %s, UDPKey %u, Challenge: %u")) % pServer
->GetListName() % pServer
->GetServerKeyUDP() % pServer
->GetChallenge());
90 uint8 protocol
= pBuffer
[0];
91 uint8 opcode
= pBuffer
[1];
93 if (protocol
== OP_EDONKEYPROT
) {
94 CMemFile
data(pBuffer
+ 2, nPayLoadLen
- 2);
95 ProcessPacket(data
, opcode
, serverip
, serverport
);
97 AddDebugLogLineM( false, logServerUDP
,
98 wxString::Format( wxT("Received invalid packet, protocol (0x%x) and opcode (0x%x)"), protocol
, opcode
));
100 theStats::AddDownOverheadOther(length
);
105 void CServerUDPSocket::ProcessPacket(CMemFile
& packet
, uint8 opcode
, uint32 ip
, uint16 port
)
107 CServer
* update
= theApp
->serverlist
->GetServerByIPUDP(ip
, port
, true);
108 unsigned size
= packet
.GetLength();
110 theStats::AddDownOverheadOther(size
);
111 AddDebugLogLineM( false, logServerUDP
,
112 CFormat( wxT("Received UDP server packet from %s:%u, opcode (0x%x)")) %
113 Uint32toStringIP(ip
) % port
% opcode
);
116 // Imported: OP_GLOBSEARCHRES, OP_GLOBFOUNDSOURCES & OP_GLOBSERVSTATRES
117 // This makes Server UDP Flags to be set correctly so we use less bandwith on asking servers for sources
118 // Also we process Search results and Found sources correctly now on 16.40 behaviour.
120 case OP_GLOBSEARCHRES
: {
122 // process all search result packets
125 theApp
->searchlist
->ProcessUDPSearchAnswer(packet
, true, ip
, port
- 4);
127 if (packet
.GetPosition() + 2 < size
) {
128 // An additional packet?
129 uint8 protocol
= packet
.ReadUInt8();
130 uint8 new_opcode
= packet
.ReadUInt8();
132 if (protocol
!= OP_EDONKEYPROT
|| new_opcode
!= OP_GLOBSEARCHRES
) {
133 AddDebugLogLineM( true, logServerUDP
,
134 wxT("Server search reply got additional bogus bytes.") );
137 AddDebugLogLineM( true, logServerUDP
,
138 wxT("Got server search reply with additional packet.") );
142 } while (packet
.GetPosition()+2 < size
);
146 case OP_GLOBFOUNDSOURCES
:{
147 // process all source packets
149 CMD4Hash fileid
= packet
.ReadHash();
150 if (CPartFile
* file
= theApp
->downloadqueue
->GetFileByID(fileid
)) {
151 file
->AddSources(packet
, ip
, port
-4, SF_REMOTE_SERVER
, false);
153 AddDebugLogLineM( true, logServerUDP
, wxT("Sources received for unknown file") );
154 // skip sources for that file
155 uint8 count
= packet
.ReadUInt8();
156 packet
.Seek(count
*(4+2), wxFromCurrent
);
159 if (packet
.GetPosition()+2 < size
) {
160 // An additional packet?
161 uint8 protocol
= packet
.ReadUInt8();
162 uint8 new_opcode
= packet
.ReadUInt8();
164 if (protocol
!= OP_EDONKEYPROT
|| new_opcode
!= OP_GLOBFOUNDSOURCES
) {
165 AddDebugLogLineM( true, logServerUDP
,
166 wxT("Server sources reply got additional bogus bytes.") );
170 } while ((packet
.GetPosition() + 2) < size
);
174 case OP_GLOBSERVSTATRES
:{
175 // Reviewed with 0.47c
177 throw wxString(wxT("Unknown server on a OP_GLOBSERVSTATRES packet (") + Uint32toStringIP(ip
) + wxString::Format(wxT(":%d)"), port
-4));
180 throw(wxString(wxString::Format(wxT("Invalid OP_GLOBSERVSTATRES packet (size=%u)"),size
)));
182 uint32 challenge
= packet
.ReadUInt32();
183 if (challenge
!= update
->GetChallenge()) {
184 throw(wxString(wxString::Format(wxT("Invalid challenge on OP_GLOBSERVSTATRES packet (0x%x != 0x%x)"),challenge
,update
->GetChallenge())));
187 update
->SetChallenge(0);
188 update
->SetCryptPingReplyPending(false);
189 uint32 tNow
= ::GetTickCount();
190 update
->SetLastPingedTime(tNow
- (rand() % HR2S(1))); // if we used Obfuscated ping, we still need to reset the time properly
192 uint32 cur_user
= packet
.ReadUInt32();
193 uint32 cur_files
= packet
.ReadUInt32();
194 uint32 cur_maxusers
= 0;
195 uint32 cur_softfiles
= 0;
196 uint32 cur_hardfiles
= 0;
197 uint32 uUDPFlags
= 0;
198 uint32 uLowIDUsers
= 0;
199 uint32 dwServerUDPKey
= 0;
200 uint16 nTCPObfuscationPort
= 0;
201 uint16 nUDPObfuscationPort
= 0;
203 cur_maxusers
= packet
.ReadUInt32();
205 cur_softfiles
= packet
.ReadUInt32();
206 cur_hardfiles
= packet
.ReadUInt32();
208 uUDPFlags
= packet
.ReadUInt32();
210 uLowIDUsers
= packet
.ReadUInt32();
212 nUDPObfuscationPort
= packet
.ReadUInt16();
213 nTCPObfuscationPort
= packet
.ReadUInt16();
214 dwServerUDPKey
= packet
.ReadUInt32();
221 update
->SetPing( ::GetTickCount() - update
->GetLastPinged() );
222 update
->SetUserCount( cur_user
);
223 update
->SetFileCount( cur_files
);
224 update
->SetMaxUsers( cur_maxusers
);
225 update
->SetSoftFiles( cur_softfiles
);
226 update
->SetHardFiles( cur_hardfiles
);
227 update
->SetUDPFlags( uUDPFlags
);
228 update
->SetLowIDUsers( uLowIDUsers
);
229 update
->SetServerKeyUDP(dwServerUDPKey
);
230 update
->SetObfuscationPortTCP(nTCPObfuscationPort
);
231 update
->SetObfuscationPortUDP(nUDPObfuscationPort
);
233 Notify_ServerRefresh( update
);
235 update
->SetLastDescPingedCount(false);
236 if (update
->GetLastDescPingedCount() < 2) {
237 // eserver 16.45+ supports a new OP_SERVER_DESC_RES answer, if the OP_SERVER_DESC_REQ contains a uint32
238 // challenge, the server returns additional info with OP_SERVER_DESC_RES. To properly distinguish the
239 // old and new OP_SERVER_DESC_RES answer, the challenge has to be selected carefully. The first 2 bytes
240 // of the challenge (in network byte order) MUST NOT be a valid string-len-int16!
241 CPacket
* sendpacket
= new CPacket(OP_SERVER_DESC_REQ
, 4, OP_EDONKEYPROT
);
242 uint32 uDescReqChallenge
= ((uint32
)GetRandomUint16() << 16) + INV_SERV_DESC_LEN
; // 0xF0FF = an 'invalid' string length.
243 update
->SetDescReqChallenge(uDescReqChallenge
);
244 sendpacket
->CopyUInt32ToDataBuffer(uDescReqChallenge
);
245 //theStats.AddUpDataOverheadServer(packet->size);
246 AddDebugLogLineM(false, logServerUDP
, CFormat(wxT(">>> Sending OP__ServDescReq to server %s:%u, challenge %08x\n")) % update
->GetAddress() % update
->GetPort() % uDescReqChallenge
);
247 theApp
->serverconnect
->SendUDPPacket(sendpacket
, update
, true);
249 update
->SetLastDescPingedCount(true);
252 theApp
->ShowUserCount();
255 case OP_SERVER_DESC_RES
:{
256 // Reviewed with 0.47c
258 throw(wxString(wxT("Received OP_SERVER_DESC_RES from an unknown server")));
261 // old packet: <name_len 2><name name_len><desc_len 2 desc_en>
262 // new packet: <challenge 4><taglist>
264 // NOTE: To properly distinguish between the two packets which are both useing the same opcode...
265 // the first two bytes of <challenge> (in network byte order) have to be an invalid <name_len> at least.
267 uint16 Len
= packet
.ReadUInt16();
269 packet
.Seek(-2, wxFromCurrent
); // Step back
271 if (size
>= 8 && Len
== INV_SERV_DESC_LEN
) {
273 if (update
->GetDescReqChallenge() != 0 && packet
.ReadUInt32() == update
->GetDescReqChallenge()) {
275 update
->SetDescReqChallenge(0);
277 uint32 uTags
= packet
.ReadUInt32();
278 for (uint32 i
= 0; i
< uTags
; ++i
) {
279 CTag
tag(packet
, update
->GetUnicodeSupport());
280 switch (tag
.GetNameID()) {
282 update
->SetListName(tag
.GetStr());
285 update
->SetDescription(tag
.GetStr());
288 update
->SetDynIP(tag
.GetStr());
292 update
->SetVersion(tag
.GetStr());
293 } else if (tag
.IsInt()) {
294 wxString strVersion
= wxString::Format(wxT("%u.%u"), tag
.GetInt() >> 16, tag
.GetInt() & 0xFFFF);
295 update
->SetVersion(strVersion
);
298 case ST_AUXPORTSLIST
:
299 update
->SetAuxPortsList(tag
.GetStr());
307 // A server sent us a new server description packet (including a challenge) although we did not
308 // ask for it. This may happen, if there are multiple servers running on the same machine with
309 // multiple IPs. If such a server is asked for a description, the server will answer 2 times,
310 // but with the same IP.
311 // ignore this packet
315 update
->SetDescription(packet
.ReadString(update
->GetUnicodeSupport()));
316 update
->SetListName(packet
.ReadString(update
->GetUnicodeSupport()));
321 AddDebugLogLineM( true, logServerUDP
,
322 wxString::Format( wxT("Unknown Server UDP opcode %x"), opcode
) );
324 } catch (const wxString
& error
) {
325 AddDebugLogLineM(false, logServerUDP
, wxT("Error while processing incoming UDP Packet: ") + error
);
326 } catch (const CInvalidPacket
& error
) {
327 AddDebugLogLineM(false, logServerUDP
, wxT("Invalid UDP packet encountered: ") + error
.what());
328 } catch (const CEOFException
& e
) {
329 AddDebugLogLineM(false, logServerUDP
, wxT("IO error while processing incoming UDP Packet: ") + e
.what());
333 update
->ResetFailedCount();
334 Notify_ServerRefresh( update
);
339 void CServerUDPSocket::OnReceiveError(int errorCode
, uint32 ip
, uint16 port
)
341 CMuleUDPSocket::OnReceiveError(errorCode
, ip
, port
);
343 // If we are not currently pinging this server, increase the failure counter
344 CServer
* pServer
= theApp
->serverlist
->GetServerByIPUDP(ip
, port
, true);
345 if (pServer
&& !pServer
->GetCryptPingReplyPending() && GetTickCount() - pServer
->GetLastPinged() >= SEC2MS(30)) {
346 pServer
->AddFailedCount();
347 Notify_ServerRefresh(pServer
);
352 void CServerUDPSocket::SendPacket(CPacket
* packet
, CServer
* host
, bool delPacket
, bool rawpacket
, uint16 port_offset
)
354 ServerUDPPacket item
= { NULL
, 0, 0, wxEmptyString
};
356 if (host
->HasDynIP()) {
357 item
.addr
= host
->GetDynIP();
359 item
.ip
= host
->GetIP();
362 // 4 (default) for standard sending, 12 for obfuscated ping, that's all for now.
363 // Might be changed if encrypted bellow, so don't move it.
364 item
.port
= host
->GetPort() + port_offset
;
366 // We might need to encrypt the packet for this server.
367 if (!rawpacket
&& thePrefs::IsServerCryptLayerUDPEnabled() && host
->GetServerKeyUDP() != 0 && host
->SupportsObfuscationUDP()) {
368 uint16 uRawPacketSize
= packet
->GetPacketSize() + 2;
369 byte
* pRawPacket
= new byte
[uRawPacketSize
];
370 memcpy(pRawPacket
, packet
->GetUDPHeader(), 2);
371 memcpy(pRawPacket
+ 2, packet
->GetDataBuffer(), packet
->GetPacketSize());
373 uRawPacketSize
= CEncryptedDatagramSocket::EncryptSendServer(&pRawPacket
, uRawPacketSize
, host
->GetServerKeyUDP());
374 AddDebugLogLineM(false, logServerUDP
, CFormat(wxT("Sending encrypted packet to server %s, UDPKey %u, port %u, original OPCode 0x%02x")) % host
->GetListName() % host
->GetServerKeyUDP() % host
->GetObfuscationPortUDP() % packet
->GetOpCode());
375 item
.port
= host
->GetObfuscationPortUDP();
377 CMemFile
encryptedpacket(pRawPacket
+ 2, uRawPacketSize
- 2);
378 item
.packet
= new CPacket(encryptedpacket
, pRawPacket
[0], pRawPacket
[1]);
386 AddDebugLogLineM(false, logServerUDP
, CFormat(wxT("Sending regular packet to server %s, port %u (raw = %s), OPCode 0x%02x")) % host
->GetListName() % host
->GetObfuscationPortUDP() % (rawpacket
? wxT("True") : wxT("False")) % packet
->GetOpCode());
388 item
.packet
= packet
;
390 item
.packet
= new CPacket(*packet
);
395 m_queue
.push_back(item
);
397 // If there is more than one item in the queue,
398 // then we are already waiting for a dns query.
399 if (m_queue
.size() == 1) {
405 void CServerUDPSocket::SendQueue()
407 while (m_queue
.size()) {
408 ServerUDPPacket item
= m_queue
.front();
409 CPacket
* packet
= item
.packet
;
411 // Do we need to do a DNS lookup before sending?
412 wxASSERT(item
.ip
|| !item
.addr
.IsEmpty());
413 if (!item
.addr
.IsEmpty()) {
414 // This not an ip but a hostname. Resolve it.
415 CServer
* update
= theApp
->serverlist
->GetServerByAddress(item
.addr
, item
.port
);
417 if (update
->GetLastDNSSolve() + DNS_SOLVE_TIME
< ::GetTickCount64()) {
418 // Its time for a new check.
419 CAsyncDNS
* dns
= new CAsyncDNS(item
.addr
, DNS_UDP
, theApp
, this);
420 if ((dns
->Create() != wxTHREAD_NO_ERROR
) || (dns
->Run() != wxTHREAD_NO_ERROR
)) {
421 // Not much we can do here, just drop the packet.
425 update
->SetDNSError(false);
426 update
->SetLastDNSSolve(::GetTickCount64());
427 // Wait for the DNS query to be resolved
430 // It has been checked recently, don't re-check yet.
431 if (update
->GetDNSError()) {
432 // Drop the packet, dns failed last time
433 AddDebugLogLineM(false, logServerUDP
, wxT("Trying to send an UDP packet to a server host that failed DNS: ")+item
.addr
);
437 // It has been solved or is solving.
438 if (update
->GetIP()) {
439 // It has been solved and ok
440 AddDebugLogLineM(false, logServerUDP
, wxT("Sending a UDP packet to a resolved DNS server host: ")+item
.addr
);
441 // Update the item IP
442 item
.ip
= update
->GetIP();
443 // It'll fallback to the sending.
445 AddDebugLogLineM(false, logServerUDP
, wxT("Trying to send an UDP packet to a server host that is checking DNS: ")+item
.addr
);
446 // Let the packet queued, and wait for resolution
447 // Meanwhile, send other packets.
449 m_queue
.push_back(item
);
455 AddDebugLogLineM(false, logServerUDP
, wxT("Trying to send an UDP packet to a server host that is not on serverlist"));
456 // Not much we can do here, just drop the packet.
462 CServer
* update
= theApp
->serverlist
->GetServerByIPUDP(item
.ip
, item
.port
, true);
464 AddDebugLogLineM(false, logServerUDP
, wxT("Sending an UDP packet to a server: ")+update
->GetAddress());
465 // Don't encrypt, as this is already either encrypted or refused to encrypt.
466 CMuleUDPSocket::SendPacket(packet
, item
.ip
, item
.port
, false, NULL
, false, 0);
468 AddDebugLogLineM(false, logServerUDP
, wxT("Sending an UDP packet to a server no in serverlist: ")+Uint32_16toStringIP_Port(item
.ip
,item
.port
));
476 void CServerUDPSocket::OnHostnameResolved(uint32 ip
)
478 wxCHECK_RET(m_queue
.size(), wxT("DNS query returned, but no packets are queued."));
480 ServerUDPPacket item
= m_queue
.front();
481 wxCHECK_RET(!item
.ip
&& !item
.addr
.IsEmpty(), wxT("DNS resolution not expected."));
483 /* An asynchronous database routine completed. */
484 CServer
* update
= theApp
->serverlist
->GetServerByAddress(item
.addr
, item
.port
);
486 update
->SetDNSError(true);
499 // File_checked_for_headers