1// 2// This file is part of the aMule Project. 3// 4// Copyright (c) 2004-2011 aMule Team ( admin@amule.org / http://www.amule.org ) 5// Copyright (c) 2004-2011 Angel Vidal ( kry@amule.org ) 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. 10// 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. 15// 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 24// 25 26#include "RemoteConnect.h" 27 28#include <common/MD5Sum.h> 29#include <common/Format.h> 30 31#include <wx/intl.h> 32 33using std::auto_ptr; 34 35DEFINE_LOCAL_EVENT_TYPE(wxEVT_EC_CONNECTION) 36 37CECLoginPacket::CECLoginPacket(const wxString& client, const wxString& version, 38 bool canZLIB, bool canUTF8numbers, bool canNotify) 39: 40CECPacket(EC_OP_AUTH_REQ) 41{ 42 AddTag(CECTag(EC_TAG_CLIENT_NAME, client)); 43 AddTag(CECTag(EC_TAG_CLIENT_VERSION, version)); 44 AddTag(CECTag(EC_TAG_PROTOCOL_VERSION, (uint64)EC_CURRENT_PROTOCOL_VERSION)); 45 46 #ifdef EC_VERSION_ID 47 CMD4Hash versionhash; 48 wxCHECK2(versionhash.Decode(wxT(EC_VERSION_ID)), /* Do nothing. */); 49 AddTag(CECTag(EC_TAG_VERSION_ID, versionhash)); 50 #endif 51 52 // Send capabilities: 53 // support ZLIB compression 54 if (canZLIB) AddTag(CECEmptyTag(EC_TAG_CAN_ZLIB)); 55 // support encoding of integers as UTF-8 56 if (canUTF8numbers) AddTag(CECEmptyTag(EC_TAG_CAN_UTF8_NUMBERS)); 57 // client accepts push messages 58 if (canNotify) AddTag(CECEmptyTag(EC_TAG_CAN_NOTIFY)); 59} 60 61CECAuthPacket::CECAuthPacket(const wxString& pass) 62: 63CECPacket(EC_OP_AUTH_PASSWD) 64{ 65 CMD4Hash passhash; 66 wxCHECK2(passhash.Decode(pass), /* Do nothing. */); 67 AddTag(CECTag(EC_TAG_PASSWD_HASH, passhash)); 68} 69 70/*! 71 * Connection to remote core 72 * 73 */ 74 75CRemoteConnect::CRemoteConnect(wxEvtHandler* evt_handler) 76: 77CECMuleSocket(evt_handler != 0), 78m_ec_state(EC_INIT), 79m_req_fifo(), 80// Give application some indication about how fast requests are served 81// When request fifo contain more that certain number of entries, it may 82// indicate that either core or network is slowing us down 83m_req_count(0), 84// This is not mean to be absolute limit, because we can't drop requests 85// out of calling context; it is just signal to application to slow down 86m_req_fifo_thr(20), 87m_notifier(evt_handler), 88m_canZLIB(false), 89m_canUTF8numbers(false), 90m_canNotify(false) 91{ 92} 93 94void CRemoteConnect::SetCapabilities(bool canZLIB, bool canUTF8numbers, bool canNotify) 95{ 96 m_canZLIB = canZLIB; 97 if (canZLIB) { 98 m_my_flags |= EC_FLAG_ZLIB; 99 } 100 m_canUTF8numbers = canUTF8numbers; 101 if (canUTF8numbers) { 102 m_my_flags |= EC_FLAG_UTF8_NUMBERS; 103 } 104 m_canNotify = canNotify; 105} 106 107bool CRemoteConnect::ConnectToCore(const wxString &host, int port, 108 const wxString &WXUNUSED(login), const wxString &pass, 109 const wxString& client, const wxString& version) 110{ 111 m_connectionPassword = pass; 112 113 m_client = client; 114 m_version = version; 115 116 // don't even try to connect without a valid password 117 if (m_connectionPassword.IsEmpty() || m_connectionPassword == wxT("d41d8cd98f00b204e9800998ecf8427e")) { 118 m_server_reply = _("You must specify a non-empty password."); 119 return false; 120 } else { 121 CMD4Hash hash; 122 if (!hash.Decode(m_connectionPassword)) { 123 m_server_reply = _("Invalid password, not a MD5 hash!"); 124 return false; 125 } else if (hash.IsEmpty()) { 126 m_server_reply = _("You must specify a non-empty password."); 127 return false; 128 } 129 } 130 131 wxIPV4address addr; 132 133 addr.Hostname(host); 134 addr.Service(port); 135 136 if (ConnectSocket(addr)) { 137 CECLoginPacket login_req(m_client, m_version, m_canZLIB, m_canUTF8numbers, m_canNotify); 138 139 std::auto_ptr<const CECPacket> getSalt(SendRecvPacket(&login_req)); 140 m_ec_state = EC_REQ_SENT; 141 142 ProcessAuthPacket(getSalt.get()); 143 144 CECAuthPacket passwdPacket(m_connectionPassword); 145 146 std::auto_ptr<const CECPacket> reply(SendRecvPacket(&passwdPacket)); 147 m_ec_state = EC_PASSWD_SENT; 148 149 return ProcessAuthPacket(reply.get()); 150 } else if (m_notifier) { 151 m_ec_state = EC_CONNECT_SENT; 152 } else { 153 return false; 154 } 155 156 return true; 157} 158 159bool CRemoteConnect::IsConnectedToLocalHost() 160{ 161 wxIPV4address addr; 162 return GetPeer(addr) ? addr.IsLocalHost() : false; 163} 164 165void CRemoteConnect::WriteDoneAndQueueEmpty() 166{ 167} 168 169void CRemoteConnect::OnConnect() { 170 if (m_notifier) { 171 wxASSERT(m_ec_state == EC_CONNECT_SENT); 172 CECLoginPacket login_req(m_client, m_version, m_canZLIB, m_canUTF8numbers, m_canNotify); 173 CECSocket::SendPacket(&login_req); 174 175 m_ec_state = EC_REQ_SENT; 176 } else { 177 // do nothing, calling code will take from here 178 } 179} 180 181void CRemoteConnect::OnLost() { 182 if (m_notifier) { 183 // Notify app of failure 184 wxECSocketEvent event(wxEVT_EC_CONNECTION,false,_("Connection failure")); 185 m_notifier->AddPendingEvent(event); 186 } 187} 188 189const CECPacket *CRemoteConnect::OnPacketReceived(const CECPacket *packet, uint32 trueSize) 190{ 191 CECPacket *next_packet = 0; 192 m_req_count--; 193 packet->DebugPrint(true, trueSize); 194 switch(m_ec_state) { 195 case EC_REQ_SENT: 196 if (ProcessAuthPacket(packet)) { 197 CECAuthPacket passwdPacket(m_connectionPassword); 198 CECSocket::SendPacket(&passwdPacket); 199 m_ec_state = EC_PASSWD_SENT; 200 } 201 break; 202 case EC_PASSWD_SENT: 203 ProcessAuthPacket(packet); 204 break; 205 case EC_OK: 206 if ( !m_req_fifo.empty() ) { 207 CECPacketHandlerBase *handler = m_req_fifo.front(); 208 m_req_fifo.pop_front(); 209 if ( handler ) { 210 handler->HandlePacket(packet); 211 } 212 } else { 213 printf("EC error - packet received, but request fifo is empty\n"); 214 } 215 break; 216 default: 217 break; 218 } 219 220 // no reply by default 221 return next_packet; 222} 223 224/* 225 * Our requests are served by core in FCFS order. And core always replies. So, even 226 * if we're not interested in reply, we preserve place in request fifo. 227 */ 228void CRemoteConnect::SendRequest(CECPacketHandlerBase *handler, const CECPacket *request) 229{ 230 m_req_count++; 231 m_req_fifo.push_back(handler); 232 CECSocket::SendPacket(request); 233} 234 235void CRemoteConnect::SendPacket(const CECPacket *request) 236{ 237 SendRequest(0, request); 238} 239 240bool CRemoteConnect::ProcessAuthPacket(const CECPacket *reply) { 241 bool result = false; 242 243 if (!reply) { 244 m_server_reply = _("EC connection failed. Empty reply."); 245 CloseSocket(); 246 } else { 247 if ((m_ec_state == EC_REQ_SENT) && (reply->GetOpCode() == EC_OP_AUTH_SALT)) { 248 const CECTag *passwordSalt = reply->GetTagByName(EC_TAG_PASSWD_SALT); 249 if ( NULL != passwordSalt) { 250 wxString saltHash = MD5Sum(CFormat(wxT("%lX")) % passwordSalt->GetInt()).GetHash(); 251 m_connectionPassword = MD5Sum(m_connectionPassword.Lower() + saltHash).GetHash(); 252 m_ec_state = EC_SALT_RECEIVED; 253 return true; 254 } else { 255 m_server_reply = _("External Connection: Bad reply, handshake failed. Connection closed."); 256 m_ec_state = EC_FAIL; 257 CloseSocket(); 258 } 259 } else if ((m_ec_state == EC_PASSWD_SENT) && (reply->GetOpCode() == EC_OP_AUTH_OK)) { 260 m_ec_state = EC_OK; 261 result = true; 262 if (reply->GetTagByName(EC_TAG_SERVER_VERSION)) { 263 m_server_reply = _("Succeeded! Connection established to aMule ") + 264 reply->GetTagByName(EC_TAG_SERVER_VERSION)->GetStringData(); 265 } else { 266 m_server_reply = _("Succeeded! Connection established."); 267 } 268 }else { 269 m_ec_state = EC_FAIL; 270 const CECTag *reason = reply->GetTagByName(EC_TAG_STRING); 271 if (reason != NULL) { 272 m_server_reply = wxString(_("External Connection: Access denied because: ")) + 273 wxGetTranslation(reason->GetStringData()); 274 } else { 275 m_server_reply = _("External Connection: Handshake failed."); 276 } 277 CloseSocket(); 278 } 279 } 280 if ( m_notifier ) { 281 wxECSocketEvent event(wxEVT_EC_CONNECTION, result, m_server_reply); 282 m_notifier->AddPendingEvent(event); 283 } 284 return result; 285} 286 287/******************** EC API ***********************/ 288 289void CRemoteConnect::StartKad() { 290 CECPacket req(EC_OP_KAD_START); 291 SendPacket(&req); 292} 293 294void CRemoteConnect::StopKad() { 295 CECPacket req(EC_OP_KAD_STOP); 296 SendPacket(&req); 297} 298 299void CRemoteConnect::ConnectED2K(uint32 ip, uint16 port) { 300 CECPacket req(EC_OP_SERVER_CONNECT); 301 if (ip && port) { 302 req.AddTag(CECTag(EC_TAG_SERVER, EC_IPv4_t(ip, port))); 303 } 304 SendPacket(&req); 305} 306 307void CRemoteConnect::DisconnectED2K() { 308 CECPacket req(EC_OP_SERVER_DISCONNECT); 309 SendPacket(&req); 310} 311 312void CRemoteConnect::RemoveServer(uint32 ip, uint16 port) { 313 CECPacket req(EC_OP_SERVER_REMOVE); 314 if (ip && port) { 315 req.AddTag(CECTag(EC_TAG_SERVER, EC_IPv4_t(ip, port))); 316 } 317 SendPacket(&req); 318} 319// File_checked_for_headers 320