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 "ClientUDPSocket.h"	// Interface declarations
28 
29 #include <protocol/Protocols.h>
30 #include <protocol/ed2k/Client2Client/TCP.h> // Sometimes we reply with TCP packets.
31 #include <protocol/ed2k/Client2Client/UDP.h>
32 #include <protocol/kad2/Client2Client/UDP.h>
33 #include <common/EventIDs.h>
34 #include <common/Format.h>	// Needed for CFormat
35 
36 #include "Preferences.h"		// Needed for CPreferences
37 #include "PartFile.h"			// Needed for CPartFile
38 #include "updownclient.h"		// Needed for CUpDownClient
39 #include "UploadQueue.h"		// Needed for CUploadQueue
40 #include "Packet.h"				// Needed for CPacket
41 #include "SharedFileList.h"		// Needed for CSharedFileList
42 #include "DownloadQueue.h"		// Needed for CDownloadQueue
43 #include "Statistics.h"			// Needed for theStats
44 #include "amule.h"				// Needed for theApp
45 #include "ClientList.h"			// Needed for clientlist (buddy support)
46 #include "ClientTCPSocket.h"	// Needed for CClientTCPSocket
47 #include "MemFile.h"			// Needed for CMemFile
48 #include "Logger.h"
49 #include "kademlia/kademlia/Kademlia.h"
50 #include "kademlia/utils/KadUDPKey.h"
51 #include <zlib.h>
52 #include "EncryptedDatagramSocket.h"
53 
54 //
55 // CClientUDPSocket -- Extended eMule UDP socket
56 //
57 
CClientUDPSocket(const amuleIPV4Address & address,const CProxyData * ProxyData)58 CClientUDPSocket::CClientUDPSocket(const amuleIPV4Address& address, const CProxyData* ProxyData)
59 	: CMuleUDPSocket(wxT("Client UDP-Socket"), ID_CLIENTUDPSOCKET_EVENT, address, ProxyData)
60 {
61 	if (!thePrefs::IsUDPDisabled()) {
62 		Open();
63 	}
64 }
65 
66 
OnReceive(int errorCode)67 void CClientUDPSocket::OnReceive(int errorCode)
68 {
69 	CMuleUDPSocket::OnReceive(errorCode);
70 
71 	// TODO: A better solution is needed.
72 	if (thePrefs::IsUDPDisabled()) {
73 		Close();
74 	}
75 }
76 
77 
OnPacketReceived(uint32 ip,uint16 port,byte * buffer,size_t length)78 void CClientUDPSocket::OnPacketReceived(uint32 ip, uint16 port, byte* buffer, size_t length)
79 {
80 	wxCHECK_RET(length >= 2, wxT("Invalid packet."));
81 
82 	uint8_t *decryptedBuffer;
83 	uint32_t receiverVerifyKey;
84 	uint32_t senderVerifyKey;
85 	int packetLen = CEncryptedDatagramSocket::DecryptReceivedClient(buffer, length, &decryptedBuffer, ip, &receiverVerifyKey, &senderVerifyKey);
86 
87 	uint8_t protocol = decryptedBuffer[0];
88 	uint8_t opcode	 = decryptedBuffer[1];
89 
90 	if (packetLen >= 1) {
91 		try {
92 			switch (protocol) {
93 				case OP_EMULEPROT:
94 					ProcessPacket(decryptedBuffer + 2, packetLen - 2, opcode, ip, port);
95 					break;
96 
97 				case OP_KADEMLIAHEADER:
98 					theStats::AddDownOverheadKad(length);
99 					if (packetLen >= 2) {
100 						Kademlia::CKademlia::ProcessPacket(decryptedBuffer, packetLen, wxUINT32_SWAP_ALWAYS(ip), port, (Kademlia::CPrefs::GetUDPVerifyKey(ip) == receiverVerifyKey), Kademlia::CKadUDPKey(senderVerifyKey, theApp->GetPublicIP(false)));
101 					} else {
102 						throw wxString(wxT("Kad packet too short"));
103 					}
104 					break;
105 
106 				case OP_KADEMLIAPACKEDPROT:
107 					theStats::AddDownOverheadKad(length);
108 					if (packetLen >= 2) {
109 						uint32_t newSize = packetLen * 10 + 300; // Should be enough...
110 						std::vector<uint8_t> unpack(newSize);
111 						uLongf unpackedsize = newSize - 2;
112 						uint16_t result = uncompress(&(unpack[2]), &unpackedsize, decryptedBuffer + 2, packetLen - 2);
113 						if (result == Z_OK) {
114 							AddDebugLogLineN(logClientKadUDP, wxT("Correctly uncompressed Kademlia packet"));
115 							unpack[0] = OP_KADEMLIAHEADER;
116 							unpack[1] = opcode;
117 							Kademlia::CKademlia::ProcessPacket(&(unpack[0]), unpackedsize + 2, wxUINT32_SWAP_ALWAYS(ip), port, (Kademlia::CPrefs::GetUDPVerifyKey(ip) == receiverVerifyKey), Kademlia::CKadUDPKey(senderVerifyKey, theApp->GetPublicIP(false)));
118 						} else {
119 							AddDebugLogLineN(logClientKadUDP, wxT("Failed to uncompress Kademlia packet"));
120 						}
121 					} else {
122 						throw wxString(wxT("Kad packet (compressed) too short"));
123 					}
124 					break;
125 
126 				default:
127 					AddDebugLogLineN(logClientUDP, CFormat(wxT("Unknown opcode on received packet: 0x%x")) % protocol);
128 			}
129 		} catch (const wxString& DEBUG_ONLY(e)) {
130 			AddDebugLogLineN(logClientUDP, wxT("Error while parsing UDP packet: ") + e);
131 		} catch (const CInvalidPacket& DEBUG_ONLY(e)) {
132 			AddDebugLogLineN(logClientUDP, wxT("Invalid UDP packet encountered: ") + e.what());
133 		} catch (const CEOFException& DEBUG_ONLY(e)) {
134 			AddDebugLogLineN(logClientUDP, wxT("Malformed packet encountered while parsing UDP packet: ") + e.what());
135 		}
136 	}
137 }
138 
139 
ProcessPacket(byte * packet,int16 size,int8 opcode,uint32 host,uint16 port)140 void CClientUDPSocket::ProcessPacket(byte* packet, int16 size, int8 opcode, uint32 host, uint16 port)
141 {
142 	switch (opcode) {
143 		case OP_REASKCALLBACKUDP: {
144 			AddDebugLogLineN( logClientUDP, wxT("Client UDP socket; OP_REASKCALLBACKUDP") );
145 			theStats::AddDownOverheadOther(size);
146 			CUpDownClient* buddy = theApp->clientlist->GetBuddy();
147 			if( buddy ) {
148 				if( size < 17 || buddy->GetSocket() == NULL ) {
149 					break;
150 				}
151 				if (!md4cmp(packet, buddy->GetBuddyID())) {
152 					/*
153 						The packet has an initial 16 bytes key for the buddy.
154 						This is currently unused, so to make the transformation
155 						we discard the first 10 bytes below and then overwrite
156 						the other 6 with ip/port.
157 					*/
158 					CMemFile mem_packet(packet+10,size-10);
159 					// Change the ip and port while leaving the rest untouched
160 					mem_packet.Seek(0,wxFromStart);
161 					mem_packet.WriteUInt32(host);
162 					mem_packet.WriteUInt16(port);
163 					CPacket* response = new CPacket(mem_packet, OP_EMULEPROT, OP_REASKCALLBACKTCP);
164 					AddDebugLogLineN( logClientUDP, wxT("Client UDP socket: send OP_REASKCALLBACKTCP") );
165 					theStats::AddUpOverheadFileRequest(response->GetPacketSize());
166 					buddy->GetSocket()->SendPacket(response);
167 				}
168 			}
169 			break;
170 		}
171 		case OP_REASKFILEPING: {
172 			AddDebugLogLineN( logClientUDP, wxT("Client UDP socket: OP_REASKFILEPING") );
173 			theStats::AddDownOverheadFileRequest(size);
174 
175 			CMemFile data_in(packet, size);
176 			CMD4Hash reqfilehash = data_in.ReadHash();
177 			CKnownFile* reqfile = theApp->sharedfiles->GetFileByID(reqfilehash);
178 			bool bSenderMultipleIpUnknown = false;
179 			CUpDownClient* sender = theApp->uploadqueue->GetWaitingClientByIP_UDP(host, port, true, &bSenderMultipleIpUnknown);
180 
181 			if (!reqfile) {
182 				CPacket* response = new CPacket(OP_FILENOTFOUND,0,OP_EMULEPROT);
183 				theStats::AddUpOverheadFileRequest(response->GetPacketSize());
184 				if (sender) {
185 					SendPacket(response, host, port, sender->ShouldReceiveCryptUDPPackets(), sender->GetUserHash().GetHash(), false, 0);
186 				} else {
187 					SendPacket(response, host, port, false, NULL, false, 0);
188 				}
189 
190 				break;
191 			}
192 
193 			if (sender){
194 				sender->CheckForAggressive();
195 
196 				//Make sure we are still thinking about the same file
197 				if (reqfilehash == sender->GetUploadFileID()) {
198 					sender->AddAskedCount();
199 					sender->SetUDPPort(port);
200 					sender->SetLastUpRequest();
201 
202 					if (sender->GetUDPVersion() > 3) {
203 						sender->ProcessExtendedInfo(&data_in, reqfile);
204 					} else  if (sender->GetUDPVersion() > 2) {
205 						uint16 nCompleteCountLast = sender->GetUpCompleteSourcesCount();
206 						uint16 nCompleteCountNew = data_in.ReadUInt16();
207 						sender->SetUpCompleteSourcesCount(nCompleteCountNew);
208 						if (nCompleteCountLast != nCompleteCountNew) {
209 							reqfile->UpdatePartsInfo();
210 						}
211 					}
212 
213 					CMemFile data_out(128);
214 					if(sender->GetUDPVersion() > 3) {
215 						if (reqfile->IsPartFile()) {
216 							static_cast<CPartFile*>(reqfile)->WritePartStatus(&data_out);
217 						} else {
218 							data_out.WriteUInt16(0);
219 						}
220 					}
221 
222 					data_out.WriteUInt16(sender->GetUploadQueueWaitingPosition());
223 					CPacket* response = new CPacket(data_out, OP_EMULEPROT, OP_REASKACK);
224 					theStats::AddUpOverheadFileRequest(response->GetPacketSize());
225 					AddDebugLogLineN( logClientUDP, wxT("Client UDP socket: OP_REASKACK to ") + sender->GetFullIP());
226 					SendPacket(response, host, port, sender->ShouldReceiveCryptUDPPackets(), sender->GetUserHash().GetHash(), false, 0);
227 				} else {
228 					AddDebugLogLineN( logClientUDP, wxT("Client UDP socket; ReaskFilePing; reqfile does not match") );
229 				}
230 			} else {
231 				if (!bSenderMultipleIpUnknown) {
232 					if ((theStats::GetWaitingUserCount() + 50) > thePrefs::GetQueueSize()) {
233 						CPacket* response = new CPacket(OP_QUEUEFULL,0,OP_EMULEPROT);
234 						theStats::AddUpOverheadFileRequest(response->GetPacketSize());
235 						SendPacket(response,host,port, false, NULL, false, 0); // we cannot answer this one encrypted since we dont know this client
236 					}
237 				} else {
238 					AddDebugLogLineN(logClientUDP, CFormat(wxT("UDP Packet received - multiple clients with the same IP but different UDP port found. Possible UDP Portmapping problem, enforcing TCP connection. IP: %s, Port: %u")) % Uint32toStringIP(host) % port);
239 				}
240 			}
241 			break;
242 		}
243 		case OP_QUEUEFULL: {
244 			AddDebugLogLineN( logClientUDP, wxT("Client UDP socket: OP_QUEUEFULL") );
245 			theStats::AddDownOverheadOther(size);
246 			CUpDownClient* sender = theApp->downloadqueue->GetDownloadClientByIP_UDP(host,port);
247 			if (sender) {
248 				sender->SetRemoteQueueFull(true);
249 				sender->UDPReaskACK(0);
250 			}
251 			break;
252 		}
253 		case OP_REASKACK: {
254 			theStats::AddDownOverheadFileRequest(size);
255 			CUpDownClient* sender = theApp->downloadqueue->GetDownloadClientByIP_UDP(host,port);
256 			if (sender) {
257 				CMemFile data_in(packet,size);
258 				if ( sender->GetUDPVersion() > 3 ) {
259 					sender->ProcessFileStatus(true, &data_in, sender->GetRequestFile());
260 				}
261 				uint16 nRank = data_in.ReadUInt16();
262 				sender->SetRemoteQueueFull(false);
263 				sender->UDPReaskACK(nRank);
264 			}
265 			break;
266 		}
267 		case OP_FILENOTFOUND: {
268 			AddDebugLogLineN( logClientUDP, wxT("Client UDP socket: OP_FILENOTFOUND") );
269 			theStats::AddDownOverheadFileRequest(size);
270 			CUpDownClient* sender = theApp->downloadqueue->GetDownloadClientByIP_UDP(host,port);
271 			if (sender){
272 				sender->UDPReaskFNF(); // may delete 'sender'!
273 				sender = NULL;
274 			}
275 			break;
276 		}
277 		case OP_DIRECTCALLBACKREQ:
278 		{
279 			AddDebugLogLineN( logClientUDP, wxT("Client UDP socket: OP_DIRECTCALLBACKREQ") );
280 			theStats::AddDownOverheadOther(size);
281 			if (!theApp->clientlist->AllowCallbackRequest(host)) {
282 				AddDebugLogLineN(logClientUDP, wxT("Ignored DirectCallback Request because this IP (") + Uint32toStringIP(host) + wxT(") has sent too many requests within a short time"));
283 				break;
284 			}
285 			// do we accept callbackrequests at all?
286 			if (Kademlia::CKademlia::IsRunning() && Kademlia::CKademlia::IsFirewalled()) {
287 				theApp->clientlist->AddTrackCallbackRequests(host);
288 				CMemFile data(packet, size);
289 				uint16_t remoteTCPPort = data.ReadUInt16();
290 				CMD4Hash userHash(data.ReadHash());
291 				uint8_t connectOptions = data.ReadUInt8();
292 				CUpDownClient* requester = NULL;
293 				CClientList::SourceList clients = theApp->clientlist->GetClientsByHash(userHash);
294 				for (CClientList::SourceList::iterator it = clients.begin(); it != clients.end(); ++it) {
295 					if ((host == 0 || it->GetIP() == host) && (remoteTCPPort == 0 || it->GetUserPort() == remoteTCPPort)) {
296 						requester = it->GetClient();
297 						break;
298 					}
299 				}
300 				if (requester == NULL) {
301 					requester = new CUpDownClient(remoteTCPPort, host, 0, 0, NULL, true, true);
302 					requester->SetUserHash(CMD4Hash(userHash));
303 					theApp->clientlist->AddClient(requester);
304 				}
305 				requester->SetConnectOptions(connectOptions, true, false);
306 				requester->SetDirectUDPCallbackSupport(false);
307 				requester->SetIP(host);
308 				requester->SetUserPort(remoteTCPPort);
309 				AddDebugLogLineN(logClientUDP, wxT("Accepting incoming DirectCallback Request from ") + Uint32toStringIP(host));
310 				requester->TryToConnect();
311 			} else {
312 				AddDebugLogLineN(logClientUDP, wxT("Ignored DirectCallback Request because we do not accept Direct Callbacks at all (") + Uint32toStringIP(host) + wxT(")"));
313 			}
314 			break;
315 		}
316 		default:
317 			theStats::AddDownOverheadOther(size);
318 	}
319 }
320 // File_checked_for_headers
321