Upstream tarball 20080304
[amule.git] / src / ClientUDPSocket.cpp
blob2f4f02fe0c8192a532704dd997e37cc31f825a57
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 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 <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
47 #include "Logger.h"
48 #include "kademlia/kademlia/Kademlia.h"
49 #include "zlib.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()) {
59 Open();
64 void CClientUDPSocket::OnReceive(int errorCode)
66 CMuleUDPSocket::OnReceive(errorCode);
68 // TODO: A better solution is needed.
69 if (thePrefs::IsUDPDisabled()) {
70 Close();
75 void CClientUDPSocket::OnPacketReceived(const wxIPV4address& addr, byte* buffer, size_t length)
77 wxCHECK_RET(length >= 2, wxT("Invalid packet."));
79 uint8 protocol = buffer[0];
80 uint8 opcode = buffer[1];
81 uint32 ip = StringIPtoUint32(addr.IPAddress());
82 uint16 port = addr.Service();
84 try {
85 switch (protocol) {
86 case OP_EMULEPROT:
87 ProcessPacket(buffer + 2,length - 2, opcode, ip, port);
88 break;
90 case OP_KADEMLIAHEADER:
91 theStats::AddDownOverheadKad(length);
92 Kademlia::CKademlia::ProcessPacket(buffer, length, wxUINT32_SWAP_ALWAYS(ip), port);
93 break;
95 case OP_KADEMLIAPACKEDPROT: {
96 theStats::AddDownOverheadKad(length);
97 uint32 nNewSize = length*10+300; // Should be enough...
98 std::vector<byte> unpack(nNewSize);
99 uLongf unpackedsize = nNewSize-2;
100 uint16 result = uncompress(&(unpack[2]), &unpackedsize, buffer + 2, length-2);
101 if (result == Z_OK) {
102 unpack[0] = OP_KADEMLIAHEADER;
103 unpack[1] = opcode;
104 Kademlia::CKademlia::ProcessPacket(&(unpack[0]), unpackedsize + 2, wxUINT32_SWAP_ALWAYS(ip), port);
105 } else {
106 AddDebugLogLineM(false, logClientKadUDP, wxT("Failed to uncompress Kademlia packet"));
108 break;
110 default:
111 AddDebugLogLineM(false, logClientUDP, wxString::Format(wxT("Unknown opcode on received packet: 0x%x"), protocol));
113 } catch (const wxString& e) {
114 AddDebugLogLineM(false, logClientUDP, wxT("Error while parsing UDP packet: ") + e);
115 } catch (const CInvalidPacket& e) {
116 AddDebugLogLineM(false, logClientUDP, wxT("Invalid UDP packet encountered: ") + e.what());
117 } catch (const CEOFException& e) {
118 AddDebugLogLineM(false, logClientUDP, wxT("Malformed packet encountered while parsing UDP packet: ") + e.what());
123 void CClientUDPSocket::ProcessPacket(byte* packet, int16 size, int8 opcode, uint32 host, uint16 port)
125 switch (opcode) {
126 case OP_REASKCALLBACKUDP: {
127 AddDebugLogLineM( false, logClientUDP, wxT("Client UDP socket; OP_REASKCALLBACKUDP") );
128 theStats::AddDownOverheadOther(size);
129 CUpDownClient* buddy = theApp->clientlist->GetBuddy();
130 if( buddy ) {
131 if( size < 17 || buddy->GetSocket() == NULL ) {
132 break;
134 if (!md4cmp(packet, buddy->GetBuddyID())) {
136 The packet has an initial 16 bytes key for the buddy.
137 This is currently unused, so to make the transformation
138 we discard the first 10 bytes below and then overwrite
139 the other 6 with ip/port.
141 CMemFile mem_packet(packet+10,size-10);
142 // Change the ip and port while leaving the rest untouched
143 mem_packet.Seek(0,wxFromStart);
144 mem_packet.WriteUInt32(host);
145 mem_packet.WriteUInt16(port);
146 CPacket* response = new CPacket(mem_packet, OP_EMULEPROT, OP_REASKCALLBACKTCP);
147 AddDebugLogLineM( false, logClientUDP, wxT("Client UDP socket: send OP_REASKCALLBACKTCP") );
148 theStats::AddUpOverheadFileRequest(response->GetPacketSize());
149 buddy->GetSocket()->SendPacket(response);
152 break;
154 case OP_REASKFILEPING: {
155 AddDebugLogLineM( false, logClientUDP, wxT("Client UDP socket: OP_REASKFILEPING") );
156 theStats::AddDownOverheadFileRequest(size);
158 CMemFile data_in(packet, size);
159 CMD4Hash reqfilehash = data_in.ReadHash();
160 CKnownFile* reqfile = theApp->sharedfiles->GetFileByID(reqfilehash);
161 bool bSenderMultipleIpUnknown = false;
162 CUpDownClient* sender = theApp->uploadqueue->GetWaitingClientByIP_UDP(host, port, true, &bSenderMultipleIpUnknown);
164 if (!reqfile) {
165 CPacket* response = new CPacket(OP_FILENOTFOUND,0,OP_EMULEPROT);
166 theStats::AddUpOverheadFileRequest(response->GetPacketSize());
167 if (sender) {
168 SendPacket(response, host, port, sender->ShouldReceiveCryptUDPPackets(), sender->GetUserHash().GetHash(), false, 0);
169 } else {
170 SendPacket(response, host, port, false, NULL, false, 0);
173 break;
176 if (sender){
177 sender->CheckForAggressive();
179 //Make sure we are still thinking about the same file
180 if (reqfilehash == sender->GetUploadFileID()) {
181 sender->AddAskedCount();
182 sender->SetUDPPort(port);
183 sender->SetLastUpRequest();
185 if (sender->GetUDPVersion() > 3) {
186 sender->ProcessExtendedInfo(&data_in, reqfile);
187 } else if (sender->GetUDPVersion() > 2) {
188 uint16 nCompleteCountLast = sender->GetUpCompleteSourcesCount();
189 uint16 nCompleteCountNew = data_in.ReadUInt16();
190 sender->SetUpCompleteSourcesCount(nCompleteCountNew);
191 if (nCompleteCountLast != nCompleteCountNew) {
192 reqfile->UpdatePartsInfo();
196 CMemFile data_out(128);
197 if(sender->GetUDPVersion() > 3) {
198 if (reqfile->IsPartFile()) {
199 ((CPartFile*)reqfile)->WritePartStatus(&data_out);
200 } else {
201 data_out.WriteUInt16(0);
205 data_out.WriteUInt16(theApp->uploadqueue->GetWaitingPosition(sender));
206 CPacket* response = new CPacket(data_out, OP_EMULEPROT, OP_REASKACK);
207 theStats::AddUpOverheadFileRequest(response->GetPacketSize());
208 AddDebugLogLineM( false, logClientUDP, wxT("Client UDP socket: OP_REASKACK to ") + sender->GetFullIP());
209 SendPacket(response, host, port, sender->ShouldReceiveCryptUDPPackets(), sender->GetUserHash().GetHash(), false, 0);
210 } else {
211 AddDebugLogLineM( false, logClientUDP, wxT("Client UDP socket; ReaskFilePing; reqfile does not match") );
213 } else {
214 if (!bSenderMultipleIpUnknown) {
215 if ((theStats::GetWaitingUserCount() + 50) > thePrefs::GetQueueSize()) {
216 CPacket* response = new CPacket(OP_QUEUEFULL,0,OP_EMULEPROT);
217 theStats::AddUpOverheadFileRequest(response->GetPacketSize());
218 SendPacket(response,host,port, false, NULL, false, 0); // we cannot answer this one encrypted since we dont know this client
220 } else {
221 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);
224 break;
226 case OP_QUEUEFULL: {
227 AddDebugLogLineM( false, logClientUDP, wxT("Client UDP socket: OP_QUEUEFULL") );
228 theStats::AddDownOverheadOther(size);
229 CUpDownClient* sender = theApp->downloadqueue->GetDownloadClientByIP_UDP(host,port);
230 if (sender) {
231 sender->SetRemoteQueueFull(true);
232 sender->UDPReaskACK(0);
234 break;
236 case OP_REASKACK: {
237 theStats::AddDownOverheadFileRequest(size);
238 CUpDownClient* sender = theApp->downloadqueue->GetDownloadClientByIP_UDP(host,port);
239 if (sender) {
240 CMemFile data_in(packet,size);
241 if ( sender->GetUDPVersion() > 3 ) {
242 sender->ProcessFileStatus(true, &data_in, sender->GetRequestFile());
244 uint16 nRank = data_in.ReadUInt16();
245 sender->SetRemoteQueueFull(false);
246 sender->UDPReaskACK(nRank);
248 break;
250 case OP_FILENOTFOUND: {
251 AddDebugLogLineM( false, logClientUDP, wxT("Client UDP socket: OP_FILENOTFOUND") );
252 theStats::AddDownOverheadFileRequest(size);
253 CUpDownClient* sender = theApp->downloadqueue->GetDownloadClientByIP_UDP(host,port);
254 if (sender){
255 sender->UDPReaskFNF(); // may delete 'sender'!
256 sender = NULL;
258 break;
261 default:
262 theStats::AddDownOverheadOther(size);
265 // File_checked_for_headers