2 // This file is part of the aMule Project.
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 )
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
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.
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
49 #include "kademlia/kademlia/Kademlia.h"
50 #include "kademlia/utils/KadUDPKey.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()) {
67 void CClientUDPSocket::OnReceive(int errorCode
)
69 CMuleUDPSocket::OnReceive(errorCode
);
71 // TODO: A better solution is needed.
72 if (thePrefs::IsUDPDisabled()) {
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];
94 ProcessPacket(decryptedBuffer
+ 2, packetLen
- 2, opcode
, ip
, port
);
97 case OP_KADEMLIAHEADER
:
98 theStats::AddDownOverheadKad(length
);
100 Kademlia::CKademlia::ProcessPacket(decryptedBuffer
, packetLen
, wxUINT32_SWAP_ALWAYS(ip
), port
, (Kademlia::CPrefs::GetUDPVerifyKey(ip
) == receiverVerifyKey
), Kademlia::CKadUDPKey(senderVerifyKey
, theApp
->GetPublicIP(false)));
102 throw wxString(wxT("Kad packet too short"));
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
;
117 Kademlia::CKademlia::ProcessPacket(&(unpack
[0]), unpackedsize
+ 2, wxUINT32_SWAP_ALWAYS(ip
), port
, (Kademlia::CPrefs::GetUDPVerifyKey(ip
) == receiverVerifyKey
), Kademlia::CKadUDPKey(senderVerifyKey
, theApp
->GetPublicIP(false)));
119 AddDebugLogLineN(logClientKadUDP
, wxT("Failed to uncompress Kademlia packet"));
122 throw wxString(wxT("Kad packet (compressed) too short"));
127 AddDebugLogLineN(logClientUDP
, CFormat(wxT("Unknown opcode on received packet: 0x%x")) % protocol
);
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());
140 void CClientUDPSocket::ProcessPacket(byte
* packet
, int16 size
, int8 opcode
, uint32 host
, uint16 port
)
143 case OP_REASKCALLBACKUDP
: {
144 AddDebugLogLineN( logClientUDP
, wxT("Client UDP socket; OP_REASKCALLBACKUDP") );
145 theStats::AddDownOverheadOther(size
);
146 CUpDownClient
* buddy
= theApp
->clientlist
->GetBuddy();
148 if( size
< 17 || buddy
->GetSocket() == NULL
) {
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 AddDebugLogLineN( logClientUDP
, wxT("Client UDP socket: send OP_REASKCALLBACKTCP") );
165 theStats::AddUpOverheadFileRequest(response
->GetPacketSize());
166 buddy
->GetSocket()->SendPacket(response
);
171 case OP_REASKFILEPING
: {
172 AddDebugLogLineN( 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
);
182 CPacket
* response
= new CPacket(OP_FILENOTFOUND
,0,OP_EMULEPROT
);
183 theStats::AddUpOverheadFileRequest(response
->GetPacketSize());
185 SendPacket(response
, host
, port
, sender
->ShouldReceiveCryptUDPPackets(), sender
->GetUserHash().GetHash(), false, 0);
187 SendPacket(response
, host
, port
, false, NULL
, false, 0);
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
);
218 data_out
.WriteUInt16(0);
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);
228 AddDebugLogLineN( logClientUDP
, wxT("Client UDP socket; ReaskFilePing; reqfile does not match") );
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
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
);
244 AddDebugLogLineN( logClientUDP
, wxT("Client UDP socket: OP_QUEUEFULL") );
245 theStats::AddDownOverheadOther(size
);
246 CUpDownClient
* sender
= theApp
->downloadqueue
->GetDownloadClientByIP_UDP(host
,port
);
248 sender
->SetRemoteQueueFull(true);
249 sender
->UDPReaskACK(0);
254 theStats::AddDownOverheadFileRequest(size
);
255 CUpDownClient
* sender
= theApp
->downloadqueue
->GetDownloadClientByIP_UDP(host
,port
);
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
);
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
);
272 sender
->UDPReaskFNF(); // may delete 'sender'!
277 case OP_DIRECTCALLBACKREQ
:
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"));
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();
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 AddDebugLogLineN(logClientUDP
, wxT("Accepting incoming DirectCallback Request from ") + Uint32toStringIP(host
));
310 requester
->TryToConnect();
312 AddDebugLogLineN(logClientUDP
, wxT("Ignored DirectCallback Request because we do not accept Direct Callbacks at all (") + Uint32toStringIP(host
) + wxT(")"));
317 theStats::AddDownOverheadOther(size
);
320 // File_checked_for_headers