Upstream tarball 10017
[amule.git] / src / MuleUDPSocket.cpp
blobd31b64b3b18807bf6b31938ce3e146a2287cecd1
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2005-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 //
6 // Any parts of this program derived from the xMule, lMule or eMule project,
7 // or contributed by third-party developers are copyrighted by their
8 // respective authors.
9 //
10 // This program is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation; either version 2 of the License, or
13 // (at your option) any later version.
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include <wx/wx.h>
26 #include <algorithm>
28 #include "MuleUDPSocket.h" // Interface declarations
30 #include <protocol/ed2k/Constants.h>
32 #include "Logger.h" // Needed for AddDebugLogLineM
33 #include "amule.h" // Needed for theApp
34 #include "GetTickCount.h" // Needed for GetTickCount()
35 #include "Packet.h" // Needed for CPacket
36 #include <common/StringFunctions.h> // Needed for unicode2char
37 #include "Proxy.h" // Needed for CDatagramSocketProxy
38 #include "Logger.h" // Needed for AddDebugLogLineM
39 #include "UploadBandwidthThrottler.h"
40 #include "EncryptedDatagramSocket.h"
41 #include "OtherFunctions.h"
42 #include "kademlia/kademlia/Prefs.h"
43 #include "ClientList.h"
46 CMuleUDPSocket::CMuleUDPSocket(const wxString& name, int id, const amuleIPV4Address& address, const CProxyData* ProxyData)
48 m_busy(false),
49 m_name(name),
50 m_id(id),
51 m_addr(address),
52 m_proxy(ProxyData),
53 m_socket(NULL)
58 CMuleUDPSocket::~CMuleUDPSocket()
60 theApp->uploadBandwidthThrottler->RemoveFromAllQueues(this);
62 wxMutexLocker lock(m_mutex);
63 DestroySocket();
67 void CMuleUDPSocket::CreateSocket()
69 wxCHECK_RET(!m_socket, wxT("Socket already opened."));
71 m_socket = new CEncryptedDatagramSocket(m_addr, wxSOCKET_NOWAIT, m_proxy);
72 m_socket->SetClientData(this);
73 m_socket->SetEventHandler(*theApp, m_id);
74 m_socket->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_OUTPUT_FLAG | wxSOCKET_LOST_FLAG);
75 m_socket->Notify(true);
77 if (!m_socket->Ok()) {
78 AddDebugLogLineM(true, logMuleUDP, wxT("Failed to create valid ") + m_name);
79 DestroySocket();
80 } else {
81 AddLogLineM(false, wxString(wxT("Created ")) << m_name << wxT(" at port ") << m_addr.Service());
86 void CMuleUDPSocket::DestroySocket()
88 if (m_socket) {
89 AddDebugLogLineM(false, logMuleUDP, wxT("Shutting down ") + m_name);
90 m_socket->SetNotify(0);
91 m_socket->Notify(false);
92 m_socket->Close();
93 m_socket->Destroy();
94 m_socket = NULL;
99 void CMuleUDPSocket::Open()
101 wxMutexLocker lock(m_mutex);
103 CreateSocket();
107 void CMuleUDPSocket::Close()
109 wxMutexLocker lock(m_mutex);
111 DestroySocket();
115 void CMuleUDPSocket::OnSend(int errorCode)
117 if (errorCode) {
118 return;
122 wxMutexLocker lock(m_mutex);
123 m_busy = false;
124 if (m_queue.empty()) {
125 return;
129 theApp->uploadBandwidthThrottler->QueueForSendingControlPacket(this);
133 const unsigned UDP_BUFFER_SIZE = 16384;
136 void CMuleUDPSocket::OnReceive(int errorCode)
138 AddDebugLogLineM(false, logMuleUDP, wxString::Format(
139 wxT("Got UDP callback for read: Error %i Socket state %i"),
140 errorCode, Ok() ? 1 : 0));
142 char buffer[UDP_BUFFER_SIZE];
143 wxIPV4address addr;
144 unsigned length = 0;
145 bool error = false;
146 int lastError = 0;
149 wxMutexLocker lock(m_mutex);
151 if (errorCode || (m_socket == NULL) || !m_socket->Ok()) {
152 DestroySocket();
153 CreateSocket();
155 return;
159 length = m_socket->RecvFrom(addr, buffer, UDP_BUFFER_SIZE).LastCount();
160 error = m_socket->Error();
161 lastError = m_socket->LastError();
164 const uint32 ip = StringIPtoUint32(addr.IPAddress());
165 const uint16 port = addr.Service();
166 if (error) {
167 OnReceiveError(lastError, ip, port);
168 } else if (length < 2) {
169 // 2 bytes (protocol and opcode) is the smallets possible packet.
170 AddDebugLogLineM(false, logMuleUDP, m_name + wxT(": Invalid Packet received"));
171 } else if (!ip) {
172 // wxFAIL;
173 AddLogLineNS(wxT("Unknown ip receiving a UDP packet! Ignoring: '") + addr.IPAddress() + wxT("'"));
174 } else if (!port) {
175 // wxFAIL;
176 AddLogLineNS(wxT("Unknown port receiving a UDP packet! Ignoring"));
177 } else if (theApp->clientlist->IsBannedClient(ip)) {
178 AddDebugLogLineM(false, logMuleUDP, m_name + wxT(": Dropped packet from banned IP ") + addr.IPAddress());
179 } else {
180 AddDebugLogLineM(false, logMuleUDP, (m_name + wxT(": Packet received ("))
181 << addr.IPAddress() << wxT(":") << port << wxT("): ")
182 << length << wxT("b"));
183 OnPacketReceived(ip, port, (byte*)buffer, length);
188 void CMuleUDPSocket::OnReceiveError(int errorCode, uint32 WXUNUSED(ip), uint16 WXUNUSED(port))
190 AddDebugLogLineM(false, logMuleUDP, (m_name + wxT(": Error while reading: ")) << errorCode);
194 void CMuleUDPSocket::OnDisconnected(int WXUNUSED(errorCode))
196 /* Due to bugs in wxWidgets, UDP sockets will sometimes
197 * be closed. This is caused by the fact that wx treats
198 * zero-length datagrams as EOF, which is only the case
199 * when dealing with streaming sockets.
201 * This has been reported as patch #1885472:
202 * http://sourceforge.net/tracker/index.php?func=detail&aid=1885472&group_id=9863&atid=309863
204 AddDebugLogLineM(true, logMuleUDP, m_name + wxT("Socket died, recreating."));
205 DestroySocket();
206 CreateSocket();
210 void CMuleUDPSocket::SendPacket(CPacket* packet, uint32 IP, uint16 port, bool bEncrypt, const uint8* pachTargetClientHashORKadID, bool bKad, uint32 nReceiverVerifyKey)
212 wxCHECK_RET(packet, wxT("Invalid packet."));
213 /*wxCHECK_RET(port, wxT("Invalid port."));
214 wxCHECK_RET(IP, wxT("Invalid IP."));
217 if (!port || !IP) {
218 return;
221 if (!Ok()) {
222 AddDebugLogLineM(false, logMuleUDP, (m_name + wxT(": Packet discarded, socket not Ok ("))
223 << Uint32_16toStringIP_Port(IP, port) << wxT("): ") << packet->GetPacketSize() << wxT("b"));
224 delete packet;
226 return;
229 AddDebugLogLineM(false, logMuleUDP, (m_name + wxT(": Packet queued ("))
230 << Uint32_16toStringIP_Port(IP, port) << wxT("): ") << packet->GetPacketSize() << wxT("b"));
232 UDPPack newpending;
233 newpending.IP = IP;
234 newpending.port = port;
235 newpending.packet = packet;
236 newpending.time = GetTickCount();
237 newpending.bEncrypt = bEncrypt && (pachTargetClientHashORKadID != NULL || (bKad && nReceiverVerifyKey != 0));
238 newpending.bKad = bKad;
239 newpending.nReceiverVerifyKey = nReceiverVerifyKey;
240 if (newpending.bEncrypt && pachTargetClientHashORKadID != NULL) {
241 md4cpy(newpending.pachTargetClientHashORKadID, pachTargetClientHashORKadID);
242 } else {
243 md4clr(newpending.pachTargetClientHashORKadID);
247 wxMutexLocker lock(m_mutex);
248 m_queue.push_back(newpending);
251 theApp->uploadBandwidthThrottler->QueueForSendingControlPacket(this);
255 bool CMuleUDPSocket::Ok()
257 wxMutexLocker lock(m_mutex);
259 return m_socket && m_socket->Ok();
263 SocketSentBytes CMuleUDPSocket::SendControlData(uint32 maxNumberOfBytesToSend, uint32 WXUNUSED(minFragSize))
265 wxMutexLocker lock(m_mutex);
266 uint32 sentBytes = 0;
267 while (!m_queue.empty() && !m_busy && (sentBytes < maxNumberOfBytesToSend)) {
268 UDPPack item = m_queue.front();
269 CPacket* packet = item.packet;
270 if (GetTickCount() - item.time < UDPMAXQUEUETIME) {
271 uint32_t len = packet->GetPacketSize() + 2;
272 uint8_t *sendbuffer = new uint8_t [len];
273 memcpy(sendbuffer, packet->GetUDPHeader(), 2);
274 memcpy(sendbuffer + 2, packet->GetDataBuffer(), packet->GetPacketSize());
276 if (item.bEncrypt && (theApp->GetPublicIP() > 0 || item.bKad)) {
277 len = CEncryptedDatagramSocket::EncryptSendClient(&sendbuffer, len, item.pachTargetClientHashORKadID, item.bKad, item.nReceiverVerifyKey, (item.bKad ? Kademlia::CPrefs::GetUDPVerifyKey(item.IP) : 0));
280 if (SendTo(sendbuffer, len, item.IP, item.port)) {
281 sentBytes += len;
282 m_queue.pop_front();
283 delete packet;
284 delete [] sendbuffer;
285 } else {
286 // TODO: Needs better error handling, see SentTo
287 delete [] sendbuffer;
288 break;
290 } else {
291 m_queue.pop_front();
292 delete packet;
295 if (!m_busy && !m_queue.empty()) {
296 theApp->uploadBandwidthThrottler->QueueForSendingControlPacket(this);
298 SocketSentBytes returnVal = { true, 0, sentBytes };
300 return returnVal;
304 bool CMuleUDPSocket::SendTo(uint8_t *buffer, uint32_t length, uint32_t ip, uint16_t port)
306 // Just pretend that we sent the packet in order to avoid infinite loops.
307 if (!(m_socket && m_socket->Ok())) {
308 return true;
311 amuleIPV4Address addr;
312 addr.Hostname(ip);
313 addr.Service(port);
315 // We better clear this flag here, status might have been changed
316 // between the U.B.T. addition and the real sending happening later
317 m_busy = false;
318 bool sent = false;
319 m_socket->SendTo(addr, buffer, length);
320 if (m_socket->Error()) {
321 wxSocketError error = m_socket->LastError();
323 if (error == wxSOCKET_WOULDBLOCK) {
324 // Socket is busy and can't send this data right now,
325 // so we just return not sent and set the wouldblock
326 // flag so it gets resent when socket is ready.
327 m_busy = true;
328 } else {
329 // An error which we can't handle happended, so we drop
330 // the packet rather than risk entering an infinite loop.
331 AddLogLineNS((wxT("WARNING! ") + m_name + wxT(": Packet to "))
332 << Uint32_16toStringIP_Port(ip, port)
333 << wxT(" discarded due to error (") << error << wxT(") while sending."));
334 sent = true;
336 } else {
337 AddDebugLogLineM(false, logMuleUDP, (m_name + wxT(": Packet sent ("))
338 << Uint32_16toStringIP_Port(ip, port) << wxT("): ")
339 << length << wxT("b"));
340 sent = true;
343 return sent;
346 // File_checked_for_headers