Upstream tarball 9597
[amule.git] / src / libs / ec / cpp / RemoteConnect.cpp
blob6fe677297c9236bd72e6c4376c41cb6cf7c9d22f
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2004-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2004-2008 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.
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
26 #include "RemoteConnect.h"
28 #include <common/MD5Sum.h>
29 #include <common/Format.h>
31 #include <wx/intl.h>
33 using std::auto_ptr;
35 DEFINE_LOCAL_EVENT_TYPE(wxEVT_EC_CONNECTION)
37 CECLoginPacket::CECLoginPacket(const wxString& client, const wxString& version)
39 CECPacket(EC_OP_AUTH_REQ)
41 AddTag(CECTag(EC_TAG_CLIENT_NAME, client));
42 AddTag(CECTag(EC_TAG_CLIENT_VERSION, version));
43 AddTag(CECTag(EC_TAG_PROTOCOL_VERSION, (uint64)EC_CURRENT_PROTOCOL_VERSION));
45 #ifdef EC_VERSION_ID
46 CMD4Hash versionhash;
47 wxCHECK2(versionhash.Decode(wxT(EC_VERSION_ID)), /* Do nothing. */);
48 AddTag(CECTag(EC_TAG_VERSION_ID, versionhash));
49 #endif
53 CECAuthPacket::CECAuthPacket(const wxString& pass)
55 CECPacket(EC_OP_AUTH_PASSWD)
57 CMD4Hash passhash;
58 wxCHECK2(passhash.Decode(pass), /* Do nothing. */);
59 AddTag(CECTag(EC_TAG_PASSWD_HASH, passhash));
62 /*!
63 * Connection to remote core
67 CRemoteConnect::CRemoteConnect(wxEvtHandler* evt_handler)
69 CECMuleSocket(evt_handler != 0),
70 m_ec_state(EC_INIT),
71 m_req_fifo(),
72 // Give application some indication about how fast requests are served
73 // When request fifo contain more that certain number of entries, it may
74 // indicate that either core or network is slowing us down
75 m_req_count(0),
76 // This is not mean to be absolute limit, because we can't drop requests
77 // out of calling context; it is just signal to application to slow down
78 m_req_fifo_thr(20),
79 m_notifier(evt_handler)
83 bool CRemoteConnect::ConnectToCore(const wxString &host, int port,
84 const wxString &WXUNUSED(login), const wxString &pass,
85 const wxString& client, const wxString& version)
87 m_connectionPassword = pass;
89 m_client = client;
90 m_version = version;
92 // don't even try to connect without a valid password
93 if (m_connectionPassword.IsEmpty() || m_connectionPassword == wxT("d41d8cd98f00b204e9800998ecf8427e")) {
94 m_server_reply = _("You must specify a non-empty password.");
95 return false;
96 } else {
97 CMD4Hash hash;
98 if (!hash.Decode(m_connectionPassword)) {
99 m_server_reply = _("Invalid password, not a MD5 hash!");
100 return false;
101 } else if (hash.IsEmpty()) {
102 m_server_reply = _("You must specify a non-empty password.");
103 return false;
107 wxIPV4address addr;
109 addr.Hostname(host);
110 addr.Service(port);
112 if (ConnectSocket(addr)) {
113 CECLoginPacket login_req(m_client, m_version);
115 std::auto_ptr<const CECPacket> getSalt(SendRecvPacket(&login_req));
116 m_ec_state = EC_REQ_SENT;
118 ProcessAuthPacket(getSalt.get());
120 CECAuthPacket passwdPacket(m_connectionPassword);
122 std::auto_ptr<const CECPacket> reply(SendRecvPacket(&passwdPacket));
123 m_ec_state = EC_PASSWD_SENT;
125 return ProcessAuthPacket(reply.get());
126 } else if (m_notifier) {
127 m_ec_state = EC_CONNECT_SENT;
128 } else {
129 return false;
132 return true;
135 void CRemoteConnect::WriteDoneAndQueueEmpty()
139 void CRemoteConnect::OnConnect() {
140 if (m_notifier) {
141 wxASSERT(m_ec_state == EC_CONNECT_SENT);
142 CECLoginPacket login_req(m_client, m_version);
143 CECSocket::SendPacket(&login_req);
145 m_ec_state = EC_REQ_SENT;
146 } else {
147 // do nothing, calling code will take from here
151 void CRemoteConnect::OnLost() {
152 if (m_notifier) {
153 // Notify app of failure
154 wxECSocketEvent event(wxEVT_EC_CONNECTION,false,_("Connection failure"));
155 m_notifier->AddPendingEvent(event);
159 const CECPacket *CRemoteConnect::OnPacketReceived(const CECPacket *packet)
161 CECPacket *next_packet = 0;
162 m_req_count--;
163 switch(m_ec_state) {
164 case EC_REQ_SENT:
165 if (ProcessAuthPacket(packet)) {
166 CECAuthPacket passwdPacket(m_connectionPassword);
167 CECSocket::SendPacket(&passwdPacket);
168 m_ec_state = EC_PASSWD_SENT;
170 break;
171 case EC_PASSWD_SENT:
172 ProcessAuthPacket(packet);
173 break;
174 case EC_OK:
175 if ( !m_req_fifo.empty() ) {
176 CECPacketHandlerBase *handler = m_req_fifo.front();
177 m_req_fifo.pop_front();
178 if ( handler ) {
179 handler->HandlePacket(packet);
181 } else {
182 printf("EC error - packet received, but request fifo is empty\n");
184 break;
185 default:
186 break;
189 // no reply by default
190 return next_packet;
194 * Our requests are served by core in FCFS order. And core always replies. So, even
195 * if we're not interested in reply, we preserve place in request fifo.
197 void CRemoteConnect::SendRequest(CECPacketHandlerBase *handler, CECPacket *request)
199 m_req_count++;
200 m_req_fifo.push_back(handler);
201 CECSocket::SendPacket(request);
204 void CRemoteConnect::SendPacket(CECPacket *request)
206 SendRequest(0, request);
209 bool CRemoteConnect::ProcessAuthPacket(const CECPacket *reply) {
210 bool result = false;
212 if (!reply) {
213 m_server_reply = _("EC connection failed. Empty reply.");
214 CloseSocket();
215 } else {
216 if ((m_ec_state == EC_REQ_SENT) && (reply->GetOpCode() == EC_OP_AUTH_SALT)) {
217 const CECTag *passwordSalt = reply->GetTagByName(EC_TAG_PASSWD_SALT);
218 if ( NULL != passwordSalt) {
219 wxString saltHash = MD5Sum(CFormat(wxT("%lX")) % passwordSalt->GetInt()).GetHash();
220 m_connectionPassword = MD5Sum(m_connectionPassword.Lower() + saltHash).GetHash();
221 m_ec_state = EC_SALT_RECEIVED;
222 return true;
223 } else {
224 m_server_reply = _("External Connection: Bad reply, handshake failed. Connection closed.");
225 m_ec_state = EC_FAIL;
226 CloseSocket();
228 } else if ((m_ec_state == EC_PASSWD_SENT) && (reply->GetOpCode() == EC_OP_AUTH_OK)) {
229 m_ec_state = EC_OK;
230 result = true;
231 if (reply->GetTagByName(EC_TAG_SERVER_VERSION)) {
232 m_server_reply = _("Succeeded! Connection established to aMule ") +
233 reply->GetTagByName(EC_TAG_SERVER_VERSION)->GetStringData();
234 } else {
235 m_server_reply = _("Succeeded! Connection established.");
237 }else {
238 m_ec_state = EC_FAIL;
239 const CECTag *reason = reply->GetTagByName(EC_TAG_STRING);
240 if (reason != NULL) {
241 m_server_reply = wxString(_("External Connection: Access denied because: ")) +
242 wxGetTranslation(reason->GetStringData());
243 } else {
244 m_server_reply = _("External Connection: Handshake failed.");
246 CloseSocket();
249 if ( m_notifier ) {
250 wxECSocketEvent event(wxEVT_EC_CONNECTION, result, m_server_reply);
251 m_notifier->AddPendingEvent(event);
253 return result;
256 /******************** EC API ***********************/
258 void CRemoteConnect::StartKad() {
259 CECPacket req(EC_OP_KAD_START);
260 SendPacket(&req);
263 void CRemoteConnect::StopKad() {
264 CECPacket req(EC_OP_KAD_STOP);
265 SendPacket(&req);
268 void CRemoteConnect::ConnectED2K(uint32 ip, uint16 port) {
269 CECPacket req(EC_OP_SERVER_CONNECT);
270 if (ip && port) {
271 req.AddTag(CECTag(EC_TAG_SERVER, EC_IPv4_t(ip, port)));
273 SendPacket(&req);
276 void CRemoteConnect::DisconnectED2K() {
277 CECPacket req(EC_OP_SERVER_DISCONNECT);
278 SendPacket(&req);
281 void CRemoteConnect::RemoveServer(uint32 ip, uint16 port) {
282 CECPacket req(EC_OP_SERVER_REMOVE);
283 if (ip && port) {
284 req.AddTag(CECTag(EC_TAG_SERVER, EC_IPv4_t(ip, port)));
286 SendPacket(&req);
288 // File_checked_for_headers