void EstablishmentManager::processConfirmed(EstablishmentStatePtr const &state) { I2P_LOG_SCOPED_TAG(m_log, "RouterHash", state->getTheirIdentity().getHash()); if(!state->verifyConfirmationSignature()) { I2P_LOG(m_log, warning) << "confirmation signature verification failed"; state->setState(EstablishmentState::State::FAILURE); post(state); return; } else I2P_LOG(m_log, debug) << "confirmation signature verification succeeded"; Endpoint ep = state->getTheirEndpoint(); PeerState ps(ep, state->getTheirIdentity().getHash()); ps.setCurrentSessionKey(state->getSessionKey()); ps.setCurrentMacKey(state->getMacKey()); std::lock_guard<std::mutex> lock(m_context.peers.getMutex()); m_context.peers.addPeer(std::move(ps)); delState(ep); m_context.ios.post(boost::bind(boost::ref(m_context.establishedSignal), state->getTheirIdentity().getHash(), (state->getDirection() == EstablishmentState::Direction::INBOUND))); }
void SearchManager::connectionFailure(RouterHash const rh) { std::lock_guard<std::mutex> lock(m_searchesMutex); if(m_searches.get<1>().count(rh)) { I2P_LOG_SCOPED_RH(m_log, rh); SearchStateByCurrent::iterator itr = m_searches.get<1>().find(rh); const SearchState& ss = *itr; m_searches.get<1>().modify(itr, InsertTried(rh)); RouterHash front; if(ss.alternates.size()) { front = ss.alternates.front(); while(!m_ctx.getDatabase().routerExists(front)) { I2P_LOG(m_log, debug) << "could not try alternate " << front << " because it doesn't exist"; m_searches.get<1>().modify(itr, PopAlternates()); if(ss.alternates.size()) front = ss.alternates.front(); else { I2P_LOG(m_log, debug) << "no more alternates left, search failed"; cancel(ss.goal); return; } } while(ss.tried.count(front)) { m_searches.get<1>().modify(itr, PopAlternates()); if(ss.alternates.size()) front = ss.alternates.front(); else { I2P_LOG(m_log, debug) << "no more alternates left, search failed"; cancel(ss.goal); return; } } I2P_LOG(m_log, debug) << "connecting to alternate " << front; m_searches.get<1>().modify(itr, ModifyState(front, rh)); m_ctx.getOutMsgDisp().getTransport()->connect(m_ctx.getDatabase().getRouterInfo(front)); } else { I2P_LOG(m_log, debug) << "connection failed and there are no more alternates, search failed"; cancel(ss.goal); return; } } }
void SearchManager::searchReply(RouterHash const from, std::array<unsigned char, 32> const query, std::list<RouterHash> const hashes) { I2P_LOG_SCOPED_RH(m_log, from); I2P_LOG(m_log, debug) << "received search reply"; std::lock_guard<std::mutex> lock(m_searchesMutex); if(m_searches.get<0>().count(query)) { I2P_LOG(m_log, debug) << "found RouterHash in pending search table"; SearchStateByGoal::iterator itr = m_searches.get<0>().find(query); const SearchState& ss = *itr; if(ss.state == SearchState::LOOKUP_SENT) { m_searches.get<0>().modify(itr, InsertTried(from)); for(auto h: hashes) { if(!ss.tried.count(h)) m_searches.get<0>().modify(itr, PushAlternates(h)); } RouterHash front; if(ss.alternates.size()) { front = ss.alternates.front(); while(ss.tried.count(front)) { m_searches.get<0>().modify(itr, PopAlternates()); if(ss.alternates.size()) front = ss.alternates.front(); else { I2P_LOG(m_log, debug) << "no more alternates left, search failed"; cancel(ss.goal); return; } } if(!m_ctx.getDatabase().routerExists(front)) { I2P_LOG(m_log, debug) << "received unknown peer hash " << front << ", asking for its RouterInfo"; m_searches.get<0>().modify(itr, ModifyState(front)); I2NP::MessagePtr dbl(new I2NP::DatabaseLookup(front, m_ctx.getIdentity().getHash(), 0, ss.excluded)); m_ctx.getOutMsgDisp().sendMessage(from, dbl); } else { I2P_LOG(m_log, debug) << "received known peer hash " << front << ", connecting"; m_searches.get<0>().modify(itr, ModifyState(front, from)); m_ctx.getOutMsgDisp().getTransport()->connect(m_ctx.getDatabase().getRouterInfo(front)); } } } } }
void EstablishmentManager::processCreated(EstablishmentStatePtr const &state) { state->calculateDHSecret(); if(!state->verifyCreationSignature()) { I2P_LOG(m_log, warning) << "creation signature verification failed"; state->setState(EstablishmentState::State::FAILURE); return; } const ByteArray& dhSecret = state->getDHSecret(); SessionKey newKey(toSessionKey(dhSecret)), newMacKey; state->setSessionKey(newKey); copy(dhSecret.begin() + 32, dhSecret.begin() + 32 + 32, newMacKey.begin()); state->setMacKey(newMacKey); Endpoint ep = state->getTheirEndpoint(); PeerState ps(ep, state->getTheirIdentity().getHash()); ps.setCurrentSessionKey(state->getSessionKey()); ps.setCurrentMacKey(state->getMacKey()); std::lock_guard<std::mutex> lock(m_context.peers.getMutex()); m_context.peers.addPeer(std::move(ps)); PacketPtr p = PacketBuilder::buildSessionConfirmed(state); p->encrypt(state->getSessionKey(), state->getMacKey()); m_context.sendPacket(p); state->setState(EstablishmentState::State::CONFIRMED_SENT); post(state); }
void DeliveryStatus::handleMessage(RouterHash const from, I2NP::MessagePtr const msg) { I2P_LOG_SCOPED_RH(m_log, from); I2P_LOG(m_log, debug) << "received DeliveryStatus message, replying with DatabaseStore message"; Mapping am; am.setValue("caps", "BC"); am.setValue("host", m_ctx.getDatabase().getConfigValue("ssu_external_ip")); am.setValue("key", m_ctx.getIdentity().getHashEncoded()); am.setValue("port", m_ctx.getDatabase().getConfigValue("ssu_external_port")); RouterAddress a(5, Date(0), "SSU", am); Mapping rm; rm.setValue("coreVersion", "0.9.6"); rm.setValue("netId", "2"); rm.setValue("router.version", "0.9.6"); rm.setValue("stat_uptime", "90m"); rm.setValue("caps", "MR"); RouterInfo myInfo(m_ctx.getIdentity(), Date(), rm); myInfo.addAddress(a); myInfo.sign(m_ctx.getSigningKey()); Botan::Pipe gzPipe(new Botan::Zlib_Compression); gzPipe.start_msg(); gzPipe.write(myInfo.serialize()); gzPipe.end_msg(); unsigned int size = gzPipe.remaining(); ByteArray gzInfoBytes(size); gzPipe.read(gzInfoBytes.data(), size); auto mydsm = std::make_shared<I2NP::DatabaseStore>(myInfo.getIdentity().getHash(), I2NP::DatabaseStore::DataType::ROUTER_INFO, 0, gzInfoBytes); m_ctx.getOutMsgDisp().sendMessage(from, mydsm); }
void DatabaseStore::handleMessage(RouterHash const from, I2NP::MessagePtr const msg) { try { std::shared_ptr<I2NP::DatabaseStore> dsm = std::dynamic_pointer_cast<I2NP::DatabaseStore>(msg); I2P_LOG_SCOPED_TAG(m_log, "RouterHash", from); I2P_LOG(m_log, debug) << "received DatabaseStore message"; Botan::Pipe ungzPipe(new Gzip_Decompression); switch(dsm->getDataType()) { case I2NP::DatabaseStore::DataType::ROUTER_INFO: { ungzPipe.start_msg(); ungzPipe.write(dsm->getData()); ungzPipe.end_msg(); unsigned int size = ungzPipe.remaining(); ByteArray inflatedData(size); ungzPipe.read(inflatedData.data(), size); auto begin = inflatedData.cbegin(); RouterInfo ri(begin, inflatedData.cend()); if(ri.verifySignature()) { m_ctx.getDatabase()->setRouterInfo(ri); I2P_LOG(m_log, debug) << "added RouterInfo to DB"; m_ctx.getSignals().invokeDatabaseStore(from, ri.getIdentity().getHash(), true); } else { I2P_LOG(m_log, error) << "RouterInfo verification failed"; } } break; case I2NP::DatabaseStore::DataType::LEASE_SET: I2P_LOG(m_log, debug) << "this is a LeaseSet"; // signal here break; } } catch(Botan::Decoding_Error &e) { I2P_LOG(m_log, error) << "problem decompressing data: " << e.what(); } }
void VariableTunnelBuildReply::handleMessage(RouterHash const from, I2NP::MessagePtr const msg) { std::shared_ptr<I2NP::VariableTunnelBuildReply> vtbr = std::dynamic_pointer_cast<I2NP::VariableTunnelBuildReply>(msg); I2P_LOG_SCOPED_TAG(m_log, "RouterHash", from); I2P_LOG(m_log, debug) << "received VariableTunnelBuildReply message"; m_ctx.getSignals().invokeTunnelRecordsReceived(vtbr->getMsgId(), vtbr->getRecords()); }
void TunnelData::handleMessage(RouterHash const from, I2NP::MessagePtr const msg) { std::shared_ptr<I2NP::TunnelData> td = std::dynamic_pointer_cast<I2NP::TunnelData>(msg); I2P_LOG_SCOPED_TAG(m_log, "RouterHash", from); I2P_LOG(m_log, debug) << "received TunnelData message"; m_ctx.getSignals().invokeTunnelData(from, td->getTunnelId(), td->getData()); }
void DatabaseSearchReply::handleMessage(RouterHash const from, I2NP::MessagePtr const msg) { std::shared_ptr<I2NP::DatabaseSearchReply> dsr = std::dynamic_pointer_cast<I2NP::DatabaseSearchReply>(msg); I2P_LOG_SCOPED_TAG(m_log, "RouterHash", from); I2P_LOG(m_log, debug) << "received DatabaseSearchReply message"; m_ctx.getSignals().invokeSearchReply(from, dsr->getKey(), dsr->getHashes()); }
void SearchManager::timeout(const boost::system::error_code& e, KademliaKey const k) { if(!e) { I2P_LOG(m_log, debug) << "timeout for " << Base64::encode(ByteArray(k.cbegin(), k.cend())); std::lock_guard<std::mutex> lock(m_searchesMutex); cancel(k); } }
void EstablishmentManager::timeoutCallback(const boost::system::error_code& e, EstablishmentStatePtr es) { if(!e) { I2P_LOG_SCOPED_TAG(m_log, "Endpoint", es->getTheirEndpoint()); I2P_LOG(m_log, debug) << "establishment timed out"; es->setState(EstablishmentState::State::FAILURE); post(es); } }
void PeerManager::callback(const boost::system::error_code &e) { try { uint32_t minPeers = std::stoi(m_ctx.getDatabase()->getConfigValue("min_peers")); uint32_t numPeers = m_ctx.getOutMsgDisp().getTransport()->numPeers(); I2P_LOG(m_log, debug) << "current number of peers: " << numPeers; I2P_LOG(m_log, debug) << boost::log::add_value("peers", (uint32_t) numPeers); int32_t gap = minPeers - numPeers; for(int32_t i = 0; i < gap; i++) m_ctx.getOutMsgDisp().getTransport()->connect(m_ctx.getProfileManager().getPeer()); } catch(std::exception &e) { I2P_LOG(m_log, error) << "exception in PeerManager: " << e.what(); } if ( ! m_graceful ) { m_timer.expires_at(m_timer.expires_at() + boost::posix_time::time_duration(0, 0, 10)); m_timer.async_wait(boost::bind(&PeerManager::callback, this, boost::asio::placeholders::error)); } }
void SearchManager::databaseStore(RouterHash const from, std::array<unsigned char, 32> const k, bool isRouterInfo) { I2P_LOG_SCOPED_RH(m_log, from); I2P_LOG(m_log, debug) << "received DatabaseStore"; std::lock_guard<std::mutex> lock(m_searchesMutex); if(m_searches.get<0>().count(k)) { I2P_LOG(m_log, debug) << "received DatabaseStore for our goal, terminating search"; SearchStateByGoal::iterator itr = m_searches.get<0>().find(k); const SearchState& ss = *itr; if(isRouterInfo) m_ios.post(boost::bind(boost::ref(m_successSignal), ss.goal, m_ctx.getDatabase().getRouterInfo(k).getIdentity().getHash())); m_searches.get<0>().erase(itr); m_timers.erase(k); return; } if(isRouterInfo) { if(m_searches.get<1>().count(from)) { I2P_LOG(m_log, debug) << "found Routerhash in pending search table"; SearchStateByCurrent::iterator itr = m_searches.get<1>().find(from); const SearchState& ss = *itr; if(ss.next == k) { I2P_LOG(m_log, debug) << "stored hash is one we're waiting for, connecting"; m_searches.get<1>().modify(itr, ModifyState(k, from)); m_ctx.getOutMsgDisp().getTransport()->connect(m_ctx.getDatabase().getRouterInfo(k)); } } } }
void SearchManager::connected(RouterHash const rh) { I2P_LOG_SCOPED_RH(m_log, rh); I2P_LOG(m_log, debug) << "connection established"; std::lock_guard<std::mutex> lock(m_searchesMutex); if(m_searches.get<1>().count(rh)) { I2P_LOG(m_log, debug) << "found RouterHash in pending search table"; SearchStateByCurrent::iterator itr = m_searches.get<1>().find(rh); const SearchState& ss = *itr; if(ss.state == SearchState::CONNECTING) { I2P_LOG(m_log, debug) << "found good SearchState, sending DatabaseLookup"; m_searches.get<1>().modify(itr, ModifyState(SearchState::LOOKUP_SENT)); I2NP::MessagePtr dbl(new I2NP::DatabaseLookup(ss.goal, m_ctx.getIdentity().getHash(), 0, ss.excluded)); m_ctx.getOutMsgDisp().getTransport()->send(rh, dbl->toBytes()); } } }
void EstablishmentManager::stateChanged(EstablishmentStatePtr es) { const Endpoint &ep = es->getTheirEndpoint(); I2P_LOG_SCOPED_TAG(m_log, "Endpoint", ep); switch(es->getState()) { case EstablishmentState::State::REQUEST_SENT: I2P_LOG(m_log, debug) << "sent session request"; break; case EstablishmentState::State::REQUEST_RECEIVED: I2P_LOG(m_log, debug) << "received session request"; processRequest(es); break; case EstablishmentState::State::CREATED_SENT: I2P_LOG(m_log, debug) << "sent session created"; break; case EstablishmentState::State::CREATED_RECEIVED: I2P_LOG(m_log, debug) << "received session created"; processCreated(es); break; case EstablishmentState::State::CONFIRMED_SENT: { const RouterHash &rh = es->getTheirIdentity().getHash(); I2P_LOG_SCOPED_TAG(m_log, "RouterHash", rh); I2P_LOG(m_log, debug) << "sent session confirmed"; m_context.ios.post(boost::bind(boost::ref(m_context.establishedSignal), rh, (es->getDirection() == EstablishmentState::Direction::INBOUND))); delState(ep); } break; case EstablishmentState::State::CONFIRMED_RECEIVED: I2P_LOG(m_log, debug) << "received session confirmed"; processConfirmed(es); break; case EstablishmentState::State::UNKNOWN: case EstablishmentState::State::FAILURE: I2P_LOG(m_log, info) << "establishment failed"; if(es->getDirection() == EstablishmentState::Direction::OUTBOUND) m_context.ios.post(boost::bind(boost::ref(m_context.failureSignal), es->getTheirIdentity().getHash())); delState(ep); break; } }
void SearchManager::createSearch(KademliaKey const &k, RouterHash const &start) { std::lock_guard<std::mutex> lock(m_searchesMutex); if(m_searches.get<0>().count(k)) return; SearchState ss; ss.goal = k; ss.current = start; m_searches.insert(ss); m_timers[k] = std::make_shared<boost::asio::deadline_timer>(m_ios, boost::posix_time::time_duration(0, 2, 0)); m_timers[k]->async_wait(boost::bind(&SearchManager::timeout, this, boost::asio::placeholders::error, k)); I2P_LOG(m_log, debug) << "created SearchState for " << Base64::encode(ByteArray(k.cbegin(), k.cend())) << " starting with " << start; m_ctx.getOutMsgDisp().getTransport()->connect(m_ctx.getDatabase().getRouterInfo(start)); }
void Router::start() { I2P_LOG(m_impl->log, info) << "local router hash: " << m_impl->ctx.getIdentity()->getHash(); m_impl->serviceThread = std::thread([&](){ while(1) { try { m_impl->ios.run(); break; } catch(std::exception &e) { // TODO Backtrace I2P_LOG(m_impl->log, error) << "exception in service thread: " << e.what(); } } }); /* Peer conected */ m_impl->ctx.getSignals().registerPeerConnected(boost::bind( &PeerManager::connected, boost::ref(m_impl->ctx.getPeerManager()), _1 )); m_impl->ctx.getSignals().registerPeerConnected(boost::bind( &OutboundMessageDispatcher::connected, boost::ref(m_impl->ctx.getOutMsgDisp()), _1 )); m_impl->ctx.getSignals().registerPeerConnected(boost::bind( &DHT::SearchManager::connected, boost::ref(m_impl->ctx.getDHT()->getSearchManager()), _1 )); /* Connection failure */ m_impl->ctx.getSignals().registerConnectionFailure(boost::bind( &DHT::SearchManager::connectionFailure, boost::ref(m_impl->ctx.getDHT()->getSearchManager()), _1 )); m_impl->ctx.getSignals().registerConnectionFailure(boost::bind( &PeerManager::failure, boost::ref(m_impl->ctx.getPeerManager()), _1 )); m_impl->ctx.getSignals().registerSearchReply(boost::bind( &DHT::SearchManager::searchReply, boost::ref(m_impl->ctx.getDHT()->getSearchManager()), _1, _2, _3 )); m_impl->ctx.getSignals().registerDatabaseStore(boost::bind( &DHT::SearchManager::databaseStore, boost::ref(m_impl->ctx.getDHT()->getSearchManager()), _1, _2, _3 )); /* Everything related to tunnels */ m_impl->ctx.getSignals().registerTunnelRecordsReceived(boost::bind( &Tunnel::Manager::receiveRecords, boost::ref(m_impl->ctx.getTunnelManager()), _1, _2 )); m_impl->ctx.getSignals().registerTunnelGatewayData(boost::bind( &Tunnel::Manager::receiveGatewayData, boost::ref(m_impl->ctx.getTunnelManager()), _1, _2, _3 )); m_impl->ctx.getSignals().registerTunnelData(boost::bind( &Tunnel::Manager::receiveData, boost::ref(m_impl->ctx.getTunnelManager()), _1, _2, _3 )); /* Everything related to the DHT */ m_impl->ctx.getDHT()->getSearchManager().registerSuccess(boost::bind( &OutboundMessageDispatcher::dhtSuccess, boost::ref(m_impl->ctx.getOutMsgDisp()), _1, _2 )); m_impl->ctx.getDHT()->getSearchManager().registerFailure( boost::bind(&OutboundMessageDispatcher::dhtFailure, boost::ref(m_impl->ctx.getOutMsgDisp()), _1 )); m_impl->ctx.getPeerManager().begin(); //m_impl->ctx.getTunnelManager().begin(); }
void PeerManager::begin() { I2P_LOG(m_log,info) << "PeerManager beginning"; m_timer.async_wait(boost::bind(&PeerManager::callback, this, boost::asio::placeholders::error)); }