2 // This file is part of the aMule Project.
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002 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 <common/EventIDs.h>
33 #include <common/Format.h> // Needed for CFormat
35 #include "Preferences.h" // Needed for CPreferences
36 #include "PartFile.h" // Needed for CPartFile
37 #include "updownclient.h" // Needed for CUpDownClient
38 #include "UploadQueue.h" // Needed for CUploadQueue
39 #include "Packet.h" // Needed for CPacket
40 #include "SharedFileList.h" // Needed for CSharedFileList
41 #include "DownloadQueue.h" // Needed for CDownloadQueue
42 #include "Statistics.h" // Needed for theStats
43 #include "amule.h" // Needed for theApp
44 #include "ClientList.h" // Needed for clientlist (buddy support)
45 #include "ClientTCPSocket.h" // Needed for CClientTCPSocket
46 #include "MemFile.h" // Needed for CMemFile
48 #include "kademlia/kademlia/Kademlia.h"
52 // CClientUDPSocket -- Extended eMule UDP socket
55 CClientUDPSocket::CClientUDPSocket(const amuleIPV4Address
& address
, const CProxyData
* ProxyData
)
56 : CMuleUDPSocket(wxT("Client UDP-Socket"), ID_CLIENTUDPSOCKET_EVENT
, address
, ProxyData
)
58 if (!thePrefs::IsUDPDisabled()) {
64 void CClientUDPSocket::OnReceive(int errorCode
)
66 CMuleUDPSocket::OnReceive(errorCode
);
68 // TODO: A better solution is needed.
69 if (thePrefs::IsUDPDisabled()) {
75 void CClientUDPSocket::OnPacketReceived(uint32 ip
, uint16 port
, byte
* buffer
, size_t length
)
77 wxCHECK_RET(length
>= 2, wxT("Invalid packet."));
79 uint8 protocol
= buffer
[0];
80 uint8 opcode
= buffer
[1];
85 ProcessPacket(buffer
+ 2,length
- 2, opcode
, ip
, port
);
88 case OP_KADEMLIAHEADER
:
89 theStats::AddDownOverheadKad(length
);
90 Kademlia::CKademlia::ProcessPacket(buffer
, length
, wxUINT32_SWAP_ALWAYS(ip
), port
);
93 case OP_KADEMLIAPACKEDPROT
: {
94 theStats::AddDownOverheadKad(length
);
95 uint32 nNewSize
= length
*10+300; // Should be enough...
96 std::vector
<byte
> unpack(nNewSize
);
97 uLongf unpackedsize
= nNewSize
-2;
98 uint16 result
= uncompress(&(unpack
[2]), &unpackedsize
, buffer
+ 2, length
-2);
100 unpack
[0] = OP_KADEMLIAHEADER
;
102 Kademlia::CKademlia::ProcessPacket(&(unpack
[0]), unpackedsize
+ 2, wxUINT32_SWAP_ALWAYS(ip
), port
);
104 AddDebugLogLineM(false, logClientKadUDP
, wxT("Failed to uncompress Kademlia packet"));
109 AddDebugLogLineM(false, logClientUDP
, wxString::Format(wxT("Unknown opcode on received packet: 0x%x"), protocol
));
111 } catch (const wxString
& e
) {
112 AddDebugLogLineM(false, logClientUDP
, wxT("Error while parsing UDP packet: ") + e
);
113 } catch (const CInvalidPacket
& e
) {
114 AddDebugLogLineM(false, logClientUDP
, wxT("Invalid UDP packet encountered: ") + e
.what());
115 } catch (const CEOFException
& e
) {
116 AddDebugLogLineM(false, logClientUDP
, wxT("Malformed packet encountered while parsing UDP packet: ") + e
.what());
121 void CClientUDPSocket::ProcessPacket(byte
* packet
, int16 size
, int8 opcode
, uint32 host
, uint16 port
)
124 case OP_REASKCALLBACKUDP
: {
125 AddDebugLogLineM( false, logClientUDP
, wxT("Client UDP socket; OP_REASKCALLBACKUDP") );
126 theStats::AddDownOverheadOther(size
);
127 CUpDownClient
* buddy
= theApp
->clientlist
->GetBuddy();
129 if( size
< 17 || buddy
->GetSocket() == NULL
) {
132 if (!md4cmp(packet
, buddy
->GetBuddyID())) {
134 The packet has an initial 16 bytes key for the buddy.
135 This is currently unused, so to make the transformation
136 we discard the first 10 bytes below and then overwrite
137 the other 6 with ip/port.
139 CMemFile
mem_packet(packet
+10,size
-10);
140 // Change the ip and port while leaving the rest untouched
141 mem_packet
.Seek(0,wxFromStart
);
142 mem_packet
.WriteUInt32(host
);
143 mem_packet
.WriteUInt16(port
);
144 CPacket
* response
= new CPacket(mem_packet
, OP_EMULEPROT
, OP_REASKCALLBACKTCP
);
145 AddDebugLogLineM( false, logClientUDP
, wxT("Client UDP socket: send OP_REASKCALLBACKTCP") );
146 theStats::AddUpOverheadFileRequest(response
->GetPacketSize());
147 buddy
->GetSocket()->SendPacket(response
);
152 case OP_REASKFILEPING
: {
153 AddDebugLogLineM( false, logClientUDP
, wxT("Client UDP socket: OP_REASKFILEPING") );
154 theStats::AddDownOverheadFileRequest(size
);
156 CMemFile
data_in(packet
, size
);
157 CMD4Hash reqfilehash
= data_in
.ReadHash();
158 CKnownFile
* reqfile
= theApp
->sharedfiles
->GetFileByID(reqfilehash
);
159 bool bSenderMultipleIpUnknown
= false;
160 CUpDownClient
* sender
= theApp
->uploadqueue
->GetWaitingClientByIP_UDP(host
, port
, true, &bSenderMultipleIpUnknown
);
163 CPacket
* response
= new CPacket(OP_FILENOTFOUND
,0,OP_EMULEPROT
);
164 theStats::AddUpOverheadFileRequest(response
->GetPacketSize());
166 SendPacket(response
, host
, port
, sender
->ShouldReceiveCryptUDPPackets(), sender
->GetUserHash().GetHash(), false, 0);
168 SendPacket(response
, host
, port
, false, NULL
, false, 0);
175 sender
->CheckForAggressive();
177 //Make sure we are still thinking about the same file
178 if (reqfilehash
== sender
->GetUploadFileID()) {
179 sender
->AddAskedCount();
180 sender
->SetUDPPort(port
);
181 sender
->SetLastUpRequest();
183 if (sender
->GetUDPVersion() > 3) {
184 sender
->ProcessExtendedInfo(&data_in
, reqfile
);
185 } else if (sender
->GetUDPVersion() > 2) {
186 uint16 nCompleteCountLast
= sender
->GetUpCompleteSourcesCount();
187 uint16 nCompleteCountNew
= data_in
.ReadUInt16();
188 sender
->SetUpCompleteSourcesCount(nCompleteCountNew
);
189 if (nCompleteCountLast
!= nCompleteCountNew
) {
190 reqfile
->UpdatePartsInfo();
194 CMemFile
data_out(128);
195 if(sender
->GetUDPVersion() > 3) {
196 if (reqfile
->IsPartFile()) {
197 ((CPartFile
*)reqfile
)->WritePartStatus(&data_out
);
199 data_out
.WriteUInt16(0);
203 data_out
.WriteUInt16(theApp
->uploadqueue
->GetWaitingPosition(sender
));
204 CPacket
* response
= new CPacket(data_out
, OP_EMULEPROT
, OP_REASKACK
);
205 theStats::AddUpOverheadFileRequest(response
->GetPacketSize());
206 AddDebugLogLineM( false, logClientUDP
, wxT("Client UDP socket: OP_REASKACK to ") + sender
->GetFullIP());
207 SendPacket(response
, host
, port
, sender
->ShouldReceiveCryptUDPPackets(), sender
->GetUserHash().GetHash(), false, 0);
209 AddDebugLogLineM( false, logClientUDP
, wxT("Client UDP socket; ReaskFilePing; reqfile does not match") );
212 if (!bSenderMultipleIpUnknown
) {
213 if ((theStats::GetWaitingUserCount() + 50) > thePrefs::GetQueueSize()) {
214 CPacket
* response
= new CPacket(OP_QUEUEFULL
,0,OP_EMULEPROT
);
215 theStats::AddUpOverheadFileRequest(response
->GetPacketSize());
216 SendPacket(response
,host
,port
, false, NULL
, false, 0); // we cannot answer this one encrypted since we dont know this client
219 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
);
225 AddDebugLogLineM( false, logClientUDP
, wxT("Client UDP socket: OP_QUEUEFULL") );
226 theStats::AddDownOverheadOther(size
);
227 CUpDownClient
* sender
= theApp
->downloadqueue
->GetDownloadClientByIP_UDP(host
,port
);
229 sender
->SetRemoteQueueFull(true);
230 sender
->UDPReaskACK(0);
235 theStats::AddDownOverheadFileRequest(size
);
236 CUpDownClient
* sender
= theApp
->downloadqueue
->GetDownloadClientByIP_UDP(host
,port
);
238 CMemFile
data_in(packet
,size
);
239 if ( sender
->GetUDPVersion() > 3 ) {
240 sender
->ProcessFileStatus(true, &data_in
, sender
->GetRequestFile());
242 uint16 nRank
= data_in
.ReadUInt16();
243 sender
->SetRemoteQueueFull(false);
244 sender
->UDPReaskACK(nRank
);
248 case OP_FILENOTFOUND
: {
249 AddDebugLogLineM( false, logClientUDP
, wxT("Client UDP socket: OP_FILENOTFOUND") );
250 theStats::AddDownOverheadFileRequest(size
);
251 CUpDownClient
* sender
= theApp
->downloadqueue
->GetDownloadClientByIP_UDP(host
,port
);
253 sender
->UDPReaskFNF(); // may delete 'sender'!
260 theStats::AddDownOverheadOther(size
);
263 // File_checked_for_headers