diff --git a/I2PControl.cpp b/I2PControl.cpp index 3e2e3997..1e8546ac 100644 --- a/I2PControl.cpp +++ b/I2PControl.cpp @@ -205,7 +205,7 @@ namespace client } /* append to json chunk of data from 1st request */ json.write(buf->data() + len, bytes_transferred - len); - remains = req.content_length() - len; + remains = req.content_length(); /* if request has Content-Length header, fetch rest of data and store to json buffer */ while (remains > 0) { len = ((long int) buf->size() < remains) ? buf->size() : remains; @@ -216,15 +216,17 @@ namespace client } else { json.write(buf->data(), bytes_transferred); } - LogPrint(eLogDebug, "I2PControl: json from request: ", json.str()); + //LogPrint(eLogDebug, "I2PControl: json from request: ", json.str()); #if GCC47_BOOST149 LogPrint (eLogError, "I2PControl: json_read is not supported due bug in boost 1.49 with gcc 4.7"); BuildErrorResponse(response, 32603, "JSON requests is not supported with this version of boost"); #else /* now try to parse json itself */ + std::string j_str = json.str(); + std::stringstream _json(j_str); try { boost::property_tree::ptree pt; - boost::property_tree::read_json (json, pt); + boost::property_tree::read_json (_json, pt); std::string id = pt.get("id"); std::string method = pt.get("method"); @@ -342,9 +344,10 @@ namespace client (this->*(it1->second))(it.second.data ()); InsertParam (results, it.first, ""); } - else + else { LogPrint (eLogError, "I2PControl: I2PControl unknown request: ", it.first); - } + } + } } void I2PControlService::PasswordHandler (const std::string& value) @@ -361,14 +364,17 @@ namespace client for (auto it = params.begin (); it != params.end (); ++it) { LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first); + if (it != params.begin ()) results << ","; auto it1 = m_RouterInfoHandlers.find (it->first); if (it1 != m_RouterInfoHandlers.end ()) { - if (it != params.begin ()) results << ","; (this->*(it1->second))(results); } else + { + InsertParam(results, it->first, ""); LogPrint (eLogError, "I2PControl: RouterInfo unknown request ", it->first); + } } } @@ -436,13 +442,18 @@ namespace client { for (auto it = params.begin (); it != params.end (); ++it) { - if (it != params.begin ()) results << ","; + if (it != params.begin ()) results << ","; LogPrint (eLogDebug, "I2PControl: RouterManager request: ", it->first); auto it1 = m_RouterManagerHandlers.find (it->first); - if (it1 != m_RouterManagerHandlers.end ()) { - (this->*(it1->second))(results); - } else + if (it1 != m_RouterManagerHandlers.end ()) + { + (this->*(it1->second))(results); + } + else + { + InsertParam(results, it->first, ""); LogPrint (eLogError, "I2PControl: RouterManager unknown request: ", it->first); + } } } @@ -488,10 +499,15 @@ namespace client if (it != params.begin ()) results << ","; LogPrint (eLogDebug, "I2PControl: NetworkSetting request: ", it->first); auto it1 = m_NetworkSettingHandlers.find (it->first); - if (it1 != m_NetworkSettingHandlers.end ()) { - (this->*(it1->second))(it->second.data (), results); - } else + if (it1 != m_NetworkSettingHandlers.end ()) + { + (this->*(it1->second))(it->second.data (), results); + } + else + { + InsertParam(results, it->first, ""); LogPrint (eLogError, "I2PControl: NetworkSetting unknown request: ", it->first); + } } } diff --git a/I2PTunnel.cpp b/I2PTunnel.cpp index e94b5ed4..1a01a3c4 100644 --- a/I2PTunnel.cpp +++ b/I2PTunnel.cpp @@ -178,7 +178,7 @@ namespace client { if (bytes_transferred > 0) Write (m_StreamBuffer, bytes_transferred); // postpone termination - else if (ecode == boost::asio::error::timed_out && m_Stream->IsOpen ()) + else if (ecode == boost::asio::error::timed_out && m_Stream && m_Stream->IsOpen ()) StreamReceive (); else Terminate (); diff --git a/NTCPSession.cpp b/NTCPSession.cpp index 712006c4..65da5def 100644 --- a/NTCPSession.cpp +++ b/NTCPSession.cpp @@ -493,7 +493,8 @@ namespace transport void NTCPSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred) { - if (ecode) { + if (ecode) + { if (ecode != boost::asio::error::operation_aborted) LogPrint (eLogDebug, "NTCP: Read error: ", ecode.message ()); //if (ecode != boost::asio::error::operation_aborted) @@ -507,48 +508,66 @@ namespace transport if (m_ReceiveBufferOffset >= 16) { - int numReloads = 0; - do - { - uint8_t * nextBlock = m_ReceiveBuffer; - while (m_ReceiveBufferOffset >= 16) + // process received data + uint8_t * nextBlock = m_ReceiveBuffer; + while (m_ReceiveBufferOffset >= 16) + { + if (!DecryptNextBlock (nextBlock)) // 16 bytes { - if (!DecryptNextBlock (nextBlock)) // 16 bytes - { - Terminate (); - return; - } - nextBlock += 16; - m_ReceiveBufferOffset -= 16; - } - if (m_ReceiveBufferOffset > 0) - memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset); - - // try to read more - if (numReloads < 5) - { - boost::system::error_code ec; - size_t moreBytes = m_Socket.available(ec); - if (moreBytes && !ec) - { - if (moreBytes > NTCP_BUFFER_SIZE - m_ReceiveBufferOffset) - moreBytes = NTCP_BUFFER_SIZE - m_ReceiveBufferOffset; - moreBytes = m_Socket.read_some (boost::asio::buffer (m_ReceiveBuffer + m_ReceiveBufferOffset, moreBytes), ec); - if (ec) - { - LogPrint (eLogInfo, "NTCP: Read more bytes error: ", ec.message ()); - Terminate (); - return; - } - m_NumReceivedBytes += moreBytes; - m_ReceiveBufferOffset += moreBytes; - numReloads++; - } + Terminate (); + return; } + nextBlock += 16; + m_ReceiveBufferOffset -= 16; } - while (m_ReceiveBufferOffset >= 16); - m_Handler.Flush (); - } + if (m_ReceiveBufferOffset > 0) + memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset); + } + + // read and process more is available + boost::system::error_code ec; + size_t moreBytes = m_Socket.available(ec); + if (moreBytes && !ec) + { + uint8_t * buf = nullptr, * moreBuf = m_ReceiveBuffer; + if (moreBytes + m_ReceiveBufferOffset > NTCP_BUFFER_SIZE) + { + buf = new uint8_t[moreBytes + m_ReceiveBufferOffset + 16]; + moreBuf = buf; + uint8_t rem = ((size_t)buf) & 0x0f; + if (rem) moreBuf += (16 - rem); // align 16 + if (m_ReceiveBufferOffset) + memcpy (moreBuf, m_ReceiveBuffer, m_ReceiveBufferOffset); + } + moreBytes = m_Socket.read_some (boost::asio::buffer (moreBuf + m_ReceiveBufferOffset, moreBytes), ec); + if (ec) + { + LogPrint (eLogInfo, "NTCP: Read more bytes error: ", ec.message ()); + delete[] buf; + Terminate (); + return; + } + m_ReceiveBufferOffset += moreBytes; + m_NumReceivedBytes += moreBytes; + i2p::transport::transports.UpdateReceivedBytes (moreBytes); + // process more data + uint8_t * nextBlock = moreBuf; + while (m_ReceiveBufferOffset >= 16) + { + if (!DecryptNextBlock (nextBlock)) // 16 bytes + { + delete[] buf; + Terminate (); + return; + } + nextBlock += 16; + m_ReceiveBufferOffset -= 16; + } + if (m_ReceiveBufferOffset > 0) + memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset); // nextBlock points to memory inside buf + delete[] buf; + } + m_Handler.Flush (); m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch (); Receive (); diff --git a/NTCPSession.h b/NTCPSession.h index b02543be..a5d6f99f 100644 --- a/NTCPSession.h +++ b/NTCPSession.h @@ -35,12 +35,11 @@ namespace transport }; const size_t NTCP_MAX_MESSAGE_SIZE = 16384; - const size_t NTCP_BUFFER_SIZE = 4160; // fits 4 tunnel messages (4*1028) + const size_t NTCP_BUFFER_SIZE = 1028; // fits 1 tunnel data message const int NTCP_CONNECT_TIMEOUT = 5; // 5 seconds const int NTCP_TERMINATION_TIMEOUT = 120; // 2 minutes const int NTCP_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds const size_t NTCP_DEFAULT_PHASE3_SIZE = 2/*size*/ + i2p::data::DEFAULT_IDENTITY_SIZE/*387*/ + 4/*ts*/ + 15/*padding*/ + 40/*signature*/; // 448 - const int NTCP_BAN_EXPIRATION_TIMEOUT = 70; // in second const int NTCP_CLOCK_SKEW = 60; // in seconds const int NTCP_MAX_OUTGOING_QUEUE_SIZE = 200; // how many messages we can queue up diff --git a/RouterContext.cpp b/RouterContext.cpp index 3c37e88c..966da6ac 100644 --- a/RouterContext.cpp +++ b/RouterContext.cpp @@ -266,8 +266,9 @@ namespace i2p } } // delete previous introducers - for (auto& addr : addresses) - addr->introducers.clear (); + for (auto& addr : addresses) + if (addr->ssu) + addr->ssu->introducers.clear (); // update UpdateRouterInfo (); @@ -298,7 +299,8 @@ namespace i2p } // delete previous introducers for (auto& addr : addresses) - addr->introducers.clear (); + if (addr->ssu) + addr->ssu->introducers.clear (); // update UpdateRouterInfo (); diff --git a/RouterInfo.cpp b/RouterInfo.cpp index 4ae2f9e5..08d46ecb 100644 --- a/RouterInfo.cpp +++ b/RouterInfo.cpp @@ -175,11 +175,14 @@ namespace data if (!strcmp (transportStyle, "NTCP")) address->transportStyle = eTransportNTCP; else if (!strcmp (transportStyle, "SSU")) + { address->transportStyle = eTransportSSU; + address->ssu.reset (new SSUExt ()); + address->ssu->mtu = 0; + } else address->transportStyle = eTransportUnknown; address->port = 0; - address->mtu = 0; uint16_t size, r = 0; s.read ((char *)&size, sizeof (size)); if (!s) return; size = be16toh (size); @@ -220,9 +223,19 @@ namespace data else if (!strcmp (key, "port")) address->port = boost::lexical_cast(value); else if (!strcmp (key, "mtu")) - address->mtu = boost::lexical_cast(value); + { + if (address->ssu) + address->ssu->mtu = boost::lexical_cast(value); + else + LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP"); + } else if (!strcmp (key, "key")) - Base64ToByteStream (value, strlen (value), address->key, 32); + { + if (address->ssu) + Base64ToByteStream (value, strlen (value), address->ssu->key, 32); + else + LogPrint (eLogWarning, "RouterInfo: Unexpected field 'key' for NTCP"); + } else if (!strcmp (key, "caps")) ExtractCaps (value); else if (key[0] == 'i') @@ -237,9 +250,9 @@ namespace data LogPrint (eLogError, "RouterInfo: Unexpected introducer's index ", index, " skipped"); if (s) continue; else return; } - if (index >= address->introducers.size ()) - address->introducers.resize (index + 1); - Introducer& introducer = address->introducers.at (index); + if (index >= address->ssu->introducers.size ()) + address->ssu->introducers.resize (index + 1); + Introducer& introducer = address->ssu->introducers.at (index); if (!strcmp (key, "ihost")) { boost::system::error_code ecode; @@ -417,10 +430,10 @@ namespace data if (address.transportStyle == eTransportSSU) { // write introducers if any - if (address.introducers.size () > 0) + if (address.ssu->introducers.size () > 0) { int i = 0; - for (const auto& introducer: address.introducers) + for (const auto& introducer: address.ssu->introducers) { WriteString ("ihost" + boost::lexical_cast(i), properties); properties << '='; @@ -429,7 +442,7 @@ namespace data i++; } i = 0; - for (const auto& introducer: address.introducers) + for (const auto& introducer: address.ssu->introducers) { WriteString ("ikey" + boost::lexical_cast(i), properties); properties << '='; @@ -441,7 +454,7 @@ namespace data i++; } i = 0; - for (const auto& introducer: address.introducers) + for (const auto& introducer: address.ssu->introducers) { WriteString ("iport" + boost::lexical_cast(i), properties); properties << '='; @@ -450,7 +463,7 @@ namespace data i++; } i = 0; - for (const auto& introducer: address.introducers) + for (const auto& introducer: address.ssu->introducers) { WriteString ("itag" + boost::lexical_cast(i), properties); properties << '='; @@ -463,16 +476,16 @@ namespace data WriteString ("key", properties); properties << '='; char value[64]; - size_t l = ByteStreamToBase64 (address.key, 32, value, 64); + size_t l = ByteStreamToBase64 (address.ssu->key, 32, value, 64); value[l] = 0; WriteString (value, properties); properties << ';'; // write mtu - if (address.mtu) + if (address.ssu->mtu) { WriteString ("mtu", properties); properties << '='; - WriteString (boost::lexical_cast(address.mtu), properties); + WriteString (boost::lexical_cast(address.ssu->mtu), properties); properties << ';'; } } @@ -589,7 +602,6 @@ namespace data addr->transportStyle = eTransportNTCP; addr->cost = 2; addr->date = 0; - addr->mtu = 0; for (const auto& it: *m_Addresses) // don't insert same address twice if (*it == *addr) return; m_SupportedTransports |= addr->host.is_v6 () ? eNTCPV6 : eNTCPV4; @@ -604,8 +616,9 @@ namespace data addr->transportStyle = eTransportSSU; addr->cost = 10; // NTCP should have priority over SSU addr->date = 0; - addr->mtu = mtu; - memcpy (addr->key, key, 32); + addr->ssu.reset (new SSUExt ()); + addr->ssu->mtu = mtu; + memcpy (addr->ssu->key, key, 32); for (const auto& it: *m_Addresses) // don't insert same address twice if (*it == *addr) return; m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4; @@ -621,9 +634,9 @@ namespace data { if (addr->transportStyle == eTransportSSU && addr->host.is_v4 ()) { - for (auto& intro: addr->introducers) + for (auto& intro: addr->ssu->introducers) if (intro.iTag == introducer.iTag) return false; // already presented - addr->introducers.push_back (introducer); + addr->ssu->introducers.push_back (introducer); return true; } } @@ -636,10 +649,10 @@ namespace data { if (addr->transportStyle == eTransportSSU && addr->host.is_v4 ()) { - for (auto it = addr->introducers.begin (); it != addr->introducers.end (); ++it) + for (auto it = addr->ssu->introducers.begin (); it != addr->ssu->introducers.end (); ++it) if ( boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e) { - addr->introducers.erase (it); + addr->ssu->introducers.erase (it); return true; } } diff --git a/RouterInfo.h b/RouterInfo.h index c51d3ce4..7300e579 100644 --- a/RouterInfo.h +++ b/RouterInfo.h @@ -79,17 +79,22 @@ namespace data uint32_t iTag; }; + struct SSUExt + { + int mtu; + IntroKey key; // intro key for SSU + std::vector introducers; + }; + struct Address { TransportStyle transportStyle; boost::asio::ip::address host; std::string addressString; - int port, mtu; + int port; uint64_t date; uint8_t cost; - // SSU only - IntroKey key; // intro key for SSU - std::vector introducers; + std::unique_ptr ssu; // not null for SSU bool IsCompatible (const boost::asio::ip::address& other) const { diff --git a/SAM.cpp b/SAM.cpp index cb704ebd..c3b4faf1 100644 --- a/SAM.cpp +++ b/SAM.cpp @@ -693,9 +693,9 @@ namespace client LogPrint (eLogDebug, "SAM: datagram received ", len); auto base64 = from.ToBase64 (); #ifdef _MSC_VER - size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len); + size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len); #else - size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len); + size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len); #endif if (len < SAM_SOCKET_BUFFER_SIZE - l) { diff --git a/SSU.cpp b/SSU.cpp index 9f74185f..9dc05608 100644 --- a/SSU.cpp +++ b/SSU.cpp @@ -417,7 +417,7 @@ namespace transport return; } // create new session - int numIntroducers = address->introducers.size (); + int numIntroducers = address->ssu->introducers.size (); if (numIntroducers > 0) { std::shared_ptr introducerSession; @@ -425,7 +425,7 @@ namespace transport // we might have a session to introducer already for (int i = 0; i < numIntroducers; i++) { - auto intr = &(address->introducers[i]); + auto intr = &(address->ssu->introducers[i]); boost::asio::ip::udp::endpoint ep (intr->iHost, intr->iPort); if (ep.address ().is_v4 ()) // ipv4 only { diff --git a/SSUData.cpp b/SSUData.cpp index ad38cf25..1782da26 100644 --- a/SSUData.cpp +++ b/SSUData.cpp @@ -54,23 +54,23 @@ namespace transport { if (remoteRouter) return; auto ssuAddress = remoteRouter->GetSSUAddress (); - if (ssuAddress && ssuAddress->mtu) + if (ssuAddress && ssuAddress->ssu->mtu) { if (m_Session.IsV6 ()) - m_PacketSize = ssuAddress->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; + m_PacketSize = ssuAddress->ssu->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; else - m_PacketSize = ssuAddress->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; + m_PacketSize = ssuAddress->ssu->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; if (m_PacketSize > 0) { // make sure packet size multiple of 16 m_PacketSize >>= 4; m_PacketSize <<= 4; if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize; - LogPrint (eLogDebug, "SSU: MTU=", ssuAddress->mtu, " packet size=", m_PacketSize); + LogPrint (eLogDebug, "SSU: MTU=", ssuAddress->ssu->mtu, " packet size=", m_PacketSize); } else { - LogPrint (eLogWarning, "SSU: Unexpected MTU ", ssuAddress->mtu); + LogPrint (eLogWarning, "SSU: Unexpected MTU ", ssuAddress->ssu->mtu); m_PacketSize = m_MaxPacketSize; } } diff --git a/SSUSession.cpp b/SSUSession.cpp index a47295fa..540cf907 100644 --- a/SSUSession.cpp +++ b/SSUSession.cpp @@ -22,14 +22,14 @@ namespace transport { // we are client auto address = router->GetSSUAddress (false); - if (address) m_IntroKey = address->key; + if (address) m_IntroKey = address->ssu->key; m_Data.AdjustPacketSize (router); // mtu } else { // we are server auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false); - if (address) m_IntroKey = address->key; + if (address) m_IntroKey = address->ssu->key; } m_CreationTime = i2p::util::GetSecondsSinceEpoch (); } @@ -115,8 +115,8 @@ namespace transport LogPrint (eLogInfo, "SSU is not supported"); return; } - if (Validate (buf, len, address->key)) - Decrypt (buf, len, address->key); + if (Validate (buf, len, address->ssu->key)) + Decrypt (buf, len, address->ssu->key); else { LogPrint (eLogWarning, "SSU: MAC verification failed ", len, " bytes from ", senderEndpoint); @@ -402,7 +402,7 @@ namespace transport payload += 2; *payload = 0; // challenge payload++; - memcpy (payload, (const uint8_t *)address->key, 32); + memcpy (payload, (const uint8_t *)address->ssu->key, 32); payload += 32; htobe32buf (payload, nonce); // nonce @@ -1075,7 +1075,7 @@ namespace transport // send our intro key to address instead it's own auto addr = i2p::context.GetRouterInfo ().GetSSUAddress (); if (addr) - memcpy (payload, addr->key, 32); // intro key + memcpy (payload, addr->ssu->key, 32); // intro key else LogPrint (eLogInfo, "SSU is not supported. Can't send peer test"); } @@ -1114,7 +1114,7 @@ namespace transport if (!nonce) nonce = 1; m_IsPeerTest = false; m_Server.NewPeerTest (nonce, ePeerTestParticipantAlice1, shared_from_this ()); - SendPeerTest (nonce, boost::asio::ip::address(), 0, address->key, false, false); // address and port always zero for Alice + SendPeerTest (nonce, boost::asio::ip::address(), 0, address->ssu->key, false, false); // address and port always zero for Alice } void SSUSession::SendKeepAlive () diff --git a/docs/configuration.md b/docs/configuration.md index b04be9fb..3212aea9 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -73,9 +73,12 @@ All options below still possible in cmdline, but better write it in config file: * --i2cp.port= - Port of I2CP server. Usually 7654. Ignored for Andorid * --i2cp.enabled= - If I2CP is enabled. false by default. Other services don't require I2CP -* --i2pcontrol.address= - The address to listen on (I2P control service) -* --i2pcontrol.port= - Port of I2P control service. Usually 7650. I2PControl is off if not specified -* --i2pcontrol.enabled= - If I2P control is enabled. false by default +* --i2pcontrol.address= - The address to listen on (I2P control service) +* --i2pcontrol.port= - Port of I2P control service. Usually 7650. I2PControl is off if not specified +* --i2pcontrol.enabled= - If I2P control is enabled. false by default +* --i2pcontrol.password= - I2P control authentication password. itoopie by default +* --i2pcontrol.cert= - I2P control HTTPS certificate file name. i2pcontrol.crt.pem by default +* --i2pcontrol.key= - I2P control HTTPS certificate key file name. i2pcontrol.key.pem by default * --upnp.enabled= - Enable or disable UPnP, false by default for CLI and true for GUI (Windows, Android) * --upnp.name= - Name i2pd appears in UPnP forwardings list. I2Pd by default