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/SmartPtr.h>	// Needed for CSmartPtr
29 #include <common/MD5Sum.h>
30 #include <common/Format.h>
31 #include "../../../amuleIPV4Address.h"
32 
33 #include <wx/intl.h>
34 
DEFINE_LOCAL_EVENT_TYPE(wxEVT_EC_CONNECTION)35 DEFINE_LOCAL_EVENT_TYPE(wxEVT_EC_CONNECTION)
36 
37 CECLoginPacket::CECLoginPacket(const wxString& client, const wxString& version,
38 							   bool canZLIB, bool canUTF8numbers, bool canNotify)
39 :
40 CECPacket(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 
CECAuthPacket(const wxString & pass)61 CECAuthPacket::CECAuthPacket(const wxString& pass)
62 :
63 CECPacket(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 
CRemoteConnect(wxEvtHandler * evt_handler)75 CRemoteConnect::CRemoteConnect(wxEvtHandler* evt_handler)
76 :
77 CECMuleSocket(evt_handler != 0),
78 m_ec_state(EC_INIT),
79 m_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
83 m_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
86 m_req_fifo_thr(20),
87 m_notifier(evt_handler),
88 m_canZLIB(false),
89 m_canUTF8numbers(false),
90 m_canNotify(false)
91 {
92 }
93 
SetCapabilities(bool canZLIB,bool canUTF8numbers,bool canNotify)94 void 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 
ConnectToCore(const wxString & host,int port,const wxString & WXUNUSED (login),const wxString & pass,const wxString & client,const wxString & version)107 bool 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 	amuleIPV4Address addr;
132 
133 	addr.Hostname(host);
134 	addr.Service(port);
135 
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);
140 
141 		CSmartPtr<const CECPacket> getSalt(SendRecvPacket(&login_req));
142 		m_ec_state = EC_REQ_SENT;
143 
144 		ProcessAuthPacket(getSalt.get());
145 
146 		CECAuthPacket passwdPacket(m_connectionPassword);
147 
148 		CSmartPtr<const CECPacket> reply(SendRecvPacket(&passwdPacket));
149 		m_ec_state = EC_PASSWD_SENT;
150 
151 		return ProcessAuthPacket(reply.get());
152 	} else if (m_notifier) {
153 		m_ec_state = EC_CONNECT_SENT;
154 	} else {
155 		return false;
156 	}
157 
158 	return true;
159 }
160 
IsConnectedToLocalHost()161 bool CRemoteConnect::IsConnectedToLocalHost()
162 {
163 	amuleIPV4Address addr;
164 	return addr.Hostname(GetPeer()) ? addr.IsLocalHost() : false;
165 }
166 
WriteDoneAndQueueEmpty()167 void CRemoteConnect::WriteDoneAndQueueEmpty()
168 {
169 }
170 
OnConnect()171 void CRemoteConnect::OnConnect() {
172 	if (m_notifier) {
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);
176 
177 		m_ec_state = EC_REQ_SENT;
178 	} else {
179 		// do nothing, calling code will take from here
180 	}
181 }
182 
OnLost()183 void CRemoteConnect::OnLost() {
184 	if (m_notifier) {
185 		// Notify app of failure
186 		wxECSocketEvent event(wxEVT_EC_CONNECTION,false,_("Connection failure"));
187 		m_notifier->AddPendingEvent(event);
188 	}
189 }
190 
OnPacketReceived(const CECPacket * packet,uint32 trueSize)191 const CECPacket *CRemoteConnect::OnPacketReceived(const CECPacket *packet, uint32 trueSize)
192 {
193 	CECPacket *next_packet = 0;
194 	m_req_count--;
195 	packet->DebugPrint(true, trueSize);
196 	switch(m_ec_state) {
197 		case EC_REQ_SENT:
198 			if (ProcessAuthPacket(packet)) {
199 				CECAuthPacket passwdPacket(m_connectionPassword);
200 				CECSocket::SendPacket(&passwdPacket);
201 				m_ec_state = EC_PASSWD_SENT;
202 			}
203 			break;
204 		case EC_PASSWD_SENT:
205 			ProcessAuthPacket(packet);
206 			break;
207 		case EC_OK:
208 			if ( !m_req_fifo.empty() ) {
209 				CECPacketHandlerBase *handler = m_req_fifo.front();
210 				m_req_fifo.pop_front();
211 				if ( handler ) {
212 					handler->HandlePacket(packet);
213 				}
214 			} else {
215 				printf("EC error - packet received, but request fifo is empty\n");
216 			}
217 			break;
218 		default:
219 			break;
220 	}
221 
222 	// no reply by default
223 	return next_packet;
224 }
225 
226 /*
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.
229  */
SendRequest(CECPacketHandlerBase * handler,const CECPacket * request)230 void CRemoteConnect::SendRequest(CECPacketHandlerBase *handler, const CECPacket *request)
231 {
232 	m_req_count++;
233 	m_req_fifo.push_back(handler);
234 	CECSocket::SendPacket(request);
235 }
236 
SendPacket(const CECPacket * request)237 void CRemoteConnect::SendPacket(const CECPacket *request)
238 {
239 	SendRequest(0, request);
240 }
241 
ProcessAuthPacket(const CECPacket * reply)242 bool CRemoteConnect::ProcessAuthPacket(const CECPacket *reply) {
243 	bool result = false;
244 
245 	if (!reply) {
246 		m_server_reply = _("EC connection failed. Empty reply.");
247 		CloseSocket();
248 	} else {
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;
255 					return true;
256 				} else {
257 					m_server_reply = _("External Connection: Bad reply, handshake failed. Connection closed.");
258 					m_ec_state = EC_FAIL;
259 					CloseSocket();
260 				}
261 		} else if ((m_ec_state == EC_PASSWD_SENT) && (reply->GetOpCode() == EC_OP_AUTH_OK)) {
262 			m_ec_state = EC_OK;
263 			result = true;
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();
267 			} else {
268 				m_server_reply = _("Succeeded! Connection established.");
269 			}
270 		}else {
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());
276 			} else {
277 				m_server_reply = _("External Connection: Handshake failed.");
278 			}
279 			CloseSocket();
280 		}
281 	}
282 	if ( m_notifier ) {
283 		wxECSocketEvent event(wxEVT_EC_CONNECTION, result, m_server_reply);
284 		m_notifier->AddPendingEvent(event);
285 	}
286 	return result;
287 }
288 
289 /******************** EC API ***********************/
290 
StartKad()291 void CRemoteConnect::StartKad() {
292 	CECPacket req(EC_OP_KAD_START);
293 	SendPacket(&req);
294 }
295 
StopKad()296 void CRemoteConnect::StopKad() {
297 	CECPacket req(EC_OP_KAD_STOP);
298 	SendPacket(&req);
299 }
300 
ConnectED2K(uint32 ip,uint16 port)301 void CRemoteConnect::ConnectED2K(uint32 ip, uint16 port) {
302 	CECPacket req(EC_OP_SERVER_CONNECT);
303 	if (ip && port) {
304 		req.AddTag(CECTag(EC_TAG_SERVER, EC_IPv4_t(ip, port)));
305 	}
306 	SendPacket(&req);
307 }
308 
DisconnectED2K()309 void CRemoteConnect::DisconnectED2K() {
310 	CECPacket req(EC_OP_SERVER_DISCONNECT);
311 	SendPacket(&req);
312 }
313 
RemoveServer(uint32 ip,uint16 port)314 void CRemoteConnect::RemoveServer(uint32 ip, uint16 port) {
315 	CECPacket req(EC_OP_SERVER_REMOVE);
316 	if (ip && port) {
317 		req.AddTag(CECTag(EC_TAG_SERVER, EC_IPv4_t(ip, port)));
318 	}
319 	SendPacket(&req);
320 }
321 // File_checked_for_headers
322