Upstream tarball 20080928
[amule.git] / src / ClientUDPSocket.cpp
blob2d63eaf024af21eef3b56211b8cbd7a1ce685c5f
1 //
2 // This file is part of the aMule Project.
3 //
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 )
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.
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.
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
27 #include "ClientUDPSocket.h" // Interface declarations
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
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"
55 // CClientUDPSocket -- Extended eMule UDP socket
58 CClientUDPSocket::CClientUDPSocket(const amuleIPV4Address& address, const CProxyData* ProxyData)
59 : CMuleUDPSocket(wxT("Client UDP-Socket"), ID_CLIENTUDPSOCKET_EVENT, address, ProxyData)
61 if (!thePrefs::IsUDPDisabled()) {
62 Open();
67 void CClientUDPSocket::OnReceive(int errorCode)
69 CMuleUDPSocket::OnReceive(errorCode);
71 // TODO: A better solution is needed.
72 if (thePrefs::IsUDPDisabled()) {
73 Close();
78 void CClientUDPSocket::OnPacketReceived(uint32 ip, uint16 port, byte* buffer, size_t length)
80 wxCHECK_RET(length >= 2, wxT("Invalid packet."));
82 uint8_t *decryptedBuffer;
83 uint32_t receiverVerifyKey;
84 uint32_t senderVerifyKey;
85 int packetLen = CEncryptedDatagramSocket::DecryptReceivedClient(buffer, length, &decryptedBuffer, ip, &receiverVerifyKey, &senderVerifyKey);
87 uint8_t protocol = decryptedBuffer[0];
88 uint8_t opcode = decryptedBuffer[1];
90 if (packetLen >= 1) {
91 try {
92 switch (protocol) {
93 case OP_EMULEPROT:
94 ProcessPacket(decryptedBuffer + 2, packetLen - 2, opcode, ip, port);
95 break;
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"));
104 break;
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 AddDebugLogLineM(false, 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 AddDebugLogLineM(false, logClientKadUDP, wxT("Failed to uncompress Kademlia packet"));
121 } else {
122 throw wxString(wxT("Kad packet (compressed) too short"));
124 break;
126 default:
127 AddDebugLogLineM(false, logClientUDP, wxString::Format(wxT("Unknown opcode on received packet: 0x%x"), protocol));
129 } catch (const wxString& e) {
130 AddDebugLogLineM(false, logClientUDP, wxT("Error while parsing UDP packet: ") + e);
131 } catch (const CInvalidPacket& e) {
132 AddDebugLogLineM(false, logClientUDP, wxT("Invalid UDP packet encountered: ") + e.what());
133 } catch (const CEOFException& e) {
134 AddDebugLogLineM(false, logClientUDP, wxT("Malformed packet encountered while parsing UDP packet: ") + e.what());
140 void CClientUDPSocket::ProcessPacket(byte* packet, int16 size, int8 opcode, uint32 host, uint16 port)
142 switch (opcode) {
143 case OP_REASKCALLBACKUDP: {
144 AddDebugLogLineM( false, 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;
151 if (!md4cmp(packet, buddy->GetBuddyID())) {
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.
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 AddDebugLogLineM( false, logClientUDP, wxT("Client UDP socket: send OP_REASKCALLBACKTCP") );
165 theStats::AddUpOverheadFileRequest(response->GetPacketSize());
166 buddy->GetSocket()->SendPacket(response);
169 break;
171 case OP_REASKFILEPING: {
172 AddDebugLogLineM( false, logClientUDP, wxT("Client UDP socket: OP_REASKFILEPING") );
173 theStats::AddDownOverheadFileRequest(size);
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);
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);
190 break;
193 if (sender){
194 sender->CheckForAggressive();
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();
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();
213 CMemFile data_out(128);
214 if(sender->GetUDPVersion() > 3) {
215 if (reqfile->IsPartFile()) {
216 ((CPartFile*)reqfile)->WritePartStatus(&data_out);
217 } else {
218 data_out.WriteUInt16(0);
222 data_out.WriteUInt16(theApp->uploadqueue->GetWaitingPosition(sender));
223 CPacket* response = new CPacket(data_out, OP_EMULEPROT, OP_REASKACK);
224 theStats::AddUpOverheadFileRequest(response->GetPacketSize());
225 AddDebugLogLineM( false, 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 AddDebugLogLineM( false, logClientUDP, wxT("Client UDP socket; ReaskFilePing; reqfile does not match") );
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
237 } else {
238 AddDebugLogLineM(false, 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);
241 break;
243 case OP_QUEUEFULL: {
244 AddDebugLogLineM( false, 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);
251 break;
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());
261 uint16 nRank = data_in.ReadUInt16();
262 sender->SetRemoteQueueFull(false);
263 sender->UDPReaskACK(nRank);
265 break;
267 case OP_FILENOTFOUND: {
268 AddDebugLogLineM( false, 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;
275 break;
277 case OP_DIRECTCALLBACKREQ:
279 AddDebugLogLineM( false, logClientUDP, wxT("Client UDP socket: OP_DIRECTCALLBACKREQ") );
280 theStats::AddDownOverheadOther(size);
281 if (!theApp->clientlist->AllowCallbackRequest(host)) {
282 AddDebugLogLineM(false, logClientUDP, wxT("Ignored DirectCallback Request because this IP (") + Uint32toStringIP(host) + wxT(") has sent too many request within a short time"));
283 break;
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;
297 break;
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);
305 requester->SetConnectOptions(connectOptions, true, false);
306 requester->SetDirectUDPCallbackSupport(false);
307 requester->SetIP(host);
308 requester->SetUserPort(remoteTCPPort);
309 //TODO LOGREMOVE
310 //AddDebugLogLineM(false, logClientUDP, wxT("Accepting incoming DirectCallbackRequest from ") + requester->DbgGetClientInfo());
311 requester->TryToConnect();
312 } else {
313 AddDebugLogLineM(false, logClientUDP, wxT("Ignored DirectCallback Request because we do not accept DirectCall backs at all (") + Uint32toStringIP(host) + wxT(")"));
315 break;
317 default:
318 theStats::AddDownOverheadOther(size);
321 // File_checked_for_headers