2 // This file is part of the aMule Project.
4 // Copyright (c) 2004-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2004-2011 Angel Vidal ( kry@amule.org )
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
26 #include "RemoteConnect.h"
28 #include <common/SmartPtr.h> // Needed for CSmartPtr
29 #include <common/MD5Sum.h>
30 #include <common/Format.h>
31 #include "../../../amuleIPV4Address.h"
35 DEFINE_LOCAL_EVENT_TYPE(wxEVT_EC_CONNECTION
)
37 CECLoginPacket::CECLoginPacket(const wxString
& client
, const wxString
& version
,
38 bool canZLIB
, bool canUTF8numbers
, bool canNotify
)
40 CECPacket(EC_OP_AUTH_REQ
)
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
));
48 wxCHECK2(versionhash
.Decode(wxT(EC_VERSION_ID
)), /* Do nothing. */);
49 AddTag(CECTag(EC_TAG_VERSION_ID
, versionhash
));
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
));
61 CECAuthPacket::CECAuthPacket(const wxString
& pass
)
63 CECPacket(EC_OP_AUTH_PASSWD
)
66 wxCHECK2(passhash
.Decode(pass
), /* Do nothing. */);
67 AddTag(CECTag(EC_TAG_PASSWD_HASH
, passhash
));
71 * Connection to remote core
75 CRemoteConnect::CRemoteConnect(wxEvtHandler
* evt_handler
)
77 CECMuleSocket(evt_handler
!= 0),
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
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
87 m_notifier(evt_handler
),
89 m_canUTF8numbers(false),
94 void CRemoteConnect::SetCapabilities(bool canZLIB
, bool canUTF8numbers
, bool canNotify
)
98 m_my_flags
|= EC_FLAG_ZLIB
;
100 m_canUTF8numbers
= canUTF8numbers
;
101 if (canUTF8numbers
) {
102 m_my_flags
|= EC_FLAG_UTF8_NUMBERS
;
104 m_canNotify
= canNotify
;
107 bool CRemoteConnect::ConnectToCore(const wxString
&host
, int port
,
108 const wxString
&WXUNUSED(login
), const wxString
&pass
,
109 const wxString
& client
, const wxString
& version
)
111 m_connectionPassword
= pass
;
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.");
122 if (!hash
.Decode(m_connectionPassword
)) {
123 m_server_reply
= _("Invalid password, not a MD5 hash!");
125 } else if (hash
.IsEmpty()) {
126 m_server_reply
= _("You must specify a non-empty password.");
131 amuleIPV4Address addr
;
136 if (ConnectSocket(addr
)) {
137 // We get here only in case of synchronous connect.
138 // Otherwise we continue in OnConnect.
139 CECLoginPacket
login_req(m_client
, m_version
, m_canZLIB
, m_canUTF8numbers
, m_canNotify
);
141 CSmartPtr
<const CECPacket
> getSalt(SendRecvPacket(&login_req
));
142 m_ec_state
= EC_REQ_SENT
;
144 ProcessAuthPacket(getSalt
.get());
146 CECAuthPacket
passwdPacket(m_connectionPassword
);
148 CSmartPtr
<const CECPacket
> reply(SendRecvPacket(&passwdPacket
));
149 m_ec_state
= EC_PASSWD_SENT
;
151 return ProcessAuthPacket(reply
.get());
152 } else if (m_notifier
) {
153 m_ec_state
= EC_CONNECT_SENT
;
161 bool CRemoteConnect::IsConnectedToLocalHost()
163 amuleIPV4Address addr
;
164 return addr
.Hostname(GetPeer()) ? addr
.IsLocalHost() : false;
167 void CRemoteConnect::WriteDoneAndQueueEmpty()
171 void CRemoteConnect::OnConnect() {
173 wxASSERT(m_ec_state
== EC_CONNECT_SENT
);
174 CECLoginPacket
login_req(m_client
, m_version
, m_canZLIB
, m_canUTF8numbers
, m_canNotify
);
175 CECSocket::SendPacket(&login_req
);
177 m_ec_state
= EC_REQ_SENT
;
179 // do nothing, calling code will take from here
183 void CRemoteConnect::OnLost() {
185 // Notify app of failure
186 wxECSocketEvent
event(wxEVT_EC_CONNECTION
,false,_("Connection failure"));
187 m_notifier
->AddPendingEvent(event
);
191 const CECPacket
*CRemoteConnect::OnPacketReceived(const CECPacket
*packet
, uint32 trueSize
)
193 CECPacket
*next_packet
= 0;
195 packet
->DebugPrint(true, trueSize
);
198 if (ProcessAuthPacket(packet
)) {
199 CECAuthPacket
passwdPacket(m_connectionPassword
);
200 CECSocket::SendPacket(&passwdPacket
);
201 m_ec_state
= EC_PASSWD_SENT
;
205 ProcessAuthPacket(packet
);
208 if ( !m_req_fifo
.empty() ) {
209 CECPacketHandlerBase
*handler
= m_req_fifo
.front();
210 m_req_fifo
.pop_front();
212 handler
->HandlePacket(packet
);
215 printf("EC error - packet received, but request fifo is empty\n");
222 // no reply by default
227 * Our requests are served by core in FCFS order. And core always replies. So, even
228 * if we're not interested in reply, we preserve place in request fifo.
230 void CRemoteConnect::SendRequest(CECPacketHandlerBase
*handler
, const CECPacket
*request
)
233 m_req_fifo
.push_back(handler
);
234 CECSocket::SendPacket(request
);
237 void CRemoteConnect::SendPacket(const CECPacket
*request
)
239 SendRequest(0, request
);
242 bool CRemoteConnect::ProcessAuthPacket(const CECPacket
*reply
) {
246 m_server_reply
= _("EC connection failed. Empty reply.");
249 if ((m_ec_state
== EC_REQ_SENT
) && (reply
->GetOpCode() == EC_OP_AUTH_SALT
)) {
250 const CECTag
*passwordSalt
= reply
->GetTagByName(EC_TAG_PASSWD_SALT
);
251 if ( NULL
!= passwordSalt
) {
252 wxString saltHash
= MD5Sum(CFormat(wxT("%lX")) % passwordSalt
->GetInt()).GetHash();
253 m_connectionPassword
= MD5Sum(m_connectionPassword
.Lower() + saltHash
).GetHash();
254 m_ec_state
= EC_SALT_RECEIVED
;
257 m_server_reply
= _("External Connection: Bad reply, handshake failed. Connection closed.");
258 m_ec_state
= EC_FAIL
;
261 } else if ((m_ec_state
== EC_PASSWD_SENT
) && (reply
->GetOpCode() == EC_OP_AUTH_OK
)) {
264 if (reply
->GetTagByName(EC_TAG_SERVER_VERSION
)) {
265 m_server_reply
= _("Succeeded! Connection established to aMule ") +
266 reply
->GetTagByName(EC_TAG_SERVER_VERSION
)->GetStringData();
268 m_server_reply
= _("Succeeded! Connection established.");
271 m_ec_state
= EC_FAIL
;
272 const CECTag
*reason
= reply
->GetTagByName(EC_TAG_STRING
);
273 if (reason
!= NULL
) {
274 m_server_reply
= wxString(_("External Connection: Access denied because: ")) +
275 wxGetTranslation(reason
->GetStringData());
277 m_server_reply
= _("External Connection: Handshake failed.");
283 wxECSocketEvent
event(wxEVT_EC_CONNECTION
, result
, m_server_reply
);
284 m_notifier
->AddPendingEvent(event
);
289 /******************** EC API ***********************/
291 void CRemoteConnect::StartKad() {
292 CECPacket
req(EC_OP_KAD_START
);
296 void CRemoteConnect::StopKad() {
297 CECPacket
req(EC_OP_KAD_STOP
);
301 void CRemoteConnect::ConnectED2K(uint32 ip
, uint16 port
) {
302 CECPacket
req(EC_OP_SERVER_CONNECT
);
304 req
.AddTag(CECTag(EC_TAG_SERVER
, EC_IPv4_t(ip
, port
)));
309 void CRemoteConnect::DisconnectED2K() {
310 CECPacket
req(EC_OP_SERVER_DISCONNECT
);
314 void CRemoteConnect::RemoveServer(uint32 ip
, uint16 port
) {
315 CECPacket
req(EC_OP_SERVER_REMOVE
);
317 req
.AddTag(CECTag(EC_TAG_SERVER
, EC_IPv4_t(ip
, port
)));
321 // File_checked_for_headers