1 /*
2 * Copyright (c) 2013-2020, The PurpleI2P Project
3 *
4 * This file is part of Purple i2pd project and licensed under BSD3
5 *
6 * See full license text in LICENSE file at top of project tree
7 */
8 
9 #include <string.h>
10 #include "Log.h"
11 #include "ClientContext.h"
12 #include "util.h"
13 #include "BOB.h"
14 
15 #undef isset  // Prevent clash with isset macro from param.h
16 
17 namespace i2p
18 {
19 namespace client
20 {
BOBI2PInboundTunnel(const boost::asio::ip::tcp::endpoint & ep,std::shared_ptr<ClientDestination> localDestination)21 	BOBI2PInboundTunnel::BOBI2PInboundTunnel (const boost::asio::ip::tcp::endpoint& ep, std::shared_ptr<ClientDestination> localDestination):
22 		BOBI2PTunnel (localDestination), m_Acceptor (localDestination->GetService (), ep)
23 	{
24 	}
25 
~BOBI2PInboundTunnel()26 	BOBI2PInboundTunnel::~BOBI2PInboundTunnel ()
27 	{
28 		Stop ();
29 	}
30 
Start()31 	void BOBI2PInboundTunnel::Start ()
32 	{
33 		m_Acceptor.listen ();
34 		Accept ();
35 	}
36 
Stop()37 	void BOBI2PInboundTunnel::Stop ()
38 	{
39 		m_Acceptor.close();
40 		ClearHandlers ();
41 	}
42 
Accept()43 	void BOBI2PInboundTunnel::Accept ()
44 	{
45 		auto receiver = std::make_shared<AddressReceiver> ();
46 		receiver->socket = std::make_shared<boost::asio::ip::tcp::socket> (GetService ());
47 		m_Acceptor.async_accept (*receiver->socket, std::bind (&BOBI2PInboundTunnel::HandleAccept, this,
48 			std::placeholders::_1, receiver));
49 	}
50 
HandleAccept(const boost::system::error_code & ecode,std::shared_ptr<AddressReceiver> receiver)51 	void BOBI2PInboundTunnel::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr<AddressReceiver> receiver)
52 	{
53 		if (!ecode)
54 		{
55 			Accept ();
56 			ReceiveAddress (receiver);
57 		}
58 	}
59 
ReceiveAddress(std::shared_ptr<AddressReceiver> receiver)60 	void BOBI2PInboundTunnel::ReceiveAddress (std::shared_ptr<AddressReceiver> receiver)
61 	{
62 		receiver->socket->async_read_some (boost::asio::buffer(
63 			receiver->buffer + receiver->bufferOffset,
64 			BOB_COMMAND_BUFFER_SIZE - receiver->bufferOffset),
65 			std::bind(&BOBI2PInboundTunnel::HandleReceivedAddress, this,
66 			std::placeholders::_1, std::placeholders::_2, receiver));
67 	}
68 
HandleReceivedAddress(const boost::system::error_code & ecode,std::size_t bytes_transferred,std::shared_ptr<AddressReceiver> receiver)69 	void BOBI2PInboundTunnel::HandleReceivedAddress (const boost::system::error_code& ecode, std::size_t bytes_transferred,
70 		std::shared_ptr<AddressReceiver> receiver)
71 	{
72 		if (ecode)
73 			LogPrint (eLogError, "BOB: Inbound tunnel read error: ", ecode.message ());
74 		else
75 		{
76 			receiver->bufferOffset += bytes_transferred;
77 			receiver->buffer[receiver->bufferOffset] = 0;
78 			char * eol = strchr (receiver->buffer, '\n');
79 			if (eol)
80 			{
81 				*eol = 0;
82 				if (eol != receiver->buffer && eol[-1] == '\r') eol[-1] = 0; // workaround for Transmission, it sends '\r\n' terminated address
83 				receiver->data = (uint8_t *)eol + 1;
84 				receiver->dataLen = receiver->bufferOffset - (eol - receiver->buffer + 1);
85 				auto addr = context.GetAddressBook ().GetAddress (receiver->buffer);
86 				if (!addr)
87 				{
88 					LogPrint (eLogError, "BOB: Address ", receiver->buffer, " not found");
89 					return;
90 				}
91 				if (addr->IsIdentHash ())
92 				{
93 					auto leaseSet = GetLocalDestination ()->FindLeaseSet (addr->identHash);
94 					if (leaseSet)
95 						CreateConnection (receiver, leaseSet);
96 					else
97 						GetLocalDestination ()->RequestDestination (addr->identHash,
98 							std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete,
99 							this, std::placeholders::_1, receiver));
100 				}
101 				else
102 					GetLocalDestination ()->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey,
103 							std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete,
104 							this, std::placeholders::_1, receiver));
105 			}
106 			else
107 			{
108 				if (receiver->bufferOffset < BOB_COMMAND_BUFFER_SIZE)
109 					ReceiveAddress (receiver);
110 				else
111 					LogPrint (eLogError, "BOB: Missing inbound address");
112 			}
113 		}
114 	}
115 
HandleDestinationRequestComplete(std::shared_ptr<i2p::data::LeaseSet> leaseSet,std::shared_ptr<AddressReceiver> receiver)116 	void BOBI2PInboundTunnel::HandleDestinationRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::shared_ptr<AddressReceiver> receiver)
117 	{
118 		if (leaseSet)
119 			CreateConnection (receiver, leaseSet);
120 		else
121 			LogPrint (eLogError, "BOB: LeaseSet for inbound destination not found");
122 	}
123 
CreateConnection(std::shared_ptr<AddressReceiver> receiver,std::shared_ptr<const i2p::data::LeaseSet> leaseSet)124 	void BOBI2PInboundTunnel::CreateConnection (std::shared_ptr<AddressReceiver> receiver, std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
125 	{
126 		LogPrint (eLogDebug, "BOB: New inbound connection");
127 		auto connection = std::make_shared<I2PTunnelConnection>(this, receiver->socket, leaseSet);
128 		AddHandler (connection);
129 		connection->I2PConnect (receiver->data, receiver->dataLen);
130 	}
131 
BOBI2POutboundTunnel(const std::string & outhost,int port,std::shared_ptr<ClientDestination> localDestination,bool quiet)132 	BOBI2POutboundTunnel::BOBI2POutboundTunnel (const std::string& outhost, int port,
133 		std::shared_ptr<ClientDestination> localDestination, bool quiet): BOBI2PTunnel (localDestination),
134 		m_Endpoint (boost::asio::ip::address::from_string (outhost), port), m_IsQuiet (quiet)
135 	{
136 	}
137 
Start()138 	void BOBI2POutboundTunnel::Start ()
139 	{
140 		Accept ();
141 	}
142 
Stop()143 	void BOBI2POutboundTunnel::Stop ()
144 	{
145 		ClearHandlers ();
146 	}
147 
Accept()148 	void BOBI2POutboundTunnel::Accept ()
149 	{
150 		auto localDestination = GetLocalDestination ();
151 		if (localDestination)
152 			localDestination->AcceptStreams (std::bind (&BOBI2POutboundTunnel::HandleAccept, this, std::placeholders::_1));
153 		else
154 			LogPrint (eLogError, "BOB: Local destination not set for server tunnel");
155 	}
156 
HandleAccept(std::shared_ptr<i2p::stream::Stream> stream)157 	void BOBI2POutboundTunnel::HandleAccept (std::shared_ptr<i2p::stream::Stream> stream)
158 	{
159 		if (stream)
160 		{
161 			auto conn = std::make_shared<I2PTunnelConnection> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), m_Endpoint, m_IsQuiet);
162 			AddHandler (conn);
163 			conn->Connect ();
164 		}
165 	}
166 
BOBDestination(std::shared_ptr<ClientDestination> localDestination,const std::string & nickname,const std::string & inhost,const std::string & outhost,const int inport,const int outport,const bool quiet)167 	BOBDestination::BOBDestination (std::shared_ptr<ClientDestination> localDestination,
168 			const std::string &nickname, const std::string &inhost, const std::string &outhost,
169 			const int inport, const int outport, const bool quiet):
170 		m_LocalDestination (localDestination),
171 		m_OutboundTunnel (nullptr), m_InboundTunnel (nullptr),
172 		m_Nickname(nickname), m_InHost(inhost), m_OutHost(outhost),
173 		m_InPort(inport), m_OutPort(outport), m_Quiet(quiet)
174 	{
175 	}
176 
~BOBDestination()177 	BOBDestination::~BOBDestination ()
178 	{
179 		delete m_OutboundTunnel;
180 		delete m_InboundTunnel;
181 		i2p::client::context.DeleteLocalDestination (m_LocalDestination);
182 	}
183 
Start()184 	void BOBDestination::Start ()
185 	{
186 		if (m_OutboundTunnel) m_OutboundTunnel->Start ();
187 		if (m_InboundTunnel) m_InboundTunnel->Start ();
188 	}
189 
Stop()190 	void BOBDestination::Stop ()
191 	{
192 		StopTunnels ();
193 		m_LocalDestination->Stop ();
194 	}
195 
StopTunnels()196 	void BOBDestination::StopTunnels ()
197 	{
198 		if (m_OutboundTunnel)
199 		{
200 			m_OutboundTunnel->Stop ();
201 			delete m_OutboundTunnel;
202 			m_OutboundTunnel = nullptr;
203 		}
204 		if (m_InboundTunnel)
205 		{
206 			m_InboundTunnel->Stop ();
207 			delete m_InboundTunnel;
208 			m_InboundTunnel = nullptr;
209 		}
210 	}
211 
CreateInboundTunnel(int port,const std::string & inhost)212 	void BOBDestination::CreateInboundTunnel (int port, const std::string& inhost)
213 	{
214 		if (!m_InboundTunnel)
215 		{
216 			// update inport and inhost (user can stop tunnel and change)
217 			m_InPort = port;
218 			m_InHost = inhost;
219 			boost::asio::ip::tcp::endpoint ep(boost::asio::ip::tcp::v4(), port);
220 			if (!inhost.empty ())
221 			{
222 				boost::system::error_code ec;
223 				auto addr = boost::asio::ip::address::from_string (inhost, ec);
224 				if (!ec)
225 					ep.address (addr);
226 				else
227 					LogPrint (eLogError, "BOB: ", ec.message ());
228 			}
229 			m_InboundTunnel = new BOBI2PInboundTunnel (ep, m_LocalDestination);
230 		}
231 	}
232 
CreateOutboundTunnel(const std::string & outhost,int port,bool quiet)233 	void BOBDestination::CreateOutboundTunnel (const std::string& outhost, int port, bool quiet)
234 	{
235 		if (!m_OutboundTunnel)
236 		{
237 			// update outport and outhost (user can stop tunnel and change)
238 			m_OutPort = port;
239 			m_OutHost = outhost;
240 			m_OutboundTunnel = new BOBI2POutboundTunnel (outhost, port, m_LocalDestination, quiet);
241 		}
242 	}
243 
BOBCommandSession(BOBCommandChannel & owner)244 	BOBCommandSession::BOBCommandSession (BOBCommandChannel& owner):
245 		m_Owner (owner), m_Socket (m_Owner.GetService ()),
246 		m_ReceiveBuffer(BOB_COMMAND_BUFFER_SIZE + 1), m_SendBuffer(BOB_COMMAND_BUFFER_SIZE + 1),
247 		m_IsOpen (true), m_IsQuiet (false), m_IsActive (false),
248 		m_InPort (0), m_OutPort (0), m_CurrentDestination (nullptr)
249 	{
250 	}
251 
~BOBCommandSession()252 	BOBCommandSession::~BOBCommandSession ()
253 	{
254 	}
255 
Terminate()256 	void BOBCommandSession::Terminate ()
257 	{
258 		m_Socket.close ();
259 		m_IsOpen = false;
260 	}
261 
Receive()262 	void BOBCommandSession::Receive ()
263 	{
264 		boost::asio::async_read_until(m_Socket, m_ReceiveBuffer, '\n',
265 			std::bind(&BOBCommandSession::HandleReceivedLine, shared_from_this(),
266 				std::placeholders::_1, std::placeholders::_2));
267 	}
268 
HandleReceivedLine(const boost::system::error_code & ecode,std::size_t bytes_transferred)269 	void BOBCommandSession::HandleReceivedLine(const boost::system::error_code& ecode, std::size_t bytes_transferred)
270 	{
271 		if(ecode)
272 		{
273 			LogPrint (eLogError, "BOB: Command channel read error: ", ecode.message());
274 			if (ecode != boost::asio::error::operation_aborted)
275 				Terminate ();
276 		}
277 		else
278 		{
279 			std::string line;
280 
281 			std::istream is(&m_ReceiveBuffer);
282 			std::getline(is, line);
283 
284 			std::string command, operand;
285 			std::istringstream iss(line);
286 			iss >> command >> operand;
287 
288 			// process command
289 			auto& handlers = m_Owner.GetCommandHandlers();
290 			auto it = handlers.find(command);
291 			if(it != handlers.end())
292 			{
293 				(this->*(it->second))(operand.c_str(), operand.length());
294 			}
295 			else
296 			{
297 				LogPrint (eLogError, "BOB: Unknown command ", command.c_str());
298 				SendReplyError ("unknown command");
299 			}
300 		}
301 	}
302 
Send()303 	void BOBCommandSession::Send ()
304 	{
305 		boost::asio::async_write (m_Socket, m_SendBuffer,
306 			boost::asio::transfer_all (),
307 			std::bind(&BOBCommandSession::HandleSent, shared_from_this (),
308 				std::placeholders::_1, std::placeholders::_2));
309 	}
310 
HandleSent(const boost::system::error_code & ecode,std::size_t bytes_transferred)311 	void BOBCommandSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
312 	{
313 		if (ecode)
314 		{
315 			LogPrint (eLogError, "BOB: Command channel send error: ", ecode.message ());
316 			if (ecode != boost::asio::error::operation_aborted)
317 				Terminate ();
318 		}
319 		else
320 		{
321 			if (m_IsOpen)
322 				Receive ();
323 			else
324 				Terminate ();
325 		}
326 	}
327 
SendReplyOK(const char * msg)328 	void BOBCommandSession::SendReplyOK (const char * msg)
329 	{
330 		std::ostream os(&m_SendBuffer);
331 		os << "OK";
332 		if(msg)
333 		{
334 			os << " " << msg;
335 		}
336 		os << std::endl;
337 		Send ();
338 	}
339 
SendReplyError(const char * msg)340 	void BOBCommandSession::SendReplyError (const char * msg)
341 	{
342 		std::ostream os(&m_SendBuffer);
343 		os << "ERROR " << msg << std::endl;
344 		Send ();
345 	}
346 
SendVersion()347 	void BOBCommandSession::SendVersion ()
348 	{
349 		std::ostream os(&m_SendBuffer);
350 		os << "BOB 00.00.10" << std::endl;
351 		SendReplyOK();
352 	}
353 
SendRaw(const char * data)354 	void BOBCommandSession::SendRaw (const char * data)
355 	{
356 		std::ostream os(&m_SendBuffer);
357 		os << data << std::endl;
358 	}
359 
BuildStatusLine(bool currentTunnel,BOBDestination * dest,std::string & out)360 	void BOBCommandSession::BuildStatusLine(bool currentTunnel, BOBDestination *dest, std::string &out)
361 	{
362 		// helper lambdas
363 		const auto issetStr = [](const std::string &str) { return str.empty() ? "not_set" : str; }; // for inhost, outhost
364 		const auto issetNum = [&issetStr](const int p) { return issetStr(p == 0 ? "" : std::to_string(p)); }; // for inport, outport
365 		const auto destExists = [](const BOBDestination * const dest) { return dest != nullptr; };
366 		const auto destReady = [](const BOBDestination * const dest) { return dest->GetLocalDestination()->IsReady(); };
367 		const auto bool_str = [](const bool v) { return v ? "true" : "false"; }; // bool -> str
368 
369 		// tunnel info
370 		const std::string nickname = currentTunnel ? m_Nickname : dest->GetNickname();
371 		const bool quiet = currentTunnel ? m_IsQuiet : dest->GetQuiet();
372 		const std::string inhost = issetStr(currentTunnel ? m_InHost : dest->GetInHost());
373 		const std::string outhost = issetStr(currentTunnel ? m_OutHost : dest->GetOutHost());
374 		const std::string inport = issetNum(currentTunnel ? m_InPort : dest->GetInPort());
375 		const std::string outport = issetNum(currentTunnel ? m_OutPort : dest->GetOutPort());
376 		const bool keys = destExists(dest); // key must exist when destination is created
377 		const bool starting = destExists(dest) && !destReady(dest);
378 		const bool running = destExists(dest) && destReady(dest);
379 		const bool stopping = false;
380 
381 		// build line
382 		std::stringstream ss;
383 		ss	<< "DATA "
384 			<< "NICKNAME: " << nickname          << " " << "STARTING: " << bool_str(starting) << " "
385 			<< "RUNNING: "  << bool_str(running) << " " << "STOPPING: " << bool_str(stopping) << " "
386 			<< "KEYS: "     << bool_str(keys)    << " " << "QUIET: "    << bool_str(quiet) << " "
387 			<< "INPORT: "   << inport            << " " << "INHOST: "   << inhost << " "
388 			<< "OUTPORT: "  << outport           << " " << "OUTHOST: "  << outhost;
389 		out = ss.str();
390 	}
391 
ZapCommandHandler(const char * operand,size_t len)392 	void BOBCommandSession::ZapCommandHandler (const char * operand, size_t len)
393 	{
394 		LogPrint (eLogDebug, "BOB: zap");
395 		Terminate ();
396 	}
397 
QuitCommandHandler(const char * operand,size_t len)398 	void BOBCommandSession::QuitCommandHandler (const char * operand, size_t len)
399 	{
400 		LogPrint (eLogDebug, "BOB: quit");
401 		m_IsOpen = false;
402 		SendReplyOK ("Bye!");
403 	}
404 
StartCommandHandler(const char * operand,size_t len)405 	void BOBCommandSession::StartCommandHandler (const char * operand, size_t len)
406 	{
407 		LogPrint (eLogDebug, "BOB: start ", m_Nickname);
408 		if (m_IsActive)
409 		{
410 			SendReplyError ("tunnel is active");
411 			return;
412 		}
413 		if (!m_Keys.GetPublic ()) // keys are set ?
414 		{
415 			SendReplyError("Keys must be set.");
416 			return;
417 		}
418 		if (m_InPort == 0
419 			&& m_OutHost.empty() && m_OutPort == 0)
420 		{
421 			SendReplyError("(inhost):inport or outhost:outport must be set.");
422 			return;
423 		}
424 		if(!m_InHost.empty())
425 		{
426 			// TODO: FIXME: temporary validation, until hostname support is added
427 			boost::system::error_code ec;
428 			boost::asio::ip::address::from_string(m_InHost, ec);
429 			if (ec)
430 			{
431 				SendReplyError("inhost must be a valid IPv4 address.");
432 				return;
433 			}
434 		}
435 		if(!m_OutHost.empty())
436 		{
437 			// TODO: FIXME: temporary validation, until hostname support is added
438 			boost::system::error_code ec;
439 			boost::asio::ip::address::from_string(m_OutHost, ec);
440 			if (ec)
441 			{
442 				SendReplyError("outhost must be a IPv4 address.");
443 				return;
444 			}
445 		}
446 
447 		if (!m_CurrentDestination)
448 		{
449 			m_CurrentDestination = new BOBDestination (i2p::client::context.CreateNewLocalDestination (m_Keys, true, &m_Options), // deleted in clear command
450 				m_Nickname, m_InHost, m_OutHost, m_InPort, m_OutPort, m_IsQuiet);
451 			m_Owner.AddDestination (m_Nickname, m_CurrentDestination);
452 		}
453 		if (m_InPort)
454 			m_CurrentDestination->CreateInboundTunnel (m_InPort, m_InHost);
455 		if (m_OutPort && !m_OutHost.empty ())
456 			m_CurrentDestination->CreateOutboundTunnel (m_OutHost, m_OutPort, m_IsQuiet);
457 		m_CurrentDestination->Start ();
458 		SendReplyOK ("Tunnel starting");
459 		m_IsActive = true;
460 	}
461 
StopCommandHandler(const char * operand,size_t len)462 	void BOBCommandSession::StopCommandHandler (const char * operand, size_t len)
463 	{
464 		LogPrint (eLogDebug, "BOB: stop ", m_Nickname);
465 		if (!m_IsActive)
466 		{
467 			SendReplyError ("tunnel is inactive");
468 			return;
469 		}
470 		auto dest = m_Owner.FindDestination (m_Nickname);
471 		if (dest)
472 		{
473 			dest->StopTunnels ();
474 			SendReplyOK ("Tunnel stopping");
475 		}
476 		else
477 			SendReplyError ("tunnel not found");
478 		m_IsActive = false;
479 	}
480 
SetNickCommandHandler(const char * operand,size_t len)481 	void BOBCommandSession::SetNickCommandHandler (const char * operand, size_t len)
482 	{
483 		LogPrint (eLogDebug, "BOB: setnick ", operand);
484 		m_Nickname = operand;
485 		std::string msg ("Nickname set to ");
486 		msg += m_Nickname;
487 		SendReplyOK (msg.c_str ());
488 	}
489 
GetNickCommandHandler(const char * operand,size_t len)490 	void BOBCommandSession::GetNickCommandHandler (const char * operand, size_t len)
491 	{
492 		LogPrint (eLogDebug, "BOB: getnick ", operand);
493 		m_CurrentDestination = m_Owner.FindDestination (operand);
494 		if (m_CurrentDestination)
495 		{
496 			m_Keys = m_CurrentDestination->GetKeys ();
497 			m_Nickname = operand;
498 		}
499 		if (m_Nickname == operand)
500 		{
501 			std::string msg ("Nickname set to ");
502 			msg += m_Nickname;
503 			SendReplyOK (msg.c_str ());
504 		}
505 		else
506 			SendReplyError ("no nickname has been set");
507 	}
508 
NewkeysCommandHandler(const char * operand,size_t len)509 	void BOBCommandSession::NewkeysCommandHandler (const char * operand, size_t len)
510 	{
511 		LogPrint (eLogDebug, "BOB: newkeys");
512 		i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1;
513 		i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL;
514 		if (*operand)
515 		{
516 			try
517 			{
518 				char * operand1 = (char *)strchr (operand, ' ');
519 				if (operand1)
520 				{
521 					*operand1 = 0; operand1++;
522 					cryptoType = std::stoi(operand1);
523 				}
524 				signatureType = std::stoi(operand);
525 			}
526 			catch (std::invalid_argument& ex)
527 			{
528 				LogPrint (eLogWarning, "BOB: Error on newkeys: ", ex.what ());
529 			}
530 		}
531 
532 
533 		m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (signatureType, cryptoType);
534 		SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ());
535 	}
536 
SetkeysCommandHandler(const char * operand,size_t len)537 	void BOBCommandSession::SetkeysCommandHandler (const char * operand, size_t len)
538 	{
539 		LogPrint (eLogDebug, "BOB: setkeys ", operand);
540 		if (m_Keys.FromBase64 (operand))
541 			SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ());
542 		else
543 			SendReplyError ("invalid keys");
544 	}
545 
GetkeysCommandHandler(const char * operand,size_t len)546 	void BOBCommandSession::GetkeysCommandHandler (const char * operand, size_t len)
547 	{
548 		LogPrint (eLogDebug, "BOB: getkeys");
549 		if (m_Keys.GetPublic ()) // keys are set ?
550 			SendReplyOK (m_Keys.ToBase64 ().c_str ());
551 		else
552 			SendReplyError ("keys are not set");
553 	}
554 
GetdestCommandHandler(const char * operand,size_t len)555 	void BOBCommandSession::GetdestCommandHandler (const char * operand, size_t len)
556 	{
557 		LogPrint (eLogDebug, "BOB: getdest");
558 		if (m_Keys.GetPublic ()) // keys are set ?
559 			SendReplyOK (m_Keys.GetPublic ()->ToBase64 ().c_str ());
560 		else
561 			SendReplyError ("keys are not set");
562 	}
563 
OuthostCommandHandler(const char * operand,size_t len)564 	void BOBCommandSession::OuthostCommandHandler (const char * operand, size_t len)
565 	{
566 		LogPrint (eLogDebug, "BOB: outhost ", operand);
567 		m_OutHost = operand;
568 		SendReplyOK ("outhost set");
569 	}
570 
OutportCommandHandler(const char * operand,size_t len)571 	void BOBCommandSession::OutportCommandHandler (const char * operand, size_t len)
572 	{
573 		LogPrint (eLogDebug, "BOB: outport ", operand);
574 		m_OutPort = std::stoi(operand);
575 		if (m_OutPort >= 0)
576 			SendReplyOK ("outbound port set");
577 		else
578 			SendReplyError ("port out of range");
579 	}
580 
InhostCommandHandler(const char * operand,size_t len)581 	void BOBCommandSession::InhostCommandHandler (const char * operand, size_t len)
582 	{
583 		LogPrint (eLogDebug, "BOB: inhost ", operand);
584 		m_InHost = operand;
585 		SendReplyOK ("inhost set");
586 	}
587 
InportCommandHandler(const char * operand,size_t len)588 	void BOBCommandSession::InportCommandHandler (const char * operand, size_t len)
589 	{
590 		LogPrint (eLogDebug, "BOB: inport ", operand);
591 		m_InPort = std::stoi(operand);
592 		if (m_InPort >= 0)
593 			SendReplyOK ("inbound port set");
594 		else
595 			SendReplyError ("port out of range");
596 	}
597 
QuietCommandHandler(const char * operand,size_t len)598 	void BOBCommandSession::QuietCommandHandler (const char * operand, size_t len)
599 	{
600 		LogPrint (eLogDebug, "BOB: quiet");
601 		if (m_Nickname.length () > 0)
602 		{
603 			if (!m_IsActive)
604 			{
605 				m_IsQuiet = true;
606 				SendReplyOK ("Quiet set");
607 			}
608 			else
609 				SendReplyError ("tunnel is active");
610 		}
611 		else
612 			SendReplyError ("no nickname has been set");
613 	}
614 
LookupCommandHandler(const char * operand,size_t len)615 	void BOBCommandSession::LookupCommandHandler (const char * operand, size_t len)
616 	{
617 		LogPrint (eLogDebug, "BOB: lookup ", operand);
618 		auto addr = context.GetAddressBook ().GetAddress (operand);
619 		if (!addr)
620 		{
621 			SendReplyError ("Address Not found");
622 			return;
623 		}
624 		auto localDestination = m_CurrentDestination ? m_CurrentDestination->GetLocalDestination () : i2p::client::context.GetSharedLocalDestination ();
625 		if (addr->IsIdentHash ())
626 		{
627 			// we might have leaseset already
628 			auto leaseSet = localDestination->FindLeaseSet (addr->identHash);
629 			if (leaseSet)
630 			{
631 				SendReplyOK (leaseSet->GetIdentity ()->ToBase64 ().c_str ());
632 				return;
633 			}
634 		}
635 		// trying to request
636 		auto s = shared_from_this ();
637 		auto requstCallback = [s](std::shared_ptr<i2p::data::LeaseSet> ls)
638 			{
639 				if (ls)
640 					s->SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ());
641 				else
642 					s->SendReplyError ("LeaseSet Not found");
643 			};
644 		if (addr->IsIdentHash ())
645 			localDestination->RequestDestination (addr->identHash, requstCallback);
646 		else
647 			localDestination->RequestDestinationWithEncryptedLeaseSet (addr->blindedPublicKey, requstCallback);
648 	}
649 
LookupLocalCommandHandler(const char * operand,size_t len)650 	void BOBCommandSession::LookupLocalCommandHandler (const char * operand, size_t len)
651 	{
652 		LogPrint (eLogDebug, "BOB: lookup local ", operand);
653 		auto addr = context.GetAddressBook ().GetAddress (operand);
654 		if (!addr)
655 		{
656 			SendReplyError ("Address Not found");
657 			return;
658 		}
659 		auto ls = i2p::data::netdb.FindLeaseSet (addr->identHash);
660 		if (ls)
661 			SendReplyOK (ls->GetIdentity ()->ToBase64 ().c_str ());
662 		else
663 			SendReplyError ("Local LeaseSet Not found");
664 	}
665 
ClearCommandHandler(const char * operand,size_t len)666 	void BOBCommandSession::ClearCommandHandler (const char * operand, size_t len)
667 	{
668 		LogPrint (eLogDebug, "BOB: clear");
669 		m_Owner.DeleteDestination (m_Nickname);
670 		m_Nickname = "";
671 		SendReplyOK ("cleared");
672 	}
673 
ListCommandHandler(const char * operand,size_t len)674 	void BOBCommandSession::ListCommandHandler (const char * operand, size_t len)
675 	{
676 		LogPrint (eLogDebug, "BOB: list");
677 		std::string statusLine;
678 		bool sentCurrent = false;
679 		const auto& destinations = m_Owner.GetDestinations ();
680 		for (const auto& it: destinations)
681 		{
682 			BuildStatusLine(false, it.second, statusLine);
683 			SendRaw(statusLine.c_str());
684 			if(m_Nickname.compare(it.second->GetNickname()) == 0)
685 				sentCurrent = true;
686 		}
687 		if(!sentCurrent && !m_Nickname.empty())
688 		{
689 			// add the current tunnel to the list.
690 			// this is for the incomplete tunnel which has not been started yet.
691 			BuildStatusLine(true, m_CurrentDestination, statusLine);
692 			SendRaw(statusLine.c_str());
693 		}
694 		SendReplyOK ("Listing done");
695 	}
696 
OptionCommandHandler(const char * operand,size_t len)697 	void BOBCommandSession::OptionCommandHandler (const char * operand, size_t len)
698 	{
699 		LogPrint (eLogDebug, "BOB: option ", operand);
700 		const char * value = strchr (operand, '=');
701 		if (value)
702 		{
703 			std::string msg ("option ");
704 			*(const_cast<char *>(value)) = 0;
705 			m_Options[operand] = value + 1;
706 			msg += operand;
707 			*(const_cast<char *>(value)) = '=';
708 			msg += " set to ";
709 			msg += value;
710 			SendReplyOK (msg.c_str ());
711 		}
712 		else
713 			SendReplyError ("malformed");
714 	}
715 
StatusCommandHandler(const char * operand,size_t len)716 	void BOBCommandSession::StatusCommandHandler (const char * operand, size_t len)
717 	{
718 		LogPrint (eLogDebug, "BOB: status ", operand);
719 		const std::string name = operand;
720 		std::string statusLine;
721 
722 		// always prefer destination
723 		auto ptr = m_Owner.FindDestination(name);
724 		if(ptr != nullptr)
725 		{
726 			// tunnel destination exists
727 			BuildStatusLine(false, ptr, statusLine);
728 			SendReplyOK(statusLine.c_str());
729 		}
730 		else
731 		{
732 			if(m_Nickname == name && !name.empty())
733 			{
734 				// tunnel is incomplete / has not been started yet
735 				BuildStatusLine(true, nullptr, statusLine);
736 				SendReplyOK(statusLine.c_str());
737 			}
738 			else
739 			{
740 				SendReplyError("no nickname has been set");
741 			}
742 		}
743 	}
HelpCommandHandler(const char * operand,size_t len)744 	void BOBCommandSession::HelpCommandHandler (const char * operand, size_t len)
745 	{
746 		auto helpStrings = m_Owner.GetHelpStrings();
747 		if(len == 0)
748 		{
749 			std::stringstream ss;
750 			ss << "COMMANDS:";
751 			for (auto const& x : helpStrings)
752 			{
753 				ss << " " << x.first;
754 			}
755 			const std::string &str = ss.str();
756 			SendReplyOK(str.c_str());
757 		}
758 		else
759 		{
760 			auto it = helpStrings.find(operand);
761 			if (it != helpStrings.end ())
762 			{
763 				SendReplyOK(it->second.c_str());
764 				return;
765 			}
766 			SendReplyError("No such command");
767 		}
768 	}
769 
BOBCommandChannel(const std::string & address,int port)770 	BOBCommandChannel::BOBCommandChannel (const std::string& address, int port):
771 		RunnableService ("BOB"),
772 		m_Acceptor (GetIOService (), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port))
773 	{
774 		// command -> handler
775 		m_CommandHandlers[BOB_COMMAND_ZAP] = &BOBCommandSession::ZapCommandHandler;
776 		m_CommandHandlers[BOB_COMMAND_QUIT] = &BOBCommandSession::QuitCommandHandler;
777 		m_CommandHandlers[BOB_COMMAND_START] = &BOBCommandSession::StartCommandHandler;
778 		m_CommandHandlers[BOB_COMMAND_STOP] = &BOBCommandSession::StopCommandHandler;
779 		m_CommandHandlers[BOB_COMMAND_SETNICK] = &BOBCommandSession::SetNickCommandHandler;
780 		m_CommandHandlers[BOB_COMMAND_GETNICK] = &BOBCommandSession::GetNickCommandHandler;
781 		m_CommandHandlers[BOB_COMMAND_NEWKEYS] = &BOBCommandSession::NewkeysCommandHandler;
782 		m_CommandHandlers[BOB_COMMAND_GETKEYS] = &BOBCommandSession::GetkeysCommandHandler;
783 		m_CommandHandlers[BOB_COMMAND_SETKEYS] = &BOBCommandSession::SetkeysCommandHandler;
784 		m_CommandHandlers[BOB_COMMAND_GETDEST] = &BOBCommandSession::GetdestCommandHandler;
785 		m_CommandHandlers[BOB_COMMAND_OUTHOST] = &BOBCommandSession::OuthostCommandHandler;
786 		m_CommandHandlers[BOB_COMMAND_OUTPORT] = &BOBCommandSession::OutportCommandHandler;
787 		m_CommandHandlers[BOB_COMMAND_INHOST] = &BOBCommandSession::InhostCommandHandler;
788 		m_CommandHandlers[BOB_COMMAND_INPORT] = &BOBCommandSession::InportCommandHandler;
789 		m_CommandHandlers[BOB_COMMAND_QUIET] = &BOBCommandSession::QuietCommandHandler;
790 		m_CommandHandlers[BOB_COMMAND_LOOKUP] = &BOBCommandSession::LookupCommandHandler;
791 		m_CommandHandlers[BOB_COMMAND_LOOKUP_LOCAL] = &BOBCommandSession::LookupLocalCommandHandler;
792 		m_CommandHandlers[BOB_COMMAND_CLEAR] = &BOBCommandSession::ClearCommandHandler;
793 		m_CommandHandlers[BOB_COMMAND_LIST] = &BOBCommandSession::ListCommandHandler;
794 		m_CommandHandlers[BOB_COMMAND_OPTION] = &BOBCommandSession::OptionCommandHandler;
795 		m_CommandHandlers[BOB_COMMAND_STATUS] = &BOBCommandSession::StatusCommandHandler;
796 		m_CommandHandlers[BOB_COMMAND_HELP] = &BOBCommandSession::HelpCommandHandler;
797 		// command -> help string
798 		m_HelpStrings[BOB_COMMAND_ZAP] = BOB_HELP_ZAP;
799 		m_HelpStrings[BOB_COMMAND_QUIT] = BOB_HELP_QUIT;
800 		m_HelpStrings[BOB_COMMAND_START] = BOB_HELP_START;
801 		m_HelpStrings[BOB_COMMAND_STOP] = BOB_HELP_STOP;
802 		m_HelpStrings[BOB_COMMAND_SETNICK] = BOB_HELP_SETNICK;
803 		m_HelpStrings[BOB_COMMAND_GETNICK] = BOB_HELP_GETNICK;
804 		m_HelpStrings[BOB_COMMAND_NEWKEYS] = BOB_HELP_NEWKEYS;
805 		m_HelpStrings[BOB_COMMAND_GETKEYS] = BOB_HELP_GETKEYS;
806 		m_HelpStrings[BOB_COMMAND_SETKEYS] = BOB_HELP_SETKEYS;
807 		m_HelpStrings[BOB_COMMAND_GETDEST] = BOB_HELP_GETDEST;
808 		m_HelpStrings[BOB_COMMAND_OUTHOST] = BOB_HELP_OUTHOST;
809 		m_HelpStrings[BOB_COMMAND_OUTPORT] = BOB_HELP_OUTPORT;
810 		m_HelpStrings[BOB_COMMAND_INHOST] = BOB_HELP_INHOST;
811 		m_HelpStrings[BOB_COMMAND_INPORT] = BOB_HELP_INPORT;
812 		m_HelpStrings[BOB_COMMAND_QUIET] = BOB_HELP_QUIET;
813 		m_HelpStrings[BOB_COMMAND_LOOKUP] = BOB_HELP_LOOKUP;
814 		m_HelpStrings[BOB_COMMAND_CLEAR] = BOB_HELP_CLEAR;
815 		m_HelpStrings[BOB_COMMAND_LIST] = BOB_HELP_LIST;
816 		m_HelpStrings[BOB_COMMAND_OPTION] = BOB_HELP_OPTION;
817 		m_HelpStrings[BOB_COMMAND_STATUS] = BOB_HELP_STATUS;
818 		m_HelpStrings[BOB_COMMAND_HELP] = BOB_HELP_HELP;
819 	}
820 
~BOBCommandChannel()821 	BOBCommandChannel::~BOBCommandChannel ()
822 	{
823 		if (IsRunning ())
824 			Stop ();
825 		for (const auto& it: m_Destinations)
826 			delete it.second;
827 	}
828 
Start()829 	void BOBCommandChannel::Start ()
830 	{
831 		Accept ();
832 		StartIOService ();
833 	}
834 
Stop()835 	void BOBCommandChannel::Stop ()
836 	{
837 		for (auto& it: m_Destinations)
838 			it.second->Stop ();
839 		m_Acceptor.cancel ();
840 		StopIOService ();
841 	}
842 
AddDestination(const std::string & name,BOBDestination * dest)843 	void BOBCommandChannel::AddDestination (const std::string& name, BOBDestination * dest)
844 	{
845 		m_Destinations[name] = dest;
846 	}
847 
DeleteDestination(const std::string & name)848 	void BOBCommandChannel::DeleteDestination (const std::string& name)
849 	{
850 		auto it = m_Destinations.find (name);
851 		if (it != m_Destinations.end ())
852 		{
853 			it->second->Stop ();
854 			delete it->second;
855 			m_Destinations.erase (it);
856 		}
857 	}
858 
FindDestination(const std::string & name)859 	BOBDestination * BOBCommandChannel::FindDestination (const std::string& name)
860 	{
861 		auto it = m_Destinations.find (name);
862 		if (it != m_Destinations.end ())
863 			return it->second;
864 		return nullptr;
865 	}
866 
Accept()867 	void BOBCommandChannel::Accept ()
868 	{
869 		auto newSession = std::make_shared<BOBCommandSession> (*this);
870 		m_Acceptor.async_accept (newSession->GetSocket (), std::bind (&BOBCommandChannel::HandleAccept, this,
871 			std::placeholders::_1, newSession));
872 	}
873 
HandleAccept(const boost::system::error_code & ecode,std::shared_ptr<BOBCommandSession> session)874 	void BOBCommandChannel::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<BOBCommandSession> session)
875 	{
876 		if (ecode != boost::asio::error::operation_aborted)
877 			Accept ();
878 
879 		if (!ecode)
880 		{
881 			LogPrint (eLogInfo, "BOB: New command connection from ", session->GetSocket ().remote_endpoint ());
882 			session->SendVersion ();
883 		}
884 		else
885 			LogPrint (eLogError, "BOB: Accept error: ", ecode.message ());
886 	}
887 }
888 }
889