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