diff --git a/CMakeLists.txt b/CMakeLists.txt index 458db79bd..aac03576b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -501,24 +501,17 @@ set(LIB_SRC llarp/crypto/types.cpp llarp/dht/bucket.cpp llarp/dht/context.cpp - llarp/dht/decode.cpp - llarp/dht/dht_immediate.cpp llarp/dht/dht.cpp - llarp/dht/find_intro.cpp - llarp/dht/find_router.cpp - llarp/dht/got_intro.cpp - llarp/dht/got_router.cpp llarp/dht/kademlia.cpp llarp/dht/key.cpp llarp/dht/message.cpp - llarp/dht/messages/all.cpp llarp/dht/messages/findintro.cpp llarp/dht/messages/findrouter.cpp llarp/dht/messages/gotintro.cpp llarp/dht/messages/gotrouter.cpp llarp/dht/messages/pubintro.cpp llarp/dht/node.cpp - llarp/dht/publish_intro.cpp + llarp/dht/txowner.cpp llarp/dns.cpp llarp/dnsc.cpp llarp/dnsd.cpp @@ -615,8 +608,10 @@ set(TEST_SRC # actual test cases test/crypto/test_llarp_crypto_types.cpp test/crypto/test_llarp_crypto.cpp + test/dht/test_llarp_dht_bucket.cpp + test/dht/test_llarp_dht_kademlia.cpp test/dht/test_llarp_dht_key.cpp - test/dht/test_llarp_dht.cpp + test/dht/test_llarp_dht_txowner.cpp test/dns/test_llarp_dns_dns.cpp test/exit/test_llarp_exit_context.cpp test/link/test_llarp_link.cpp diff --git a/daemon/main.cpp b/daemon/main.cpp index 5a72455bb..feb5f773a 100644 --- a/daemon/main.cpp +++ b/daemon/main.cpp @@ -150,6 +150,7 @@ main(int argc, char *argv[]) fs::path basepath = homedir / fs::path(".lokinet"); fs::path fpath = basepath / "lokinet.ini"; + llarp::LogDebug("Find or create ", basepath.string()); std::error_code ec; // These paths are guaranteed to exist - $APPDATA or $HOME // so only create .lokinet/* @@ -170,8 +171,12 @@ main(int argc, char *argv[]) } if(genconfigOnly) + { return 0; + } + // this is important, can downgrade from Info though + llarp::LogInfo("Using config file: ", conffname); ctx = llarp_main_init(conffname.c_str(), multiThreaded); int code = 1; if(ctx) diff --git a/llarp/config.cpp b/llarp/config.cpp index c18f60a2c..7aa4c1eb6 100644 --- a/llarp/config.cpp +++ b/llarp/config.cpp @@ -76,6 +76,8 @@ llarp_ensure_config(const char *fname, const char *basedir, bool overwrite, #endif } + llarp::LogInfo("Attempting to create config file ", fname); + // abort if client.ini already exists if(!asRouter) { diff --git a/llarp/dht/bucket.hpp b/llarp/dht/bucket.hpp index dac6d5bb8..ad09e79cf 100644 --- a/llarp/dht/bucket.hpp +++ b/llarp/dht/bucket.hpp @@ -1,7 +1,6 @@ #ifndef LLARP_DHT_BUCKET_HPP #define LLARP_DHT_BUCKET_HPP -#include #include #include @@ -17,28 +16,47 @@ namespace llarp struct Bucket { using BucketStorage_t = std::map< Key_t, Val_t, XorMetric >; + using Random_t = std::function< uint64_t() >; - Bucket(const Key_t& us) : nodes(XorMetric(us)){}; + Bucket(const Key_t& us, Random_t r) : nodes(XorMetric(us)), random(r){}; size_t - Size() const + size() const { return nodes.size(); } + struct SetIntersector + { + bool + operator()(const typename BucketStorage_t::value_type& lhs, + const Key_t& rhs) + { + return lhs.first < rhs; + } + + bool + operator()(const Key_t& lhs, + const typename BucketStorage_t::value_type& rhs) + { + return lhs < rhs.first; + } + }; + bool GetRandomNodeExcluding(Key_t& result, const std::set< Key_t >& exclude) const { - std::vector< Key_t > candidates; - for(const auto& item : nodes) + std::vector< typename BucketStorage_t::value_type > candidates; + std::set_difference(nodes.begin(), nodes.end(), exclude.begin(), + exclude.end(), std::back_inserter(candidates), + SetIntersector()); + + if(candidates.empty()) { - if(exclude.find(item.first) == exclude.end()) - candidates.push_back(item.first); - } - if(candidates.size() == 0) return false; - result = candidates[llarp::randint() % candidates.size()]; + } + result = candidates[random() % candidates.size()].first; return true; } @@ -62,7 +80,7 @@ namespace llarp bool GetManyRandom(std::set< Key_t >& result, size_t N) const { - if(nodes.size() < N) + if(nodes.size() < N || nodes.empty()) { llarp::LogWarn("Not enough dht nodes, have ", nodes.size(), " want ", N); @@ -70,10 +88,10 @@ namespace llarp } if(nodes.size() == N) { - for(const auto& node : nodes) - { - result.insert(node.first); - } + std::transform(nodes.begin(), nodes.end(), + std::inserter(result, result.end()), + [](const auto& a) { return a.first; }); + return true; } size_t expecting = N; @@ -81,31 +99,15 @@ namespace llarp while(N) { auto itr = nodes.begin(); - std::advance(itr, llarp::randint() % sz); + std::advance(itr, random() % sz); if(result.insert(itr->first).second) + { --N; + } } return result.size() == expecting; } - bool - GetManyNearExcluding(const Key_t& target, std::set< Key_t >& result, - size_t N, const std::set< Key_t >& exclude) const - { - std::set< Key_t > s; - for(const auto& k : exclude) - s.insert(k); - Key_t peer; - while(N--) - { - if(!FindCloseExcluding(target, peer, s)) - return false; - s.insert(peer); - result.insert(peer); - } - return true; - } - bool FindCloseExcluding(const Key_t& target, Key_t& result, const std::set< Key_t >& exclude) const @@ -117,7 +119,9 @@ namespace llarp for(const auto& item : nodes) { if(exclude.count(item.first)) + { continue; + } auto curDist = item.first ^ target; if(curDist < mindist) @@ -129,12 +133,33 @@ namespace llarp return mindist < maxdist; } + bool + GetManyNearExcluding(const Key_t& target, std::set< Key_t >& result, + size_t N, const std::set< Key_t >& exclude) const + { + std::set< Key_t > s(exclude.begin(), exclude.end()); + + Key_t peer; + while(N--) + { + if(!FindCloseExcluding(target, peer, s)) + { + return false; + } + s.insert(peer); + result.insert(peer); + } + return true; + } + void PutNode(const Val_t& val) { auto itr = nodes.find(val.ID); if(itr == nodes.end() || itr->second < val) + { nodes[val.ID] = val; + } } void @@ -142,7 +167,9 @@ namespace llarp { auto itr = nodes.find(key); if(itr != nodes.end()) + { nodes.erase(itr); + } } bool @@ -151,7 +178,14 @@ namespace llarp return nodes.find(key) != nodes.end(); } + void + Clear() + { + nodes.clear(); + } + BucketStorage_t nodes; + Random_t random; }; } // namespace dht } // namespace llarp diff --git a/llarp/dht/context.cpp b/llarp/dht/context.cpp index 62a1bfa77..5b488372d 100644 --- a/llarp/dht/context.cpp +++ b/llarp/dht/context.cpp @@ -1,6 +1,10 @@ #include +#include +#include #include +#include +#include #include #include #include @@ -268,9 +272,9 @@ namespace llarp { router = r; ourKey = us; - nodes = new Bucket< RCNode >(ourKey); - services = new Bucket< ISNode >(ourKey); - llarp::LogDebug("intialize dht with key ", ourKey); + nodes = new Bucket< RCNode >(ourKey, llarp::randint); + services = new Bucket< ISNode >(ourKey, llarp::randint); + llarp::LogDebug("initialize dht with key ", ourKey); // start exploring r->logic->call_later( @@ -288,7 +292,7 @@ namespace llarp void Context::DHTSendTo(const RouterID &peer, IMessage *msg, bool keepalive) { - llarp::DHTImmeidateMessage m; + llarp::DHTImmediateMessage m; m.msgs.emplace_back(msg); router->SendToOrQueue(peer, &m); if(keepalive) @@ -452,7 +456,7 @@ namespace llarp if(I.A != introset.A) { llarp::LogWarn( - "publish introset acknoledgement acked a different service"); + "publish introset acknowledgement acked a different service"); return false; } return true; @@ -666,14 +670,14 @@ namespace llarp if(!nodes) return false; - size_t nodeCount = nodes->Size(); + size_t nodeCount = nodes->size(); if(nodeCount == 0) { llarp::LogError( "cannot handle exploritory router lookup, no dht peers"); return false; } - llarp::LogDebug("We have ", nodes->Size(), + llarp::LogDebug("We have ", nodes->size(), " connected nodes into the DHT"); // ourKey should never be in the connected list // requester is likely in the connected list diff --git a/llarp/dht/context.hpp b/llarp/dht/context.hpp index 4074cd7c3..1f1e84dbb 100644 --- a/llarp/dht/context.hpp +++ b/llarp/dht/context.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -18,40 +19,6 @@ namespace llarp namespace dht { - struct TXOwner - { - Key_t node; - uint64_t txid = 0; - - TXOwner() = default; - - TXOwner(const Key_t& k, uint64_t id) : node(k), txid(id) - { - } - - bool - operator==(const TXOwner& other) const - { - return txid == other.txid && node == other.node; - } - bool - operator<(const TXOwner& other) const - { - return txid < other.txid || node < other.node; - } - - struct Hash - { - std::size_t - operator()(TXOwner const& o) const noexcept - { - std::size_t sz2; - memcpy(&sz2, &o.node[0], sizeof(std::size_t)); - return o.txid ^ (sz2 << 1); - } - }; - }; - struct Context; template < typename K, typename V > @@ -224,7 +191,7 @@ namespace llarp const Key_t& target, bool recursive, std::vector< std::unique_ptr< IMessage > >& replies); - /// relay a dht messeage from a local path to the main network + /// relay a dht message from a local path to the main network bool RelayRequestForPath(const llarp::PathID_t& localPath, const IMessage* msg); diff --git a/llarp/dht/decode.cpp b/llarp/dht/decode.cpp deleted file mode 100644 index 6f83f9987..000000000 --- a/llarp/dht/decode.cpp +++ /dev/null @@ -1,135 +0,0 @@ -#include -#include - -namespace llarp -{ - namespace dht - { - struct MessageDecoder - { - const Key_t &From; - std::unique_ptr< IMessage > msg; - bool firstKey = true; - bool relayed = false; - - MessageDecoder(const Key_t &from) : From(from) - { - } - - static bool - on_key(dict_reader *r, llarp_buffer_t *key) - { - llarp_buffer_t strbuf; - MessageDecoder *dec = static_cast< MessageDecoder * >(r->user); - // check for empty dict - if(!key) - return !dec->firstKey; - - // first key - if(dec->firstKey) - { - if(!llarp_buffer_eq(*key, "A")) - return false; - if(!bencode_read_string(r->buffer, &strbuf)) - return false; - // bad msg size? - if(strbuf.sz != 1) - return false; - llarp::LogInfo("Handle DHT message ", *strbuf.base, - " relayed=", dec->relayed); - switch(*strbuf.base) - { - case 'F': - dec->msg.reset(new FindIntroMessage(dec->From, dec->relayed)); - break; - case 'R': - if(dec->relayed) - dec->msg.reset(new RelayedFindRouterMessage(dec->From)); - else - dec->msg.reset(new FindRouterMessage(dec->From)); - break; - case 'S': - dec->msg.reset(new GotRouterMessage(dec->From, dec->relayed)); - break; - case 'I': - dec->msg.reset(new PublishIntroMessage()); - break; - case 'G': - if(dec->relayed) - { - dec->msg.reset(new RelayedGotIntroMessage()); - break; - } - else - { - dec->msg.reset(new GotIntroMessage(dec->From)); - break; - } - default: - llarp::LogWarn("unknown dht message type: ", (char)*strbuf.base); - // bad msg type - return false; - } - dec->firstKey = false; - return dec->msg != nullptr; - } - else - return dec->msg->DecodeKey(*key, r->buffer); - } - }; - - std::unique_ptr< IMessage > - DecodeMesssage(const Key_t &from, llarp_buffer_t *buf, bool relayed) - { - MessageDecoder dec(from); - dec.relayed = relayed; - dict_reader r; - r.user = &dec; - r.on_key = &MessageDecoder::on_key; - if(!bencode_read_dict(buf, &r)) - return nullptr; - - return std::unique_ptr< IMessage >(std::move(dec.msg)); - } - - struct ListDecoder - { - ListDecoder(const Key_t &from, - std::vector< std::unique_ptr< IMessage > > &list) - : From(from), l(list){}; - - bool relayed = false; - const Key_t &From; - std::vector< std::unique_ptr< IMessage > > &l; - - static bool - on_item(list_reader *r, bool has) - { - ListDecoder *dec = static_cast< ListDecoder * >(r->user); - if(!has) - return true; - auto msg = DecodeMesssage(dec->From, r->buffer, dec->relayed); - if(msg) - { - dec->l.emplace_back(std::move(msg)); - return true; - } - else - return false; - } - }; - - bool - DecodeMesssageList(Key_t from, llarp_buffer_t *buf, - std::vector< std::unique_ptr< IMessage > > &list, - bool relayed) - { - ListDecoder dec(from, list); - dec.relayed = relayed; - list_reader r; - r.user = &dec; - r.on_item = &ListDecoder::on_item; - return bencode_read_list(buf, &r); - } - } // namespace dht -} // namespace llarp diff --git a/llarp/dht/dht_immediate.cpp b/llarp/dht/dht_immediate.cpp deleted file mode 100644 index 74b648db3..000000000 --- a/llarp/dht/dht_immediate.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include - -#include - -namespace llarp -{ - DHTImmeidateMessage::~DHTImmeidateMessage() - { - } - - void - DHTImmeidateMessage::Clear() - { - msgs.clear(); - } - - bool - DHTImmeidateMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t *buf) - { - if(llarp_buffer_eq(key, "m")) - return llarp::dht::DecodeMesssageList(dht::Key_t(session->GetPubKey()), - buf, msgs); - if(llarp_buffer_eq(key, "v")) - { - if(!bencode_read_integer(buf, &version)) - return false; - return version == LLARP_PROTO_VERSION; - } - // bad key - return false; - } - - bool - DHTImmeidateMessage::BEncode(llarp_buffer_t *buf) const - { - if(!bencode_start_dict(buf)) - return false; - - // message type - if(!bencode_write_bytestring(buf, "a", 1)) - return false; - if(!bencode_write_bytestring(buf, "m", 1)) - return false; - - // dht messages - if(!bencode_write_bytestring(buf, "m", 1)) - return false; - // begin list - if(!bencode_start_list(buf)) - return false; - for(const auto &msg : msgs) - { - if(!msg->BEncode(buf)) - return false; - } - // end list - if(!bencode_end(buf)) - return false; - - // protocol version - if(!bencode_write_version_entry(buf)) - return false; - - return bencode_end(buf); - } - - bool - DHTImmeidateMessage::HandleMessage(llarp::Router *router) const - { - DHTImmeidateMessage reply; - reply.session = session; - bool result = true; - for(auto &msg : msgs) - { - result &= msg->HandleMessage(router->dht, reply.msgs); - } - if(reply.msgs.size()) - { - if(result) - { - result = router->SendToOrQueue(session->GetPubKey(), &reply); - } - } - return result; - } -} // namespace llarp diff --git a/llarp/dht/find_intro.cpp b/llarp/dht/find_intro.cpp deleted file mode 100644 index 441b3e466..000000000 --- a/llarp/dht/find_intro.cpp +++ /dev/null @@ -1,217 +0,0 @@ -#include -#include -#include -#include - -namespace llarp -{ - namespace dht - { - /* - struct IntroSetLookupInformer - { - llarp::Router* router; - service::Address target; - - void - SendReply(const llarp::routing::IMessage* msg) - { - } - }; - */ - - FindIntroMessage::~FindIntroMessage() - { - } - - bool - FindIntroMessage::DecodeKey(llarp_buffer_t k, llarp_buffer_t* val) - { - bool read = false; - - if(!BEncodeMaybeReadDictEntry("N", N, read, k, val)) - return false; - - if(!BEncodeMaybeReadDictInt("R", R, read, k, val)) - return false; - - if(!BEncodeMaybeReadDictEntry("S", S, read, k, val)) - return false; - - if(!BEncodeMaybeReadDictInt("T", T, read, k, val)) - return false; - - if(!BEncodeMaybeReadVersion("V", version, LLARP_PROTO_VERSION, read, k, - val)) - return false; - - return read; - } - - bool - FindIntroMessage::BEncode(llarp_buffer_t* buf) const - { - if(!bencode_start_dict(buf)) - return false; - - // message id - if(!BEncodeWriteDictMsgType(buf, "A", "F")) - return false; - if(N.Empty()) - { - // recursion - if(!BEncodeWriteDictInt("R", R, buf)) - return false; - // service address - if(!BEncodeWriteDictEntry("S", S, buf)) - return false; - } - else - { - if(!BEncodeWriteDictEntry("N", N, buf)) - return false; - // recursion - if(!BEncodeWriteDictInt("R", R, buf)) - return false; - } - // txid - if(!BEncodeWriteDictInt("T", T, buf)) - return false; - // protocol version - if(!BEncodeWriteDictInt("V", LLARP_PROTO_VERSION, buf)) - return false; - - return bencode_end(buf); - } - - bool - FindIntroMessage::HandleMessage( - llarp_dht_context* ctx, - std::vector< std::unique_ptr< IMessage > >& replies) const - { - if(R > 5) - { - llarp::LogError("R value too big, ", R, "> 5"); - return false; - } - auto& dht = ctx->impl; - if(dht.pendingIntrosetLookups.HasPendingLookupFrom(TXOwner{From, T})) - { - llarp::LogWarn("duplicate FIM from ", From, " txid=", T); - return false; - } - Key_t peer; - std::set< Key_t > exclude = {dht.OurKey(), From}; - if(N.Empty()) - { - llarp::LogInfo("lookup ", S.ToString()); - const auto introset = dht.GetIntroSetByServiceAddress(S); - if(introset) - { - service::IntroSet i = *introset; - replies.emplace_back(new GotIntroMessage({i}, T)); - return true; - } - else - { - if(R == 0) - { - // we don't have it - Key_t target = S.ToKey(); - Key_t closer; - // find closer peer - if(!dht.nodes->FindClosest(target, closer)) - return false; - if(relayed) - dht.LookupIntroSetForPath(S, T, pathID, closer); - else - replies.emplace_back(new GotIntroMessage(From, closer, T)); - return true; - } - else - { - Key_t us = dht.OurKey(); - Key_t target = S.ToKey(); - // we are recursive - if(dht.nodes->FindCloseExcluding(target, peer, exclude)) - { - if(relayed) - dht.LookupIntroSetForPath(S, T, pathID, peer); - else - { - if((us ^ target) < (peer ^ target)) - { - // we are not closer than our peer to the target so don't - // recurse farther - replies.emplace_back(new GotIntroMessage({}, T)); - return true; - } - else if(R > 0) - dht.LookupIntroSetRecursive(S, From, T, peer, R - 1); - else - dht.LookupIntroSetIterative(S, From, T, peer); - } - return true; - } - else - { - // no more closer peers - replies.emplace_back(new GotIntroMessage({}, T)); - return true; - } - } - } - } - else - { - if(relayed) - { - // tag lookup - if(dht.nodes->GetRandomNodeExcluding(peer, exclude)) - { - dht.LookupTagForPath(N, T, pathID, peer); - } - else - { - // no more closer peers - replies.emplace_back(new GotIntroMessage({}, T)); - return true; - } - } - else - { - if(R == 0) - { - // base case - auto introsets = dht.FindRandomIntroSetsWithTagExcluding(N, 2, {}); - std::vector< service::IntroSet > reply; - for(const auto& introset : introsets) - { - reply.push_back(introset); - } - replies.emplace_back(new GotIntroMessage(reply, T)); - return true; - } - else if(R < 5) - { - // tag lookup - if(dht.nodes->GetRandomNodeExcluding(peer, exclude)) - { - dht.LookupTagRecursive(N, From, T, peer, R - 1); - } - else - { - replies.emplace_back(new GotIntroMessage({}, T)); - } - } - else - { - // too big R value - replies.emplace_back(new GotIntroMessage({}, T)); - } - } - } - return true; - } - } // namespace dht -} // namespace llarp diff --git a/llarp/dht/find_router.cpp b/llarp/dht/find_router.cpp deleted file mode 100644 index 88643d664..000000000 --- a/llarp/dht/find_router.cpp +++ /dev/null @@ -1,172 +0,0 @@ -#include - -#include -#include -#include -#include - -namespace llarp -{ - namespace dht - { - bool - RelayedFindRouterMessage::HandleMessage( - llarp_dht_context *ctx, - std::vector< std::unique_ptr< IMessage > > &replies) const - { - auto &dht = ctx->impl; - /// lookup for us, send an immeidate reply - Key_t us = dht.OurKey(); - Key_t k{K}; - if(K == us) - { - auto path = dht.router->paths.GetByUpstream(K, pathID); - if(path) - { - replies.emplace_back( - new GotRouterMessage(k, txid, {dht.router->rc()}, false)); - return true; - } - return false; - } - - Key_t peer; - // check if we know this in our nodedb first - RouterContact found; - if(dht.router->nodedb->Get(K, found)) - { - replies.emplace_back(new GotRouterMessage(k, txid, {found}, false)); - return true; - } - // lookup if we don't have it in our nodedb - if(dht.nodes->FindClosest(k, peer)) - dht.LookupRouterForPath(K, txid, pathID, peer); - return true; - } - - FindRouterMessage::~FindRouterMessage() - { - } - - bool - FindRouterMessage::BEncode(llarp_buffer_t *buf) const - { - if(!bencode_start_dict(buf)) - return false; - - // message type - if(!bencode_write_bytestring(buf, "A", 1)) - return false; - if(!bencode_write_bytestring(buf, "R", 1)) - return false; - - // exploritory or not? - if(!bencode_write_bytestring(buf, "E", 1)) - return false; - if(!bencode_write_uint64(buf, exploritory ? 1 : 0)) - return false; - - // iterative or not? - if(!bencode_write_bytestring(buf, "I", 1)) - return false; - if(!bencode_write_uint64(buf, iterative ? 1 : 0)) - return false; - - // key - if(!bencode_write_bytestring(buf, "K", 1)) - return false; - if(!bencode_write_bytestring(buf, K.data(), K.size())) - return false; - - // txid - if(!bencode_write_bytestring(buf, "T", 1)) - return false; - if(!bencode_write_uint64(buf, txid)) - return false; - - // version - if(!bencode_write_bytestring(buf, "V", 1)) - return false; - if(!bencode_write_uint64(buf, version)) - return false; - - return bencode_end(buf); - } - - bool - FindRouterMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t *val) - { - llarp_buffer_t strbuf; - - if(llarp_buffer_eq(key, "E")) - { - uint64_t result; - if(!bencode_read_integer(val, &result)) - return false; - - exploritory = result != 0; - return true; - } - - if(llarp_buffer_eq(key, "I")) - { - uint64_t result; - if(!bencode_read_integer(val, &result)) - return false; - - iterative = result != 0; - return true; - } - if(llarp_buffer_eq(key, "K")) - { - if(!bencode_read_string(val, &strbuf)) - return false; - if(strbuf.sz != K.size()) - return false; - - std::copy(strbuf.base, strbuf.base + K.SIZE, K.begin()); - return true; - } - if(llarp_buffer_eq(key, "T")) - { - return bencode_read_integer(val, &txid); - } - if(llarp_buffer_eq(key, "V")) - { - return bencode_read_integer(val, &version); - } - return false; - } - - bool - FindRouterMessage::HandleMessage( - llarp_dht_context *ctx, - std::vector< std::unique_ptr< IMessage > > &replies) const - { - auto &dht = ctx->impl; - if(!dht.allowTransit) - { - llarp::LogWarn("Got DHT lookup from ", From, - " when we are not allowing dht transit"); - return false; - } - if(dht.pendingRouterLookups.HasPendingLookupFrom({From, txid})) - { - llarp::LogWarn("Duplicate FRM from ", From, " txid=", txid); - return false; - } - RouterContact found; - Key_t k{K}; - if(exploritory) - return dht.HandleExploritoryRouterLookup(From, txid, K, replies); - else if(dht.router->nodedb->Get(K, found)) - { - replies.emplace_back(new GotRouterMessage(k, txid, {found}, false)); - return true; - } - else - dht.LookupRouterRelayed(From, txid, k, !iterative, replies); - return true; - } - } // namespace dht -} // namespace llarp diff --git a/llarp/dht/got_intro.cpp b/llarp/dht/got_intro.cpp deleted file mode 100644 index c60a4d28c..000000000 --- a/llarp/dht/got_intro.cpp +++ /dev/null @@ -1,124 +0,0 @@ -#include -#include -#include -#include - -namespace llarp -{ - namespace dht - { - GotIntroMessage::GotIntroMessage( - const std::vector< llarp::service::IntroSet > &results, uint64_t tx) - : IMessage({}), I(results), T(tx) - { - } - - GotIntroMessage::~GotIntroMessage() - { - } - - bool - GotIntroMessage::HandleMessage( - llarp_dht_context *ctx, - __attribute__((unused)) - std::vector< std::unique_ptr< IMessage > > &replies) const - { - auto &dht = ctx->impl; - auto crypto = &dht.router->crypto; - - for(const auto &introset : I) - { - if(!introset.Verify(crypto, dht.Now())) - { - llarp::LogWarn( - "Invalid introset while handling direct GotIntro " - "from ", - From); - return false; - } - } - TXOwner owner(From, T); - auto tagLookup = dht.pendingTagLookups.GetPendingLookupFrom(owner); - if(tagLookup) - { - dht.pendingTagLookups.Found(owner, tagLookup->target, I); - return true; - } - auto serviceLookup = - dht.pendingIntrosetLookups.GetPendingLookupFrom(owner); - if(serviceLookup) - { - if(I.size()) - { - dht.pendingIntrosetLookups.Found(owner, serviceLookup->target, I); - } - else - { - dht.pendingIntrosetLookups.NotFound(owner, K); - } - return true; - } - llarp::LogError("no pending TX for GIM from ", From, " txid=", T); - return false; - } - - bool - RelayedGotIntroMessage::HandleMessage( - llarp_dht_context *ctx, - __attribute__((unused)) - std::vector< std::unique_ptr< IMessage > > &replies) const - { - // TODO: implement me better? - auto pathset = ctx->impl.router->paths.GetLocalPathSet(pathID); - if(pathset) - { - return pathset->HandleGotIntroMessage(this); - } - llarp::LogWarn("No path for got intro message pathid=", pathID); - return false; - } - - bool - GotIntroMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t *buf) - { - if(llarp_buffer_eq(key, "I")) - { - return BEncodeReadList(I, buf); - } - if(llarp_buffer_eq(key, "K")) - { - if(K) // duplicate key? - return false; - K.reset(new dht::Key_t()); - return K->BDecode(buf); - } - bool read = false; - if(!BEncodeMaybeReadDictInt("T", T, read, key, buf)) - return false; - if(!BEncodeMaybeReadDictInt("V", version, read, key, buf)) - return false; - return read; - } - - bool - GotIntroMessage::BEncode(llarp_buffer_t *buf) const - { - if(!bencode_start_dict(buf)) - return false; - if(!BEncodeWriteDictMsgType(buf, "A", "G")) - return false; - if(!BEncodeWriteDictList("I", I, buf)) - return false; - if(K) - { - if(!BEncodeWriteDictEntry("K", *K.get(), buf)) - return false; - } - if(!BEncodeWriteDictInt("T", T, buf)) - return false; - if(!BEncodeWriteDictInt("V", version, buf)) - return false; - return bencode_end(buf); - } - } // namespace dht -} // namespace llarp diff --git a/llarp/dht/got_router.cpp b/llarp/dht/got_router.cpp deleted file mode 100644 index 60fa5d1f7..000000000 --- a/llarp/dht/got_router.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include -#include - -#include - -namespace llarp -{ - namespace dht - { - GotRouterMessage::~GotRouterMessage() - { - } - - bool - GotRouterMessage::BEncode(llarp_buffer_t *buf) const - { - if(!bencode_start_dict(buf)) - return false; - - // message type - if(!BEncodeWriteDictMsgType(buf, "A", "S")) - return false; - - if(K) - { - if(!BEncodeWriteDictEntry("K", *K.get(), buf)) - return false; - } - - // near - if(N.size()) - { - if(!BEncodeWriteDictList("N", N, buf)) - return false; - } - - if(!BEncodeWriteDictList("R", R, buf)) - return false; - - // txid - if(!BEncodeWriteDictInt("T", txid, buf)) - return false; - - // version - if(!BEncodeWriteDictInt("V", version, buf)) - return false; - - return bencode_end(buf); - } - - bool - GotRouterMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t *val) - { - if(llarp_buffer_eq(key, "K")) - { - if(K) // duplicate key? - return false; - K.reset(new dht::Key_t()); - return K->BDecode(val); - } - if(llarp_buffer_eq(key, "N")) - { - return BEncodeReadList(N, val); - } - if(llarp_buffer_eq(key, "R")) - { - return BEncodeReadList(R, val); - } - if(llarp_buffer_eq(key, "T")) - { - return bencode_read_integer(val, &txid); - } - bool read = false; - if(!BEncodeMaybeReadVersion("V", version, LLARP_PROTO_VERSION, read, key, - val)) - return false; - - return read; - } - - bool - GotRouterMessage::HandleMessage( - llarp_dht_context *ctx, - __attribute__((unused)) - std::vector< std::unique_ptr< IMessage > > &replies) const - { - auto &dht = ctx->impl; - if(relayed) - { - auto pathset = ctx->impl.router->paths.GetLocalPathSet(pathID); - return pathset && pathset->HandleGotRouterMessage(this); - } - // not relayed - TXOwner owner(From, txid); - - if(dht.pendingExploreLookups.HasPendingLookupFrom(owner)) - { - if(N.size() == 0) - dht.pendingExploreLookups.NotFound(owner, K); - else - { - dht.pendingExploreLookups.Found(owner, From.as_array(), N); - } - return true; - } - // not explore lookup - - if(!dht.pendingRouterLookups.HasPendingLookupFrom(owner)) - { - llarp::LogWarn("Unwarrented GRM from ", From, " txid=", txid); - return false; - } - // no pending lookup - - llarp::LogInfo("DHT no pending lookup"); - if(R.size() == 1) - dht.pendingRouterLookups.Found(owner, R[0].pubkey, {R[0]}); - else - dht.pendingRouterLookups.NotFound(owner, K); - return true; - } - } // namespace dht -} // namespace llarp diff --git a/llarp/dht/message.cpp b/llarp/dht/message.cpp index 658478c22..dd822cd76 100644 --- a/llarp/dht/message.cpp +++ b/llarp/dht/message.cpp @@ -1 +1,140 @@ -#include +#include + +#include +#include +#include +#include +#include + +namespace llarp +{ + namespace dht + { + struct MessageDecoder + { + const Key_t &From; + std::unique_ptr< IMessage > msg; + bool firstKey = true; + bool relayed = false; + + MessageDecoder(const Key_t &from) : From(from) + { + } + + static bool + on_key(dict_reader *r, llarp_buffer_t *key) + { + llarp_buffer_t strbuf; + MessageDecoder *dec = static_cast< MessageDecoder * >(r->user); + // check for empty dict + if(!key) + return !dec->firstKey; + + // first key + if(dec->firstKey) + { + if(!llarp_buffer_eq(*key, "A")) + return false; + if(!bencode_read_string(r->buffer, &strbuf)) + return false; + // bad msg size? + if(strbuf.sz != 1) + return false; + llarp::LogInfo("Handle DHT message ", *strbuf.base, + " relayed=", dec->relayed); + switch(*strbuf.base) + { + case 'F': + dec->msg.reset(new FindIntroMessage(dec->From, dec->relayed)); + break; + case 'R': + if(dec->relayed) + dec->msg.reset(new RelayedFindRouterMessage(dec->From)); + else + dec->msg.reset(new FindRouterMessage(dec->From)); + break; + case 'S': + dec->msg.reset(new GotRouterMessage(dec->From, dec->relayed)); + break; + case 'I': + dec->msg.reset(new PublishIntroMessage()); + break; + case 'G': + if(dec->relayed) + { + dec->msg.reset(new RelayedGotIntroMessage()); + break; + } + else + { + dec->msg.reset(new GotIntroMessage(dec->From)); + break; + } + default: + llarp::LogWarn("unknown dht message type: ", (char)*strbuf.base); + // bad msg type + return false; + } + dec->firstKey = false; + return dec->msg != nullptr; + } + else + return dec->msg->DecodeKey(*key, r->buffer); + } + }; + + std::unique_ptr< IMessage > + DecodeMesssage(const Key_t &from, llarp_buffer_t *buf, bool relayed) + { + MessageDecoder dec(from); + dec.relayed = relayed; + dict_reader r; + r.user = &dec; + r.on_key = &MessageDecoder::on_key; + if(!bencode_read_dict(buf, &r)) + return nullptr; + + return std::unique_ptr< IMessage >(std::move(dec.msg)); + } + + struct ListDecoder + { + ListDecoder(const Key_t &from, + std::vector< std::unique_ptr< IMessage > > &list) + : From(from), l(list){}; + + bool relayed = false; + const Key_t &From; + std::vector< std::unique_ptr< IMessage > > &l; + + static bool + on_item(list_reader *r, bool has) + { + ListDecoder *dec = static_cast< ListDecoder * >(r->user); + if(!has) + return true; + auto msg = DecodeMesssage(dec->From, r->buffer, dec->relayed); + if(msg) + { + dec->l.emplace_back(std::move(msg)); + return true; + } + else + return false; + } + }; + + bool + DecodeMesssageList(Key_t from, llarp_buffer_t *buf, + std::vector< std::unique_ptr< IMessage > > &list, + bool relayed) + { + ListDecoder dec(from, list); + dec.relayed = relayed; + list_reader r; + r.user = &dec; + r.on_item = &ListDecoder::on_item; + return bencode_read_list(buf, &r); + } + } // namespace dht +} // namespace llarp diff --git a/llarp/dht/messages/all.cpp b/llarp/dht/messages/all.cpp deleted file mode 100644 index ea40fc3f8..000000000 --- a/llarp/dht/messages/all.cpp +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/llarp/dht/messages/all.hpp b/llarp/dht/messages/all.hpp deleted file mode 100644 index f8ac315ff..000000000 --- a/llarp/dht/messages/all.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef LLARP_DHT_MESSAGES_ALL_HPP -#define LLARP_DHT_MESSAGES_ALL_HPP -#include -#include -#include -#include -#include -#endif diff --git a/llarp/dht/messages/findintro.cpp b/llarp/dht/messages/findintro.cpp index 5c1805e09..fc0e03a93 100644 --- a/llarp/dht/messages/findintro.cpp +++ b/llarp/dht/messages/findintro.cpp @@ -1 +1,204 @@ +#include #include +#include +#include + +namespace llarp +{ + namespace dht + { + FindIntroMessage::~FindIntroMessage() + { + } + + bool + FindIntroMessage::DecodeKey(llarp_buffer_t k, llarp_buffer_t* val) + { + bool read = false; + + if(!BEncodeMaybeReadDictEntry("N", N, read, k, val)) + return false; + + if(!BEncodeMaybeReadDictInt("R", R, read, k, val)) + return false; + + if(!BEncodeMaybeReadDictEntry("S", S, read, k, val)) + return false; + + if(!BEncodeMaybeReadDictInt("T", T, read, k, val)) + return false; + + if(!BEncodeMaybeReadVersion("V", version, LLARP_PROTO_VERSION, read, k, + val)) + return false; + + return read; + } + + bool + FindIntroMessage::BEncode(llarp_buffer_t* buf) const + { + if(!bencode_start_dict(buf)) + return false; + + // message id + if(!BEncodeWriteDictMsgType(buf, "A", "F")) + return false; + if(N.Empty()) + { + // recursion + if(!BEncodeWriteDictInt("R", R, buf)) + return false; + // service address + if(!BEncodeWriteDictEntry("S", S, buf)) + return false; + } + else + { + if(!BEncodeWriteDictEntry("N", N, buf)) + return false; + // recursion + if(!BEncodeWriteDictInt("R", R, buf)) + return false; + } + // txid + if(!BEncodeWriteDictInt("T", T, buf)) + return false; + // protocol version + if(!BEncodeWriteDictInt("V", LLARP_PROTO_VERSION, buf)) + return false; + + return bencode_end(buf); + } + + bool + FindIntroMessage::HandleMessage( + llarp_dht_context* ctx, + std::vector< std::unique_ptr< IMessage > >& replies) const + { + if(R > 5) + { + llarp::LogError("R value too big, ", R, "> 5"); + return false; + } + auto& dht = ctx->impl; + if(dht.pendingIntrosetLookups.HasPendingLookupFrom(TXOwner{From, T})) + { + llarp::LogWarn("duplicate FIM from ", From, " txid=", T); + return false; + } + Key_t peer; + std::set< Key_t > exclude = {dht.OurKey(), From}; + if(N.Empty()) + { + llarp::LogInfo("lookup ", S.ToString()); + const auto introset = dht.GetIntroSetByServiceAddress(S); + if(introset) + { + service::IntroSet i = *introset; + replies.emplace_back(new GotIntroMessage({i}, T)); + return true; + } + else + { + if(R == 0) + { + // we don't have it + Key_t target = S.ToKey(); + Key_t closer; + // find closer peer + if(!dht.nodes->FindClosest(target, closer)) + return false; + if(relayed) + dht.LookupIntroSetForPath(S, T, pathID, closer); + else + replies.emplace_back(new GotIntroMessage(From, closer, T)); + return true; + } + else + { + Key_t us = dht.OurKey(); + Key_t target = S.ToKey(); + // we are recursive + if(dht.nodes->FindCloseExcluding(target, peer, exclude)) + { + if(relayed) + dht.LookupIntroSetForPath(S, T, pathID, peer); + else + { + if((us ^ target) < (peer ^ target)) + { + // we are not closer than our peer to the target so don't + // recurse farther + replies.emplace_back(new GotIntroMessage({}, T)); + return true; + } + else if(R > 0) + dht.LookupIntroSetRecursive(S, From, T, peer, R - 1); + else + dht.LookupIntroSetIterative(S, From, T, peer); + } + return true; + } + else + { + // no more closer peers + replies.emplace_back(new GotIntroMessage({}, T)); + return true; + } + } + } + } + else + { + if(relayed) + { + // tag lookup + if(dht.nodes->GetRandomNodeExcluding(peer, exclude)) + { + dht.LookupTagForPath(N, T, pathID, peer); + } + else + { + // no more closer peers + replies.emplace_back(new GotIntroMessage({}, T)); + return true; + } + } + else + { + if(R == 0) + { + // base case + auto introsets = dht.FindRandomIntroSetsWithTagExcluding(N, 2, {}); + std::vector< service::IntroSet > reply; + for(const auto& introset : introsets) + { + reply.push_back(introset); + } + replies.emplace_back(new GotIntroMessage(reply, T)); + return true; + } + else if(R < 5) + { + // tag lookup + if(dht.nodes->GetRandomNodeExcluding(peer, exclude)) + { + dht.LookupTagRecursive(N, From, T, peer, R - 1); + } + else + { + replies.emplace_back(new GotIntroMessage({}, T)); + } + } + else + { + // too big R value + replies.emplace_back(new GotIntroMessage({}, T)); + } + } + } + return true; + } + } // namespace dht +} // namespace llarp diff --git a/llarp/dht/messages/findrouter.cpp b/llarp/dht/messages/findrouter.cpp index f9019d2bd..88643d664 100644 --- a/llarp/dht/messages/findrouter.cpp +++ b/llarp/dht/messages/findrouter.cpp @@ -1 +1,172 @@ #include + +#include +#include +#include +#include + +namespace llarp +{ + namespace dht + { + bool + RelayedFindRouterMessage::HandleMessage( + llarp_dht_context *ctx, + std::vector< std::unique_ptr< IMessage > > &replies) const + { + auto &dht = ctx->impl; + /// lookup for us, send an immeidate reply + Key_t us = dht.OurKey(); + Key_t k{K}; + if(K == us) + { + auto path = dht.router->paths.GetByUpstream(K, pathID); + if(path) + { + replies.emplace_back( + new GotRouterMessage(k, txid, {dht.router->rc()}, false)); + return true; + } + return false; + } + + Key_t peer; + // check if we know this in our nodedb first + RouterContact found; + if(dht.router->nodedb->Get(K, found)) + { + replies.emplace_back(new GotRouterMessage(k, txid, {found}, false)); + return true; + } + // lookup if we don't have it in our nodedb + if(dht.nodes->FindClosest(k, peer)) + dht.LookupRouterForPath(K, txid, pathID, peer); + return true; + } + + FindRouterMessage::~FindRouterMessage() + { + } + + bool + FindRouterMessage::BEncode(llarp_buffer_t *buf) const + { + if(!bencode_start_dict(buf)) + return false; + + // message type + if(!bencode_write_bytestring(buf, "A", 1)) + return false; + if(!bencode_write_bytestring(buf, "R", 1)) + return false; + + // exploritory or not? + if(!bencode_write_bytestring(buf, "E", 1)) + return false; + if(!bencode_write_uint64(buf, exploritory ? 1 : 0)) + return false; + + // iterative or not? + if(!bencode_write_bytestring(buf, "I", 1)) + return false; + if(!bencode_write_uint64(buf, iterative ? 1 : 0)) + return false; + + // key + if(!bencode_write_bytestring(buf, "K", 1)) + return false; + if(!bencode_write_bytestring(buf, K.data(), K.size())) + return false; + + // txid + if(!bencode_write_bytestring(buf, "T", 1)) + return false; + if(!bencode_write_uint64(buf, txid)) + return false; + + // version + if(!bencode_write_bytestring(buf, "V", 1)) + return false; + if(!bencode_write_uint64(buf, version)) + return false; + + return bencode_end(buf); + } + + bool + FindRouterMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t *val) + { + llarp_buffer_t strbuf; + + if(llarp_buffer_eq(key, "E")) + { + uint64_t result; + if(!bencode_read_integer(val, &result)) + return false; + + exploritory = result != 0; + return true; + } + + if(llarp_buffer_eq(key, "I")) + { + uint64_t result; + if(!bencode_read_integer(val, &result)) + return false; + + iterative = result != 0; + return true; + } + if(llarp_buffer_eq(key, "K")) + { + if(!bencode_read_string(val, &strbuf)) + return false; + if(strbuf.sz != K.size()) + return false; + + std::copy(strbuf.base, strbuf.base + K.SIZE, K.begin()); + return true; + } + if(llarp_buffer_eq(key, "T")) + { + return bencode_read_integer(val, &txid); + } + if(llarp_buffer_eq(key, "V")) + { + return bencode_read_integer(val, &version); + } + return false; + } + + bool + FindRouterMessage::HandleMessage( + llarp_dht_context *ctx, + std::vector< std::unique_ptr< IMessage > > &replies) const + { + auto &dht = ctx->impl; + if(!dht.allowTransit) + { + llarp::LogWarn("Got DHT lookup from ", From, + " when we are not allowing dht transit"); + return false; + } + if(dht.pendingRouterLookups.HasPendingLookupFrom({From, txid})) + { + llarp::LogWarn("Duplicate FRM from ", From, " txid=", txid); + return false; + } + RouterContact found; + Key_t k{K}; + if(exploritory) + return dht.HandleExploritoryRouterLookup(From, txid, K, replies); + else if(dht.router->nodedb->Get(K, found)) + { + replies.emplace_back(new GotRouterMessage(k, txid, {found}, false)); + return true; + } + else + dht.LookupRouterRelayed(From, txid, k, !iterative, replies); + return true; + } + } // namespace dht +} // namespace llarp diff --git a/llarp/dht/messages/gotintro.cpp b/llarp/dht/messages/gotintro.cpp index 3024f84a8..d32653a7f 100644 --- a/llarp/dht/messages/gotintro.cpp +++ b/llarp/dht/messages/gotintro.cpp @@ -1 +1,125 @@ #include + +#include +#include +#include + +namespace llarp +{ + namespace dht + { + GotIntroMessage::GotIntroMessage( + const std::vector< llarp::service::IntroSet > &results, uint64_t tx) + : IMessage({}), I(results), T(tx) + { + } + + GotIntroMessage::~GotIntroMessage() + { + } + + bool + GotIntroMessage::HandleMessage( + llarp_dht_context *ctx, + __attribute__((unused)) + std::vector< std::unique_ptr< IMessage > > &replies) const + { + auto &dht = ctx->impl; + auto crypto = &dht.router->crypto; + + for(const auto &introset : I) + { + if(!introset.Verify(crypto, dht.Now())) + { + llarp::LogWarn( + "Invalid introset while handling direct GotIntro " + "from ", + From); + return false; + } + } + TXOwner owner(From, T); + auto tagLookup = dht.pendingTagLookups.GetPendingLookupFrom(owner); + if(tagLookup) + { + dht.pendingTagLookups.Found(owner, tagLookup->target, I); + return true; + } + auto serviceLookup = + dht.pendingIntrosetLookups.GetPendingLookupFrom(owner); + if(serviceLookup) + { + if(I.size()) + { + dht.pendingIntrosetLookups.Found(owner, serviceLookup->target, I); + } + else + { + dht.pendingIntrosetLookups.NotFound(owner, K); + } + return true; + } + llarp::LogError("no pending TX for GIM from ", From, " txid=", T); + return false; + } + + bool + RelayedGotIntroMessage::HandleMessage( + llarp_dht_context *ctx, + __attribute__((unused)) + std::vector< std::unique_ptr< IMessage > > &replies) const + { + // TODO: implement me better? + auto pathset = ctx->impl.router->paths.GetLocalPathSet(pathID); + if(pathset) + { + return pathset->HandleGotIntroMessage(this); + } + llarp::LogWarn("No path for got intro message pathid=", pathID); + return false; + } + + bool + GotIntroMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t *buf) + { + if(llarp_buffer_eq(key, "I")) + { + return BEncodeReadList(I, buf); + } + if(llarp_buffer_eq(key, "K")) + { + if(K) // duplicate key? + return false; + K.reset(new dht::Key_t()); + return K->BDecode(buf); + } + bool read = false; + if(!BEncodeMaybeReadDictInt("T", T, read, key, buf)) + return false; + if(!BEncodeMaybeReadDictInt("V", version, read, key, buf)) + return false; + return read; + } + + bool + GotIntroMessage::BEncode(llarp_buffer_t *buf) const + { + if(!bencode_start_dict(buf)) + return false; + if(!BEncodeWriteDictMsgType(buf, "A", "G")) + return false; + if(!BEncodeWriteDictList("I", I, buf)) + return false; + if(K) + { + if(!BEncodeWriteDictEntry("K", *K.get(), buf)) + return false; + } + if(!BEncodeWriteDictInt("T", T, buf)) + return false; + if(!BEncodeWriteDictInt("V", version, buf)) + return false; + return bencode_end(buf); + } + } // namespace dht +} // namespace llarp diff --git a/llarp/dht/messages/gotrouter.cpp b/llarp/dht/messages/gotrouter.cpp index 6632ef521..60fa5d1f7 100644 --- a/llarp/dht/messages/gotrouter.cpp +++ b/llarp/dht/messages/gotrouter.cpp @@ -1 +1,123 @@ +#include #include + +#include + +namespace llarp +{ + namespace dht + { + GotRouterMessage::~GotRouterMessage() + { + } + + bool + GotRouterMessage::BEncode(llarp_buffer_t *buf) const + { + if(!bencode_start_dict(buf)) + return false; + + // message type + if(!BEncodeWriteDictMsgType(buf, "A", "S")) + return false; + + if(K) + { + if(!BEncodeWriteDictEntry("K", *K.get(), buf)) + return false; + } + + // near + if(N.size()) + { + if(!BEncodeWriteDictList("N", N, buf)) + return false; + } + + if(!BEncodeWriteDictList("R", R, buf)) + return false; + + // txid + if(!BEncodeWriteDictInt("T", txid, buf)) + return false; + + // version + if(!BEncodeWriteDictInt("V", version, buf)) + return false; + + return bencode_end(buf); + } + + bool + GotRouterMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t *val) + { + if(llarp_buffer_eq(key, "K")) + { + if(K) // duplicate key? + return false; + K.reset(new dht::Key_t()); + return K->BDecode(val); + } + if(llarp_buffer_eq(key, "N")) + { + return BEncodeReadList(N, val); + } + if(llarp_buffer_eq(key, "R")) + { + return BEncodeReadList(R, val); + } + if(llarp_buffer_eq(key, "T")) + { + return bencode_read_integer(val, &txid); + } + bool read = false; + if(!BEncodeMaybeReadVersion("V", version, LLARP_PROTO_VERSION, read, key, + val)) + return false; + + return read; + } + + bool + GotRouterMessage::HandleMessage( + llarp_dht_context *ctx, + __attribute__((unused)) + std::vector< std::unique_ptr< IMessage > > &replies) const + { + auto &dht = ctx->impl; + if(relayed) + { + auto pathset = ctx->impl.router->paths.GetLocalPathSet(pathID); + return pathset && pathset->HandleGotRouterMessage(this); + } + // not relayed + TXOwner owner(From, txid); + + if(dht.pendingExploreLookups.HasPendingLookupFrom(owner)) + { + if(N.size() == 0) + dht.pendingExploreLookups.NotFound(owner, K); + else + { + dht.pendingExploreLookups.Found(owner, From.as_array(), N); + } + return true; + } + // not explore lookup + + if(!dht.pendingRouterLookups.HasPendingLookupFrom(owner)) + { + llarp::LogWarn("Unwarrented GRM from ", From, " txid=", txid); + return false; + } + // no pending lookup + + llarp::LogInfo("DHT no pending lookup"); + if(R.size() == 1) + dht.pendingRouterLookups.Found(owner, R[0].pubkey, {R[0]}); + else + dht.pendingRouterLookups.NotFound(owner, K); + return true; + } + } // namespace dht +} // namespace llarp diff --git a/llarp/dht/messages/pubintro.cpp b/llarp/dht/messages/pubintro.cpp index cf561872d..d78f184f5 100644 --- a/llarp/dht/messages/pubintro.cpp +++ b/llarp/dht/messages/pubintro.cpp @@ -1 +1,121 @@ #include + +#include +#include +#include +#include +#include + +namespace llarp +{ + namespace dht + { + PublishIntroMessage::~PublishIntroMessage() + { + } + + bool + PublishIntroMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t *val) + { + bool read = false; + if(llarp_buffer_eq(key, "E")) + { + return BEncodeReadList(E, val); + } + if(!BEncodeMaybeReadDictEntry("I", I, read, key, val)) + return false; + if(!BEncodeMaybeReadDictInt("R", R, read, key, val)) + return false; + if(llarp_buffer_eq(key, "S")) + { + read = true; + hasS = true; + if(!bencode_read_integer(val, &S)) + return false; + } + if(!BEncodeMaybeReadDictInt("T", txID, read, key, val)) + return false; + if(!BEncodeMaybeReadDictInt("V", version, read, key, val)) + return false; + return read; + } + + bool + PublishIntroMessage::HandleMessage( + llarp_dht_context *ctx, + std::vector< std::unique_ptr< IMessage > > &replies) const + { + auto now = ctx->impl.Now(); + if(S > 5) + { + llarp::LogWarn("invalid S value ", S, " > 5"); + return false; + } + auto &dht = ctx->impl; + if(!I.Verify(&dht.router->crypto, now)) + { + llarp::LogWarn("invalid introset: ", I); + // don't propogate or store + replies.emplace_back(new GotIntroMessage({}, txID)); + return true; + } + if(I.W && !I.W->IsValid(dht.router->crypto.shorthash, now)) + { + llarp::LogWarn("proof of work not good enough for IntroSet"); + // don't propogate or store + replies.emplace_back(new GotIntroMessage({}, txID)); + return true; + } + llarp::dht::Key_t addr; + if(!I.A.CalculateAddress(addr.as_array())) + { + llarp::LogWarn( + "failed to calculate hidden service address for PubIntro message"); + return false; + } + + now += llarp::service::MAX_INTROSET_TIME_DELTA; + if(I.IsExpired(now)) + { + // don't propogate or store + replies.emplace_back(new GotIntroMessage({}, txID)); + return true; + } + dht.services->PutNode(I); + replies.emplace_back(new GotIntroMessage({I}, txID)); + Key_t peer; + std::set< Key_t > exclude; + for(const auto &e : E) + exclude.insert(e); + exclude.insert(From); + exclude.insert(dht.OurKey()); + if(S && dht.nodes->FindCloseExcluding(addr, peer, exclude)) + { + dht.PropagateIntroSetTo(From, txID, I, peer, S - 1, exclude); + } + return true; + } + + bool + PublishIntroMessage::BEncode(llarp_buffer_t *buf) const + { + if(!bencode_start_dict(buf)) + return false; + if(!BEncodeWriteDictMsgType(buf, "A", "I")) + return false; + if(!BEncodeWriteDictList("E", E, buf)) + return false; + if(!BEncodeWriteDictEntry("I", I, buf)) + return false; + if(!BEncodeWriteDictInt("R", R, buf)) + return false; + if(!BEncodeWriteDictInt("S", S, buf)) + return false; + if(!BEncodeWriteDictInt("T", txID, buf)) + return false; + if(!BEncodeWriteDictInt("V", LLARP_PROTO_VERSION, buf)) + return false; + return bencode_end(buf); + } + } // namespace dht +} // namespace llarp diff --git a/llarp/dht/node.hpp b/llarp/dht/node.hpp index a0fb201bc..5ad58eaea 100644 --- a/llarp/dht/node.hpp +++ b/llarp/dht/node.hpp @@ -11,8 +11,7 @@ namespace llarp { struct RCNode { - llarp::RouterContact rc; - + RouterContact rc; Key_t ID; RCNode() @@ -20,7 +19,7 @@ namespace llarp ID.Zero(); } - RCNode(const llarp::RouterContact& other) : rc(other), ID(other.pubkey) + RCNode(const RouterContact& other) : rc(other), ID(other.pubkey) { } @@ -33,7 +32,7 @@ namespace llarp struct ISNode { - llarp::service::IntroSet introset; + service::IntroSet introset; Key_t ID; @@ -42,7 +41,7 @@ namespace llarp ID.Zero(); } - ISNode(const llarp::service::IntroSet& other) + ISNode(const service::IntroSet& other) { introset = other; introset.A.CalculateAddress(ID.as_array()); diff --git a/llarp/dht/publish_intro.cpp b/llarp/dht/publish_intro.cpp deleted file mode 100644 index e49a12e60..000000000 --- a/llarp/dht/publish_intro.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include -#include -#include -#include -#include - -namespace llarp -{ - namespace dht - { - PublishIntroMessage::~PublishIntroMessage() - { - } - - bool - PublishIntroMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t *val) - { - bool read = false; - if(llarp_buffer_eq(key, "E")) - { - return BEncodeReadList(E, val); - } - if(!BEncodeMaybeReadDictEntry("I", I, read, key, val)) - return false; - if(!BEncodeMaybeReadDictInt("R", R, read, key, val)) - return false; - if(llarp_buffer_eq(key, "S")) - { - read = true; - hasS = true; - if(!bencode_read_integer(val, &S)) - return false; - } - if(!BEncodeMaybeReadDictInt("T", txID, read, key, val)) - return false; - if(!BEncodeMaybeReadDictInt("V", version, read, key, val)) - return false; - return read; - } - - bool - PublishIntroMessage::HandleMessage( - llarp_dht_context *ctx, - std::vector< std::unique_ptr< IMessage > > &replies) const - { - auto now = ctx->impl.Now(); - if(S > 5) - { - llarp::LogWarn("invalid S value ", S, " > 5"); - return false; - } - auto &dht = ctx->impl; - if(!I.Verify(&dht.router->crypto, now)) - { - llarp::LogWarn("invalid introset: ", I); - // don't propogate or store - replies.emplace_back(new GotIntroMessage({}, txID)); - return true; - } - if(I.W && !I.W->IsValid(dht.router->crypto.shorthash, now)) - { - llarp::LogWarn("proof of work not good enough for IntroSet"); - // don't propogate or store - replies.emplace_back(new GotIntroMessage({}, txID)); - return true; - } - llarp::dht::Key_t addr; - if(!I.A.CalculateAddress(addr.as_array())) - { - llarp::LogWarn( - "failed to calculate hidden service address for PubIntro message"); - return false; - } - - now += llarp::service::MAX_INTROSET_TIME_DELTA; - if(I.IsExpired(now)) - { - // don't propogate or store - replies.emplace_back(new GotIntroMessage({}, txID)); - return true; - } - dht.services->PutNode(I); - replies.emplace_back(new GotIntroMessage({I}, txID)); - Key_t peer; - std::set< Key_t > exclude; - for(const auto &e : E) - exclude.insert(e); - exclude.insert(From); - exclude.insert(dht.OurKey()); - if(S && dht.nodes->FindCloseExcluding(addr, peer, exclude)) - { - dht.PropagateIntroSetTo(From, txID, I, peer, S - 1, exclude); - } - return true; - } - - bool - PublishIntroMessage::BEncode(llarp_buffer_t *buf) const - { - if(!bencode_start_dict(buf)) - return false; - if(!BEncodeWriteDictMsgType(buf, "A", "I")) - return false; - if(!BEncodeWriteDictList("E", E, buf)) - return false; - if(!BEncodeWriteDictEntry("I", I, buf)) - return false; - if(!BEncodeWriteDictInt("R", R, buf)) - return false; - if(!BEncodeWriteDictInt("S", S, buf)) - return false; - if(!BEncodeWriteDictInt("T", txID, buf)) - return false; - if(!BEncodeWriteDictInt("V", LLARP_PROTO_VERSION, buf)) - return false; - return bencode_end(buf); - } - } // namespace dht -} // namespace llarp diff --git a/llarp/dht/txowner.cpp b/llarp/dht/txowner.cpp new file mode 100644 index 000000000..3707b15f4 --- /dev/null +++ b/llarp/dht/txowner.cpp @@ -0,0 +1 @@ +#include diff --git a/llarp/dht/txowner.hpp b/llarp/dht/txowner.hpp new file mode 100644 index 000000000..1626380f2 --- /dev/null +++ b/llarp/dht/txowner.hpp @@ -0,0 +1,54 @@ +#ifndef LLARP_DHT_TXOWNER_HPP +#define LLARP_DHT_TXOWNER_HPP + +#include + +#include + +namespace llarp +{ + namespace dht + { + struct TXOwner + { + Key_t node; + uint64_t txid = 0; + + TXOwner() = default; + TXOwner(const TXOwner&) = default; + TXOwner(TXOwner&&) = default; + + TXOwner& + operator=(const TXOwner&) = default; + + TXOwner(const Key_t& k, uint64_t id) : node(k), txid(id) + { + } + + bool + operator==(const TXOwner& other) const + { + return std::tie(txid, node) == std::tie(other.txid, other.node); + } + + bool + operator<(const TXOwner& other) const + { + return std::tie(txid, node) < std::tie(other.txid, other.node); + } + + struct Hash + { + std::size_t + operator()(const TXOwner& o) const noexcept + { + std::size_t sz2; + memcpy(&sz2, &o.node[0], sizeof(std::size_t)); + return o.txid ^ (sz2 << 1); + } + }; + }; + } // namespace dht +} // namespace llarp + +#endif diff --git a/llarp/ev/ev.cpp b/llarp/ev/ev.cpp index 3ce147434..dc20eb910 100644 --- a/llarp/ev/ev.cpp +++ b/llarp/ev/ev.cpp @@ -143,13 +143,54 @@ llarp_ev_udp_sendto(struct llarp_udp_io *udp, const sockaddr *to, } #ifndef _WIN32 +#include + bool llarp_ev_add_tun(struct llarp_ev_loop *loop, struct llarp_tun_io *tun) { + if(strcmp(tun->ifaddr, "") == 0 || strcmp(tun->ifaddr, "auto")) + { + std::string ifaddr = llarp::findFreePrivateRange(); + auto pos = ifaddr.find("/"); + if(pos == std::string::npos) + { + llarp::LogWarn("Auto ifaddr didn't return a netmask: ", ifaddr); + return false; + } + int num; + std::string part = ifaddr.substr(pos + 1); +#if defined(ANDROID) || defined(RPI) + num = atoi(part.c_str()); +#else + num = std::stoi(part); +#endif + if(num <= 0) + { + llarp::LogError("bad ifaddr netmask value: ", ifaddr); + return false; + } + tun->netmask = num; + const std::string addr = ifaddr.substr(0, pos); + std::copy_n(addr.begin(), std::min(sizeof(tun->ifaddr), addr.size()), tun->ifaddr); + llarp::LogInfo("IfAddr autodetect: ", tun->ifaddr, "/", tun->netmask); + } + if(strcmp(tun->ifname, "") == 0 || strcmp(tun->ifname, "auto")) + { + std::string ifname = llarp::findFreeLokiTunIfName(); + std::copy_n(ifname.begin(), std::min(sizeof(tun->ifname), ifname.size()), tun->ifname); + llarp::LogInfo("IfName autodetect: ", tun->ifname); + } + llarp::LogDebug("Tun Interface will use the following settings:"); + llarp::LogDebug("IfAddr: ", tun->ifaddr); + llarp::LogDebug("IfName: ", tun->ifname); + llarp::LogDebug("IfNMsk: ", tun->netmask); auto dev = loop->create_tun(tun); tun->impl = dev; if(dev) + { return loop->add_ev(dev, false); + } + llarp::LogWarn("Loop could not create tun"); return false; } #else diff --git a/llarp/ev/ev_kqueue.hpp b/llarp/ev/ev_kqueue.hpp index ba3adfcf6..e191f32e8 100644 --- a/llarp/ev/ev_kqueue.hpp +++ b/llarp/ev/ev_kqueue.hpp @@ -286,17 +286,33 @@ namespace llarp setup() { llarp::LogDebug("set up tunif"); + if(tuntap_start(tunif, TUNTAP_MODE_TUNNEL, TUNTAP_ID_ANY) == -1) + { + llarp::LogWarn("Can't set tuntap to tunnel mode"); return false; + } if(tuntap_up(tunif) == -1) + { + llarp::LogWarn("Can't bring up tuntap"); return false; + } + if(tuntap_set_ifname(tunif, t->ifname) == -1) + { + llarp::LogInfo("Can't set tun interface name to ", tunif->if_name); return false; - llarp::LogInfo("set ", tunif->if_name, " to use address ", t->ifaddr); + } + // currently trying to set + llarp::LogInfo("setting ", tunif->if_name, " to use address ", t->ifaddr); if(tuntap_set_ip(tunif, t->ifaddr, t->ifaddr, t->netmask) == -1) + { + llarp::LogWarn("Can't set tuntap ip on ", tunif->if_name, " to ", + t->ifaddr, " netmask ", t->netmask); return false; + } fd = tunif->tun_fd; return fd != -1; } @@ -370,6 +386,7 @@ struct llarp_kqueue_loop : public llarp_ev_loop llarp::tun* t = new llarp::tun(tun, this); if(t->setup()) return t; + llarp::LogWarn("Could not set up tun"); delete t; return nullptr; } @@ -432,13 +449,11 @@ struct llarp_kqueue_loop : public llarp_ev_loop llarp::ev_io* ev = static_cast< llarp::ev_io* >(events[idx].udata); if(ev) { + if(events[idx].filter & EVFILT_WRITE) + ev->flush_write_buffers(events[idx].data); if(events[idx].filter & EVFILT_READ) ev->read(readbuf, std::min(sizeof(readbuf), size_t(events[idx].data))); - if(events[idx].filter & EVFILT_WRITE) - { - ev->flush_write_buffers(events[idx].data); - } } ++idx; } diff --git a/llarp/exit/context.cpp b/llarp/exit/context.cpp index b692890b8..58c331fc0 100644 --- a/llarp/exit/context.cpp +++ b/llarp/exit/context.cpp @@ -106,13 +106,20 @@ namespace llarp while(itr != conf.end()) { if(!endpoint->SetOption(itr->first, itr->second)) + { + llarp::LogWarn("Couldn't set option ", itr->first, " to ", + itr->second); return false; + } ++itr; } } // add endpoint if(!endpoint->Start()) + { + llarp::LogWarn("Couldn't start exit endpoint"); return false; + } m_Exits.emplace(name, std::move(endpoint)); return true; } diff --git a/llarp/exit/session.cpp b/llarp/exit/session.cpp index 1dc30ead0..5430f9e2d 100644 --- a/llarp/exit/session.cpp +++ b/llarp/exit/session.cpp @@ -1,4 +1,5 @@ #include + #include #include @@ -85,7 +86,7 @@ namespace llarp if(p->SendExitRequest(&obtain, router)) llarp::LogInfo("asking ", m_ExitRouter, " for exit"); else - llarp::LogError("faild to send exit request"); + llarp::LogError("failed to send exit request"); } bool diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index fe500a235..e3036d90f 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -224,9 +224,14 @@ namespace llarp if(m_ShouldInitTun) { if(!llarp_ev_add_tun(GetRouter()->netloop, &m_Tun)) + { + llarp::LogWarn("Could not create tunnel for exit endpoint"); return false; + } if(m_UpstreamResolvers.size() == 0) m_UpstreamResolvers.emplace_back("8.8.8.8", 53); + llarp::LogInfo("Trying to start resolver ", + m_LocalResolverAddr.ToString()); return m_Resolver.Start(m_LocalResolverAddr, m_UpstreamResolvers); } return true; diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index d2fd7ee7e..cd06f2889 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -55,6 +55,8 @@ namespace llarp bool TunEndpoint::SetOption(const std::string &k, const std::string &v) { + // Name won't be set because we need to read the config before we can read + // the keyfile if(k == "exit-node") { llarp::RouterID exitRouter; @@ -82,7 +84,7 @@ namespace llarp dnsport = std::atoi(v.substr(pos + 1).c_str()); } m_LocalResolverAddr = llarp::Addr(resolverAddr, dnsport); - llarp::LogInfo(Name(), " local dns set to ", m_LocalResolverAddr); + llarp::LogInfo(Name(), " binding DNS server to ", m_LocalResolverAddr); } if(k == "upstream-dns") { @@ -95,7 +97,7 @@ namespace llarp dnsport = std::atoi(v.substr(pos + 1).c_str()); } m_UpstreamResolvers.emplace_back(resolverAddr, dnsport); - llarp::LogInfo(Name(), " adding upstream dns set to ", resolverAddr, + llarp::LogInfo(Name(), " adding upstream DNS server ", resolverAddr, ":", dnsport); } if(k == "mapaddr") @@ -379,7 +381,10 @@ namespace llarp TunEndpoint::Start() { if(!Endpoint::Start()) + { + llarp::LogWarn("Couldn't start endpoint"); return false; + } return SetupNetworking(); } @@ -395,7 +400,9 @@ namespace llarp { if(!llarp_ev_add_tun(EndpointNetLoop(), &tunif)) { - llarp::LogError(Name(), " failed to set up tun interface"); + llarp::LogError(Name(), + " failed to set up tun interface: ", tunif.ifaddr, + " on ", tunif.ifname); return false; } diff --git a/llarp/link/utp_internal.hpp b/llarp/link/utp_internal.hpp index 983789080..c47a6949d 100644 --- a/llarp/link/utp_internal.hpp +++ b/llarp/link/utp_internal.hpp @@ -36,7 +36,8 @@ namespace llarp constexpr size_t FragmentBufferSize = FragmentOverheadSize + FragmentBodySize; - static_assert(FragmentBufferSize == 1120, "Buffer size invalid"); + static_assert(FragmentBufferSize == 1120, + "Fragement Buffer Size is not 1120"); /// buffer for a single utp fragment using FragmentBuffer = llarp::AlignedBuffer< FragmentBufferSize >; diff --git a/llarp/messages/dht_immediate.cpp b/llarp/messages/dht_immediate.cpp index 0d9536cd9..e9da3b54e 100644 --- a/llarp/messages/dht_immediate.cpp +++ b/llarp/messages/dht_immediate.cpp @@ -1 +1,86 @@ #include + +#include + +namespace llarp +{ + DHTImmediateMessage::~DHTImmediateMessage() + { + } + + void + DHTImmediateMessage::Clear() + { + msgs.clear(); + } + + bool + DHTImmediateMessage::DecodeKey(llarp_buffer_t key, llarp_buffer_t *buf) + { + if(llarp_buffer_eq(key, "m")) + return llarp::dht::DecodeMesssageList(dht::Key_t(session->GetPubKey()), + buf, msgs); + if(llarp_buffer_eq(key, "v")) + { + if(!bencode_read_integer(buf, &version)) + return false; + return version == LLARP_PROTO_VERSION; + } + // bad key + return false; + } + + bool + DHTImmediateMessage::BEncode(llarp_buffer_t *buf) const + { + if(!bencode_start_dict(buf)) + return false; + + // message type + if(!bencode_write_bytestring(buf, "a", 1)) + return false; + if(!bencode_write_bytestring(buf, "m", 1)) + return false; + + // dht messages + if(!bencode_write_bytestring(buf, "m", 1)) + return false; + // begin list + if(!bencode_start_list(buf)) + return false; + for(const auto &msg : msgs) + { + if(!msg->BEncode(buf)) + return false; + } + // end list + if(!bencode_end(buf)) + return false; + + // protocol version + if(!bencode_write_version_entry(buf)) + return false; + + return bencode_end(buf); + } + + bool + DHTImmediateMessage::HandleMessage(llarp::Router *router) const + { + DHTImmediateMessage reply; + reply.session = session; + bool result = true; + for(auto &msg : msgs) + { + result &= msg->HandleMessage(router->dht, reply.msgs); + } + if(reply.msgs.size()) + { + if(result) + { + result = router->SendToOrQueue(session->GetPubKey(), &reply); + } + } + return result; + } +} // namespace llarp diff --git a/llarp/messages/dht_immediate.hpp b/llarp/messages/dht_immediate.hpp index ccbf07cd8..702fc17d7 100644 --- a/llarp/messages/dht_immediate.hpp +++ b/llarp/messages/dht_immediate.hpp @@ -8,13 +8,13 @@ namespace llarp { - struct DHTImmeidateMessage : public ILinkMessage + struct DHTImmediateMessage : public ILinkMessage { - DHTImmeidateMessage() : ILinkMessage() + DHTImmediateMessage() : ILinkMessage() { } - ~DHTImmeidateMessage(); + ~DHTImmediateMessage(); std::vector< std::unique_ptr< llarp::dht::IMessage > > msgs; diff --git a/llarp/messages/link_message_parser.hpp b/llarp/messages/link_message_parser.hpp index 8c7c199fe..7340c8504 100644 --- a/llarp/messages/link_message_parser.hpp +++ b/llarp/messages/link_message_parser.hpp @@ -46,7 +46,7 @@ namespace llarp LinkIntroMessage i; RelayDownstreamMessage d; RelayUpstreamMessage u; - DHTImmeidateMessage m; + DHTImmediateMessage m; LR_CommitMessage c; DiscardMessage x; }; diff --git a/llarp/path/pathbuilder.hpp b/llarp/path/pathbuilder.hpp index 2e31a4012..281fcd966 100644 --- a/llarp/path/pathbuilder.hpp +++ b/llarp/path/pathbuilder.hpp @@ -4,6 +4,8 @@ #include #include +struct llarp_dht_context; + namespace llarp { namespace path @@ -25,7 +27,7 @@ namespace llarp } llarp::Router* router; - struct llarp_dht_context* dht; + llarp_dht_context* dht; llarp::SecretKey enckey; size_t numHops; llarp_time_t lastBuild = 0; @@ -35,7 +37,7 @@ namespace llarp std::atomic< uint8_t > keygens; /// construct - Builder(llarp::Router* p_router, struct llarp_dht_context* p_dht, + Builder(llarp::Router* p_router, llarp_dht_context* p_dht, size_t numPaths, size_t numHops); virtual ~Builder(); diff --git a/llarp/path/pathset.hpp b/llarp/path/pathset.hpp index 13fe42a20..de6b216f8 100644 --- a/llarp/path/pathset.hpp +++ b/llarp/path/pathset.hpp @@ -1,7 +1,6 @@ #ifndef LLARP_PATHSET_HPP #define LLARP_PATHSET_HPP -#include #include #include #include @@ -18,10 +17,13 @@ struct llarp_nodedb; namespace llarp { + struct RouterContact; + namespace dht { struct GotIntroMessage; - } + struct GotRouterMessage; + } // namespace dht namespace path { @@ -34,12 +36,12 @@ namespace llarp ePathExpired }; - /// the role of this path can fuffill + /// the role of this path can fulfill using PathRole = int; /// capable of any role constexpr PathRole ePathRoleAny = 0; - /// outbound hs traffic capabale + /// outbound hs traffic capable constexpr PathRole ePathRoleOutboundHS = (1 << 0); /// inbound hs traffic capable constexpr PathRole ePathRoleInboundHS = (1 << 1); diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index bd254eb28..0476d9a4c 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -422,7 +423,7 @@ namespace llarp Router::EnsureEncryptionKey() { return llarp_findOrCreateEncryption(&crypto, encryption_keyfile, - encryption); + this->encryption); } void @@ -904,7 +905,7 @@ namespace llarp { auto buf = llarp::ConstBuffer(itr->second.front()); if(!chosen->SendTo(remote, buf)) - llarp::LogWarn("failed to send outboud message to ", remote, " via ", + llarp::LogWarn("failed to send outbound message to ", remote, " via ", chosen->Name()); itr->second.pop(); @@ -1110,12 +1111,17 @@ namespace llarp llarp::LogError("failed to regenerate keys and sign RC"); return false; } - // generate default hidden service - llarp::LogInfo("setting up default network endpoint"); - if(!CreateDefaultHiddenService()) + + // don't create default if we already have some defined + if(this->ShouldCreateDefaultHiddenService()) { - llarp::LogError("failed to set up default network endpoint"); - return false; + // generate default hidden service + llarp::LogInfo("setting up default network endpoint"); + if(!CreateDefaultHiddenService()) + { + llarp::LogError("failed to set up default network endpoint"); + return false; + } } } @@ -1156,6 +1162,84 @@ namespace llarp link->Stop(); } + bool + Router::ShouldCreateDefaultHiddenService() + { + std::string defaultIfAddr = "auto"; + std::string defaultIfName = "auto"; + std::string enabledOption = "auto"; + auto itr = netConfig.find("defaultIfAddr"); + if(itr != netConfig.end()) + { + defaultIfAddr = itr->second; + } + itr = netConfig.find("defaultIfName"); + if(itr != netConfig.end()) + { + defaultIfName = itr->second; + } + itr = netConfig.find("enabled"); + if(itr != netConfig.end()) + { + enabledOption = itr->second; + } + llarp::LogDebug("IfName: ", defaultIfName, " IfAddr: ", defaultIfAddr, + " Enabled: ", enabledOption); + // llarp::LogInfo("IfAddr: ", itr->second); + // llarp::LogInfo("IfName: ", itr->second); + if(enabledOption == "false") + { + llarp::LogInfo("Disabling default hidden service"); + return false; + } + else if(enabledOption == "auto") + { + // auto detect if we have any pre-defined endpoints + // no if we have a endpoints + if(hiddenServiceContext.hasEndpoints()) + { + llarp::LogInfo("Auto mode detected and we have endpoints"); + netConfig.emplace("enabled", "false"); + return false; + } + netConfig.emplace("enabled", "true"); + } + // ev.cpp llarp_ev_add_tun now handles this + /* + // so basically enabled at this point + if(defaultIfName == "auto") + { + // we don't have any endpoints, auto configure settings + // set a default IP range + defaultIfAddr = llarp::findFreePrivateRange(); + if(defaultIfAddr == "") + { + llarp::LogError( + "Could not find any free lokitun interface names, can't + auto set up " "default HS context for client"); defaultIfAddr = "no"; + netConfig.emplace(std::make_pair("defaultIfAddr", defaultIfAddr)); + return false; + } + netConfig.emplace(std::make_pair("defaultIfAddr", defaultIfAddr)); + } + if(defaultIfName == "auto") + { + // pick an ifName + defaultIfName = llarp::findFreeLokiTunIfName(); + if(defaultIfName == "") + { + llarp::LogError( + "Could not find any free private ip ranges, can't auto + set up " "default HS context for client"); defaultIfName = "no"; + netConfig.emplace(std::make_pair("defaultIfName", defaultIfName)); + return false; + } + netConfig.emplace(std::make_pair("defaultIfName", defaultIfName)); + } + */ + return true; + } + void Router::Stop() { @@ -1321,6 +1405,7 @@ namespace llarp Router::CreateDefaultHiddenService() { // fallback defaults + // To NeuroScr: why run findFree* here instead of in tun.cpp? static const std::unordered_map< std::string, std::function< std::string(void) > > netConfigDefaults = { @@ -1576,7 +1661,7 @@ namespace llarp { self->transport_keyfile = val; } - if(StrEq(key, "ident-privkey") && !self->usingSNSeed) + if((StrEq(key, "identity-privkey") || StrEq(key, "ident-privkey")) && !self->usingSNSeed) { self->ident_keyfile = val; } diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index 1d3a628f3..2aaa94229 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -163,6 +163,9 @@ namespace llarp bool CreateDefaultHiddenService(); + bool + ShouldCreateDefaultHiddenService(); + const std::string DefaultRPCBindAddr = "127.0.0.1:1190"; bool enableRPCServer = true; std::unique_ptr< llarp::rpc::Server > rpcServer; diff --git a/llarp/service/context.cpp b/llarp/service/context.cpp index 7c58c9528..243346ec9 100644 --- a/llarp/service/context.cpp +++ b/llarp/service/context.cpp @@ -262,10 +262,13 @@ namespace llarp } // extract type std::string endpointType = "tun"; + std::string keyfile = ""; for(const auto &option : conf.second) { if(option.first == "type") endpointType = option.second; + if(option.first == "keyfile") + keyfile = option.second; } std::unique_ptr< llarp::service::Endpoint > service; @@ -295,6 +298,17 @@ namespace llarp // construct service.reset(itr->second(conf.first, m_Router)); + + // if ephemeral, then we need to regen key + // if privkey file, then set it and load it + if(keyfile != "") + { + service->SetOption("keyfile", keyfile); + // load keyfile, so we have the correct name for logging + } + llarp::LogInfo("Establishing endpoint identity"); + service->LoadKeyFile(); // only start endpoint not tun + // now Name() will be correct } // configure for(const auto &option : conf.second) diff --git a/llarp/service/endpoint.cpp b/llarp/service/endpoint.cpp index 2eb0dbdbd..f324faf78 100644 --- a/llarp/service/endpoint.cpp +++ b/llarp/service/endpoint.cpp @@ -1,6 +1,10 @@ #include #include +#include +#include +#include +#include #include #include #include @@ -486,18 +490,29 @@ namespace llarp } bool - Endpoint::Start() + Endpoint::LoadKeyFile() { auto crypto = &m_Router->crypto; if(m_Keyfile.size()) { if(!m_Identity.EnsureKeys(m_Keyfile, crypto)) + { + llarp::LogWarn("Can't ensure keyfile [", m_Keyfile, "]"); return false; + } } else { m_Identity.RegenerateKeys(crypto); } + return true; + } + + bool + Endpoint::Start() + { + // how can I tell if a m_Identity isn't loaded? + //this->LoadKeyFile(); if(!m_DataHandler) { m_DataHandler = this; @@ -508,7 +523,10 @@ namespace llarp if(m_OnInit.front()()) m_OnInit.pop_front(); else + { + llarp::LogWarn("Can't call init of network isolation"); return false; + } } return true; } @@ -1688,7 +1706,7 @@ namespace llarp if(markedBad) return false; bool should = path::Builder::ShouldBuildMore(now); - // determinte newest intro + // determine newest intro Introduction intro; if(!GetNewestIntro(intro)) return should; diff --git a/llarp/service/endpoint.hpp b/llarp/service/endpoint.hpp index 67e4a9813..e49acf4cf 100644 --- a/llarp/service/endpoint.hpp +++ b/llarp/service/endpoint.hpp @@ -71,6 +71,9 @@ namespace llarp return m_Router; } + virtual bool + LoadKeyFile(); + virtual bool Start(); diff --git a/lokinet-bootstrap b/lokinet-bootstrap index 094014362..ec8487a7f 100755 --- a/lokinet-bootstrap +++ b/lokinet-bootstrap @@ -1,7 +1,7 @@ #!/usr/bin/env bash # this shell script will be replaced by a proper program in the future (probably) # - +# from https://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux RED='\033[0;31m' GREEN='\033[0;32m' NC='\033[0m' # No Color diff --git a/test/dht/test_llarp_dht.cpp b/test/dht/test_llarp_dht.cpp deleted file mode 100644 index 1e6038206..000000000 --- a/test/dht/test_llarp_dht.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include -#include -#include - -#include - -using Key_t = llarp::dht::Key_t; - -class KademliaDHTTest : public ::testing::Test -{ - public: - KademliaDHTTest() - { - } - ~KademliaDHTTest() - { - } - - void - SetUp() - { - us.Fill(16); - nodes = new llarp::dht::Bucket< llarp::dht::RCNode >(us); - size_t numNodes = 10; - byte_t fill = 1; - while(numNodes) - { - llarp::dht::RCNode n; - n.ID.Fill(fill); - nodes->PutNode(n); - --numNodes; - ++fill; - } - } - - void - TearDown() - { - delete nodes; - } - - llarp::dht::Bucket< llarp::dht::RCNode >* nodes = nullptr; - llarp::dht::Key_t us; -}; - -TEST_F(KademliaDHTTest, TestBucketFindClosest) -{ - llarp::dht::Key_t result; - llarp::dht::Key_t target; - llarp::dht::Key_t oldResult; - target.Fill(5); - ASSERT_TRUE(nodes->FindClosest(target, result)); - ASSERT_TRUE(target == result); - oldResult = result; - target.Fill(0xf5); - ASSERT_TRUE(nodes->FindClosest(target, result)); - ASSERT_TRUE(oldResult == result); -}; - -TEST_F(KademliaDHTTest, TestBucketOperators) -{ - llarp::dht::Key_t zero; - llarp::dht::Key_t one; - llarp::dht::Key_t three; - - zero.Zero(); - one.Fill(1); - three.Fill(3); - ASSERT_TRUE(zero < one); - ASSERT_TRUE(zero < three); - ASSERT_FALSE(zero > one); - ASSERT_FALSE(zero > three); - ASSERT_TRUE(zero != three); - ASSERT_FALSE(zero == three); - ASSERT_TRUE((zero ^ one) == one); - ASSERT_TRUE(one < three); - ASSERT_TRUE(three > one); - ASSERT_TRUE(one != three); - ASSERT_FALSE(one == three); - ASSERT_TRUE((one ^ three) == (three ^ one)); -}; - -TEST_F(KademliaDHTTest, TestBucketRandomized_1000) -{ - size_t moreNodes = 100; - while(moreNodes--) - { - llarp::dht::RCNode n; - n.ID.Randomize(); - nodes->PutNode(n); - } - const size_t count = 1000; - size_t left = count; - while(left--) - { - llarp::dht::Key_t result; - llarp::dht::Key_t target; - llarp::dht::Key_t expect; - target.Randomize(); - expect = target; - ASSERT_TRUE(nodes->FindClosest(target, result)); - if(target == result) - { - ASSERT_FALSE((result ^ target) < (expect ^ target)); - ASSERT_FALSE((result ^ target) != (expect ^ target)); - ASSERT_TRUE((result ^ target) == (expect ^ target)); - } - else - { - Key_t dist = result ^ target; - Key_t oldDist = expect ^ target; - ASSERT_TRUE((result ^ target) != (expect ^ target)); - if((result ^ target) < (expect ^ target)) - { - std::cout << "result=" << result << "expect=" << expect << std::endl; - std::cout << dist << ">=" << oldDist << "iteration=" << (count - left) - << std::endl; - ASSERT_TRUE(false); - } - ASSERT_FALSE((result ^ target) == (expect ^ target)); - } - } -}; diff --git a/test/dht/test_llarp_dht_bucket.cpp b/test/dht/test_llarp_dht_bucket.cpp new file mode 100644 index 000000000..1918596b0 --- /dev/null +++ b/test/dht/test_llarp_dht_bucket.cpp @@ -0,0 +1,370 @@ +#include +#include +#include + +#include + +using Key_t = llarp::dht::Key_t; +using Value_t = llarp::dht::RCNode; +using Bucket_t = llarp::dht::Bucket< Value_t >; + +class TestDhtBucket : public ::testing::Test +{ + public: + TestDhtBucket() : randInt(0) + { + us.Fill(16); + nodes = std::make_unique< Bucket_t >(us, [&]() { return randInt++; }); + size_t numNodes = 10; + byte_t fill = 1; + while(numNodes) + { + Value_t n; + n.ID.Fill(fill); + nodes->PutNode(n); + --numNodes; + ++fill; + } + } + + uint64_t randInt; + + llarp::dht::Key_t us; + std::unique_ptr< Bucket_t > nodes; +}; + +TEST_F(TestDhtBucket, simple_cycle) +{ + // Empty the current bucket. + nodes->Clear(); + + // Create a simple value, and add it to the bucket. + Value_t val; + val.ID.Fill(1); + + nodes->PutNode(val); + + // Verify the value is in the bucket + ASSERT_TRUE(nodes->HasNode(val.ID)); + ASSERT_EQ(1u, nodes->size()); + + // Verify after deletion, the value is no longer in the bucket + nodes->DelNode(val.ID); + ASSERT_FALSE(nodes->HasNode(val.ID)); + + // Verify deleting again succeeds; + nodes->DelNode(val.ID); + ASSERT_FALSE(nodes->HasNode(val.ID)); +} + +TEST_F(TestDhtBucket, get_random_node_excluding) +{ + // Empty the current bucket. + nodes->Clear(); + + // We expect not to find anything + Key_t result; + std::set< Key_t > excludeSet; + ASSERT_FALSE(nodes->GetRandomNodeExcluding(result, excludeSet)); + + // Create a simple value. + Value_t val; + val.ID.Fill(1); + + // Add the simple value to the exclude set + excludeSet.insert(val.ID); + ASSERT_FALSE(nodes->GetRandomNodeExcluding(result, excludeSet)); + + // Add the simple value to the bucket + nodes->PutNode(val); + ASSERT_FALSE(nodes->GetRandomNodeExcluding(result, excludeSet)); + + excludeSet.clear(); + + ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet)); + ASSERT_EQ(val.ID, result); + + // Add an element to the exclude set which isn't the bucket. + Key_t other; + other.Fill(0xff); + excludeSet.insert(other); + + ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet)); + ASSERT_EQ(val.ID, result); + + // Add a node which is in both bucket and excludeSet + Value_t nextVal; + nextVal.ID.Fill(0xAA); + excludeSet.insert(nextVal.ID); + nodes->PutNode(nextVal); + + ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet)); + ASSERT_EQ(val.ID, result); + + // Clear the excludeSet - we should still have 2 nodes in the bucket + excludeSet.clear(); + + randInt = 0; + ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet)); + ASSERT_EQ(val.ID, result); + + // Set the random value to be 1, we should get the other node. + randInt = 1; + ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet)); + ASSERT_EQ(nextVal.ID, result); + + // Set the random value to be 100, we should get the first node. + randInt = 100; + ASSERT_TRUE(nodes->GetRandomNodeExcluding(result, excludeSet)); + ASSERT_EQ(val.ID, result); +} + +TEST_F(TestDhtBucket, find_closest) +{ + // Empty the current bucket. + nodes->Clear(); + + // We expect not to find anything + Key_t target; + target.Fill(0xF0); + + Key_t result; + ASSERT_FALSE(nodes->FindClosest(target, result)); + + // Add a node to the bucket + Value_t first; + first.ID.Zero(); + nodes->PutNode(first); + + ASSERT_TRUE(nodes->FindClosest(target, result)); + ASSERT_EQ(result, first.ID); + + // Add another node to the bucket, closer to the target + Value_t second; + second.ID.Fill(0x10); + nodes->PutNode(second); + ASSERT_TRUE(nodes->FindClosest(target, result)); + ASSERT_EQ(result, second.ID); + + // Add a third node to the bucket, closer to the target + Value_t third; + third.ID.Fill(0x20); + nodes->PutNode(third); + ASSERT_TRUE(nodes->FindClosest(target, result)); + ASSERT_EQ(result, third.ID); + + // Add a fourth node to the bucket, greater than the target + Value_t fourth; + fourth.ID.Fill(0xF1); + nodes->PutNode(fourth); + ASSERT_TRUE(nodes->FindClosest(target, result)); + ASSERT_EQ(result, fourth.ID); + + // Add a fifth node to the bucket, equal to the target + Value_t fifth; + fifth.ID.Fill(0xF0); + nodes->PutNode(fifth); + ASSERT_TRUE(nodes->FindClosest(target, result)); + ASSERT_EQ(result, fifth.ID); +} + +TEST_F(TestDhtBucket, get_many_random) +{ + // Empty the current bucket. + nodes->Clear(); + + // Verify behaviour with empty node set + std::set< Key_t > result; + ASSERT_FALSE(nodes->GetManyRandom(result, 0)); + ASSERT_FALSE(nodes->GetManyRandom(result, 1)); + + // Add 5 nodes to the bucket + std::set< Value_t > curValues; + std::set< Key_t > curKeys; + for(byte_t i = 0x00; i < 0x05; ++i) + { + Value_t v; + v.ID.Fill(i); + ASSERT_TRUE(curKeys.insert(v.ID).second); + nodes->PutNode(v); + } + + // Fetching more than the current size fails + ASSERT_EQ(5u, nodes->size()); + ASSERT_FALSE(nodes->GetManyRandom(result, nodes->size() + 1)); + + // Fetching the current size succeeds + ASSERT_TRUE(nodes->GetManyRandom(result, nodes->size())); + ASSERT_EQ(curKeys, result); + + // Fetching a subset succeeds. + // Note we hack this by "fixing" the random number generator + result.clear(); + + ASSERT_TRUE(nodes->GetManyRandom(result, 1u)); + ASSERT_EQ(1u, result.size()); + ASSERT_EQ(*curKeys.begin(), *result.begin()); + + randInt = 0; + result.clear(); + + ASSERT_TRUE(nodes->GetManyRandom(result, nodes->size() - 1)); + ASSERT_EQ(nodes->size() - 1, result.size()); + ASSERT_EQ(std::set< Key_t >(++curKeys.rbegin(), curKeys.rend()), result); +} + +TEST_F(TestDhtBucket, find_close_excluding) +{ + // Empty the current bucket. + nodes->Clear(); + + Key_t target; + target.Zero(); + std::set< Key_t > exclude; + Key_t result; + + // Empty node + exclude set fails + ASSERT_FALSE(nodes->FindCloseExcluding(target, result, exclude)); + + Value_t first; + first.ID.Fill(0xF0); + exclude.insert(first.ID); + + // Empty nodes fails + ASSERT_FALSE(nodes->FindCloseExcluding(target, result, exclude)); + + // Nodes and exclude set match + nodes->PutNode(first); + ASSERT_FALSE(nodes->FindCloseExcluding(target, result, exclude)); + + // Exclude set empty + exclude.clear(); + ASSERT_TRUE(nodes->FindCloseExcluding(target, result, exclude)); + result = first.ID; + + Value_t second; + second.ID.Fill(0x01); + nodes->PutNode(second); + + ASSERT_TRUE(nodes->FindCloseExcluding(target, result, exclude)); + result = second.ID; + + exclude.insert(second.ID); + ASSERT_TRUE(nodes->FindCloseExcluding(target, result, exclude)); + result = first.ID; +} + +TEST_F(TestDhtBucket, find_many_near_excluding) +{ + // Empty the current bucket. + nodes->Clear(); + + Key_t target; + target.Zero(); + std::set< Key_t > exclude; + std::set< Key_t > result; + + // Empty node + exclude set, with size 0 succeeds + ASSERT_TRUE(nodes->GetManyNearExcluding(target, result, 0, exclude)); + ASSERT_EQ(0u, result.size()); + // Empty node + exclude set fails + ASSERT_FALSE(nodes->GetManyNearExcluding(target, result, 1, exclude)); + + Value_t first; + first.ID.Fill(0xF0); + exclude.insert(first.ID); + + // Empty nodes fails + ASSERT_FALSE(nodes->GetManyNearExcluding(target, result, 1, exclude)); + + // Nodes and exclude set match + nodes->PutNode(first); + ASSERT_FALSE(nodes->GetManyNearExcluding(target, result, 1, exclude)); + + // Single node succeeds + exclude.clear(); + ASSERT_TRUE(nodes->GetManyNearExcluding(target, result, 1, exclude)); + ASSERT_EQ(result, std::set< Key_t >({first.ID})); + + // Trying to grab 2 nodes from a 1 node set fails + result.clear(); + ASSERT_FALSE(nodes->GetManyNearExcluding(target, result, 2, exclude)); + + // two nodes finds closest + Value_t second; + second.ID.Fill(0x01); + nodes->PutNode(second); + result.clear(); + ASSERT_TRUE(nodes->GetManyNearExcluding(target, result, 1, exclude)); + ASSERT_EQ(result, std::set< Key_t >({second.ID})); + + // 3 nodes finds 2 closest + Value_t third; + third.ID.Fill(0x02); + nodes->PutNode(third); + result.clear(); + ASSERT_TRUE(nodes->GetManyNearExcluding(target, result, 2, exclude)); + ASSERT_EQ(result, std::set< Key_t >({second.ID, third.ID})); + + // 4 nodes, one in exclude set finds 2 closest + Value_t fourth; + fourth.ID.Fill(0x03); + nodes->PutNode(fourth); + exclude.insert(third.ID); + result.clear(); + ASSERT_TRUE(nodes->GetManyNearExcluding(target, result, 2, exclude)); + ASSERT_EQ(result, std::set< Key_t >({second.ID, fourth.ID})); +} + +TEST_F(TestDhtBucket, TestBucketFindClosest) +{ + llarp::dht::Key_t result; + llarp::dht::Key_t target; + target.Fill(5); + ASSERT_TRUE(nodes->FindClosest(target, result)); + ASSERT_EQ(target, result); + const llarp::dht::Key_t oldResult = result; + target.Fill(0xf5); + ASSERT_TRUE(nodes->FindClosest(target, result)); + ASSERT_EQ(oldResult, result); +}; + +TEST_F(TestDhtBucket, TestBucketRandomized_1000) +{ + size_t moreNodes = 100; + while(moreNodes--) + { + llarp::dht::RCNode n; + n.ID.Fill(randInt); + randInt++; + nodes->PutNode(n); + } + const size_t count = 1000; + size_t left = count; + while(left--) + { + llarp::dht::Key_t result; + llarp::dht::Key_t target; + target.Randomize(); + const llarp::dht::Key_t expect = target; + ASSERT_TRUE(nodes->FindClosest(target, result)); + if(target == result) + { + ASSERT_GE(result ^ target, expect ^ target); + ASSERT_EQ(result ^ target, expect ^ target); + ASSERT_EQ(result ^ target, expect ^ target); + } + else + { + Key_t dist = result ^ target; + Key_t oldDist = expect ^ target; + ASSERT_NE(result ^ target, expect ^ target); + + ASSERT_GE(result ^ target, expect ^ target) + << "result=" << result << "expect=" << expect << std::endl + << dist << ">=" << oldDist << "iteration=" << (count - left); + + ASSERT_NE(result ^ target, expect ^ target); + } + } +}; diff --git a/test/dht/test_llarp_dht_kademlia.cpp b/test/dht/test_llarp_dht_kademlia.cpp new file mode 100644 index 000000000..321bb199e --- /dev/null +++ b/test/dht/test_llarp_dht_kademlia.cpp @@ -0,0 +1,88 @@ +#include + +#include + +using llarp::dht::Key_t; + +using Array = std::array< byte_t, Key_t::SIZE >; + +struct XorMetricData +{ + Array us; + Array left; + Array right; + bool result; + + XorMetricData(const Array& u, const Array& l, const Array& r, bool res) + : us(u), left(l), right(r), result(res) + { + } +}; + +std::ostream& +operator<<(std::ostream& stream, const XorMetricData& x) +{ + stream << int(x.us[0]) << " " << int(x.left[0]) << " " << int(x.right[0]) + << " " << std::boolalpha << x.result; + + return stream; +} + +struct XorMetric : public ::testing::TestWithParam< XorMetricData > +{ +}; + +TEST_P(XorMetric, test) +{ + auto d = GetParam(); + ASSERT_EQ(llarp::dht::XorMetric{Key_t{d.us}}(Key_t{d.left}, Key_t{d.right}), + d.result); +} + +std::vector< XorMetricData > +makeData() +{ + std::vector< XorMetricData > result; + + Array zero; + zero.fill(0); + Array one; + one.fill(1); + Array two; + two.fill(2); + Array three; + three.fill(3); + + result.emplace_back(zero, zero, zero, false); + result.emplace_back(zero, zero, one, true); + result.emplace_back(zero, zero, two, true); + result.emplace_back(zero, one, zero, false); + result.emplace_back(zero, one, one, false); + result.emplace_back(zero, one, two, true); + result.emplace_back(zero, two, zero, false); + result.emplace_back(zero, two, one, false); + result.emplace_back(zero, two, two, false); + result.emplace_back(one, zero, zero, false); + result.emplace_back(one, zero, one, false); + result.emplace_back(one, zero, two, true); + result.emplace_back(one, one, zero, true); + result.emplace_back(one, one, one, false); + result.emplace_back(one, one, two, true); + result.emplace_back(one, two, zero, false); + result.emplace_back(one, two, one, false); + result.emplace_back(one, two, two, false); + result.emplace_back(two, zero, zero, false); + result.emplace_back(two, zero, one, true); + result.emplace_back(two, zero, two, false); + result.emplace_back(two, one, zero, false); + result.emplace_back(two, one, one, false); + result.emplace_back(two, one, two, false); + result.emplace_back(two, two, zero, true); + result.emplace_back(two, two, one, true); + result.emplace_back(two, two, two, false); + + return result; +} + +INSTANTIATE_TEST_CASE_P(TestDhtXorMetric, XorMetric, + ::testing::ValuesIn(makeData())); diff --git a/test/dht/test_llarp_dht_key.cpp b/test/dht/test_llarp_dht_key.cpp index 855b20e90..3ab020299 100644 --- a/test/dht/test_llarp_dht_key.cpp +++ b/test/dht/test_llarp_dht_key.cpp @@ -30,20 +30,11 @@ TEST_P(DHT, constructor) } } -static constexpr Array emptyArray{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +static constexpr Array emptyArray {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; -static constexpr Array fullArray{ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +static constexpr Array fullArray {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}; -static constexpr Array seqArray{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}; +static constexpr Array seqArray {{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}}; static const Array data[] = {emptyArray, fullArray, seqArray}; @@ -98,3 +89,26 @@ TEST(TestDhtKey, XOR) ASSERT_EQ(dht::Key_t(xorResult), dht::Key_t(seqArray) ^ dht::Key_t(fullArray)); } + +TEST(TestDhtKey, TestBucketOperators) +{ + dht::Key_t zero; + dht::Key_t one; + dht::Key_t three; + + zero.Zero(); + one.Fill(1); + three.Fill(3); + ASSERT_LT(zero, one); + ASSERT_LT(zero, three); + ASSERT_FALSE(zero > one); + ASSERT_FALSE(zero > three); + ASSERT_NE(zero, three); + ASSERT_FALSE(zero == three); + ASSERT_EQ(zero ^ one, one); + ASSERT_LT(one, three); + ASSERT_GT(three, one); + ASSERT_NE(one, three); + ASSERT_FALSE(one == three); + ASSERT_EQ(one ^ three, three ^ one); +}; diff --git a/test/dht/test_llarp_dht_txowner.cpp b/test/dht/test_llarp_dht_txowner.cpp new file mode 100644 index 000000000..fbb000a45 --- /dev/null +++ b/test/dht/test_llarp_dht_txowner.cpp @@ -0,0 +1,123 @@ +#include + +#include + +namespace +{ + using llarp::dht::Key_t; + using llarp::dht::TXOwner; + + struct TxOwnerData + { + Key_t node; + uint64_t id; + size_t expectedHash; + + TxOwnerData(const Key_t& k, uint64_t i, size_t h) + : node(k), id(i), expectedHash(h) + { + } + }; + + struct TxOwner : public ::testing::TestWithParam< TxOwnerData > + { + }; + + TEST_F(TxOwner, default_construct) + { + TXOwner dc; + ASSERT_TRUE(dc.node.IsZero()); + ASSERT_EQ(0u, dc.txid); + ASSERT_EQ(0u, TXOwner::Hash()(dc)); + } + + TEST_P(TxOwner, hash) + { + // test single interactions (constructor and hash) + auto d = GetParam(); + TXOwner constructor(d.node, d.id); + + ASSERT_EQ(d.expectedHash, TXOwner::Hash()(constructor)); + } + + std::vector< TxOwnerData > + makeData() + { + std::vector< TxOwnerData > result; + + Key_t zero; + zero.Zero(); + Key_t one; + one.Fill(0x01); + Key_t two; + two.Fill(0x02); + + uint64_t max = std::numeric_limits< uint64_t >::max(); + + result.emplace_back(zero, 0, 0ull); + result.emplace_back(zero, 1, 1ull); + result.emplace_back(one, 0, 144680345676153346ull); + result.emplace_back(one, 1, 144680345676153347ull); + result.emplace_back(two, 0, 289360691352306692ull); + result.emplace_back(two, 2, 289360691352306694ull); + result.emplace_back(zero, max, 18446744073709551615ull); + result.emplace_back(one, max, 18302063728033398269ull); + result.emplace_back(two, max, 18157383382357244923ull); + + return result; + } + + struct TxOwnerCmpData + { + TXOwner lhs; + TXOwner rhs; + bool equal; + bool less; + + TxOwnerCmpData(const TXOwner& l, const TXOwner& r, bool e, bool ls) + : lhs(l), rhs(r), equal(e), less(ls) + { + } + }; + + struct TxOwnerOps : public ::testing::TestWithParam< TxOwnerCmpData > + { + }; + + TEST_P(TxOwnerOps, operators) + { + // test single interactions (constructor and hash) + auto d = GetParam(); + + ASSERT_EQ(d.lhs == d.rhs, d.equal); + ASSERT_EQ(d.lhs < d.rhs, d.less); + } + + std::vector< TxOwnerCmpData > + makeCmpData() + { + std::vector< TxOwnerCmpData > result; + + Key_t zero; + zero.Fill(0x00); + Key_t one; + one.Fill(0x01); + Key_t two; + two.Fill(0x02); + + result.emplace_back(TXOwner(zero, 0), TXOwner(zero, 0), true, false); + result.emplace_back(TXOwner(one, 0), TXOwner(one, 0), true, false); + result.emplace_back(TXOwner(two, 0), TXOwner(two, 0), true, false); + + result.emplace_back(TXOwner(zero, 0), TXOwner(one, 0), false, true); + result.emplace_back(TXOwner(two, 0), TXOwner(one, 0), false, false); + + return result; + } +} // namespace + +INSTANTIATE_TEST_CASE_P(TestDhtTxOwner, TxOwner, + ::testing::ValuesIn(makeData())); + +INSTANTIATE_TEST_CASE_P(TestDhtTxOwner, TxOwnerOps, + ::testing::ValuesIn(makeCmpData()));