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 27#include "ServerUDPSocket.h" // Interface declarations. 28 29#include <protocol/Protocols.h> 30#include <common/EventIDs.h> 31#include <tags/ServerTags.h> 32 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 43#include "Logger.h" 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" 51 52// 53// (TCP+3) UDP socket 54// 55 56CServerUDPSocket::CServerUDPSocket(amuleIPV4Address &address, const CProxyData *ProxyData) 57 : CMuleUDPSocket(wxT("Server UDP-Socket"), ID_SERVERUDPSOCKET_EVENT, address, ProxyData) 58{ 59 Open(); 60} 61 62 63void CServerUDPSocket::OnPacketReceived(uint32 serverip, uint16 serverport, byte* buffer, size_t length) 64{ 65 wxCHECK_RET(length >= 2, wxT("Invalid packet.")); 66 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))) 72 { 73 // eMule TODO 74 uint32 dwKey = 0; 75 if (pServer->GetCryptPingReplyPending() && pServer->GetChallenge() != 0 /* && pServer->GetPort() == ntohs(sockAddr.sin_port) - 12 */) { 76 dwKey = pServer->GetChallenge(); 77 } else { 78 dwKey = pServer->GetServerKeyUDP(); 79 } 80 81 wxASSERT( dwKey != 0 ); 82 nPayLoadLen = CEncryptedDatagramSocket::DecryptReceivedServer(buffer, length, &pBuffer, dwKey, serverip); 83 if (nPayLoadLen == length) { 84 AddDebugLogLineN(logServerUDP, CFormat(wxT("Expected encrypted packet, but received unencrytped from server %s, UDPKey %u, Challenge: %u")) % pServer->GetListName() % pServer->GetServerKeyUDP() % pServer->GetChallenge()); 85 } else { 86 AddDebugLogLineN(logServerUDP, CFormat(wxT("Received encrypted packet from server %s, UDPKey %u, Challenge: %u")) % pServer->GetListName() % pServer->GetServerKeyUDP() % pServer->GetChallenge()); 87 } 88 } 89 90 uint8 protocol = pBuffer[0]; 91 uint8 opcode = pBuffer[1]; 92 93 if (protocol == OP_EDONKEYPROT) { 94 CMemFile data(pBuffer + 2, nPayLoadLen - 2); 95 ProcessPacket(data, opcode, serverip, serverport); 96 } else { 97 AddDebugLogLineN(logServerUDP, CFormat(wxT("Received invalid packet, protocol (0x%x) and opcode (0x%x)")) % protocol % opcode); 98 99 theStats::AddDownOverheadOther(length); 100 } 101} 102 103 104void CServerUDPSocket::ProcessPacket(CMemFile& packet, uint8 opcode, uint32 ip, uint16 port) 105{ 106 CServer* update = theApp->serverlist->GetServerByIPUDP(ip, port, true); 107 unsigned size = packet.GetLength(); 108 109 theStats::AddDownOverheadOther(size); 110 AddDebugLogLineN( logServerUDP, 111 CFormat( wxT("Received UDP server packet from %s:%u, opcode (0x%x)")) % 112 Uint32toStringIP(ip) % port % opcode ); 113 114 try { 115 // Imported: OP_GLOBSEARCHRES, OP_GLOBFOUNDSOURCES & OP_GLOBSERVSTATRES 116 // This makes Server UDP Flags to be set correctly so we use less bandwith on asking servers for sources 117 // Also we process Search results and Found sources correctly now on 16.40 behaviour. 118 switch(opcode){ 119 case OP_GLOBSEARCHRES: { 120 121 // process all search result packets 122 123 do{ 124 theApp->searchlist->ProcessUDPSearchAnswer(packet, true, ip, port - 4); 125 126 if (packet.GetPosition() + 2 < size) { 127 // An additional packet? 128 uint8 protocol = packet.ReadUInt8(); 129 uint8 new_opcode = packet.ReadUInt8(); 130 131 if (protocol != OP_EDONKEYPROT || new_opcode != OP_GLOBSEARCHRES) { 132 AddDebugLogLineC( logServerUDP, 133 wxT("Server search reply got additional bogus bytes.") ); 134 break; 135 } else { 136 AddDebugLogLineC( logServerUDP, 137 wxT("Got server search reply with additional packet.") ); 138 } 139 } 140 141 } while (packet.GetPosition()+2 < size); 142 143 break; 144 } 145 case OP_GLOBFOUNDSOURCES:{ 146 // process all source packets 147 do { 148 CMD4Hash fileid = packet.ReadHash(); 149 if (CPartFile* file = theApp->downloadqueue->GetFileByID(fileid)) { 150 file->AddSources(packet, ip, port-4, SF_REMOTE_SERVER, false); 151 } else { 152 AddDebugLogLineC( logServerUDP, wxT("Sources received for unknown file") ); 153 // skip sources for that file 154 uint8 count = packet.ReadUInt8(); 155 packet.Seek(count*(4+2), wxFromCurrent); 156 } 157 158 if (packet.GetPosition()+2 < size) { 159 // An additional packet? 160 uint8 protocol = packet.ReadUInt8(); 161 uint8 new_opcode = packet.ReadUInt8(); 162 163 if (protocol != OP_EDONKEYPROT || new_opcode != OP_GLOBFOUNDSOURCES) { 164 AddDebugLogLineC( logServerUDP, 165 wxT("Server sources reply got additional bogus bytes.") ); 166 break; 167 } 168 } 169 } while ((packet.GetPosition() + 2) < size); 170 break; 171 } 172 173 case OP_GLOBSERVSTATRES:{ 174 // Reviewed with 0.47c 175 if (!update) { 176 throw wxString(CFormat(wxT("Unknown server on a OP_GLOBSERVSTATRES packet (%s:%d)")) % Uint32toStringIP(ip) % (port-4)); 177 } 178 if (size < 12) { 179 throw wxString(CFormat(wxT("Invalid OP_GLOBSERVSTATRES packet (size=%u)")) % size); 180 } 181 uint32 challenge = packet.ReadUInt32(); 182 if (challenge != update->GetChallenge()) { 183 throw wxString(CFormat(wxT("Invalid challenge on OP_GLOBSERVSTATRES packet (0x%x != 0x%x)")) % challenge % update->GetChallenge()); 184 } 185 186 update->SetChallenge(0); 187 update->SetCryptPingReplyPending(false); 188 uint32 tNow = ::GetTickCount(); 189 update->SetLastPingedTime(tNow - (rand() % HR2S(1))); // if we used Obfuscated ping, we still need to reset the time properly 190 191 uint32 cur_user = packet.ReadUInt32(); 192 uint32 cur_files = packet.ReadUInt32(); 193 uint32 cur_maxusers = 0; 194 uint32 cur_softfiles = 0; 195 uint32 cur_hardfiles = 0; 196 uint32 uUDPFlags = 0; 197 uint32 uLowIDUsers = 0; 198 uint32 dwServerUDPKey = 0; 199 uint16 nTCPObfuscationPort = 0; 200 uint16 nUDPObfuscationPort = 0; 201 if( size >= 16 ){ 202 cur_maxusers = packet.ReadUInt32(); 203 if( size >= 24 ){ 204 cur_softfiles = packet.ReadUInt32(); 205 cur_hardfiles = packet.ReadUInt32(); 206 if( size >= 28 ){ 207 uUDPFlags = packet.ReadUInt32(); 208 if( size >= 32 ){ 209 uLowIDUsers = packet.ReadUInt32(); 210 if (size >= 40) { 211 nUDPObfuscationPort = packet.ReadUInt16(); 212 nTCPObfuscationPort = packet.ReadUInt16(); 213 dwServerUDPKey = packet.ReadUInt32(); 214 } 215 } 216 } 217 } 218 } 219 220 update->SetPing( ::GetTickCount() - update->GetLastPinged() ); 221 update->SetUserCount( cur_user ); 222 update->SetFileCount( cur_files ); 223 update->SetMaxUsers( cur_maxusers ); 224 update->SetSoftFiles( cur_softfiles ); 225 update->SetHardFiles( cur_hardfiles ); 226 update->SetUDPFlags( uUDPFlags ); 227 update->SetLowIDUsers( uLowIDUsers ); 228 update->SetServerKeyUDP(dwServerUDPKey); 229 update->SetObfuscationPortTCP(nTCPObfuscationPort); 230 update->SetObfuscationPortUDP(nUDPObfuscationPort); 231 232 Notify_ServerRefresh( update ); 233 234 update->SetLastDescPingedCount(false); 235 if (update->GetLastDescPingedCount() < 2) { 236 // eserver 16.45+ supports a new OP_SERVER_DESC_RES answer, if the OP_SERVER_DESC_REQ contains a uint32 237 // challenge, the server returns additional info with OP_SERVER_DESC_RES. To properly distinguish the 238 // old and new OP_SERVER_DESC_RES answer, the challenge has to be selected carefully. The first 2 bytes 239 // of the challenge (in network byte order) MUST NOT be a valid string-len-int16! 240 CPacket* sendpacket = new CPacket(OP_SERVER_DESC_REQ, 4, OP_EDONKEYPROT); 241 uint32 uDescReqChallenge = ((uint32)GetRandomUint16() << 16) + INV_SERV_DESC_LEN; // 0xF0FF = an 'invalid' string length. 242 update->SetDescReqChallenge(uDescReqChallenge); 243 sendpacket->CopyUInt32ToDataBuffer(uDescReqChallenge); 244 //theStats.AddUpDataOverheadServer(packet->size); 245 AddDebugLogLineN(logServerUDP, CFormat(wxT(">>> Sending OP__ServDescReq to server %s:%u, challenge %08x\n")) % update->GetAddress() % update->GetPort() % uDescReqChallenge); 246 theApp->serverconnect->SendUDPPacket(sendpacket, update, true); 247 } else { 248 update->SetLastDescPingedCount(true); 249 } 250 251 theApp->ShowUserCount(); 252 break; 253 } 254 case OP_SERVER_DESC_RES:{ 255 // Reviewed with 0.47c 256 if (!update) { 257 throw(wxString(wxT("Received OP_SERVER_DESC_RES from an unknown server"))); 258 } 259 260 // old packet: <name_len 2><name name_len><desc_len 2 desc_en> 261 // new packet: <challenge 4><taglist> 262 // 263 // NOTE: To properly distinguish between the two packets which are both useing the same opcode... 264 // the first two bytes of <challenge> (in network byte order) have to be an invalid <name_len> at least. 265 266 uint16 Len = packet.ReadUInt16(); 267 268 packet.Seek(-2, wxFromCurrent); // Step back 269 270 if (size >= 8 && Len == INV_SERV_DESC_LEN) { 271 272 if (update->GetDescReqChallenge() != 0 && packet.ReadUInt32() == update->GetDescReqChallenge()) { 273 274 update->SetDescReqChallenge(0); 275 276 uint32 uTags = packet.ReadUInt32(); 277 for (uint32 i = 0; i < uTags; ++i) { 278 CTag tag(packet, update->GetUnicodeSupport()); 279 switch (tag.GetNameID()) { 280 case ST_SERVERNAME: 281 update->SetListName(tag.GetStr()); 282 break; 283 case ST_DESCRIPTION: 284 update->SetDescription(tag.GetStr()); 285 break; 286 case ST_DYNIP: 287 update->SetDynIP(tag.GetStr()); 288 break; 289 case ST_VERSION: 290 if (tag.IsStr()) { 291 update->SetVersion(tag.GetStr()); 292 } else if (tag.IsInt()) { 293 update->SetVersion(CFormat(wxT("%u.%u")) % (tag.GetInt() >> 16) % (tag.GetInt() & 0xFFFF)); 294 } 295 break; 296 case ST_AUXPORTSLIST: 297 update->SetAuxPortsList(tag.GetStr()); 298 break; 299 default: 300 // Unknown tag 301 ; 302 } 303 } 304 } else { 305 // A server sent us a new server description packet (including a challenge) although we did not 306 // ask for it. This may happen, if there are multiple servers running on the same machine with 307 // multiple IPs. If such a server is asked for a description, the server will answer 2 times, 308 // but with the same IP. 309 // ignore this packet 310 311 } 312 } else { 313 update->SetDescription(packet.ReadString(update->GetUnicodeSupport())); 314 update->SetListName(packet.ReadString(update->GetUnicodeSupport())); 315 } 316 break; 317 } 318 default: 319 AddDebugLogLineC(logServerUDP, CFormat(wxT("Unknown Server UDP opcode %x")) % opcode); 320 } 321 } catch (const wxString& DEBUG_ONLY(error)) { 322 AddDebugLogLineN(logServerUDP, wxT("Error while processing incoming UDP Packet: ") + error); 323 } catch (const CInvalidPacket& DEBUG_ONLY(error)) { 324 AddDebugLogLineN(logServerUDP, wxT("Invalid UDP packet encountered: ") + error.what()); 325 } catch (const CEOFException& DEBUG_ONLY(e)) { 326 AddDebugLogLineN(logServerUDP, wxT("IO error while processing incoming UDP Packet: ") + e.what()); 327 } 328 329 if (update) { 330 update->ResetFailedCount(); 331 Notify_ServerRefresh( update ); 332 } 333 334} 335 336void CServerUDPSocket::OnReceiveError(int errorCode, uint32 ip, uint16 port) 337{ 338 CMuleUDPSocket::OnReceiveError(errorCode, ip, port); 339 340 // If we are not currently pinging this server, increase the failure counter 341 CServer* pServer = theApp->serverlist->GetServerByIPUDP(ip, port, true); 342 if (pServer && !pServer->GetCryptPingReplyPending() && GetTickCount() - pServer->GetLastPinged() >= SEC2MS(30)) { 343 pServer->AddFailedCount(); 344 Notify_ServerRefresh(pServer); 345 } 346 347} 348 349void CServerUDPSocket::SendPacket(CPacket* packet, CServer* host, bool delPacket, bool rawpacket, uint16 port_offset) 350{ 351 ServerUDPPacket item = { NULL, 0, 0, wxEmptyString }; 352 353 if (host->HasDynIP()) { 354 item.addr = host->GetDynIP(); 355 } else { 356 item.ip = host->GetIP(); 357 } 358 359 // 4 (default) for standard sending, 12 for obfuscated ping, that's all for now. 360 // Might be changed if encrypted bellow, so don't move it. 361 item.port = host->GetPort() + port_offset; 362 363 // We might need to encrypt the packet for this server. 364 if (!rawpacket && thePrefs::IsServerCryptLayerUDPEnabled() && host->GetServerKeyUDP() != 0 && host->SupportsObfuscationUDP()) { 365 uint16 uRawPacketSize = packet->GetPacketSize() + 2; 366 byte* pRawPacket = new byte[uRawPacketSize]; 367 memcpy(pRawPacket, packet->GetUDPHeader(), 2); 368 memcpy(pRawPacket + 2, packet->GetDataBuffer(), packet->GetPacketSize()); 369 370 uRawPacketSize = CEncryptedDatagramSocket::EncryptSendServer(&pRawPacket, uRawPacketSize, host->GetServerKeyUDP()); 371 AddDebugLogLineN(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()); 372 item.port = host->GetObfuscationPortUDP(); 373 374 CMemFile encryptedpacket(pRawPacket + 2, uRawPacketSize - 2); 375 item.packet = new CPacket(encryptedpacket, pRawPacket[0], pRawPacket[1]); 376 delete[] pRawPacket; 377 378 if (delPacket) { 379 delete packet; 380 } 381 382 } else { 383 AddDebugLogLineN(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()); 384 if (delPacket) { 385 item.packet = packet; 386 } else { 387 item.packet = new CPacket(*packet); 388 } 389 } 390 391 392 m_queue.push_back(item); 393 394 // If there is more than one item in the queue, 395 // then we are already waiting for a dns query. 396 if (m_queue.size() == 1) { 397 SendQueue(); 398 } 399} 400 401 402void CServerUDPSocket::SendQueue() 403{ 404 while (m_queue.size()) { 405 ServerUDPPacket item = m_queue.front(); 406 CPacket* packet = item.packet; 407 408 // Do we need to do a DNS lookup before sending? 409 wxASSERT(item.ip || !item.addr.IsEmpty()); 410 if (!item.addr.IsEmpty()) { 411 // This not an ip but a hostname. Resolve it. 412 CServer* update = theApp->serverlist->GetServerByAddress(item.addr, item.port); 413 if (update) { 414 if (update->GetLastDNSSolve() + DNS_SOLVE_TIME < ::GetTickCount64()) { 415 // Its time for a new check. 416 CAsyncDNS* dns = new CAsyncDNS(item.addr, DNS_UDP, theApp, this); 417 if ((dns->Create() != wxTHREAD_NO_ERROR) || (dns->Run() != wxTHREAD_NO_ERROR)) { 418 // Not much we can do here, just drop the packet. 419 m_queue.pop_front(); 420 continue; 421 } 422 update->SetDNSError(false); 423 update->SetLastDNSSolve(::GetTickCount64()); 424 // Wait for the DNS query to be resolved 425 return; 426 } else { 427 // It has been checked recently, don't re-check yet. 428 if (update->GetDNSError()) { 429 // Drop the packet, dns failed last time 430 AddDebugLogLineN(logServerUDP, wxT("Trying to send a UDP packet to a server host that failed DNS: ")+item.addr); 431 m_queue.pop_front(); 432 continue; 433 } else { 434 // It has been solved or is solving. 435 if (update->GetIP()) { 436 // It has been solved and ok 437 AddDebugLogLineN(logServerUDP, wxT("Sending a UDP packet to a resolved DNS server host: ")+item.addr); 438 // Update the item IP 439 item.ip = update->GetIP(); 440 // It'll fallback to the sending. 441 } else { 442 AddDebugLogLineN(logServerUDP, wxT("Trying to send a UDP packet to a server host that is checking DNS: ")+item.addr); 443 // Let the packet queued, and wait for resolution 444 // Meanwhile, send other packets. 445 m_queue.pop_front(); 446 m_queue.push_back(item); 447 return; 448 } 449 } 450 } 451 } else { 452 AddDebugLogLineN(logServerUDP, wxT("Trying to send a UDP packet to a server host that is not on serverlist")); 453 // Not much we can do here, just drop the packet. 454 m_queue.pop_front(); 455 continue; 456 } 457 } 458 459 CServer* update = theApp->serverlist->GetServerByIPUDP(item.ip, item.port, true); 460 if (update) { 461 AddDebugLogLineN(logServerUDP, wxT("Sending a UDP packet to a server: ")+update->GetAddress()); 462 // Don't encrypt, as this is already either encrypted or refused to encrypt. 463 CMuleUDPSocket::SendPacket(packet, item.ip, item.port, false, NULL, false, 0); 464 } else { 465 AddDebugLogLineN(logServerUDP, wxT("Sending a UDP packet to a server no in serverlist: ")+Uint32_16toStringIP_Port(item.ip,item.port)); 466 } 467 468 m_queue.pop_front(); 469 } 470} 471 472 473void CServerUDPSocket::OnHostnameResolved(uint32 ip) 474{ 475 wxCHECK_RET(m_queue.size(), wxT("DNS query returned, but no packets are queued.")); 476 477 ServerUDPPacket item = m_queue.front(); 478 wxCHECK_RET(!item.ip && !item.addr.IsEmpty(), wxT("DNS resolution not expected.")); 479 480 /* An asynchronous database routine completed. */ 481 CServer* update = theApp->serverlist->GetServerByAddress(item.addr, item.port); 482 if (ip == 0) { 483 update->SetDNSError(true); 484 m_queue.pop_front(); 485 } else { 486 if (update) { 487 update->SetID(ip); 488 } 489 490 item.addr.Clear(); 491 item.ip = ip; 492 } 493 494 SendQueue(); 495} 496// File_checked_for_headers 497