void TunnelPool::CreateInboundTunnel () { OutboundTunnel * outboundTunnel = m_OutboundTunnels.size () > 0 ? *m_OutboundTunnels.begin () : tunnels.GetNextOutboundTunnel (); LogPrint ("Creating destination inbound tunnel..."); const i2p::data::RouterInfo * prevHop = &i2p::context.GetRouterInfo (); std::vector<const i2p::data::RouterInfo *> hops; int numHops = m_NumHops; if (outboundTunnel) { // last hop auto hop = outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router; if (hop->GetIdentHash () != i2p::context.GetIdentHash ()) // outbound shouldn't be zero-hop tunnel { prevHop = hop; hops.push_back (prevHop); numHops--; } } for (int i = 0; i < numHops; i++) { auto hop = i2p::data::netdb.GetRandomRouter (prevHop); prevHop = hop; hops.push_back (hop); } std::reverse (hops.begin (), hops.end ()); auto * tunnel = tunnels.CreateTunnel<InboundTunnel> (new TunnelConfig (hops)); tunnel->SetTunnelPool (this); }
void RouterContext::UpdateNTCPV6Address (const boost::asio::ip::address& host) { bool updated = false, found = false; int port = 0; auto& addresses = m_RouterInfo.GetAddresses (); for (auto& addr : addresses) { if (addr.host.is_v6 () && addr.transportStyle == i2p::data::RouterInfo::eTransportNTCP) { if (addr.host != host) { addr.host = host; updated = true; } found = true; } else port = addr.port; } if (!found) { // create new address m_RouterInfo.AddNTCPAddress (host.to_string ().c_str (), port); auto mtu = i2p::util::net::GetMTU (host); if (mtu) { LogPrint ("Our v6 MTU=", mtu); if (mtu > 1472) mtu = 1472; } m_RouterInfo.AddSSUAddress (host.to_string ().c_str (), port, GetIdentHash (), mtu ? mtu : 1472); // TODO updated = true; } if (updated) UpdateRouterInfo (); }
void GarlicDestination::CleanupExpiredTags () { // incoming uint32_t ts = i2p::util::GetSecondsSinceEpoch (); int numExpiredTags = 0; for (auto it = m_Tags.begin (); it != m_Tags.end ();) { if (ts > it->first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT) { numExpiredTags++; it = m_Tags.erase (it); } else it++; } if (numExpiredTags > 0) LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ()); // outgoing std::unique_lock<std::mutex> l(m_SessionsMutex); for (auto it = m_Sessions.begin (); it != m_Sessions.end ();) { it->second->GetSharedRoutingPath (); // delete shared path if necessary if (!it->second->CleanupExpiredTags ()) { LogPrint (eLogInfo, "Routing session to ", it->first.ToBase32 (), " deleted"); it = m_Sessions.erase (it); } else it++; } }
bool AddressBook::GetAddress (const std::string& address, i2p::data::IdentityEx& identity) { if (!m_Storage) m_Storage = CreateStorage (); i2p::data::IdentHash ident; if (!GetIdentHash (address, ident)) return false; return m_Storage->GetAddress (ident, identity); }
void NetDbRequests::ManageRequests() { uint64_t ts = i2p::util::GetSecondsSinceEpoch(); std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex); for (auto it = m_RequestedDestinations.begin(); it != m_RequestedDestinations.end();) { auto& dest = it->second; bool done = false; // request is worthless after 1 minute if (ts < dest->GetCreationTime() + 60) { // no response for 5 seconds if (ts > dest->GetCreationTime() + 5) { auto count = dest->GetExcludedPeers().size(); std::size_t attempts(7); if (!dest->IsExploratory() && count < attempts) { auto pool = i2p::tunnel::tunnels.GetExploratoryPool(); auto outbound = pool->GetNextOutboundTunnel(); auto inbound = pool->GetNextInboundTunnel(); auto nextFloodfill = netdb.GetClosestFloodfill( dest->GetDestination(), dest->GetExcludedPeers()); if (nextFloodfill && outbound && inbound) { outbound->SendTunnelDataMsg( nextFloodfill->GetIdentHash(), 0, dest->CreateRequestMessage( nextFloodfill, inbound)); } else { done = true; if (!inbound) LogPrint(eLogWarn, "NetDbRequests: no inbound tunnels"); if (!outbound) LogPrint(eLogWarn, "NetDbRequests: no outbound tunnels"); if (!nextFloodfill) LogPrint(eLogWarn, "NetDbRequests: no more floodfills"); } } else { if (!dest->IsExploratory()) LogPrint(eLogWarn, "NetDbRequests: ", dest->GetDestination().ToBase64(), " not found after ", attempts, " attempts"); done = true; } } } else { // delete obsolete request done = true; } if (done) it = m_RequestedDestinations.erase(it); else it++; } }
void GarlicDestination::SaveTags () { if (m_Tags.empty ()) return; std::string ident = GetIdentHash().ToBase32(); std::string path = i2p::fs::DataDirPath("tags", (ident + ".tags")); std::ofstream f (path, std::ofstream::binary | std::ofstream::out | std::ofstream::trunc); uint32_t ts = i2p::util::GetSecondsSinceEpoch (); // 4 bytes timestamp, 32 bytes tag, 32 bytes key for (auto it: m_Tags) { if (ts < it.first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT) { f.write ((char *)&it.first.creationTime, 4); f.write ((char *)it.first.data (), 32); f.write ((char *)it.second->GetKey ().data (), 32); } } }
void TunnelPool::CreateInboundTunnel () { OutboundTunnel * outboundTunnel = m_OutboundTunnels.size () > 0 ? *m_OutboundTunnels.begin () : tunnels.GetNextOutboundTunnel (); LogPrint ("Creating destination inbound tunnel..."); auto firstHop = i2p::data::netdb.GetRandomRouter (outboundTunnel ? outboundTunnel->GetEndpointRouter () : nullptr); auto secondHop = outboundTunnel ? outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router : nullptr; if (!secondHop || secondHop->GetIdentHash () == i2p::context.GetIdentHash ()) secondHop = i2p::data::netdb.GetRandomRouter (firstHop); auto * tunnel = tunnels.CreateTunnel<InboundTunnel> ( new TunnelConfig (std::vector<const i2p::data::RouterInfo *> { firstHop, secondHop }), outboundTunnel); tunnel->SetTunnelPool (this); }
void GarlicDestination::LoadTags () { std::string ident = GetIdentHash().ToBase32(); std::string path = i2p::fs::DataDirPath("tags", (ident + ".tags")); uint32_t ts = i2p::util::GetSecondsSinceEpoch (); if (ts < i2p::fs::GetLastUpdateTime (path) + INCOMING_TAGS_EXPIRATION_TIMEOUT) { // might contain non-expired tags std::ifstream f (path, std::ifstream::binary); if (f) { std::map<i2p::crypto::AESKey, std::shared_ptr<AESDecryption> > keys; // 4 bytes timestamp, 32 bytes tag, 32 bytes key while (!f.eof ()) { uint32_t t; uint8_t tag[32], key[32]; f.read ((char *)&t, 4); if (f.eof ()) break; if (ts < t + INCOMING_TAGS_EXPIRATION_TIMEOUT) { f.read ((char *)tag, 32); f.read ((char *)key, 32); } else f.seekg (64, std::ios::cur); // skip if (f.eof ()) break; std::shared_ptr<AESDecryption> decryption; auto it = keys.find (key); if (it != keys.end ()) decryption = it->second; else decryption = std::make_shared<AESDecryption>(key); m_Tags.insert (std::make_pair (SessionTag (tag, ts), decryption)); } if (!m_Tags.empty ()) LogPrint (eLogInfo, m_Tags.size (), " loaded for ", ident); } } i2p::fs::Remove (path); }
void GarlicDestination::HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg) { uint8_t * buf = msg->GetPayload (); uint32_t length = bufbe32toh (buf); if (length > msg->GetLength ()) { LogPrint (eLogError, "Garlic message length ", length, " exceeds I2NP message length ", msg->GetLength ()); return; } buf += 4; // length auto it = m_Tags.find (SessionTag(buf)); if (it != m_Tags.end ()) { // tag found. Use AES if (length >= 32) { uint8_t iv[32]; // IV is first 16 bytes CryptoPP::SHA256().CalculateDigest(iv, buf, 32); it->second->SetIV (iv); it->second->Decrypt (buf + 32, length - 32, buf + 32); HandleAESBlock (buf + 32, length - 32, it->second, msg->from); } else LogPrint (eLogError, "Garlic message length ", length, " is less than 32 bytes"); m_Tags.erase (it); // tag might be used only once } else { // tag not found. Use ElGamal ElGamalBlock elGamal; if (length >= 514 && i2p::crypto::ElGamalDecrypt (GetEncryptionPrivateKey (), buf, (uint8_t *)&elGamal, true)) { auto decryption = std::make_shared<i2p::crypto::CBCDecryption>(); decryption->SetKey (elGamal.sessionKey); uint8_t iv[32]; // IV is first 16 bytes CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32); decryption->SetIV (iv); decryption->Decrypt(buf + 514, length - 514, buf + 514); HandleAESBlock (buf + 514, length - 514, decryption, msg->from); } else LogPrint (eLogError, "Failed to decrypt garlic"); } // cleanup expired tags uint32_t ts = i2p::util::GetSecondsSinceEpoch (); if (ts > m_LastTagsCleanupTime + INCOMING_TAGS_EXPIRATION_TIMEOUT) { if (m_LastTagsCleanupTime) { int numExpiredTags = 0; for (auto it = m_Tags.begin (); it != m_Tags.end ();) { if (ts > it->first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT) { numExpiredTags++; it = m_Tags.erase (it); } else it++; } LogPrint (numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ()); } m_LastTagsCleanupTime = ts; } }