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