diff --git a/BOB.cpp b/BOB.cpp index f983c67a..caf766af 100644 --- a/BOB.cpp +++ b/BOB.cpp @@ -70,7 +70,7 @@ namespace client if (eol) { *eol = 0; - + if (eol != receiver->buffer && eol[-1] == '\r') eol[-1] = 0; // workaround for Transmission, it sends '\r\n' terminated address receiver->data = (uint8_t *)eol + 1; receiver->dataLen = receiver->bufferOffset - (eol - receiver->buffer + 1); i2p::data::IdentHash ident; @@ -203,7 +203,7 @@ namespace client BOBCommandSession::BOBCommandSession (BOBCommandChannel& owner): m_Owner (owner), m_Socket (m_Owner.GetService ()), - m_ReceiveBufferOffset (0), m_IsOpen (true), m_IsQuiet (false), + m_ReceiveBufferOffset (0), m_IsOpen (true), m_IsQuiet (false), m_IsActive (false), m_InPort (0), m_OutPort (0), m_CurrentDestination (nullptr) { } @@ -354,6 +354,11 @@ namespace client void BOBCommandSession::StartCommandHandler (const char * operand, size_t len) { LogPrint (eLogDebug, "BOB: start ", m_Nickname); + if (m_IsActive) + { + SendReplyError ("tunnel is active"); + return; + } if (!m_CurrentDestination) { m_CurrentDestination = new BOBDestination (i2p::client::context.CreateNewLocalDestination (m_Keys, true, &m_Options)); @@ -364,19 +369,27 @@ namespace client if (m_OutPort && !m_Address.empty ()) m_CurrentDestination->CreateOutboundTunnel (m_Address, m_OutPort, m_IsQuiet); m_CurrentDestination->Start (); - SendReplyOK ("tunnel starting"); + SendReplyOK ("Tunnel starting"); + m_IsActive = true; } void BOBCommandSession::StopCommandHandler (const char * operand, size_t len) { + LogPrint (eLogDebug, "BOB: stop ", m_Nickname); + if (!m_IsActive) + { + SendReplyError ("tunnel is inactive"); + return; + } auto dest = m_Owner.FindDestination (m_Nickname); if (dest) { dest->StopTunnels (); - SendReplyOK ("tunnel stopping"); + SendReplyOK ("Tunnel stopping"); } else SendReplyError ("tunnel not found"); + m_IsActive = false; } void BOBCommandSession::SetNickCommandHandler (const char * operand, size_t len) @@ -384,7 +397,7 @@ namespace client LogPrint (eLogDebug, "BOB: setnick ", operand); m_Nickname = operand; std::string msg ("Nickname set to "); - msg += operand; + msg += m_Nickname; SendReplyOK (msg.c_str ()); } @@ -396,12 +409,15 @@ namespace client { m_Keys = m_CurrentDestination->GetKeys (); m_Nickname = operand; + } + if (m_Nickname == operand) + { std::string msg ("Nickname set to "); - msg += operand; + msg += m_Nickname; SendReplyOK (msg.c_str ()); - } + } else - SendReplyError ("tunnel not found"); + SendReplyError ("no nickname has been set"); } void BOBCommandSession::NewkeysCommandHandler (const char * operand, size_t len) @@ -441,7 +457,10 @@ namespace client { LogPrint (eLogDebug, "BOB: outport ", operand); m_OutPort = boost::lexical_cast(operand); - SendReplyOK ("outbound port set"); + if (m_OutPort >= 0) + SendReplyOK ("outbound port set"); + else + SendReplyError ("port out of range"); } void BOBCommandSession::InhostCommandHandler (const char * operand, size_t len) @@ -455,14 +474,27 @@ namespace client { LogPrint (eLogDebug, "BOB: inport ", operand); m_InPort = boost::lexical_cast(operand); - SendReplyOK ("inbound port set"); + if (m_InPort >= 0) + SendReplyOK ("inbound port set"); + else + SendReplyError ("port out of range"); } void BOBCommandSession::QuietCommandHandler (const char * operand, size_t len) { LogPrint (eLogDebug, "BOB: quiet"); - m_IsQuiet = true; - SendReplyOK ("quiet"); + if (m_Nickname.length () > 0) + { + if (!m_IsActive) + { + m_IsQuiet = true; + SendReplyOK ("Quiet set"); + } + else + SendReplyError ("tunnel is active"); + } + else + SendReplyError ("no nickname has been set"); } void BOBCommandSession::LookupCommandHandler (const char * operand, size_t len) @@ -497,6 +529,7 @@ namespace client { LogPrint (eLogDebug, "BOB: clear"); m_Owner.DeleteDestination (m_Nickname); + m_Nickname = ""; SendReplyOK ("cleared"); } @@ -515,10 +548,14 @@ namespace client const char * value = strchr (operand, '='); if (value) { + std::string msg ("option "); *(const_cast(value)) = 0; m_Options[operand] = value + 1; + msg += operand; *(const_cast(value)) = '='; - SendReplyOK ("option"); + msg += " set to "; + msg += value; + SendReplyOK (msg.c_str ()); } else SendReplyError ("malformed"); @@ -527,24 +564,24 @@ namespace client void BOBCommandSession::StatusCommandHandler (const char * operand, size_t len) { LogPrint (eLogDebug, "BOB: status ", operand); - if (operand == m_Nickname) + if (m_Nickname == operand) { std::stringstream s; - s << "DATA"; s << " NICKNAME:"; s << operand; + s << "DATA"; s << " NICKNAME: "; s << m_Nickname; if (m_CurrentDestination->GetLocalDestination ()->IsReady ()) - s << " STARTING:false RUNNING:true STOPPING:false"; + s << " STARTING: false RUNNING: true STOPPING: false"; else - s << " STARTING:true RUNNING:false STOPPING:false"; - s << " KEYS: true"; s << " QUIET:"; s << (m_IsQuiet ? "true":"false"); + s << " STARTING: true RUNNING: false STOPPING: false"; + s << " KEYS: true"; s << " QUIET: "; s << (m_IsQuiet ? "true":"false"); if (m_InPort) { - s << " INPORT:" << m_InPort; - s << " INHOST:" << (m_Address.length () > 0 ? m_Address : "127.0.0.1"); + s << " INPORT: " << m_InPort; + s << " INHOST: " << (m_Address.length () > 0 ? m_Address : "127.0.0.1"); } if (m_OutPort) { - s << " OUTPORT:" << m_OutPort; - s << " OUTHOST:" << (m_Address.length () > 0 ? m_Address : "127.0.0.1"); + s << " OUTPORT: " << m_OutPort; + s << " OUTHOST: " << (m_Address.length () > 0 ? m_Address : "127.0.0.1"); } SendReplyOK (s.str().c_str()); } diff --git a/BOB.h b/BOB.h index d2118c5c..104073bd 100644 --- a/BOB.h +++ b/BOB.h @@ -188,7 +188,7 @@ namespace client boost::asio::ip::tcp::socket m_Socket; char m_ReceiveBuffer[BOB_COMMAND_BUFFER_SIZE + 1], m_SendBuffer[BOB_COMMAND_BUFFER_SIZE + 1]; size_t m_ReceiveBufferOffset; - bool m_IsOpen, m_IsQuiet; + bool m_IsOpen, m_IsQuiet, m_IsActive; std::string m_Nickname, m_Address; int m_InPort, m_OutPort; i2p::data::PrivateKeys m_Keys; diff --git a/DaemonLinux.cpp b/DaemonLinux.cpp index 118fc5f5..f6664926 100644 --- a/DaemonLinux.cpp +++ b/DaemonLinux.cpp @@ -13,6 +13,7 @@ #include "FS.h" #include "Log.h" #include "RouterContext.h" +#include "ClientContext.h" void handle_signal(int sig) { @@ -21,6 +22,7 @@ void handle_signal(int sig) case SIGHUP: LogPrint(eLogInfo, "Daemon: Got SIGHUP, reopening log..."); i2p::log::Logger().Reopen (); + i2p::client::context.ReloadConfig(); break; case SIGINT: if (i2p::context.AcceptsTunnels () && !Daemon.gracefullShutdownInterval) diff --git a/Reseed.cpp b/Reseed.cpp index 66609414..236531f9 100644 --- a/Reseed.cpp +++ b/Reseed.cpp @@ -15,6 +15,7 @@ #include "Identity.h" #include "NetDb.h" #include "HTTP.h" +#include "util.h" namespace i2p { diff --git a/RouterContext.cpp b/RouterContext.cpp index 737a92fc..f2c2bc48 100644 --- a/RouterContext.cpp +++ b/RouterContext.cpp @@ -53,16 +53,27 @@ namespace i2p bool ipv6; i2p::config::GetOption("ipv6", ipv6); bool nat; i2p::config::GetOption("nat", nat); std::string ifname; i2p::config::GetOption("ifname", ifname); - std::string host = ipv6 ? "::" : "127.0.0.1"; - if (nat) { + if (ipv4) + { + std::string host = "127.0.0.1"; if (!i2p::config::IsDefault("host")) i2p::config::GetOption("host", host); - } else if (!ifname.empty()) { - /* bind to interface, we have no NAT so set external address too */ - host = i2p::util::net::GetInterfaceAddress(ifname, ipv6).to_string(); + else if (!nat && !ifname.empty()) + /* bind to interface, we have no NAT so set external address too */ + host = i2p::util::net::GetInterfaceAddress(ifname, false).to_string(); // v4 + routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ()); + routerInfo.AddNTCPAddress (host.c_str(), port); + } + if (ipv6) + { + std::string host = "::"; + if (!i2p::config::IsDefault("host") && !ipv4) // override if v6 only + i2p::config::GetOption("host", host); + else if (!ifname.empty()) + host = i2p::util::net::GetInterfaceAddress(ifname, true).to_string(); // v6 + routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ()); + routerInfo.AddNTCPAddress (host.c_str(), port); } - routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ()); - routerInfo.AddNTCPAddress (host.c_str(), port); routerInfo.SetCaps (i2p::data::RouterInfo::eReachable | i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer); // LR, BC routerInfo.SetProperty ("netId", std::to_string (I2PD_NET_ID)); diff --git a/SSU.cpp b/SSU.cpp index 4693a4f6..f4277a5a 100644 --- a/SSU.cpp +++ b/SSU.cpp @@ -459,8 +459,31 @@ namespace transport return GetRandomV4Session ( [excluded](std::shared_ptr session)->bool { - return session->GetState () == eSessionStateEstablished && !session->IsV6 () && - session != excluded; + return session->GetState () == eSessionStateEstablished && session != excluded; + } + ); + } + + template + std::shared_ptr SSUServer::GetRandomV6Session (Filter filter) // v6 only + { + std::vector > filteredSessions; + for (auto s :m_SessionsV6) + if (filter (s.second)) filteredSessions.push_back (s.second); + if (filteredSessions.size () > 0) + { + auto ind = rand () % filteredSessions.size (); + return filteredSessions[ind]; + } + return nullptr; + } + + std::shared_ptr SSUServer::GetRandomEstablishedV6Session (std::shared_ptr excluded) // v6 only + { + return GetRandomV6Session ( + [excluded](std::shared_ptr session)->bool + { + return session->GetState () == eSessionStateEstablished && session != excluded; } ); } diff --git a/SSU.h b/SSU.h index 0fdf0621..d779c025 100644 --- a/SSU.h +++ b/SSU.h @@ -48,6 +48,7 @@ namespace transport std::shared_ptr FindSession (std::shared_ptr router) const; std::shared_ptr FindSession (const boost::asio::ip::udp::endpoint& e) const; std::shared_ptr GetRandomEstablishedV4Session (std::shared_ptr excluded); + std::shared_ptr GetRandomEstablishedV6Session (std::shared_ptr excluded); void DeleteSession (std::shared_ptr session); void DeleteAllSessions (); @@ -79,7 +80,9 @@ namespace transport void CreateSessionThroughIntroducer (std::shared_ptr router, bool peerTest = false); template std::shared_ptr GetRandomV4Session (Filter filter); - + template + std::shared_ptr GetRandomV6Session (Filter filter); + std::set FindIntroducers (int maxNumIntroducers); void ScheduleIntroducersUpdateTimer (); void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode); diff --git a/SSUSession.cpp b/SSUSession.cpp index 9c90ff88..3aae06ba 100644 --- a/SSUSession.cpp +++ b/SSUSession.cpp @@ -930,10 +930,10 @@ namespace transport { uint32_t nonce = bufbe32toh (buf); // 4 bytes uint8_t size = buf[4]; // 1 byte - uint32_t address = (size == 4) ? buf32toh(buf + 5) : 0; // big endian, size bytes + const uint8_t * address = buf + 5; // big endian, size bytes uint16_t port = buf16toh(buf + size + 5); // big endian, 2 bytes const uint8_t * introKey = buf + size + 7; - if (port && !address) + if (port && (size != 4) && (size != 16)) { LogPrint (eLogWarning, "SSU: Address of ", size, " bytes not supported"); return; @@ -954,8 +954,7 @@ namespace transport LogPrint (eLogDebug, "SSU: first peer test from Charlie. We are Alice"); i2p::context.SetStatus (eRouterStatusOK); m_Server.UpdatePeerTest (nonce, ePeerTestParticipantAlice2); - SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (), - senderEndpoint.port (), introKey, true, false); // to Charlie + SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey, true, false); // to Charlie } break; } @@ -984,8 +983,7 @@ namespace transport case ePeerTestParticipantCharlie: { LogPrint (eLogDebug, "SSU: peer test from Alice. We are Charlie"); - SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (), - senderEndpoint.port (), introKey); // to Alice with her actual address + SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey); // to Alice with her actual address m_Server.RemovePeerTest (nonce); // nonce has been used break; } @@ -1000,17 +998,29 @@ namespace transport LogPrint (eLogDebug, "SSU: peer test from Bob. We are Charlie"); m_Server.NewPeerTest (nonce, ePeerTestParticipantCharlie); Send (PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Bob - SendPeerTest (nonce, be32toh (address), be16toh (port), introKey); // to Alice with her address received from Bob + boost::asio::ip::address addr; // Alice's address + if (size == 4) // v4 + { + boost::asio::ip::address_v4::bytes_type bytes; + memcpy (bytes.data (), address, 4); + addr = boost::asio::ip::address_v4 (bytes); + } + else // v6 + { + boost::asio::ip::address_v6::bytes_type bytes; + memcpy (bytes.data (), address, 6); + addr = boost::asio::ip::address_v6 (bytes); + } + SendPeerTest (nonce, addr, be16toh (port), introKey); // to Alice with her address received from Bob } else { LogPrint (eLogDebug, "SSU: peer test from Alice. We are Bob"); - auto session = m_Server.GetRandomEstablishedV4Session (shared_from_this ()); // Charlie, TODO: implement v6 support + auto session = senderEndpoint.address ().is_v4 () ? m_Server.GetRandomEstablishedV4Session (shared_from_this ()) : m_Server.GetRandomEstablishedV6Session (shared_from_this ()); // Charlie if (session) { m_Server.NewPeerTest (nonce, ePeerTestParticipantBob, shared_from_this ()); - session->SendPeerTest (nonce, senderEndpoint.address ().to_v4 ().to_ulong (), - senderEndpoint.port (), introKey, false); // to Charlie with Alice's actual address + session->SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey, false); // to Charlie with Alice's actual address } } } @@ -1020,7 +1030,7 @@ namespace transport } } - void SSUSession::SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port, + void SSUSession::SendPeerTest (uint32_t nonce, const boost::asio::ip::address& address, uint16_t port, const uint8_t * introKey, bool toAddress, bool sendAddress) // toAddress is true for Alice<->Chalie communications only // sendAddress is false if message comes from Alice @@ -1031,12 +1041,21 @@ namespace transport htobe32buf (payload, nonce); payload += 4; // nonce // address and port - if (sendAddress && address) - { - *payload = 4; - payload++; // size - htobe32buf (payload, address); - payload += 4; // address + if (sendAddress) + { + if (address.is_v4 ()) + { + *payload = 4; + memcpy (payload + 1, address.to_v4 ().to_bytes ().data (), 4); // our IP V4 + } + else if (address.is_v6 ()) + { + *payload = 6; + memcpy (payload + 1, address.to_v6 ().to_bytes ().data (), 16); // our IP V6 + } + else + *payload = 0; + payload += (payload[0] + 1); } else { @@ -1064,7 +1083,7 @@ namespace transport { // encrypt message with specified intro key FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80, introKey, iv, introKey); - boost::asio::ip::udp::endpoint e (boost::asio::ip::address_v4 (address), port); + boost::asio::ip::udp::endpoint e (address, port); m_Server.Send (buf, 80, e); } else @@ -1090,7 +1109,7 @@ namespace transport if (!nonce) nonce = 1; m_IsPeerTest = false; m_Server.NewPeerTest (nonce, ePeerTestParticipantAlice1); - SendPeerTest (nonce, 0, 0, address->key, false, false); // address and port always zero for Alice + SendPeerTest (nonce, boost::asio::ip::address(), 0, address->key, false, false); // address and port always zero for Alice } void SSUSession::SendKeepAlive () diff --git a/SSUSession.h b/SSUSession.h index cfe24f6c..c2f24ce3 100644 --- a/SSUSession.h +++ b/SSUSession.h @@ -118,7 +118,7 @@ namespace transport void ScheduleConnectTimer (); void HandleConnectTimer (const boost::system::error_code& ecode); void ProcessPeerTest (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); - void SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port, const uint8_t * introKey, bool toAddress = true, bool sendAddress = true); + void SendPeerTest (uint32_t nonce, const boost::asio::ip::address& address, uint16_t port, const uint8_t * introKey, bool toAddress = true, bool sendAddress = true); void ProcessData (uint8_t * buf, size_t len); void SendSesionDestroyed (); void Send (uint8_t type, const uint8_t * payload, size_t len); // with session key