#ifndef TUNNEL_CONFIG_H__ #define TUNNEL_CONFIG_H__ #include #include #include #include #include "TunnelCrypto.h" #include "RouterInfo.h" #include "RouterContext.h" #include "util/Timestamp.h" namespace i2p { namespace tunnel { struct TunnelHopConfig { std::shared_ptr router, nextRouter; uint32_t tunnelID, nextTunnelID; uint8_t layerKey[32]; uint8_t ivKey[32]; uint8_t replyKey[32]; uint8_t replyIV[16]; bool isGateway, isEndpoint; TunnelHopConfig * next, * prev; i2p::crypto::TunnelDecryption decryption; int recordIndex; // record # in tunnel build message TunnelHopConfig (std::shared_ptr r) { CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); rnd.GenerateBlock (layerKey, 32); rnd.GenerateBlock (ivKey, 32); rnd.GenerateBlock (replyIV, 16); tunnelID = rnd.GenerateWord32 (); isGateway = true; isEndpoint = true; router = r; //nextRouter = nullptr; nextTunnelID = 0; next = nullptr; prev = nullptr; } void SetNextRouter (std::shared_ptr r) { nextRouter = r; isEndpoint = false; CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator (); nextTunnelID = rnd.GenerateWord32 (); } void SetReplyHop (const TunnelHopConfig * replyFirstHop) { nextRouter = replyFirstHop->router; nextTunnelID = replyFirstHop->tunnelID; isEndpoint = true; } void SetNext (TunnelHopConfig * n) { next = n; if (next) { next->prev = this; next->isGateway = false; isEndpoint = false; nextRouter = next->router; nextTunnelID = next->tunnelID; } } void SetPrev (TunnelHopConfig * p) { prev = p; if (prev) { prev->next = this; prev->isEndpoint = false; isGateway = false; } } void CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID) const { uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE] = {}; htobe32buf (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID); memcpy (clearText + BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET, router->GetIdentHash (), 32); htobe32buf (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID); memcpy (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextRouter->GetIdentHash (), 32); memcpy (clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32); memcpy (clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32); memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32); memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16); uint8_t flag = 0; if (isGateway) flag |= 0x80; if (isEndpoint) flag |= 0x40; clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag; htobe32buf (clearText + BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetHoursSinceEpoch ()); htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID); // TODO: fill padding router->GetElGamalEncryption ()->Encrypt (clearText, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET); memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)router->GetIdentHash (), 16); } }; class TunnelConfig: public std::enable_shared_from_this { public: TunnelConfig (std::vector > peers, std::shared_ptr replyTunnelConfig = nullptr) // replyTunnelConfig=nullptr means inbound { TunnelHopConfig * prev = nullptr; for (auto it: peers) { auto hop = new TunnelHopConfig (it); if (prev) prev->SetNext (hop); else m_FirstHop = hop; prev = hop; } m_LastHop = prev; if (replyTunnelConfig) // outbound { m_FirstHop->isGateway = false; m_LastHop->SetReplyHop (replyTunnelConfig->GetFirstHop ()); } else // inbound m_LastHop->SetNextRouter (i2p::context.GetSharedRouterInfo ()); } ~TunnelConfig () { TunnelHopConfig * hop = m_FirstHop; while (hop) { auto tmp = hop; hop = hop->next; delete tmp; } } TunnelHopConfig * GetFirstHop () const { return m_FirstHop; } TunnelHopConfig * GetLastHop () const { return m_LastHop; } int GetNumHops () const { int num = 0; TunnelHopConfig * hop = m_FirstHop; while (hop) { num++; hop = hop->next; } return num; } bool IsInbound () const { return m_FirstHop->isGateway; } std::vector > GetPeers () const { std::vector > peers; TunnelHopConfig * hop = m_FirstHop; while (hop) { peers.push_back (hop->router); hop = hop->next; } return peers; } void Print (std::stringstream& s) const { TunnelHopConfig * hop = m_FirstHop; if (!IsInbound ()) // outbound s << "me"; s << "-->" << m_FirstHop->tunnelID; while (hop) { s << ":" << hop->router->GetIdentHashAbbreviation () << "-->"; if (!hop->isEndpoint) s << hop->nextTunnelID; else return; hop = hop->next; } // we didn't reach endpoint that mean we are last hop s << ":me"; } std::shared_ptr Invert () const { auto peers = GetPeers (); std::reverse (peers.begin (), peers.end ()); // we use ourself as reply tunnel for outbound tunnel return IsInbound () ? std::make_shared(peers, shared_from_this ()) : std::make_shared(peers); } std::shared_ptr Clone (std::shared_ptr replyTunnelConfig = nullptr) const { return std::make_shared (GetPeers (), replyTunnelConfig); } private: // this constructor can't be called from outside TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr) { } private: TunnelHopConfig * m_FirstHop, * m_LastHop; }; } } #endif