From ad882d0d70bd6da739865e442a39de6848e007ef Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 20 May 2020 07:41:42 -0400 Subject: [PATCH] initial working code --- CMakeLists.txt | 4 -- cmake/unix.cmake | 14 ++-- llarp/config/key_manager.cpp | 121 +------------------------------- llarp/config/key_manager.hpp | 13 +--- llarp/context.cpp | 3 - llarp/router/abstractrouter.hpp | 10 ++- llarp/router/router.cpp | 47 ++++++++----- llarp/router/router.hpp | 14 ++-- llarp/rpc/lokid_rpc_client.cpp | 116 ++++++++++++++++++++---------- llarp/rpc/lokid_rpc_client.hpp | 15 ++++ llarp/service/protocol.cpp | 6 +- 11 files changed, 156 insertions(+), 207 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a59abbd36..781a8ae8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -276,10 +276,6 @@ if(SUBMODULE_CHECK) endif() endif() -add_subdirectory(external/loki-mq) -include_directories(external/loki-mq) -include_directories(external/loki-mq/mapbox-variant/include) - # We only actually need pybind11 with WITH_HIVE, but if we don't load it here then something further # down loads a broken PythonInterp that loads Python2, but Python2 headers are not C++17 compatible. # So load this here universally so that pybind's more intelligent python finder finds python3.x diff --git a/cmake/unix.cmake b/cmake/unix.cmake index 44a9ce430..8cc8d359c 100644 --- a/cmake/unix.cmake +++ b/cmake/unix.cmake @@ -64,13 +64,13 @@ elseif(DOWNLOAD_UV) endif() include_directories(${LIBUV_INCLUDE_DIRS}) -#find_package(LokiMQ) -#if(LokiMQ_FOUND) -# message(STATUS "using system lokimq") -#else() -message(STATUS "using lokimq submodule") -add_subdirectory(${CMAKE_SOURCE_DIR}/external/loki-mq) -#endif() +find_package(LokiMQ 1.2) +if(LokiMQ_FOUND) + message(STATUS "using system lokimq") +else() + message(STATUS "using lokimq submodule") + add_subdirectory(${CMAKE_SOURCE_DIR}/external/loki-mq EXCLUDE_FROM_ALL) +endif() if(EMBEDDED_CFG OR ${CMAKE_SYSTEM_NAME} MATCHES "Linux") link_libatomic() diff --git a/llarp/config/key_manager.cpp b/llarp/config/key_manager.cpp index 871988f27..5818db155 100644 --- a/llarp/config/key_manager.cpp +++ b/llarp/config/key_manager.cpp @@ -6,19 +6,6 @@ #include "crypto/crypto.hpp" #include "crypto/types.hpp" -#ifdef HAVE_CURL -#include -#endif - -/// curl callback -static size_t -curl_RecvIdentKey(char* ptr, size_t, size_t nmemb, void* userdata) -{ - for (size_t idx = 0; idx < nmemb; idx++) - static_cast*>(userdata)->push_back(ptr[idx]); - return nmemb; -} - namespace llarp { KeyManager::KeyManager() : m_initialized(false), m_needBackup(false) @@ -55,11 +42,6 @@ namespace llarp m_encKeyPath = deriveFile(our_enc_key_filename, config.router.m_encryptionKeyFile); m_transportKeyPath = deriveFile(our_transport_key_filename, config.router.m_transportKeyFile); - m_usingLokid = config.lokid.whitelistRouters; - m_lokidRPCAddr = config.lokid.lokidRPCAddr; - m_lokidRPCUser = config.lokid.lokidRPCUser; - m_lokidRPCPassword = config.lokid.lokidRPCPassword; - RouterContact rc; bool exists = rc.Read(m_rcPath); if (not exists and not genIfAbsent) @@ -99,7 +81,7 @@ namespace llarp } } - if (not m_usingLokid) + if (not config.lokid.whitelistRouters) { // load identity key or create if needed auto identityKeygen = [](llarp::SecretKey& key) { @@ -109,11 +91,6 @@ namespace llarp if (not loadOrCreateKey(m_idKeyPath, identityKey, identityKeygen)) return false; } - else - { - if (not loadIdentityFromLokid()) - return false; - } // load encryption key auto encryptionKeygen = [](llarp::SecretKey& key) { @@ -227,100 +204,4 @@ namespace llarp return key.LoadFromFile(filepath); } - bool - KeyManager::loadIdentityFromLokid() - { -#ifndef HAVE_CURL - LogError("this lokinet was not built with service node mode support"); - return false; -#else - CURL* curl = curl_easy_init(); - if (curl) - { - bool ret = false; - std::stringstream ss; - ss << "http://" << m_lokidRPCAddr << "/json_rpc"; - const auto url = ss.str(); - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_ANY); - const auto auth = m_lokidRPCUser + ":" + m_lokidRPCPassword; - curl_easy_setopt(curl, CURLOPT_USERPWD, auth.c_str()); - curl_slist* list = nullptr; - list = curl_slist_append(list, "Content-Type: application/json"); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); - - nlohmann::json request = { - {"id", "0"}, {"jsonrpc", "2.0"}, {"method", "get_service_node_privkey"}}; - const auto data = request.dump(); - std::vector resp; - - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, data.size()); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str()); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_RecvIdentKey); - - resp.clear(); - LogInfo("Getting Identity Keys from lokid..."); - if (curl_easy_perform(curl) == CURLE_OK) - { - try - { - auto j = nlohmann::json::parse(resp); - if (not j.is_object()) - return false; - - const auto itr = j.find("result"); - if (itr == j.end()) - return false; - if (not itr->is_object()) - return false; - const auto k = (*itr)["service_node_ed25519_privkey"].get(); - if (k.size() != (identityKey.size() * 2)) - { - if (k.empty()) - { - LogError("lokid gave no identity key"); - } - else - { - LogError("lokid gave invalid identity key"); - } - return false; - } - if (not HexDecode(k.c_str(), identityKey.data(), identityKey.size())) - return false; - if (CryptoManager::instance()->check_identity_privkey(identityKey)) - { - ret = true; - } - else - { - LogError("lokid gave bogus identity key"); - } - } - catch (nlohmann::json::exception& ex) - { - LogError("Bad response from lokid: ", ex.what()); - } - } - else - { - LogError("failed to get identity keys"); - } - if (ret) - { - LogInfo("Got Identity Keys from lokid: ", RouterID(seckey_topublic(identityKey))); - } - curl_easy_cleanup(curl); - curl_slist_free_all(list); - return ret; - } - else - { - LogError("failed to init curl"); - return false; - } -#endif - } - } // namespace llarp diff --git a/llarp/config/key_manager.hpp b/llarp/config/key_manager.hpp index 73df85d21..ca089b1e9 100644 --- a/llarp/config/key_manager.hpp +++ b/llarp/config/key_manager.hpp @@ -18,7 +18,6 @@ namespace llarp /// In addition, the KeyManager detects when the keys obsolete (e.g. as a /// result of a software upgrade) and backs up existing keys before writing /// out new ones. - struct KeyManager { /// Utility function to backup a file by moving it. Attempts to find a new @@ -34,8 +33,7 @@ namespace llarp /// Constructor KeyManager(); - /// Initializes keys using the provided config, loading from disk and/or - /// lokid via HTTP request. + /// Initializes keys using the provided config, loading from disk /// /// NOTE: Must be called prior to obtaining any keys. /// NOTE: blocks on I/O @@ -75,11 +73,6 @@ namespace llarp std::atomic_bool m_initialized; std::atomic_bool m_needBackup; - bool m_usingLokid = false; - std::string m_lokidRPCAddr = "127.0.0.1:22023"; - std::string m_lokidRPCUser; - std::string m_lokidRPCPassword; - /// Backup each key file (by copying, e.g. foo -> foo.bak) bool backupKeyFilesByMoving() const; @@ -92,10 +85,6 @@ namespace llarp const fs::path& filepath, llarp::SecretKey& key, std::function keygen); - - /// Requests the identity key from lokid via HTTP (curl) - bool - loadIdentityFromLokid(); }; } // namespace llarp diff --git a/llarp/context.cpp b/llarp/context.cpp index f86d125fd..67706d3c9 100644 --- a/llarp/context.cpp +++ b/llarp/context.cpp @@ -117,9 +117,6 @@ namespace llarp llarp::LogError("cannot run non configured context"); return 1; } - // run - if (!router->StartJsonRpc()) - return 1; if (!opts.background) { diff --git a/llarp/router/abstractrouter.hpp b/llarp/router/abstractrouter.hpp index c988483c2..d303e7ebd 100644 --- a/llarp/router/abstractrouter.hpp +++ b/llarp/router/abstractrouter.hpp @@ -47,6 +47,11 @@ namespace llarp struct Context; } + namespace rpc + { + struct LokidRpcClient; + } + namespace path { struct PathContext; @@ -83,6 +88,9 @@ namespace llarp virtual LMQ_ptr lmq() const = 0; + virtual std::shared_ptr + RpcClient() const = 0; + virtual std::shared_ptr logic() const = 0; @@ -153,7 +161,7 @@ namespace llarp IsServiceNode() const = 0; virtual bool - StartJsonRpc() = 0; + StartRpcServer() = 0; virtual bool Run() = 0; diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 22932ae37..315dc5c33 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -64,7 +64,7 @@ namespace llarp #else , _randomStartDelay(std::chrono::seconds((llarp::randint() % 30) + 10)) #endif - , m_lokidRpcClient(m_lmq, this) + , m_lokidRpcClient(std::make_shared(m_lmq, this)) { m_keyManager = std::make_shared(); @@ -203,6 +203,8 @@ namespace llarp bool Router::EnsureIdentity() { + _encryption = m_keyManager->encryptionKey; + if (whitelistRouters) { #if defined(ANDROID) || defined(IOS) @@ -214,10 +216,14 @@ namespace llarp return false; #endif #endif + const auto maybe = RpcClient()->ObtainIdentityKey(); + if (maybe.has_value()) + _identity = *maybe; + } + else + { + _identity = m_keyManager->identityKey; } - - _identity = m_keyManager->identityKey; - _encryption = m_keyManager->encryptionKey; if (_identity.IsZero()) return false; @@ -236,6 +242,23 @@ namespace llarp } _nodedb = nodedb; + // we need this first so we can start lmq to fetch keys + enableRPCServer = conf->api.m_enableRPCServer; + rpcBindAddr = conf->api.m_rpcBindAddr; + whitelistRouters = conf->lokid.whitelistRouters; + lokidRPCAddr = conf->lokid.lokidRPCAddr; + + if (not StartRpcServer()) + throw std::runtime_error("Failed to start rpc server"); + + m_lmq->start(); + + if (whitelistRouters) + { + m_lokidRpcClient->ConnectAsync(std::string_view{lokidRPCAddr}); + } + + // fetch keys if (not m_keyManager->initialize(*conf, true, isRouter)) throw std::runtime_error("KeyManager failed to initialize"); @@ -555,8 +578,6 @@ namespace llarp } // API config - enableRPCServer = conf->api.m_enableRPCServer; - rpcBindAddr = IpAddress(conf->api.m_rpcBindAddr); // TODO: make this an IpAddress in config if (not IsServiceNode()) { hiddenServiceContext().AddEndpoint(*conf); @@ -818,18 +839,19 @@ namespace llarp } bool - Router::StartJsonRpc() + Router::StartRpcServer() { if (_running || _stopping) return false; if (enableRPCServer) { - if (rpcBindAddr.isEmpty()) + if (rpcBindAddr.empty()) { rpcBindAddr = DefaultRPCBindAddr; } - LogInfo("Bound RPC server to ", rpcBindAddr); + // TODO: set up rpc server + // LogInfo("Bound RPC server to ", rpcBindAddr); } return true; @@ -841,13 +863,6 @@ namespace llarp if (_running || _stopping) return false; - if (whitelistRouters) - { - m_lokidRpcClient.ConnectAsync(std::string_view{lokidRPCAddr}); - } - - m_lmq->start(); - if (!cryptoworker->start()) { LogError("crypto worker failed to start"); diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index 2b21fa07d..c1e8f8c61 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -81,6 +81,12 @@ namespace llarp return m_lmq; } + std::shared_ptr + RpcClient() const override + { + return m_lokidRpcClient; + } + std::shared_ptr logic() const override { @@ -250,12 +256,12 @@ namespace llarp NetworkConfig networkConfig; DnsConfig dnsConfig; - const IpAddress DefaultRPCBindAddr = IpAddress("127.0.0.1:1190"); + const std::string DefaultRPCBindAddr = "127.0.0.1:1190"; bool enableRPCServer = false; - IpAddress rpcBindAddr = DefaultRPCBindAddr; + std::string rpcBindAddr = DefaultRPCBindAddr; const llarp_time_t _randomStartDelay; - rpc::LokidRpcClient m_lokidRpcClient; + std::shared_ptr m_lokidRpcClient; std::string lokidRPCAddr = "ipc://loki.sock"; std::string lokidRPCUser; @@ -337,7 +343,7 @@ namespace llarp Configure(Config* conf, bool isRouter, llarp_nodedb* nodedb = nullptr) override; bool - StartJsonRpc() override; + StartRpcServer() override; bool Run() override; diff --git a/llarp/rpc/lokid_rpc_client.cpp b/llarp/rpc/lokid_rpc_client.cpp index 6eca11c7e..acf134248 100644 --- a/llarp/rpc/lokid_rpc_client.cpp +++ b/llarp/rpc/lokid_rpc_client.cpp @@ -61,52 +61,53 @@ namespace llarp m_lokiMQ->send(*m_Connection, std::move(cmd)); } + void + LokidRpcClient::UpdateServiceNodeList() + { + nlohmann::json request; + request["pubkey_ed25519"] = true; + request["active_only"] = true; + if (not m_CurrentBlockHash.empty()) + request["poll_block_hash"] = m_CurrentBlockHash; + Request( + "rpc.get_service_nodes", + [self = shared_from_this()](bool success, std::vector data) { + if (not success) + { + LogWarn("failed to update service node list"); + return; + } + if (data.size() < 2) + { + LogWarn("lokid gave empty reply for service node list"); + return; + } + try + { + self->HandleGotServiceNodeList(std::move(data[1])); + } + catch (std::exception& ex) + { + LogError("failed to process service node list: ", ex.what()); + } + }, + request.dump()); + } + void LokidRpcClient::Connected() { constexpr auto PingInterval = 1min; - constexpr auto NodeListUpdateInterval = 90s; + constexpr auto NodeListUpdateInterval = 30s; LogInfo("we connected to lokid [", *m_Connection, "]"); + Command("admin.lokinet_ping"); m_lokiMQ->add_timer( - [self = shared_from_this()]() { - LogInfo("Telling lokid we are live"); - self->Command("rpc.lokinet_ping"); - }, - PingInterval); + [self = shared_from_this()]() { self->Command("admin.lokinet_ping"); }, PingInterval); + UpdateServiceNodeList(); m_lokiMQ->add_timer( - [self = shared_from_this()]() { - nlohmann::json request; - request["pubkey_ed25519"] = true; - request["active_only"] = true; - if (not self->m_CurrentBlockHash.empty()) - request["poll_block_hash"] = self->m_CurrentBlockHash; - self->Request( - "rpc.get_service_nodes", - [self](bool success, std::vector data) { - if (not success) - { - LogWarn("failed to update service node list"); - return; - } - if (data.size() < 2) - { - LogWarn("lokid gave empty reply for service node list"); - return; - } - try - { - self->HandleGotServiceNodeList(std::move(data[1])); - } - catch (std::exception& ex) - { - LogError("failed to process service node list: ", ex.what()); - } - }, - request.dump()); - }, - NodeListUpdateInterval); + [self = shared_from_this()]() { self->UpdateServiceNodeList(); }, NodeListUpdateInterval); } void @@ -154,10 +155,49 @@ namespace llarp LogWarn("got empty service node list from lokid"); return; } - // inform router about the new list LogicCall(m_Router->logic(), [r = m_Router, nodeList]() { r->SetRouterWhitelist(nodeList); }); } + std::optional + LokidRpcClient::ObtainIdentityKey() + { + std::promise> promise; + + Request( + "admin.get_service_privkeys", + [self = shared_from_this(), &promise](bool success, std::vector data) { + if (not success) + { + LogError("failed to get private key"); + promise.set_value(std::nullopt); + return; + } + if (data.size() < 2) + { + LogError("failed to get private key, no response"); + promise.set_value(std::nullopt); + return; + } + try + { + auto j = nlohmann::json::parse(data[1]); + SecretKey k; + if (not k.FromHex(j.at("service_node_ed25519_privkey").get())) + { + promise.set_value(std::nullopt); + return; + } + promise.set_value(k); + } + catch (std::exception& ex) + { + LogError("failed to get private key: ", ex.what()); + promise.set_value(std::nullopt); + } + }); + return promise.get_future().get(); + } + } // namespace rpc } // namespace llarp diff --git a/llarp/rpc/lokid_rpc_client.hpp b/llarp/rpc/lokid_rpc_client.hpp index 296646d92..f5c4daaa5 100644 --- a/llarp/rpc/lokid_rpc_client.hpp +++ b/llarp/rpc/lokid_rpc_client.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace llarp { @@ -20,6 +21,10 @@ namespace llarp void ConnectAsync(std::string_view url); + /// blocking request identity key from lokid + std::optional + ObtainIdentityKey(); + private: /// called when we have connected to lokid via lokimq void @@ -29,6 +34,9 @@ namespace llarp void Command(std::string_view cmd); + void + UpdateServiceNodeList(); + template void Request(std::string_view cmd, HandlerFunc_t func, const Args_t& args) @@ -36,6 +44,13 @@ namespace llarp m_lokiMQ->request(*m_Connection, std::move(cmd), std::move(func), args); } + template + void + Request(std::string_view cmd, HandlerFunc_t func) + { + m_lokiMQ->request(*m_Connection, std::move(cmd), std::move(func)); + } + void HandleGotServiceNodeList(std::string json); diff --git a/llarp/service/protocol.cpp b/llarp/service/protocol.cpp index 59876a81d..0e8bb9b35 100644 --- a/llarp/service/protocol.cpp +++ b/llarp/service/protocol.cpp @@ -388,7 +388,8 @@ namespace llarp // we need to dh auto dh = new AsyncFrameDecrypt(logic, localIdent, handler, msg, *this, recvPath->intro); dh->path = recvPath; - return worker->addJob(std::bind(&AsyncFrameDecrypt::Work, dh)); + worker->addJob(std::bind(&AsyncFrameDecrypt::Work, dh)); + return true; } auto v = new AsyncDecrypt(); @@ -407,7 +408,7 @@ namespace llarp return false; } v->frame = *this; - return worker->addJob([v, msg = std::move(msg), recvPath = std::move(recvPath)]() { + worker->addJob([v, msg = std::move(msg), recvPath = std::move(recvPath)]() { if (not v->frame.Verify(v->si)) { LogError("Signature failure from ", v->si.Addr()); @@ -427,6 +428,7 @@ namespace llarp msg->handler->QueueRecvData(std::move(ev)); delete v; }); + return true; } bool