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