diff --git a/CMakeLists.txt b/CMakeLists.txt index 8cf5e5d44..979a5801e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -510,8 +510,12 @@ set(LIB_SRC llarp/dht/bucket.cpp llarp/dht/context.cpp llarp/dht/dht.cpp + llarp/dht/explorenetworkjob.cpp llarp/dht/kademlia.cpp llarp/dht/key.cpp + llarp/dht/localtaglookup.cpp + llarp/dht/localrouterlookup.cpp + llarp/dht/localserviceaddresslookup.cpp llarp/dht/message.cpp llarp/dht/messages/findintro.cpp llarp/dht/messages/findrouter.cpp @@ -519,6 +523,12 @@ set(LIB_SRC llarp/dht/messages/gotrouter.cpp llarp/dht/messages/pubintro.cpp llarp/dht/node.cpp + llarp/dht/publishservicejob.cpp + llarp/dht/recursiverouterlookup.cpp + llarp/dht/serviceaddresslookup.cpp + llarp/dht/taglookup.cpp + llarp/dht/tx.cpp + llarp/dht/txholder.cpp llarp/dht/txowner.cpp llarp/dns.cpp llarp/dnsc.cpp @@ -612,13 +622,16 @@ set(DNS_SRC set(TEST_SRC # helpers test/main.cpp + test/dht/mock_context.cpp test/test_util.cpp # 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_explorenetworkjob.cpp test/dht/test_llarp_dht_kademlia.cpp test/dht/test_llarp_dht_key.cpp + test/dht/test_llarp_dht_tx.cpp test/dht/test_llarp_dht_txowner.cpp test/dns/test_llarp_dns_dns.cpp test/exit/test_llarp_exit_context.cpp diff --git a/daemon/main.cpp b/daemon/main.cpp index feb5f773a..b2dae2afa 100644 --- a/daemon/main.cpp +++ b/daemon/main.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -56,6 +57,22 @@ handle_signal_win32(DWORD fdwCtrlType) } #endif +/// resolve ~ and symlinks into actual paths (so we know the real path on disk, +/// to remove assumptions and confusion with permissions) +std::string +resolvePath(std::string conffname) +{ + wordexp_t exp_result; + wordexp(conffname.c_str(), &exp_result, 0); + char *resolvedPath = realpath(exp_result.we_wordv[0], NULL); + if(!resolvedPath) + { + llarp::LogWarn("Can't resolve path: ", exp_result.we_wordv[0]); + return ""; + } + return resolvedPath; +} + int main(int argc, char *argv[]) { @@ -108,7 +125,7 @@ main(int argc, char *argv[]) } } - std::string conffname; + std::string conffname; // suggestions: confFName? conf_fname? if(optind < argc) { @@ -116,15 +133,33 @@ main(int argc, char *argv[]) fs::path fname = fs::path(argv[optind]); fs::path basedir = fname.parent_path(); conffname = fname.string(); + conffname = resolvePath(conffname); + std::error_code ec; + + // llarp::LogDebug("Basedir: ", basedir); if(basedir.string().empty()) { - if(!llarp_ensure_config(fname.string().c_str(), nullptr, overWrite, - asRouter)) - return 1; + // relative path to config + + // does this file exist? + if(genconfigOnly) + { + if(!llarp_ensure_config(conffname.c_str(), nullptr, overWrite, + asRouter)) + return 1; + } + else + { + if(!fs::exists(fname, ec)) + { + llarp::LogError("Config file not found ", conffname); + return 1; + } + } } else { - std::error_code ec; + // absolute path to config if(!fs::create_directories(basedir, ec)) { if(ec) @@ -134,9 +169,22 @@ main(int argc, char *argv[]) return 1; } } - if(!llarp_ensure_config(fname.string().c_str(), basedir.string().c_str(), - overWrite, asRouter)) - return 1; + if(genconfigOnly) + { + // find or create file + if(!llarp_ensure_config(conffname.c_str(), basedir.string().c_str(), + overWrite, asRouter)) + return 1; + } + else + { + // does this file exist? + if(!fs::exists(conffname, ec)) + { + llarp::LogError("Config file not found ", conffname); + return 1; + } + } } } else @@ -149,6 +197,8 @@ main(int argc, char *argv[]) #endif fs::path basepath = homedir / fs::path(".lokinet"); fs::path fpath = basepath / "lokinet.ini"; + // I don't think this is necessary with this condition + // conffname = resolvePath(conffname); llarp::LogDebug("Find or create ", basepath.string()); std::error_code ec; @@ -164,6 +214,7 @@ main(int argc, char *argv[]) } } + // if using default INI file, we're create it even if you don't ask us too if(!llarp_ensure_config(fpath.string().c_str(), basepath.string().c_str(), overWrite, asRouter)) return 1; @@ -176,6 +227,7 @@ main(int argc, char *argv[]) } // this is important, can downgrade from Info though + llarp::LogInfo("Running from: ", cpp17::filesystem::current_path()); llarp::LogInfo("Using config file: ", conffname); ctx = llarp_main_init(conffname.c_str(), multiThreaded); int code = 1; diff --git a/llarp/context.cpp b/llarp/context.cpp index 4af4d6897..406a4e930 100644 --- a/llarp/context.cpp +++ b/llarp/context.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -97,8 +98,7 @@ namespace llarp int Context::LoadDatabase() { - crypto = std::unique_ptr< llarp::Crypto >( - new llarp::Crypto{llarp::Crypto::sodium{}}); + crypto = std::make_unique< sodium::CryptoLibSodium >(); nodedb = new llarp_nodedb(crypto.get(), router->disk); if(!llarp_nodedb::ensure_dir(nodedb_dir.c_str())) diff --git a/llarp/crypto/crypto.hpp b/llarp/crypto/crypto.hpp index 0b96f36a0..a4c3b659a 100644 --- a/llarp/crypto/crypto.hpp +++ b/llarp/crypto/crypto.hpp @@ -19,8 +19,6 @@ namespace llarp { - /// label functors - /// PKE(result, publickey, secretkey, nonce) using path_dh_func = std::function< bool( SharedSecret &, const PubKey &, const SecretKey &, const TunnelNonce &) >; @@ -29,88 +27,83 @@ namespace llarp using transport_dh_func = std::function< bool( SharedSecret &, const PubKey &, const SecretKey &, const TunnelNonce &) >; - /// SD/SE(buffer, key, nonce) - using sym_cipher_func = std::function< bool( - llarp_buffer_t, const SharedSecret &, const TunnelNonce &) >; - - /// SD/SE(dst, src, key, nonce) - using sym_cipher_alt_func = std::function< bool( - llarp_buffer_t, llarp_buffer_t, const SharedSecret &, const byte_t *) >; - - /// H(result, body) - using hash_func = std::function< bool(byte_t *, llarp_buffer_t) >; - /// SH(result, body) using shorthash_func = std::function< bool(ShortHash &, llarp_buffer_t) >; - /// MDS(result, body, shared_secret) - using hmac_func = - std::function< bool(byte_t *, llarp_buffer_t, const SharedSecret &) >; - - /// S(sig, secretkey, body) - using sign_func = - std::function< bool(Signature &, const SecretKey &, llarp_buffer_t) >; - - /// V(pubkey, body, sig) - using verify_func = - std::function< bool(const PubKey &, llarp_buffer_t, const Signature &) >; - - /// converts seed to secretkey - using seed_to_secret_func = - std::function< bool(llarp::SecretKey &, const llarp::IdentitySecret &) >; - /// library crypto configuration struct Crypto { + virtual ~Crypto() = 0; + /// xchacha symmetric cipher - sym_cipher_func xchacha20; + virtual bool + xchacha20(llarp_buffer_t, const SharedSecret &, const TunnelNonce &) = 0; + /// xchacha symmetric cipher (multibuffer) - sym_cipher_alt_func xchacha20_alt; + virtual bool + xchacha20_alt(llarp_buffer_t, llarp_buffer_t, const SharedSecret &, + const byte_t *) = 0; + /// path dh creator's side - path_dh_func dh_client; + virtual bool + dh_client(SharedSecret &, const PubKey &, const SecretKey &, + const TunnelNonce &) = 0; /// path dh relay side - path_dh_func dh_server; + virtual bool + dh_server(SharedSecret &, const PubKey &, const SecretKey &, + const TunnelNonce &) = 0; /// transport dh client side - transport_dh_func transport_dh_client; + virtual bool + transport_dh_client(SharedSecret &, const PubKey &, const SecretKey &, + const TunnelNonce &) = 0; /// transport dh server side - transport_dh_func transport_dh_server; + virtual bool + transport_dh_server(SharedSecret &, const PubKey &, const SecretKey &, + const TunnelNonce &) = 0; /// blake2b 512 bit - hash_func hash; + virtual bool + hash(byte_t *, llarp_buffer_t) = 0; /// blake2b 256 bit - shorthash_func shorthash; + virtual bool + shorthash(ShortHash &, llarp_buffer_t) = 0; /// blake2s 256 bit hmac - hmac_func hmac; + virtual bool + hmac(byte_t *, llarp_buffer_t, const SharedSecret &) = 0; /// ed25519 sign - sign_func sign; + virtual bool + sign(Signature &, const SecretKey &, llarp_buffer_t) = 0; /// ed25519 verify - verify_func verify; + virtual bool + verify(const PubKey &, llarp_buffer_t, const Signature &) = 0; /// seed to secretkey - seed_to_secret_func seed_to_secretkey; + virtual bool + seed_to_secretkey(llarp::SecretKey &, const llarp::IdentitySecret &) = 0; /// randomize buffer - std::function< void(llarp_buffer_t) > randomize; + virtual void randomize(llarp_buffer_t) = 0; /// randomizer memory - std::function< void(void *, size_t) > randbytes; + virtual void + randbytes(void *, size_t) = 0; /// generate signing keypair - std::function< void(SecretKey &) > identity_keygen; + virtual void + identity_keygen(SecretKey &) = 0; /// generate encryption keypair - std::function< void(SecretKey &) > encryption_keygen; + virtual void + encryption_keygen(SecretKey &) = 0; /// generate post quantum encrytion key - std::function< void(PQKeyPair &) > pqe_keygen; + virtual void + pqe_keygen(PQKeyPair &) = 0; /// post quantum decrypt (buffer, sharedkey_dst, sec) - std::function< bool(const PQCipherBlock &, SharedSecret &, const byte_t *) > - pqe_decrypt; + virtual bool + pqe_decrypt(const PQCipherBlock &, SharedSecret &, const byte_t *) = 0; /// post quantum encrypt (buffer, sharedkey_dst, pub) - std::function< bool(PQCipherBlock &, SharedSecret &, const PQPubKey &) > - pqe_encrypt; - - // Give a basic type tag for the constructor to pick libsodium - struct sodium - { - }; - - Crypto(Crypto::sodium tag); + virtual bool + pqe_encrypt(PQCipherBlock &, SharedSecret &, const PQPubKey &) = 0; }; + inline Crypto::~Crypto() + { + } + /// return random 64bit unsigned interger uint64_t randint(); diff --git a/llarp/crypto/crypto_libsodium.cpp b/llarp/crypto/crypto_libsodium.cpp index 4b07073f6..6d5ddc683 100644 --- a/llarp/crypto/crypto_libsodium.cpp +++ b/llarp/crypto/crypto_libsodium.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -18,24 +18,6 @@ namespace llarp { namespace sodium { - static bool - xchacha20(llarp_buffer_t buff, const SharedSecret &k, const TunnelNonce &n) - { - return crypto_stream_xchacha20_xor(buff.base, buff.base, buff.sz, - n.data(), k.data()) - == 0; - } - - static bool - xchacha20_alt(llarp_buffer_t out, llarp_buffer_t in, const SharedSecret &k, - const byte_t *n) - { - if(in.sz > out.sz) - return false; - return crypto_stream_xchacha20_xor(out.base, in.base, in.sz, n, k.data()) - == 0; - } - static bool dh(llarp::SharedSecret &out, const PubKey &client_pk, const PubKey &server_pk, const uint8_t *themPub, const SecretKey &usSec) @@ -44,7 +26,9 @@ namespace llarp crypto_generichash_state h; if(crypto_scalarmult_curve25519(shared.data(), usSec.data(), themPub)) + { return false; + } crypto_generichash_blake2b_init(&h, nullptr, 0U, shared.size()); crypto_generichash_blake2b_update(&h, client_pk.data(), 32); crypto_generichash_blake2b_update(&h, server_pk.data(), 32); @@ -54,8 +38,8 @@ namespace llarp } static bool - dh_client(llarp::SharedSecret &shared, const PubKey &pk, - const SecretKey &sk, const TunnelNonce &n) + dh_client_priv(llarp::SharedSecret &shared, const PubKey &pk, + const SecretKey &sk, const TunnelNonce &n) { llarp::SharedSecret dh_result; @@ -70,8 +54,8 @@ namespace llarp } static bool - dh_server(llarp::SharedSecret &shared, const PubKey &pk, - const SecretKey &sk, const TunnelNonce &n) + dh_server_priv(llarp::SharedSecret &shared, const PubKey &pk, + const SecretKey &sk, const TunnelNonce &n) { llarp::SharedSecret dh_result; if(dh(dh_result, pk, sk.toPublic(), pk.data(), sk)) @@ -84,113 +68,184 @@ namespace llarp return false; } - static bool - hash(uint8_t *result, llarp_buffer_t buff) + CryptoLibSodium::CryptoLibSodium() + { + if(sodium_init() == -1) + { + throw std::runtime_error("sodium_init() returned -1"); + } + char *avx2 = std::getenv("AVX2_FORCE_DISABLE"); + if(avx2 && std::string(avx2) == "1") + { + ntru_init(1); + } + else + { + ntru_init(0); + } + int seed = 0; + this->randbytes(&seed, sizeof(seed)); + srand(seed); + } + + bool + CryptoLibSodium::xchacha20(llarp_buffer_t buff, const SharedSecret &k, + const TunnelNonce &n) + { + return crypto_stream_xchacha20_xor(buff.base, buff.base, buff.sz, + n.data(), k.data()) + == 0; + } + + bool + CryptoLibSodium::xchacha20_alt(llarp_buffer_t out, llarp_buffer_t in, + const SharedSecret &k, const byte_t *n) + { + if(in.sz > out.sz) + return false; + return crypto_stream_xchacha20_xor(out.base, in.base, in.sz, n, k.data()) + == 0; + } + + bool + CryptoLibSodium::dh_client(llarp::SharedSecret &shared, const PubKey &pk, + const SecretKey &sk, const TunnelNonce &n) + { + return dh_client_priv(shared, pk, sk, n); + } + /// path dh relay side + bool + CryptoLibSodium::dh_server(llarp::SharedSecret &shared, const PubKey &pk, + const SecretKey &sk, const TunnelNonce &n) + { + return dh_server_priv(shared, pk, sk, n); + } + /// transport dh client side + bool + CryptoLibSodium::transport_dh_client(llarp::SharedSecret &shared, + const PubKey &pk, const SecretKey &sk, + const TunnelNonce &n) + { + return dh_client_priv(shared, pk, sk, n); + } + /// transport dh server side + bool + CryptoLibSodium::transport_dh_server(llarp::SharedSecret &shared, + const PubKey &pk, const SecretKey &sk, + const TunnelNonce &n) + { + return dh_server_priv(shared, pk, sk, n); + } + + bool + CryptoLibSodium::hash(uint8_t *result, llarp_buffer_t buff) { return crypto_generichash_blake2b(result, HASHSIZE, buff.base, buff.sz, nullptr, 0) != -1; } - static bool - shorthash(ShortHash &result, llarp_buffer_t buff) + bool + CryptoLibSodium::shorthash(ShortHash &result, llarp_buffer_t buff) { return crypto_generichash_blake2b(result.data(), ShortHash::SIZE, buff.base, buff.sz, nullptr, 0) != -1; } - static bool - hmac(byte_t *result, llarp_buffer_t buff, const SharedSecret &secret) + bool + CryptoLibSodium::hmac(byte_t *result, llarp_buffer_t buff, + const SharedSecret &secret) { return crypto_generichash_blake2b(result, HMACSIZE, buff.base, buff.sz, secret.data(), HMACSECSIZE) != -1; } - static bool - sign(Signature &result, const SecretKey &secret, llarp_buffer_t buff) + bool + CryptoLibSodium::sign(Signature &result, const SecretKey &secret, + llarp_buffer_t buff) { int rc = crypto_sign_detached(result.data(), nullptr, buff.base, buff.sz, secret.data()); return rc != -1; } - static bool - verify(const PubKey &pub, llarp_buffer_t buff, const Signature &sig) + bool + CryptoLibSodium::verify(const PubKey &pub, llarp_buffer_t buff, + const Signature &sig) { int rc = crypto_sign_verify_detached(sig.data(), buff.base, buff.sz, pub.data()); return rc != -1; } - static bool - seed_to_secretkey(llarp::SecretKey &secret, - const llarp::IdentitySecret &seed) + bool + CryptoLibSodium::seed_to_secretkey(llarp::SecretKey &secret, + const llarp::IdentitySecret &seed) { byte_t pk[crypto_sign_ed25519_PUBLICKEYBYTES]; return crypto_sign_ed25519_seed_keypair(pk, secret.data(), seed.data()) != -1; } - static void - randomize(llarp_buffer_t buff) + void + CryptoLibSodium::randomize(llarp_buffer_t buff) { randombytes((unsigned char *)buff.base, buff.sz); } - static inline void - randbytes(void *ptr, size_t sz) + void + CryptoLibSodium::randbytes(void *ptr, size_t sz) { randombytes((unsigned char *)ptr, sz); } - static void - sigkeygen(llarp::SecretKey &keys) + void + CryptoLibSodium::identity_keygen(llarp::SecretKey &keys) { byte_t *d = keys.data(); crypto_sign_keypair(d + 32, d); } - static void - enckeygen(llarp::SecretKey &keys) + void + CryptoLibSodium::encryption_keygen(llarp::SecretKey &keys) { auto d = keys.data(); randombytes(d, 32); crypto_scalarmult_curve25519_base(d + 32, d); } - } // namespace sodium - - const byte_t * - seckey_topublic(const SecretKey &sec) - { - return sec.data() + 32; - } - namespace pq - { bool - encrypt(PQCipherBlock &ciphertext, SharedSecret &sharedkey, - const PQPubKey &pubkey) + CryptoLibSodium::pqe_encrypt(PQCipherBlock &ciphertext, + SharedSecret &sharedkey, + const PQPubKey &pubkey) { return crypto_kem_enc(ciphertext.data(), sharedkey.data(), pubkey.data()) != -1; } bool - decrypt(const PQCipherBlock &ciphertext, SharedSecret &sharedkey, - const byte_t *secretkey) + CryptoLibSodium::pqe_decrypt(const PQCipherBlock &ciphertext, + SharedSecret &sharedkey, + const byte_t *secretkey) { return crypto_kem_dec(sharedkey.data(), ciphertext.data(), secretkey) != -1; } void - keygen(PQKeyPair &keypair) + CryptoLibSodium::pqe_keygen(PQKeyPair &keypair) { auto d = keypair.data(); crypto_kem_keypair(d + PQ_SECRETKEYSIZE, d); } - } // namespace pq + } // namespace sodium + + const byte_t * + seckey_topublic(const SecretKey &sec) + { + return sec.data() + 32; + } const byte_t * pq_keypair_to_public(const PQKeyPair &k) @@ -204,40 +259,6 @@ namespace llarp return k.data(); } - Crypto::Crypto(Crypto::sodium tag) - { - (void)tag; - if(sodium_init() == -1) - throw std::runtime_error("sodium_init() returned -1"); - char *avx2 = std::getenv("AVX2_FORCE_DISABLE"); - if(avx2 && std::string(avx2) == "1") - ntru_init(1); - else - ntru_init(0); - this->xchacha20 = llarp::sodium::xchacha20; - this->xchacha20_alt = llarp::sodium::xchacha20_alt; - this->dh_client = llarp::sodium::dh_client; - this->dh_server = llarp::sodium::dh_server; - this->transport_dh_client = llarp::sodium::dh_client; - this->transport_dh_server = llarp::sodium::dh_server; - this->hash = llarp::sodium::hash; - this->shorthash = llarp::sodium::shorthash; - this->hmac = llarp::sodium::hmac; - this->sign = llarp::sodium::sign; - this->verify = llarp::sodium::verify; - this->randomize = llarp::sodium::randomize; - this->randbytes = llarp::sodium::randbytes; - this->identity_keygen = llarp::sodium::sigkeygen; - this->encryption_keygen = llarp::sodium::enckeygen; - this->seed_to_secretkey = llarp::sodium::seed_to_secretkey; - this->pqe_encrypt = llarp::pq::encrypt; - this->pqe_decrypt = llarp::pq::decrypt; - this->pqe_keygen = llarp::pq::keygen; - int seed = 0; - this->randbytes(&seed, sizeof(seed)); - srand(seed); - } - uint64_t randint() { diff --git a/llarp/crypto/crypto_libsodium.hpp b/llarp/crypto/crypto_libsodium.hpp new file mode 100644 index 000000000..57e79c737 --- /dev/null +++ b/llarp/crypto/crypto_libsodium.hpp @@ -0,0 +1,89 @@ +#ifndef LLARP_CRYPTO_LIBSODIUM_HPP +#define LLARP_CRYPTO_LIBSODIUM_HPP + +#include + +namespace llarp +{ + namespace sodium + { + struct CryptoLibSodium final : public Crypto + { + CryptoLibSodium(); + + ~CryptoLibSodium() + { + } + + /// xchacha symmetric cipher + bool + xchacha20(llarp_buffer_t, const SharedSecret &, + const TunnelNonce &) override; + + /// xchacha symmetric cipher (multibuffer) + bool + xchacha20_alt(llarp_buffer_t, llarp_buffer_t, const SharedSecret &, + const byte_t *) override; + + /// path dh creator's side + bool + dh_client(SharedSecret &, const PubKey &, const SecretKey &, + const TunnelNonce &) override; + /// path dh relay side + bool + dh_server(SharedSecret &, const PubKey &, const SecretKey &, + const TunnelNonce &) override; + /// transport dh client side + bool + transport_dh_client(SharedSecret &, const PubKey &, const SecretKey &, + const TunnelNonce &) override; + /// transport dh server side + bool + transport_dh_server(SharedSecret &, const PubKey &, const SecretKey &, + const TunnelNonce &) override; + /// blake2b 512 bit + bool + hash(byte_t *, llarp_buffer_t) override; + /// blake2b 256 bit + bool + shorthash(ShortHash &, llarp_buffer_t) override; + /// blake2s 256 bit hmac + bool + hmac(byte_t *, llarp_buffer_t, const SharedSecret &) override; + /// ed25519 sign + bool + sign(Signature &, const SecretKey &, llarp_buffer_t) override; + /// ed25519 verify + bool + verify(const PubKey &, llarp_buffer_t, const Signature &) override; + /// seed to secretkey + bool + seed_to_secretkey(llarp::SecretKey &, + const llarp::IdentitySecret &) override; + /// randomize buffer + void randomize(llarp_buffer_t) override; + /// randomizer memory + void + randbytes(void *, size_t) override; + /// generate signing keypair + void + identity_keygen(SecretKey &) override; + /// generate encryption keypair + void + encryption_keygen(SecretKey &) override; + /// generate post quantum encrytion key + void + pqe_keygen(PQKeyPair &) override; + /// post quantum decrypt (buffer, sharedkey_dst, sec) + bool + pqe_decrypt(const PQCipherBlock &, SharedSecret &, + const byte_t *) override; + /// post quantum encrypt (buffer, sharedkey_dst, pub) + bool + pqe_encrypt(PQCipherBlock &, SharedSecret &, const PQPubKey &) override; + }; + } // namespace sodium + +} // namespace llarp + +#endif diff --git a/llarp/crypto/encrypted_frame.cpp b/llarp/crypto/encrypted_frame.cpp index 666ec7b26..0558c3e27 100644 --- a/llarp/crypto/encrypted_frame.cpp +++ b/llarp/crypto/encrypted_frame.cpp @@ -24,10 +24,6 @@ namespace llarp SharedSecret shared; - auto DH = crypto->dh_client; - auto Encrypt = crypto->xchacha20; - auto MDS = crypto->hmac; - llarp_buffer_t buf; buf.base = body; buf.cur = buf.base; @@ -40,14 +36,14 @@ namespace llarp TunnelNonce nonce(noncePtr); // derive shared key - if(!DH(shared, otherPubkey, ourSecretKey, nonce)) + if(!crypto->dh_client(shared, otherPubkey, ourSecretKey, nonce)) { llarp::LogError("DH failed"); return false; } // encrypt body - if(!Encrypt(buf, shared, nonce)) + if(!crypto->xchacha20(buf, shared, nonce)) { llarp::LogError("encrypt failed"); return false; @@ -58,7 +54,7 @@ namespace llarp buf.cur = buf.base; buf.sz = size() - SHORTHASHSIZE; - if(!MDS(hash, buf, shared)) + if(!crypto->hmac(hash, buf, shared)) { llarp::LogError("Failed to generate message auth"); return false; @@ -82,14 +78,10 @@ namespace llarp TunnelNonce nonce(noncePtr); PubKey otherPubkey(noncePtr + TUNNONCESIZE); - // use dh_server because we are not the creator of this message - auto DH = crypto->dh_server; - auto Decrypt = crypto->xchacha20; - auto MDS = crypto->hmac; - SharedSecret shared; - if(!DH(shared, otherPubkey, ourSecretKey, nonce)) + // use dh_server because we are not the creator of this message + if(!crypto->dh_server(shared, otherPubkey, ourSecretKey, nonce)) { llarp::LogError("DH failed"); return false; @@ -101,7 +93,7 @@ namespace llarp buf.sz = size() - SHORTHASHSIZE; ShortHash digest; - if(!MDS(digest.data(), buf, shared)) + if(!crypto->hmac(digest.data(), buf, shared)) { llarp::LogError("Digest failed"); return false; @@ -117,7 +109,7 @@ namespace llarp buf.cur = body; buf.sz = size() - EncryptedFrameOverheadSize; - if(!Decrypt(buf, shared, nonce)) + if(!crypto->xchacha20(buf, shared, nonce)) { llarp::LogError("decrypt failed"); return false; diff --git a/llarp/crypto/types.hpp b/llarp/crypto/types.hpp index 90090ac42..2a47847d3 100644 --- a/llarp/crypto/types.hpp +++ b/llarp/crypto/types.hpp @@ -58,6 +58,24 @@ namespace llarp } }; + inline bool + operator==(const PubKey &lhs, const PubKey &rhs) + { + return lhs.as_array() == rhs.as_array(); + } + + inline bool + operator==(const PubKey &lhs, const RouterID &rhs) + { + return lhs.as_array() == rhs.as_array(); + } + + inline bool + operator==(const RouterID &lhs, const PubKey &rhs) + { + return lhs.as_array() == rhs.as_array(); + } + struct SecretKey final : public AlignedBuffer< SECKEYSIZE > { SecretKey() : AlignedBuffer< SECKEYSIZE >(){}; diff --git a/llarp/dht/context.cpp b/llarp/dht/context.cpp index 5b488372d..020633e42 100644 --- a/llarp/dht/context.cpp +++ b/llarp/dht/context.cpp @@ -1,10 +1,18 @@ #include +#include +#include +#include +#include #include #include #include #include #include +#include +#include +#include +#include #include #include #include @@ -15,18 +23,11 @@ namespace llarp { namespace dht { - Context::Context() - { - randombytes((byte_t *)&ids, sizeof(uint64_t)); - allowTransit = false; - } + AbstractContext::~AbstractContext() {} - Context::~Context() + Context::Context() : router(nullptr), allowTransit(false) { - if(nodes) - delete nodes; - if(services) - delete services; + randombytes((byte_t *)&ids, sizeof(uint64_t)); } void @@ -45,53 +46,6 @@ namespace llarp llarp::LogError("failed to select random nodes for exploration"); } - struct ExploreNetworkJob : public TX< RouterID, RouterID > - { - ExploreNetworkJob(const RouterID &peer, Context *ctx) - : TX< RouterID, RouterID >(TXOwner{}, peer, ctx) - { - } - - bool - Validate(const RouterID &) const override - { - // TODO: check with lokid - return true; - } - - void - Start(const TXOwner &peer) override - { - parent->DHTSendTo(peer.node.as_array(), - new FindRouterMessage(peer.txid)); - } - - bool - GetNextPeer(Key_t &, const std::set< Key_t > &) override - { - return false; - } - - void - DoNextRequest(const Key_t &) override - { - } - - void - SendReply() override - { - llarp::LogInfo("got ", valuesFound.size(), " routers from exploration"); - for(const auto &pk : valuesFound) - { - // lookup router - parent->LookupRouter( - pk, - std::bind(&llarp::Router::HandleDHTLookupForExplore, - parent->router, pk, std::placeholders::_1)); - } - } - }; - void Context::ExploreNetworkVia(const Key_t &askpeer) { @@ -272,8 +226,8 @@ namespace llarp { router = r; ourKey = us; - nodes = new Bucket< RCNode >(ourKey, llarp::randint); - services = new Bucket< ISNode >(ourKey, llarp::randint); + nodes = std::make_unique< Bucket< RCNode > >(ourKey, llarp::randint); + services = std::make_unique< Bucket< ISNode > >(ourKey, llarp::randint); llarp::LogDebug("initialize dht with key ", ourKey); // start exploring @@ -308,7 +262,7 @@ namespace llarp llarp::routing::DHTMessage reply; if(!msg->HandleMessage(router->dht, reply.M)) return false; - if(reply.M.size()) + if(!reply.M.empty()) { auto path = router->paths.GetByUpstream(router->pubkey(), id); return path && path->SendRoutingMessage(&reply, router); @@ -316,112 +270,6 @@ namespace llarp return true; } - struct ServiceAddressLookup - : public TX< service::Address, service::IntroSet > - { - IntroSetLookupHandler handleResult; - uint64_t R; - - ServiceAddressLookup(const TXOwner &asker, const service::Address &addr, - Context *ctx, uint64_t r, - IntroSetLookupHandler handler) - : TX< service::Address, service::IntroSet >(asker, addr, ctx) - , handleResult(handler) - , R(r) - { - peersAsked.insert(ctx->OurKey()); - } - - bool - Validate(const service::IntroSet &value) const override - { - if(!value.Verify(parent->Crypto(), parent->Now())) - { - llarp::LogWarn("Got invalid introset from service lookup"); - return false; - } - if(value.A.Addr() != target) - { - llarp::LogWarn("got introset with wrong target from service lookup"); - return false; - } - return true; - } - - bool - GetNextPeer(Key_t &next, const std::set< Key_t > &exclude) override - { - Key_t k = target.ToKey(); - return parent->nodes->FindCloseExcluding(k, next, exclude); - } - - void - Start(const TXOwner &peer) override - { - parent->DHTSendTo(peer.node.as_array(), - new FindIntroMessage(peer.txid, target, R)); - } - - void - DoNextRequest(const Key_t &ask) override - { - if(R) - parent->LookupIntroSetRecursive(target, whoasked.node, whoasked.txid, - ask, R - 1); - else - parent->LookupIntroSetIterative(target, whoasked.node, whoasked.txid, - ask); - } - - virtual void - SendReply() override - { - if(handleResult) - handleResult(valuesFound); - - parent->DHTSendTo(whoasked.node.as_array(), - new GotIntroMessage(valuesFound, whoasked.txid)); - } - }; - - struct LocalServiceAddressLookup : public ServiceAddressLookup - { - PathID_t localPath; - - LocalServiceAddressLookup(const PathID_t &pathid, uint64_t txid, - const service::Address &addr, Context *ctx, - __attribute__((unused)) const Key_t &askpeer) - : ServiceAddressLookup(TXOwner{ctx->OurKey(), txid}, addr, ctx, 5, - nullptr) - , localPath(pathid) - { - } - - void - SendReply() override - { - auto path = parent->router->paths.GetByUpstream( - parent->OurKey().as_array(), localPath); - if(!path) - { - llarp::LogWarn( - "did not send reply for relayed dht request, no such local path " - "for pathid=", - localPath); - return; - } - routing::DHTMessage msg; - msg.M.emplace_back(new GotIntroMessage(valuesFound, whoasked.txid)); - if(!path->SendRoutingMessage(&msg, parent->router)) - { - llarp::LogWarn( - "failed to send routing message when informing result of dht " - "request, pathid=", - localPath); - } - } - }; - void Context::LookupIntroSetForPath(const service::Address &addr, uint64_t txid, const llarp::PathID_t &path, @@ -434,62 +282,6 @@ namespace llarp new LocalServiceAddressLookup(path, txid, addr, this, askpeer)); } - struct PublishServiceJob : public TX< service::Address, service::IntroSet > - { - uint64_t S; - std::set< Key_t > dontTell; - service::IntroSet I; - PublishServiceJob(const TXOwner &asker, const service::IntroSet &introset, - Context *ctx, uint64_t s, - const std::set< Key_t > &exclude) - : TX< service::Address, service::IntroSet >(asker, introset.A.Addr(), - ctx) - , S(s) - , dontTell(exclude) - , I(introset) - { - } - - bool - Validate(const service::IntroSet &introset) const override - { - if(I.A != introset.A) - { - llarp::LogWarn( - "publish introset acknowledgement acked a different service"); - return false; - } - return true; - } - - void - Start(const TXOwner &peer) override - { - std::vector< Key_t > exclude; - for(const auto &router : dontTell) - exclude.push_back(router); - parent->DHTSendTo(peer.node.as_array(), - new PublishIntroMessage(I, peer.txid, S, exclude)); - } - - bool - GetNextPeer(Key_t &, const std::set< Key_t > &) override - { - return false; - } - - void - DoNextRequest(const Key_t &) override - { - } - - void - SendReply() override - { - // don't need this - } - }; - void Context::PropagateIntroSetTo(const Key_t &from, uint64_t txid, const service::IntroSet &introset, @@ -508,7 +300,7 @@ namespace llarp Context::LookupIntroSetRecursive(const service::Address &addr, const Key_t &whoasked, uint64_t txid, const Key_t &askpeer, uint64_t R, - IntroSetLookupHandler handler) + service::IntroSetLookupHandler handler) { TXOwner asker(whoasked, txid); TXOwner peer(askpeer, ++ids); @@ -521,7 +313,7 @@ namespace llarp Context::LookupIntroSetIterative(const service::Address &addr, const Key_t &whoasked, uint64_t txid, const Key_t &askpeer, - IntroSetLookupHandler handler) + service::IntroSetLookupHandler handler) { TXOwner asker(whoasked, txid); TXOwner peer(askpeer, ++ids); @@ -530,76 +322,6 @@ namespace llarp new ServiceAddressLookup(asker, addr, this, 0, handler)); } - struct TagLookup : public TX< service::Tag, service::IntroSet > - { - uint64_t R; - TagLookup(const TXOwner &asker, const service::Tag &tag, Context *ctx, - uint64_t r) - : TX< service::Tag, service::IntroSet >(asker, tag, ctx), R(r) - { - } - - bool - Validate(const service::IntroSet &introset) const override - { - if(!introset.Verify(parent->Crypto(), parent->Now())) - { - llarp::LogWarn("got invalid introset from tag lookup"); - return false; - } - if(introset.topic != target) - { - llarp::LogWarn("got introset with missmatched topic in tag lookup"); - return false; - } - return true; - } - - void - Start(const TXOwner &peer) override - { - parent->DHTSendTo(peer.node.as_array(), - new FindIntroMessage(target, peer.txid, R)); - } - - bool - GetNextPeer(Key_t &, const std::set< Key_t > &) override - { - return false; - } - - void - DoNextRequest(const Key_t &) override - { - } - - void - SendReply() override - { - std::set< service::IntroSet > found; - for(const auto &remoteTag : valuesFound) - { - found.insert(remoteTag); - } - // collect our local values if we haven't hit a limit - if(found.size() < 2) - { - for(const auto &localTag : - parent->FindRandomIntroSetsWithTagExcluding(target, 1, found)) - { - found.insert(localTag); - } - } - std::vector< service::IntroSet > values; - for(const auto &introset : found) - { - values.push_back(introset); - } - parent->DHTSendTo(whoasked.node.as_array(), - new GotIntroMessage(values, whoasked.txid)); - } - }; - void Context::LookupTagRecursive(const service::Tag &tag, const Key_t &whoasked, uint64_t whoaskedTX, const Key_t &askpeer, @@ -613,42 +335,6 @@ namespace llarp " R=", R); } - struct LocalTagLookup : public TagLookup - { - PathID_t localPath; - - LocalTagLookup(const PathID_t &path, uint64_t txid, - const service::Tag &target, Context *ctx) - : TagLookup(TXOwner{ctx->OurKey(), txid}, target, ctx, 0) - , localPath(path) - { - } - - void - SendReply() override - { - auto path = parent->router->paths.GetByUpstream( - parent->OurKey().as_array(), localPath); - if(!path) - { - llarp::LogWarn( - "did not send reply for relayed dht request, no such local path " - "for pathid=", - localPath); - return; - } - routing::DHTMessage msg; - msg.M.emplace_back(new GotIntroMessage(valuesFound, whoasked.txid)); - if(!path->SendRoutingMessage(&msg, parent->router)) - { - llarp::LogWarn( - "failed to send routing message when informing result of dht " - "request, pathid=", - localPath); - } - } - }; - void Context::LookupTagForPath(const service::Tag &tag, uint64_t txid, const llarp::PathID_t &path, const Key_t &askpeer) @@ -699,101 +385,6 @@ namespace llarp return true; } - struct RecursiveRouterLookup : public TX< RouterID, RouterContact > - { - RouterLookupHandler resultHandler; - RecursiveRouterLookup(const TXOwner &whoasked, const RouterID &target, - Context *ctx, RouterLookupHandler result) - : TX< RouterID, RouterContact >(whoasked, target, ctx) - , resultHandler(result) - - { - peersAsked.insert(ctx->OurKey()); - } - - bool - Validate(const RouterContact &rc) const override - { - if(!rc.Verify(parent->Crypto(), parent->Now())) - { - llarp::LogWarn("rc from lookup result is invalid"); - return false; - } - return true; - } - - bool - GetNextPeer(Key_t &, const std::set< Key_t > &) override - { - return false; - } - - void - DoNextRequest(const Key_t &) override - { - } - - void - Start(const TXOwner &peer) override - { - parent->DHTSendTo(peer.node.as_array(), - new FindRouterMessage(peer.txid, target)); - } - - virtual void - SendReply() override - { - if(resultHandler) - { - resultHandler(valuesFound); - } - else - { - parent->DHTSendTo( - whoasked.node.as_array(), - new GotRouterMessage({}, whoasked.txid, valuesFound, false)); - } - } - }; - - struct LocalRouterLookup : public RecursiveRouterLookup - { - PathID_t localPath; - - LocalRouterLookup(const PathID_t &path, uint64_t txid, - const RouterID &target, Context *ctx) - : RecursiveRouterLookup(TXOwner{ctx->OurKey(), txid}, target, ctx, - nullptr) - , localPath(path) - { - } - - void - SendReply() override - { - auto path = parent->router->paths.GetByUpstream( - parent->OurKey().as_array(), localPath); - if(!path) - { - llarp::LogWarn( - "did not send reply for relayed dht request, no such local path " - "for pathid=", - localPath); - return; - } - routing::DHTMessage msg; - msg.M.emplace_back(new GotRouterMessage(parent->OurKey(), whoasked.txid, - valuesFound, true)); - if(!path->SendRoutingMessage(&msg, parent->router)) - { - llarp::LogWarn( - "failed to send routing message when informing result of dht " - "request, pathid=", - localPath); - } - } - }; - void Context::LookupRouterForPath(const RouterID &target, uint64_t txid, const llarp::PathID_t &path, @@ -824,13 +415,13 @@ namespace llarp } llarp::Crypto * - Context::Crypto() + Context::Crypto() const { - return &router->crypto; + return router->crypto.get(); } llarp_time_t - Context::Now() + Context::Now() const { return llarp_ev_loop_time_now_ms(router->netloop); } diff --git a/llarp/dht/context.hpp b/llarp/dht/context.hpp index 1f1e84dbb..82961f1aa 100644 --- a/llarp/dht/context.hpp +++ b/llarp/dht/context.hpp @@ -1,5 +1,5 @@ -#ifndef LLARP_DHT_CONTEXT_HPP -#define LLARP_DHT_CONTEXT_HPP +#ifndef LLARP_DHT_CONTEXT +#define LLARP_DHT_CONTEXT #include #include @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include #include @@ -19,96 +21,61 @@ namespace llarp namespace dht { - struct Context; - - template < typename K, typename V > - struct TX + struct AbstractContext { - TX(const TXOwner& asker, const K& k, Context* p) - : target(k), whoasked(asker) - { - parent = p; - } - - virtual ~TX(){}; - - K target; - Context* parent; - std::set< Key_t > peersAsked; - std::vector< V > valuesFound; - TXOwner whoasked; + virtual ~AbstractContext() = 0; virtual bool - Validate(const V& value) const = 0; + LookupRouter(const RouterID& target, RouterLookupHandler result) = 0; - void - OnFound(const Key_t askedPeer, const V& value) - { - peersAsked.insert(askedPeer); - if(Validate(value)) - valuesFound.push_back(value); - } + /// on behalf of whoasked request introset for target from dht router with + /// key askpeer + virtual void + LookupIntroSetRecursive(const service::Address& target, + const Key_t& whoasked, uint64_t whoaskedTX, + const Key_t& askpeer, uint64_t R, + service::IntroSetLookupHandler result = nullptr) = 0; virtual void - Start(const TXOwner& peer) = 0; + LookupIntroSetIterative(const service::Address& target, + const Key_t& whoasked, uint64_t whoaskedTX, + const Key_t& askpeer, + service::IntroSetLookupHandler result = nullptr) = 0; - virtual bool - GetNextPeer(Key_t& next, const std::set< Key_t >& exclude) = 0; + + virtual std::set< service::IntroSet > + FindRandomIntroSetsWithTagExcluding( + const service::Tag& tag, size_t max = 2, + const std::set< service::IntroSet >& excludes = {}) = 0; virtual void - DoNextRequest(const Key_t& peer) = 0; + DHTSendTo(const RouterID& peer, IMessage* msg, bool keepalive = true) = 0; - /// return true if we want to persist this tx - bool - AskNextPeer(const Key_t& prevPeer, const std::unique_ptr< Key_t >& next) - { - peersAsked.insert(prevPeer); - Key_t peer; - if(next) - { - // explicit next peer provided - peer = *next; - } - else if(!GetNextPeer(peer, peersAsked)) - { - // no more peers - llarp::LogInfo("no more peers for request asking for", target); - return false; - } + virtual llarp_time_t + Now() const = 0; - const Key_t targetKey{target}; - if((prevPeer ^ targetKey) < (peer ^ targetKey)) - { - // next peer is not closer - llarp::LogInfo("next peer ", peer, " is not closer to ", target, - " than ", prevPeer); - return false; - } - else - { - peersAsked.insert(peer); - } - DoNextRequest(peer); - return true; - } + virtual llarp::Crypto* + Crypto() const = 0; - virtual void - SendReply() = 0; - }; + virtual llarp::Router* + GetRouter() const = 0; - using IntroSetLookupHandler = - std::function< void(const std::vector< service::IntroSet >&) >; + virtual const Key_t& + OurKey() const = 0; - using RouterLookupHandler = - std::function< void(const std::vector< RouterContact >&) >; + virtual Bucket< RCNode >* Nodes() const = 0; + }; - struct Context + struct Context final : public AbstractContext { Context(); - ~Context(); + + ~Context() + { + } llarp::Crypto* - Crypto(); + Crypto() const override; /// on behalf of whoasked request introset for target from dht router with /// key askpeer @@ -116,13 +83,13 @@ namespace llarp LookupIntroSetRecursive(const service::Address& target, const Key_t& whoasked, uint64_t whoaskedTX, const Key_t& askpeer, uint64_t R, - IntroSetLookupHandler result = nullptr); + service::IntroSetLookupHandler result = nullptr) override; void LookupIntroSetIterative(const service::Address& target, const Key_t& whoasked, uint64_t whoaskedTX, const Key_t& askpeer, - IntroSetLookupHandler result = nullptr); + service::IntroSetLookupHandler result = nullptr) override; /// on behalf of whoasked request router with public key target from dht /// router with key askpeer @@ -132,11 +99,13 @@ namespace llarp RouterLookupHandler result = nullptr); bool - LookupRouter(const RouterID& target, RouterLookupHandler result) + LookupRouter(const RouterID& target, RouterLookupHandler result) override { Key_t askpeer; if(!nodes->FindClosest(Key_t(target), askpeer)) + { return false; + } LookupRouterRecursive(target, OurKey(), 0, askpeer, result); return true; } @@ -172,7 +141,7 @@ namespace llarp /// send a dht message to peer, if keepalive is true then keep the session /// with that peer alive for 10 seconds void - DHTSendTo(const RouterID& peer, IMessage* msg, bool keepalive = true); + DHTSendTo(const RouterID& peer, IMessage* msg, bool keepalive = true) override; /// get routers closest to target excluding requester bool @@ -183,7 +152,7 @@ namespace llarp std::set< service::IntroSet > FindRandomIntroSetsWithTagExcluding( const service::Tag& tag, size_t max = 2, - const std::set< service::IntroSet >& excludes = {}); + const std::set< service::IntroSet >& excludes = {}) override; /// handle rc lookup from requester for target void @@ -221,143 +190,24 @@ namespace llarp void Explore(size_t N = 3); - llarp::Router* router = nullptr; + llarp::Router* router; // for router contacts - Bucket< RCNode >* nodes = nullptr; + std::unique_ptr< Bucket< RCNode > > nodes; // for introduction sets - Bucket< ISNode >* services = nullptr; - bool allowTransit = false; + std::unique_ptr< Bucket< ISNode > > services; + bool allowTransit; + + Bucket< RCNode >* Nodes() const override { return nodes.get(); } const Key_t& - OurKey() const + OurKey() const override { return ourKey; } - template < typename K, typename V, typename K_Hash, - llarp_time_t requestTimeoutMS = 5000UL > - struct TXHolder - { - // tx who are waiting for a reply for each key - std::unordered_multimap< K, TXOwner, K_Hash > waiting; - // tx timesouts by key - std::unordered_map< K, llarp_time_t, K_Hash > timeouts; - // maps remote peer with tx to handle reply from them - std::unordered_map< TXOwner, std::unique_ptr< TX< K, V > >, - TXOwner::Hash > - tx; - - const TX< K, V >* - GetPendingLookupFrom(const TXOwner& owner) const - { - auto itr = tx.find(owner); - if(itr == tx.end()) - return nullptr; - else - return itr->second.get(); - } - - bool - HasLookupFor(const K& target) const - { - return timeouts.find(target) != timeouts.end(); - } - - bool - HasPendingLookupFrom(const TXOwner& owner) const - { - return GetPendingLookupFrom(owner) != nullptr; - } - - void - NewTX(const TXOwner& askpeer, const TXOwner& whoasked, const K& k, - TX< K, V >* t) - { - (void)whoasked; - tx.emplace(askpeer, std::unique_ptr< TX< K, V > >(t)); - auto count = waiting.count(k); - waiting.insert(std::make_pair(k, askpeer)); - - auto itr = timeouts.find(k); - if(itr == timeouts.end()) - { - timeouts.insert( - std::make_pair(k, time_now_ms() + requestTimeoutMS)); - } - if(count == 0) - t->Start(askpeer); - } - - /// mark tx as not fond - void - NotFound(const TXOwner& from, const std::unique_ptr< Key_t >& next) - { - bool sendReply = true; - auto txitr = tx.find(from); - if(txitr == tx.end()) - return; - - // ask for next peer - if(txitr->second->AskNextPeer(from.node, next)) - sendReply = false; - - llarp::LogWarn("Target key ", txitr->second->target); - Inform(from, txitr->second->target, {}, sendReply, sendReply); - } - - void - Found(const TXOwner& from, const K& k, const std::vector< V >& values) - { - Inform(from, k, values, true); - } - - /// inform all watches for key of values found - void - Inform(TXOwner from, K key, std::vector< V > values, - bool sendreply = false, bool removeTimeouts = true) - { - auto range = waiting.equal_range(key); - auto itr = range.first; - while(itr != range.second) - { - auto txitr = tx.find(itr->second); - if(txitr != tx.end()) - { - for(const auto& value : values) - txitr->second->OnFound(from.node, value); - if(sendreply) - { - txitr->second->SendReply(); - tx.erase(txitr); - } - } - ++itr; - } - - if(sendreply) - waiting.erase(key); - - if(removeTimeouts) - timeouts.erase(key); - } - - void - Expire(llarp_time_t now) - { - auto itr = timeouts.begin(); - while(itr != timeouts.end()) - { - if(now > itr->second && now - itr->second >= requestTimeoutMS) - { - Inform(TXOwner{}, itr->first, {}, true, false); - itr = timeouts.erase(itr); - } - else - ++itr; - } - } - }; + llarp::Router* + GetRouter() const override { return router; } TXHolder< service::Address, service::IntroSet, service::Address::Hash > pendingIntrosetLookups; @@ -376,7 +226,7 @@ namespace llarp } llarp_time_t - Now(); + Now() const override; void ExploreNetworkVia(const Key_t& peer); diff --git a/llarp/dht/explorenetworkjob.cpp b/llarp/dht/explorenetworkjob.cpp new file mode 100644 index 000000000..58b132c1d --- /dev/null +++ b/llarp/dht/explorenetworkjob.cpp @@ -0,0 +1,32 @@ +#include + +#include +#include +#include + +namespace llarp +{ + namespace dht + { + void + ExploreNetworkJob::Start(const TXOwner &peer) + { + parent->DHTSendTo(peer.node.as_array(), new FindRouterMessage(peer.txid)); + } + + void + ExploreNetworkJob::SendReply() + { + llarp::LogInfo("got ", valuesFound.size(), " routers from exploration"); + + auto router = parent->GetRouter(); + using std::placeholders::_1; + for(const auto &pk : valuesFound) + { + // lookup router + parent->LookupRouter( + pk, std::bind(&Router::HandleDHTLookupForExplore, router, pk, _1)); + } + } + } // namespace dht +} // namespace llarp diff --git a/llarp/dht/explorenetworkjob.hpp b/llarp/dht/explorenetworkjob.hpp new file mode 100644 index 000000000..d88b6b9cc --- /dev/null +++ b/llarp/dht/explorenetworkjob.hpp @@ -0,0 +1,45 @@ +#ifndef LLARP_DHT_EXPLORENETWORKJOB +#define LLARP_DHT_EXPLORENETWORKJOB + +#include +#include + +namespace llarp +{ + namespace dht + { + struct ExploreNetworkJob : public TX< RouterID, RouterID > + { + ExploreNetworkJob(const RouterID &peer, AbstractContext *ctx) + : TX< RouterID, RouterID >(TXOwner{}, peer, ctx) + { + } + + bool + Validate(const RouterID &) const override + { + // TODO: check with lokid + return true; + } + + void + Start(const TXOwner &peer) override; + + bool + GetNextPeer(Key_t &, const std::set< Key_t > &) override + { + return false; + } + + void + DoNextRequest(const Key_t &) override + { + } + + void + SendReply() override; + }; + } // namespace dht +} // namespace llarp + +#endif diff --git a/llarp/dht/localrouterlookup.cpp b/llarp/dht/localrouterlookup.cpp new file mode 100644 index 000000000..f5f806522 --- /dev/null +++ b/llarp/dht/localrouterlookup.cpp @@ -0,0 +1,46 @@ +#include + +#include +#include +#include +#include +#include + +namespace llarp +{ + namespace dht + { + LocalRouterLookup::LocalRouterLookup(const PathID_t &path, uint64_t txid, + const RouterID &target, AbstractContext *ctx) + : RecursiveRouterLookup(TXOwner{ctx->OurKey(), txid}, target, ctx, + nullptr) + , localPath(path) + { + } + + void + LocalRouterLookup::SendReply() + { + auto path = parent->GetRouter()->paths.GetByUpstream( + parent->OurKey().as_array(), localPath); + if(!path) + { + llarp::LogWarn( + "did not send reply for relayed dht request, no such local path " + "for pathid=", + localPath); + return; + } + routing::DHTMessage msg; + msg.M.emplace_back(new GotRouterMessage(parent->OurKey(), whoasked.txid, + valuesFound, true)); + if(!path->SendRoutingMessage(&msg, parent->GetRouter())) + { + llarp::LogWarn( + "failed to send routing message when informing result of dht " + "request, pathid=", + localPath); + } + } + } // namespace dht +} // namespace llarp diff --git a/llarp/dht/localrouterlookup.hpp b/llarp/dht/localrouterlookup.hpp new file mode 100644 index 000000000..35834d0df --- /dev/null +++ b/llarp/dht/localrouterlookup.hpp @@ -0,0 +1,27 @@ +#ifndef LLARP_DHT_LOCALROUTERLOOKUP +#define LLARP_DHT_LOCALROUTERLOOKUP + +#include + +#include +#include +#include + +namespace llarp +{ + namespace dht + { + struct LocalRouterLookup : public RecursiveRouterLookup + { + PathID_t localPath; + + LocalRouterLookup(const PathID_t &path, uint64_t txid, + const RouterID &target, AbstractContext *ctx); + + void + SendReply() override; + }; + } // namespace dht +} // namespace llarp + +#endif diff --git a/llarp/dht/localserviceaddresslookup.cpp b/llarp/dht/localserviceaddresslookup.cpp new file mode 100644 index 000000000..8e416acd5 --- /dev/null +++ b/llarp/dht/localserviceaddresslookup.cpp @@ -0,0 +1,45 @@ +#include + +#include +#include +#include +#include + +namespace llarp +{ + namespace dht + { + LocalServiceAddressLookup::LocalServiceAddressLookup( + const PathID_t &pathid, uint64_t txid, const service::Address &addr, + AbstractContext *ctx, __attribute__((unused)) const Key_t &askpeer) + : ServiceAddressLookup(TXOwner{ctx->OurKey(), txid}, addr, ctx, 5, + nullptr) + , localPath(pathid) + { + } + + void + LocalServiceAddressLookup::SendReply() + { + auto path = parent->GetRouter()->paths.GetByUpstream( + parent->OurKey().as_array(), localPath); + if(!path) + { + llarp::LogWarn( + "did not send reply for relayed dht request, no such local path " + "for pathid=", + localPath); + return; + } + routing::DHTMessage msg; + msg.M.emplace_back(new GotIntroMessage(valuesFound, whoasked.txid)); + if(!path->SendRoutingMessage(&msg, parent->GetRouter())) + { + llarp::LogWarn( + "failed to send routing message when informing result of dht " + "request, pathid=", + localPath); + } + } + } // namespace dht +} // namespace llarp diff --git a/llarp/dht/localserviceaddresslookup.hpp b/llarp/dht/localserviceaddresslookup.hpp new file mode 100644 index 000000000..63a054efd --- /dev/null +++ b/llarp/dht/localserviceaddresslookup.hpp @@ -0,0 +1,27 @@ +#ifndef LLARP_DHT_LOCALSERVICEADDRESSLOOKUP +#define LLARP_DHT_LOCALSERVICEADDRESSLOOKUP + +#include + +#include + +namespace llarp +{ + namespace dht + { + struct LocalServiceAddressLookup : public ServiceAddressLookup + { + PathID_t localPath; + + LocalServiceAddressLookup(const PathID_t &pathid, uint64_t txid, + const service::Address &addr, AbstractContext *ctx, + __attribute__((unused)) const Key_t &askpeer); + + void + SendReply() override; + }; + + } // namespace dht +} // namespace llarp + +#endif diff --git a/llarp/dht/localtaglookup.cpp b/llarp/dht/localtaglookup.cpp new file mode 100644 index 000000000..7d7c53fcd --- /dev/null +++ b/llarp/dht/localtaglookup.cpp @@ -0,0 +1,43 @@ +#include + +#include +#include +#include +#include + +namespace llarp +{ + namespace dht + { + LocalTagLookup::LocalTagLookup(const PathID_t &path, uint64_t txid, + const service::Tag &target, AbstractContext *ctx) + : TagLookup(TXOwner{ctx->OurKey(), txid}, target, ctx, 0) + , localPath(path) + { + } + + void + LocalTagLookup::SendReply() + { + auto path = parent->GetRouter()->paths.GetByUpstream( + parent->OurKey().as_array(), localPath); + if(!path) + { + llarp::LogWarn( + "did not send reply for relayed dht request, no such local path " + "for pathid=", + localPath); + return; + } + routing::DHTMessage msg; + msg.M.emplace_back(new GotIntroMessage(valuesFound, whoasked.txid)); + if(!path->SendRoutingMessage(&msg, parent->GetRouter())) + { + llarp::LogWarn( + "failed to send routing message when informing result of dht " + "request, pathid=", + localPath); + } + } + } // namespace dht +} // namespace llarp diff --git a/llarp/dht/localtaglookup.hpp b/llarp/dht/localtaglookup.hpp new file mode 100644 index 000000000..27aff4852 --- /dev/null +++ b/llarp/dht/localtaglookup.hpp @@ -0,0 +1,23 @@ +#ifndef LLARP_DHT_LOOKUPTAGLOOKUP +#define LLARP_DHT_LOOKUPTAGLOOKUP + +#include + +namespace llarp +{ + namespace dht + { + struct LocalTagLookup : public TagLookup + { + PathID_t localPath; + + LocalTagLookup(const PathID_t &path, uint64_t txid, + const service::Tag &target, AbstractContext *ctx); + + void + SendReply() override; + }; + } // namespace dht +} // namespace llarp + +#endif diff --git a/llarp/dht/messages/gotintro.cpp b/llarp/dht/messages/gotintro.cpp index d32653a7f..08073bc54 100644 --- a/llarp/dht/messages/gotintro.cpp +++ b/llarp/dht/messages/gotintro.cpp @@ -25,7 +25,7 @@ namespace llarp std::vector< std::unique_ptr< IMessage > > &replies) const { auto &dht = ctx->impl; - auto crypto = &dht.router->crypto; + auto crypto = dht.router->crypto.get(); for(const auto &introset : I) { diff --git a/llarp/dht/messages/pubintro.cpp b/llarp/dht/messages/pubintro.cpp index d78f184f5..fe452a004 100644 --- a/llarp/dht/messages/pubintro.cpp +++ b/llarp/dht/messages/pubintro.cpp @@ -52,14 +52,18 @@ namespace llarp return false; } auto &dht = ctx->impl; - if(!I.Verify(&dht.router->crypto, now)) + if(!I.Verify(dht.router->crypto.get(), 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)) + + using namespace std::placeholders; + shorthash_func shorthash = + std::bind(&Crypto::shorthash, dht.router->crypto.get(), _1, _2); + if(I.W && !I.W->IsValid(shorthash, now)) { llarp::LogWarn("proof of work not good enough for IntroSet"); // don't propogate or store diff --git a/llarp/dht/node.hpp b/llarp/dht/node.hpp index 5ad58eaea..3ae1ee0a7 100644 --- a/llarp/dht/node.hpp +++ b/llarp/dht/node.hpp @@ -41,9 +41,8 @@ namespace llarp ID.Zero(); } - ISNode(const service::IntroSet& other) + ISNode(const service::IntroSet& other) : introset(other) { - introset = other; introset.A.CalculateAddress(ID.as_array()); } diff --git a/llarp/dht/publishservicejob.cpp b/llarp/dht/publishservicejob.cpp new file mode 100644 index 000000000..12a9a7752 --- /dev/null +++ b/llarp/dht/publishservicejob.cpp @@ -0,0 +1,46 @@ +#include + +#include +#include + +namespace llarp +{ + namespace dht + { + PublishServiceJob::PublishServiceJob(const TXOwner &asker, + const service::IntroSet &introset, + AbstractContext *ctx, uint64_t s, + const std::set< Key_t > &exclude) + : TX< service::Address, service::IntroSet >(asker, introset.A.Addr(), + ctx) + , S(s) + , dontTell(exclude) + , I(introset) + { + } + + bool + PublishServiceJob::Validate(const service::IntroSet &introset) const + { + if(I.A != introset.A) + { + llarp::LogWarn( + "publish introset acknowledgement acked a different service"); + return false; + } + return true; + } + + void + PublishServiceJob::Start(const TXOwner &peer) + { + std::vector< Key_t > exclude; + for(const auto &router : dontTell) + { + exclude.push_back(router); + } + parent->DHTSendTo(peer.node.as_array(), + new PublishIntroMessage(I, peer.txid, S, exclude)); + } + } // namespace dht +} // namespace llarp diff --git a/llarp/dht/publishservicejob.hpp b/llarp/dht/publishservicejob.hpp new file mode 100644 index 000000000..b69877cbc --- /dev/null +++ b/llarp/dht/publishservicejob.hpp @@ -0,0 +1,51 @@ +#ifndef LLARP_DHT_PUBLISHSERVICEJOB +#define LLARP_DHT_PUBLISHSERVICEJOB + +#include +#include +#include +#include + +#include + +namespace llarp +{ + namespace dht + { + struct PublishServiceJob : public TX< service::Address, service::IntroSet > + { + uint64_t S; + std::set< Key_t > dontTell; + service::IntroSet I; + + PublishServiceJob(const TXOwner &asker, const service::IntroSet &introset, + AbstractContext *ctx, uint64_t s, + const std::set< Key_t > &exclude); + + bool + Validate(const service::IntroSet &introset) const override; + + void + Start(const TXOwner &peer) override; + + bool + GetNextPeer(Key_t &, const std::set< Key_t > &) override + { + return false; + } + + void + DoNextRequest(const Key_t &) override + { + } + + void + SendReply() override + { + // don't need this + } + }; + } // namespace dht +} // namespace llarp + +#endif diff --git a/llarp/dht/recursiverouterlookup.cpp b/llarp/dht/recursiverouterlookup.cpp new file mode 100644 index 000000000..0bcab6951 --- /dev/null +++ b/llarp/dht/recursiverouterlookup.cpp @@ -0,0 +1,55 @@ +#include + +#include +#include +#include + +namespace llarp +{ + namespace dht + { + RecursiveRouterLookup::RecursiveRouterLookup(const TXOwner &whoasked, + const RouterID &target, + AbstractContext *ctx, + RouterLookupHandler result) + : TX< RouterID, RouterContact >(whoasked, target, ctx) + , resultHandler(result) + + { + peersAsked.insert(ctx->OurKey()); + } + + bool + RecursiveRouterLookup::Validate(const RouterContact &rc) const + { + if(!rc.Verify(parent->Crypto(), parent->Now())) + { + llarp::LogWarn("rc from lookup result is invalid"); + return false; + } + return true; + } + + void + RecursiveRouterLookup::Start(const TXOwner &peer) + { + parent->DHTSendTo(peer.node.as_array(), + new FindRouterMessage(peer.txid, target)); + } + + void + RecursiveRouterLookup::SendReply() + { + if(resultHandler) + { + resultHandler(valuesFound); + } + else + { + parent->DHTSendTo( + whoasked.node.as_array(), + new GotRouterMessage({}, whoasked.txid, valuesFound, false)); + } + } + } // namespace dht +} // namespace llarp diff --git a/llarp/dht/recursiverouterlookup.hpp b/llarp/dht/recursiverouterlookup.hpp new file mode 100644 index 000000000..5acbbdc06 --- /dev/null +++ b/llarp/dht/recursiverouterlookup.hpp @@ -0,0 +1,42 @@ +#ifndef LLARP_DHT_RECURSIVEROUTERLOOKUP +#define LLARP_DHT_RECURSIVEROUTERLOOKUP + +#include + +#include +#include + +namespace llarp +{ + namespace dht + { + struct RecursiveRouterLookup : public TX< RouterID, RouterContact > + { + RouterLookupHandler resultHandler; + RecursiveRouterLookup(const TXOwner &whoasked, const RouterID &target, + AbstractContext *ctx, RouterLookupHandler result); + + bool + Validate(const RouterContact &rc) const override; + + bool + GetNextPeer(Key_t &, const std::set< Key_t > &) override + { + return false; + } + + void + DoNextRequest(const Key_t &) override + { + } + + void + Start(const TXOwner &peer) override; + + virtual void + SendReply() override; + }; + } // namespace dht +} // namespace llarp + +#endif diff --git a/llarp/dht/serviceaddresslookup.cpp b/llarp/dht/serviceaddresslookup.cpp new file mode 100644 index 000000000..5017842f1 --- /dev/null +++ b/llarp/dht/serviceaddresslookup.cpp @@ -0,0 +1,79 @@ +#include + +#include +#include +#include + +namespace llarp +{ + namespace dht + { + ServiceAddressLookup::ServiceAddressLookup( + const TXOwner &asker, const service::Address &addr, AbstractContext *ctx, + uint64_t r, service::IntroSetLookupHandler handler) + : TX< service::Address, service::IntroSet >(asker, addr, ctx) + , handleResult(handler) + , R(r) + { + peersAsked.insert(ctx->OurKey()); + } + + bool + ServiceAddressLookup::Validate(const service::IntroSet &value) const + { + if(!value.Verify(parent->Crypto(), parent->Now())) + { + llarp::LogWarn("Got invalid introset from service lookup"); + return false; + } + if(value.A.Addr() != target) + { + llarp::LogWarn("got introset with wrong target from service lookup"); + return false; + } + return true; + } + + bool + ServiceAddressLookup::GetNextPeer(Key_t &next, + const std::set< Key_t > &exclude) + { + Key_t k = target.ToKey(); + return parent->Nodes()->FindCloseExcluding(k, next, exclude); + } + + void + ServiceAddressLookup::Start(const TXOwner &peer) + { + parent->DHTSendTo(peer.node.as_array(), + new FindIntroMessage(peer.txid, target, R)); + } + + void + ServiceAddressLookup::DoNextRequest(const Key_t &ask) + { + if(R) + { + parent->LookupIntroSetRecursive(target, whoasked.node, whoasked.txid, + ask, R - 1); + } + else + { + parent->LookupIntroSetIterative(target, whoasked.node, whoasked.txid, + ask); + } + } + + void + ServiceAddressLookup::SendReply() + { + if(handleResult) + { + handleResult(valuesFound); + } + + parent->DHTSendTo(whoasked.node.as_array(), + new GotIntroMessage(valuesFound, whoasked.txid)); + } + } // namespace dht +} // namespace llarp diff --git a/llarp/dht/serviceaddresslookup.hpp b/llarp/dht/serviceaddresslookup.hpp new file mode 100644 index 000000000..708eac5d0 --- /dev/null +++ b/llarp/dht/serviceaddresslookup.hpp @@ -0,0 +1,44 @@ +#ifndef LLARP_DHT_SERVICEADDRESSLOOKUP +#define LLARP_DHT_SERVICEADDRESSLOOKUP + +#include +#include +#include +#include + +namespace llarp +{ + namespace dht + { + struct TXOwner; + + struct ServiceAddressLookup + : public TX< service::Address, service::IntroSet > + { + service::IntroSetLookupHandler handleResult; + uint64_t R; + + ServiceAddressLookup(const TXOwner &asker, const service::Address &addr, + AbstractContext *ctx, uint64_t r, + service::IntroSetLookupHandler handler); + + bool + Validate(const service::IntroSet &value) const override; + + bool + GetNextPeer(Key_t &next, const std::set< Key_t > &exclude) override; + + void + Start(const TXOwner &peer) override; + + void + DoNextRequest(const Key_t &ask) override; + + virtual void + SendReply() override; + }; + } // namespace dht + +} // namespace llarp + +#endif diff --git a/llarp/dht/taglookup.cpp b/llarp/dht/taglookup.cpp new file mode 100644 index 000000000..8c84792b0 --- /dev/null +++ b/llarp/dht/taglookup.cpp @@ -0,0 +1,59 @@ +#include + +#include +#include + +namespace llarp +{ + namespace dht + { + bool + TagLookup::Validate(const service::IntroSet &introset) const + { + if(!introset.Verify(parent->Crypto(), parent->Now())) + { + llarp::LogWarn("got invalid introset from tag lookup"); + return false; + } + if(introset.topic != target) + { + llarp::LogWarn("got introset with missmatched topic in tag lookup"); + return false; + } + return true; + } + + void + TagLookup::Start(const TXOwner &peer) + { + parent->DHTSendTo(peer.node.as_array(), + new FindIntroMessage(target, peer.txid, R)); + } + + void + TagLookup::SendReply() + { + std::set< service::IntroSet > found; + for(const auto &remoteTag : valuesFound) + { + found.insert(remoteTag); + } + // collect our local values if we haven't hit a limit + if(found.size() < 2) + { + for(const auto &localTag : + parent->FindRandomIntroSetsWithTagExcluding(target, 1, found)) + { + found.insert(localTag); + } + } + std::vector< service::IntroSet > values; + for(const auto &introset : found) + { + values.push_back(introset); + } + parent->DHTSendTo(whoasked.node.as_array(), + new GotIntroMessage(values, whoasked.txid)); + } + } // namespace dht +} // namespace llarp diff --git a/llarp/dht/taglookup.hpp b/llarp/dht/taglookup.hpp new file mode 100644 index 000000000..2fbfaf537 --- /dev/null +++ b/llarp/dht/taglookup.hpp @@ -0,0 +1,44 @@ +#ifndef LLARP_DHT_TAGLOOKUP +#define LLARP_DHT_TAGLOOKUP + +#include +#include +#include + +namespace llarp +{ + namespace dht + { + struct TagLookup : public TX< service::Tag, service::IntroSet > + { + uint64_t R; + TagLookup(const TXOwner &asker, const service::Tag &tag, AbstractContext *ctx, + uint64_t r) + : TX< service::Tag, service::IntroSet >(asker, tag, ctx), R(r) + { + } + + bool + Validate(const service::IntroSet &introset) const override; + + void + Start(const TXOwner &peer) override; + + bool + GetNextPeer(Key_t &, const std::set< Key_t > &) override + { + return false; + } + + void + DoNextRequest(const Key_t &) override + { + } + + void + SendReply() override; + }; + } // namespace dht +} // namespace llarp + +#endif diff --git a/llarp/dht/tx.cpp b/llarp/dht/tx.cpp new file mode 100644 index 000000000..4e60e79f0 --- /dev/null +++ b/llarp/dht/tx.cpp @@ -0,0 +1 @@ +#include diff --git a/llarp/dht/tx.hpp b/llarp/dht/tx.hpp new file mode 100644 index 000000000..15c966dfb --- /dev/null +++ b/llarp/dht/tx.hpp @@ -0,0 +1,104 @@ +#ifndef LLARP_DHT_TX +#define LLARP_DHT_TX + +#include +#include +#include + +#include +#include + +namespace llarp +{ + namespace dht + { + struct AbstractContext; + + template < typename K, typename V > + struct TX + { + K target; + AbstractContext* parent; + std::set< Key_t > peersAsked; + std::vector< V > valuesFound; + TXOwner whoasked; + + TX(const TXOwner& asker, const K& k, AbstractContext* p) + : target(k), parent(p), whoasked(asker) + { + } + + virtual ~TX(){}; + + void + OnFound(const Key_t& askedPeer, const V& value); + + /// return true if we want to persist this tx + bool + AskNextPeer(const Key_t& prevPeer, const std::unique_ptr< Key_t >& next); + + virtual bool + Validate(const V& value) const = 0; + + virtual void + Start(const TXOwner& peer) = 0; + + virtual bool + GetNextPeer(Key_t& next, const std::set< Key_t >& exclude) = 0; + + virtual void + DoNextRequest(const Key_t& peer) = 0; + + virtual void + SendReply() = 0; + }; + + template < typename K, typename V > + inline void + TX< K, V >::OnFound(const Key_t& askedPeer, const V& value) + { + peersAsked.insert(askedPeer); + if(Validate(value)) + { + valuesFound.push_back(value); + } + } + + template < typename K, typename V > + inline bool + TX< K, V >::AskNextPeer(const Key_t& prevPeer, + const std::unique_ptr< Key_t >& next) + { + peersAsked.insert(prevPeer); + Key_t peer; + if(next) + { + // explicit next peer provided + peer = *next; + } + else if(!GetNextPeer(peer, peersAsked)) + { + // no more peers + llarp::LogInfo("no more peers for request asking for ", target); + return false; + } + + const Key_t targetKey{target}; + if((prevPeer ^ targetKey) < (peer ^ targetKey)) + { + // next peer is not closer + llarp::LogInfo("next peer ", peer, " is not closer to ", target, + " than ", prevPeer); + return false; + } + else + { + peersAsked.insert(peer); + } + DoNextRequest(peer); + return true; + } + } // namespace dht +} // namespace llarp + +#endif diff --git a/llarp/dht/txholder.cpp b/llarp/dht/txholder.cpp new file mode 100644 index 000000000..2f7bf76a7 --- /dev/null +++ b/llarp/dht/txholder.cpp @@ -0,0 +1 @@ +#include diff --git a/llarp/dht/txholder.hpp b/llarp/dht/txholder.hpp new file mode 100644 index 000000000..41107748f --- /dev/null +++ b/llarp/dht/txholder.hpp @@ -0,0 +1,190 @@ +#ifndef LLARP_DHT_TXHOLDER +#define LLARP_DHT_TXHOLDER + +#include +#include +#include +#include + +#include +#include + +namespace llarp +{ + namespace dht + { + template < typename K, typename V, typename K_Hash, + llarp_time_t requestTimeoutMS = 5000UL > + struct TXHolder + { + using TXPtr = std::unique_ptr< TX< K, V > >; + // tx who are waiting for a reply for each key + std::unordered_multimap< K, TXOwner, K_Hash > waiting; + // tx timesouts by key + std::unordered_map< K, llarp_time_t, K_Hash > timeouts; + // maps remote peer with tx to handle reply from them + std::unordered_map< TXOwner, TXPtr, TXOwner::Hash > tx; + + const TX< K, V >* + GetPendingLookupFrom(const TXOwner& owner) const; + + bool + HasLookupFor(const K& target) const + { + return timeouts.find(target) != timeouts.end(); + } + + bool + HasPendingLookupFrom(const TXOwner& owner) const + { + return GetPendingLookupFrom(owner) != nullptr; + } + + void + NewTX(const TXOwner& askpeer, const TXOwner& whoasked, const K& k, + TX< K, V >* t); + + /// mark tx as not fond + void + NotFound(const TXOwner& from, const std::unique_ptr< Key_t >& next); + + void + Found(const TXOwner& from, const K& k, const std::vector< V >& values) + { + Inform(from, k, values, true); + } + + /// inform all watches for key of values found + void + Inform(TXOwner from, K key, std::vector< V > values, + bool sendreply = false, bool removeTimeouts = true); + + void + Expire(llarp_time_t now); + }; + + template < typename K, typename V, typename K_Hash, + llarp_time_t requestTimeoutMS > + const TX< K, V >* + TXHolder< K, V, K_Hash, requestTimeoutMS >::GetPendingLookupFrom( + const TXOwner& owner) const + { + auto itr = tx.find(owner); + if(itr == tx.end()) + { + return nullptr; + } + else + { + return itr->second.get(); + } + } + + template < typename K, typename V, typename K_Hash, + llarp_time_t requestTimeoutMS > + void + TXHolder< K, V, K_Hash, requestTimeoutMS >::NewTX(const TXOwner& askpeer, + const TXOwner& whoasked, + const K& k, TX< K, V >* t) + { + (void)whoasked; + tx.emplace(askpeer, std::unique_ptr< TX< K, V > >(t)); + auto count = waiting.count(k); + waiting.emplace(k, askpeer); + + auto itr = timeouts.find(k); + if(itr == timeouts.end()) + { + timeouts.emplace(k, time_now_ms() + requestTimeoutMS); + } + if(count == 0) + { + t->Start(askpeer); + } + } + + template < typename K, typename V, typename K_Hash, + llarp_time_t requestTimeoutMS > + void + TXHolder< K, V, K_Hash, requestTimeoutMS >::NotFound( + const TXOwner& from, const std::unique_ptr< Key_t >& next) + { + bool sendReply = true; + auto txitr = tx.find(from); + if(txitr == tx.end()) + { + return; + } + + // ask for next peer + if(txitr->second->AskNextPeer(from.node, next)) + { + sendReply = false; + } + + llarp::LogWarn("Target key ", txitr->second->target); + Inform(from, txitr->second->target, {}, sendReply, sendReply); + } + + template < typename K, typename V, typename K_Hash, + llarp_time_t requestTimeoutMS > + void + TXHolder< K, V, K_Hash, requestTimeoutMS >::Inform(TXOwner from, K key, + std::vector< V > values, + bool sendreply, + bool removeTimeouts) + { + auto range = waiting.equal_range(key); + auto itr = range.first; + while(itr != range.second) + { + auto txitr = tx.find(itr->second); + if(txitr != tx.end()) + { + for(const auto& value : values) + { + txitr->second->OnFound(from.node, value); + } + if(sendreply) + { + txitr->second->SendReply(); + tx.erase(txitr); + } + } + ++itr; + } + + if(sendreply) + { + waiting.erase(key); + } + + if(removeTimeouts) + { + timeouts.erase(key); + } + } + + template < typename K, typename V, typename K_Hash, + llarp_time_t requestTimeoutMS > + void + TXHolder< K, V, K_Hash, requestTimeoutMS >::Expire(llarp_time_t now) + { + auto itr = timeouts.begin(); + while(itr != timeouts.end()) + { + if(now > itr->second && now - itr->second >= requestTimeoutMS) + { + Inform(TXOwner{}, itr->first, {}, true, false); + itr = timeouts.erase(itr); + } + else + { + ++itr; + } + } + } + } // namespace dht +} // namespace llarp + +#endif diff --git a/llarp/dns/dns.hpp b/llarp/dns/dns.hpp index 01377a4c5..55c34bcc9 100644 --- a/llarp/dns/dns.hpp +++ b/llarp/dns/dns.hpp @@ -7,6 +7,7 @@ namespace llarp { namespace dns { + constexpr uint16_t qTypeAAAA = 28; constexpr uint16_t qTypeTXT = 16; constexpr uint16_t qTypeMX = 15; constexpr uint16_t qTypePTR = 12; diff --git a/llarp/dns/message.cpp b/llarp/dns/message.cpp index 67d4f48f9..dcb28aca8 100644 --- a/llarp/dns/message.cpp +++ b/llarp/dns/message.cpp @@ -247,14 +247,17 @@ namespace llarp { hdr_fields |= (1 << 15) | (1 << 3); const auto& question = questions[0]; - answers.emplace_back(); - auto& nx = answers.back(); - nx.rr_name = question.qname; - nx.rr_type = question.qtype; - nx.rr_class = question.qclass; - nx.ttl = ttl; - nx.rData.resize(1); - nx.rData.data()[0] = 0; + if(question.qtype != qTypeAAAA) + { + answers.emplace_back(); + auto& nx = answers.back(); + nx.rr_name = question.qname; + nx.rr_type = question.qtype; + nx.rr_class = question.qclass; + nx.ttl = ttl; + nx.rData.resize(1); + nx.rData.data()[0] = 0; + } } } diff --git a/llarp/exit/session.cpp b/llarp/exit/session.cpp index 0d4125a77..d99a589c7 100644 --- a/llarp/exit/session.cpp +++ b/llarp/exit/session.cpp @@ -16,7 +16,7 @@ namespace llarp , m_Counter(0) , m_LastUse(0) { - r->crypto.identity_keygen(m_ExitIdentity); + r->crypto->identity_keygen(m_ExitIdentity); } BaseSession::~BaseSession() @@ -78,7 +78,7 @@ namespace llarp obtain.S = p->NextSeqNo(); obtain.T = llarp::randint(); PopulateRequest(obtain); - if(!obtain.Sign(&router->crypto, m_ExitIdentity)) + if(!obtain.Sign(router->crypto.get(), m_ExitIdentity)) { llarp::LogError("Failed to sign exit request"); return; @@ -107,7 +107,7 @@ namespace llarp { llarp::LogInfo(p->Name(), " closing exit path"); llarp::routing::CloseExitMessage msg; - if(!(msg.Sign(&router->crypto, m_ExitIdentity) + if(!(msg.Sign(router->crypto.get(), m_ExitIdentity) && p->SendExitClose(&msg, router))) llarp::LogWarn(p->Name(), " failed to send exit close message"); } diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index e3036d90f..902e0ce46 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -246,7 +246,7 @@ namespace llarp Crypto * ExitEndpoint::GetCrypto() { - return &m_Router->crypto; + return m_Router->crypto.get(); } huint32_t diff --git a/llarp/handlers/null.hpp b/llarp/handlers/null.hpp index 02b47314c..fb61d4d1c 100644 --- a/llarp/handlers/null.hpp +++ b/llarp/handlers/null.hpp @@ -9,8 +9,9 @@ namespace llarp { struct NullEndpoint final : public llarp::service::Endpoint { - NullEndpoint(const std::string &name, llarp::Router *r) - : llarp::service::Endpoint(name, r){}; + NullEndpoint(const std::string &name, llarp::Router *r, + llarp::service::Context *parent) + : llarp::service::Endpoint(name, r, parent){}; bool HandleWriteIPPacket(llarp_buffer_t, std::function< huint32_t(void) >) override diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index 6a7ca7afa..a8568627e 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -29,8 +29,9 @@ namespace llarp self->Flush(); } - TunEndpoint::TunEndpoint(const std::string &nickname, llarp::Router *r) - : service::Endpoint(nickname, r) + TunEndpoint::TunEndpoint(const std::string &nickname, llarp::Router *r, + service::Context *parent) + : service::Endpoint(nickname, r, parent) , m_UserToNetworkPktQueue(nickname + "_sendq", r->netloop, r->netloop) , m_NetworkToUserPktQueue(nickname + "_recvq", r->netloop, r->netloop) , m_Resolver(r->netloop, this) @@ -204,10 +205,7 @@ namespace llarp return false; } std::string qname = msg.questions[0].qname; - if(msg.questions[0].qtype == dns::qTypeCNAME) - { - } - else if(msg.questions[0].qtype == dns::qTypeMX) + if(msg.questions[0].qtype == dns::qTypeMX) { // mx record llarp::service::Address addr; @@ -228,19 +226,44 @@ namespace llarp else msg.AddNXReply(); } + else if(msg.questions[0].qname == "localhost.loki" + || msg.questions[0].qname == "localhost.loki.") + { + size_t counter = 0; + context->ForEachService( + [&](const std::string &, + const std::unique_ptr< service::Endpoint > &service) -> bool { + service::Address addr = service->GetIdentity().pub.Addr(); + msg.AddCNAMEReply(addr.ToString(), 1); + ++counter; + return true; + }); + if(counter == 0) + msg.AddNXReply(); + } else msg.AddNXReply(); } else if(msg.questions[0].qtype == dns::qTypeA) { - // forward dns llarp::service::Address addr; - if(qname == "random.snode" || qname == "random.snode.") + // forward dns + if(msg.questions[0].qname == "localhost.loki" + || msg.questions[0].qname == "localhost.loki.") { - RouterID random; - if(Router()->GetRandomGoodRouter(random)) - msg.AddCNAMEReply(random.ToString(), 1); - else + size_t counter = 0; + context->ForEachService( + [&](const std::string &, + const std::unique_ptr< service::Endpoint > &service) -> bool { + if(service->HasIfAddr()) + { + huint32_t ip = service->GetIfAddr(); + msg.AddINReply(ip); + ++counter; + } + return true; + }); + if(counter == 0) msg.AddNXReply(); } else if(addr.FromString(qname, ".loki")) @@ -317,14 +340,18 @@ namespace llarp // always hook mx records if(msg.questions[0].qtype == llarp::dns::qTypeMX) return true; - // always hook random.snode + // hook random.snode for CNAME if(msg.questions[0].qname == "random.snode" || msg.questions[0].qname == "random.snode.") return true; - // always hook .loki + // hook localhost.loki + if(msg.questions[0].qname == "localhost.loki" + || msg.questions[0].qname == "localhost.loki.") + return true; + // hook .loki A records if(addr.FromString(msg.questions[0].qname, ".loki")) return true; - // always hook .snode + // hook .snode A records if(addr.FromString(msg.questions[0].qname, ".snode")) return true; // hook any ranges we own diff --git a/llarp/handlers/tun.hpp b/llarp/handlers/tun.hpp index d7d36c65b..a11faaa36 100644 --- a/llarp/handlers/tun.hpp +++ b/llarp/handlers/tun.hpp @@ -20,7 +20,8 @@ namespace llarp struct TunEndpoint : public service::Endpoint, public dns::IQueryHandler { - TunEndpoint(const std::string& nickname, llarp::Router* r); + TunEndpoint(const std::string& nickname, llarp::Router* r, + llarp::service::Context* parent); ~TunEndpoint(); virtual bool @@ -70,6 +71,13 @@ namespace llarp bool QueueOutboundTraffic(llarp::net::IPv4Packet&& pkt); + /// we have a resolvable ip address + bool + HasIfAddr() const override + { + return true; + } + /// get the local interface's address huint32_t GetIfAddr() const; diff --git a/llarp/link/utp.cpp b/llarp/link/utp.cpp index 0f82e0d36..6b5bb239d 100644 --- a/llarp/link/utp.cpp +++ b/llarp/link/utp.cpp @@ -28,6 +28,8 @@ namespace llarp { namespace utp { + using namespace std::placeholders; + bool InboundMessage::IsExpired(llarp_time_t now) const { @@ -549,7 +551,7 @@ namespace llarp NewServerFromRouter(llarp::Router* r) { return NewServer( - &r->crypto, r->encryption, std::bind(&llarp::Router::rc, r), + r->crypto.get(), r->encryption, std::bind(&llarp::Router::rc, r), std::bind(&llarp::Router::HandleRecvLinkMessageBuffer, r, std::placeholders::_1, std::placeholders::_2), std::bind(&llarp::Router::OnSessionEstablished, r, @@ -665,8 +667,10 @@ namespace llarp remoteRC = msg->rc; Crypto()->shorthash(txKey, remoteRC.pubkey.as_buffer()); - if(!DoKeyExchange(Crypto()->transport_dh_server, rxKey, msg->N, - remoteRC.enckey, parent->TransportSecretKey())) + if(!DoKeyExchange(std::bind(&Crypto::transport_dh_server, Crypto(), _1, + _2, _3, _4), + rxKey, msg->N, remoteRC.enckey, + parent->TransportSecretKey())) return false; byte_t tmp[LinkIntroMessage::MaxSize]; @@ -706,8 +710,10 @@ namespace llarp Close(); return false; } - if(!DoKeyExchange(Crypto()->transport_dh_client, txKey, replymsg.N, - remoteRC.enckey, parent->RouterEncryptionSecret())) + if(!DoKeyExchange(std::bind(&Crypto::transport_dh_client, Crypto(), _1, + _2, _3, _4), + txKey, replymsg.N, remoteRC.enckey, + parent->RouterEncryptionSecret())) return false; llarp::LogDebug("Sent reply LIM"); @@ -761,8 +767,10 @@ namespace llarp } remoteRC = msg->rc; gotLIM = true; - if(!DoKeyExchange(Crypto()->transport_dh_server, rxKey, msg->N, - remoteRC.enckey, parent->RouterEncryptionSecret())) + + if(!DoKeyExchange( + std::bind(&Crypto::transport_dh_server, Crypto(), _1, _2, _3, _4), + rxKey, msg->N, remoteRC.enckey, parent->RouterEncryptionSecret())) { Close(); return false; @@ -814,9 +822,11 @@ namespace llarp Close(); return; } - if(!DoKeyExchange(Crypto()->transport_dh_client, txKey, msg.N, - remoteTransportPubKey, - parent->RouterEncryptionSecret())) + + if(!DoKeyExchange( + std::bind(&Crypto::transport_dh_client, Crypto(), _1, _2, _3, _4), + txKey, msg.N, remoteTransportPubKey, + parent->RouterEncryptionSecret())) { llarp::LogError("failed to mix keys for outbound session to ", remoteAddr); @@ -988,8 +998,9 @@ namespace llarp // set remote rc remoteRC = msg->rc; // recalcuate rx key - return DoKeyExchange(Crypto()->transport_dh_server, rxKey, msg->N, - remoteRC.enckey, parent->RouterEncryptionSecret()); + return DoKeyExchange( + std::bind(&Crypto::transport_dh_server, Crypto(), _1, _2, _3, _4), + rxKey, msg->N, remoteRC.enckey, parent->RouterEncryptionSecret()); } bool @@ -1012,8 +1023,9 @@ namespace llarp if(!SendMessageBuffer(buf)) return false; // regen our tx Key - return DoKeyExchange(Crypto()->transport_dh_client, txKey, lim.N, - remoteRC.enckey, parent->RouterEncryptionSecret()); + return DoKeyExchange( + std::bind(&Crypto::transport_dh_client, Crypto(), _1, _2, _3, _4), + txKey, lim.N, remoteRC.enckey, parent->RouterEncryptionSecret()); } bool @@ -1080,7 +1092,9 @@ namespace llarp // get message if(m_RecvMsgs.find(msgid) == m_RecvMsgs.end()) + { m_RecvMsgs.emplace(msgid, InboundMessage{}); + } auto itr = m_RecvMsgs.find(msgid); // add message activity diff --git a/llarp/messages/link_intro.cpp b/llarp/messages/link_intro.cpp index c314e2b6a..7a1c6ce52 100644 --- a/llarp/messages/link_intro.cpp +++ b/llarp/messages/link_intro.cpp @@ -117,7 +117,7 @@ namespace llarp bool LinkIntroMessage::HandleMessage(llarp::Router* router) const { - if(!Verify(&router->crypto)) + if(!Verify(router->crypto.get())) return false; return session->GotLIM(this); } diff --git a/llarp/messages/relay_commit.cpp b/llarp/messages/relay_commit.cpp index 4a855225e..df74fd9fc 100644 --- a/llarp/messages/relay_commit.cpp +++ b/llarp/messages/relay_commit.cpp @@ -269,19 +269,21 @@ namespace llarp return; } // generate path key as we are in a worker thread - auto DH = self->context->Crypto()->dh_server; - if(!DH(self->hop->pathKey, self->record.commkey, - self->context->EncryptionSecretKey(), self->record.tunnelNonce)) + auto crypto = self->context->Crypto(); + if(!crypto->dh_server(self->hop->pathKey, self->record.commkey, + self->context->EncryptionSecretKey(), + self->record.tunnelNonce)) { llarp::LogError("LRCM DH Failed ", info); delete self; return; } // generate hash of hop key for nonce mutation - self->context->Crypto()->shorthash(self->hop->nonceXOR, - self->hop->pathKey.as_buffer()); + crypto->shorthash(self->hop->nonceXOR, self->hop->pathKey.as_buffer()); + using namespace std::placeholders; if(self->record.work - && self->record.work->IsValid(self->context->Crypto()->shorthash, now)) + && self->record.work->IsValid( + std::bind(&Crypto::shorthash, crypto, _1, _2), now)) { llarp::LogDebug("LRCM extended lifetime by ", self->record.work->extendedLifetime, " seconds for ", diff --git a/llarp/net/exit_info.hpp b/llarp/net/exit_info.hpp index a5de731eb..d3a3c8610 100644 --- a/llarp/net/exit_info.hpp +++ b/llarp/net/exit_info.hpp @@ -23,9 +23,9 @@ namespace llarp struct in6_addr netmask; PubKey pubkey; - ExitInfo(const PubKey &pk, const nuint32_t &ipv4_exit) : IBEncodeMessage() + ExitInfo(const PubKey &pk, const nuint32_t &ipv4_exit) : IBEncodeMessage(), + pubkey(pk) { - pubkey = pk; memset(address.s6_addr, 0, 16); address.s6_addr[11] = 0xff; address.s6_addr[10] = 0xff; @@ -37,12 +37,11 @@ namespace llarp { } - ExitInfo(const ExitInfo &other) : IBEncodeMessage() + ExitInfo(const ExitInfo &other) : IBEncodeMessage(other.version), + pubkey(other.pubkey) { - pubkey = other.pubkey; memcpy(address.s6_addr, other.address.s6_addr, 16); memcpy(netmask.s6_addr, other.netmask.s6_addr, 16); - version = other.version; } ~ExitInfo(); diff --git a/llarp/path/path.cpp b/llarp/path/path.cpp index 204e4bab8..6b41569bb 100644 --- a/llarp/path/path.cpp +++ b/llarp/path/path.cpp @@ -43,7 +43,7 @@ namespace llarp llarp::Crypto* PathContext::Crypto() { - return &m_Router->crypto; + return m_Router->crypto.get(); } llarp::Logic* @@ -155,24 +155,26 @@ namespace llarp IHopHandler* PathContext::GetByUpstream(const RouterID& remote, const PathID_t& id) { - auto own = MapGet(m_OurPaths, id, - [](__attribute__((unused)) const PathSet* s) -> bool { - // TODO: is this right? - return true; - }, - [remote, id](PathSet* p) -> IHopHandler* { - return p->GetByUpstream(remote, id); - }); + auto own = MapGet( + m_OurPaths, id, + [](__attribute__((unused)) const PathSet* s) -> bool { + // TODO: is this right? + return true; + }, + [remote, id](PathSet* p) -> IHopHandler* { + return p->GetByUpstream(remote, id); + }); if(own) return own; - return MapGet(m_TransitPaths, id, - [remote](const std::shared_ptr< TransitHop >& hop) -> bool { - return hop->info.upstream == remote; - }, - [](const std::shared_ptr< TransitHop >& h) -> IHopHandler* { - return h.get(); - }); + return MapGet( + m_TransitPaths, id, + [remote](const std::shared_ptr< TransitHop >& hop) -> bool { + return hop->info.upstream == remote; + }, + [](const std::shared_ptr< TransitHop >& h) -> IHopHandler* { + return h.get(); + }); } bool @@ -189,13 +191,14 @@ namespace llarp IHopHandler* PathContext::GetByDownstream(const RouterID& remote, const PathID_t& id) { - return MapGet(m_TransitPaths, id, - [remote](const std::shared_ptr< TransitHop >& hop) -> bool { - return hop->info.downstream == remote; - }, - [](const std::shared_ptr< TransitHop >& h) -> IHopHandler* { - return h.get(); - }); + return MapGet( + m_TransitPaths, id, + [remote](const std::shared_ptr< TransitHop >& hop) -> bool { + return hop->info.downstream == remote; + }, + [](const std::shared_ptr< TransitHop >& h) -> IHopHandler* { + return h.get(); + }); } PathSet* @@ -506,7 +509,7 @@ namespace llarp TunnelNonce n = Y; for(const auto& hop : hops) { - r->crypto.xchacha20(buf, hop.shared, n); + r->crypto->xchacha20(buf, hop.shared, n); n ^= hop.nonceXOR; } RelayUpstreamMessage msg; @@ -546,7 +549,7 @@ namespace llarp for(const auto& hop : hops) { n ^= hop.nonceXOR; - r->crypto.xchacha20(buf, hop.shared, n); + r->crypto->xchacha20(buf, hop.shared, n); } return HandleRoutingMessage(buf, r); } @@ -605,7 +608,7 @@ namespace llarp if(buf.sz < MESSAGE_PAD_SIZE) { // randomize padding - r->crypto.randbytes(buf.cur, MESSAGE_PAD_SIZE - buf.sz); + r->crypto->randbytes(buf.cur, MESSAGE_PAD_SIZE - buf.sz); buf.sz = MESSAGE_PAD_SIZE; } buf.cur = buf.base; @@ -716,7 +719,7 @@ namespace llarp /// allows exits to close from their end if(SupportsAnyRoles(ePathRoleExit | ePathRoleSVC)) { - if(msg->Verify(&r->crypto, EndpointPubKey())) + if(msg->Verify(r->crypto.get(), EndpointPubKey())) { llarp::LogInfo(Name(), " had its exit closed"); _role &= ~ePathRoleExit; @@ -775,7 +778,7 @@ namespace llarp { if(m_ExitObtainTX && msg->T == m_ExitObtainTX) { - if(!msg->Verify(&r->crypto, EndpointPubKey())) + if(!msg->Verify(r->crypto.get(), EndpointPubKey())) { llarp::LogError(Name(), "RXM invalid signature"); return false; @@ -794,7 +797,7 @@ namespace llarp { if(m_ExitObtainTX && msg->T == m_ExitObtainTX) { - if(!msg->Verify(&r->crypto, EndpointPubKey())) + if(!msg->Verify(r->crypto.get(), EndpointPubKey())) { llarp::LogError(Name(), " GXM signature failed"); return false; diff --git a/llarp/path/pathbuilder.cpp b/llarp/path/pathbuilder.cpp index f8be5cf27..c6ab6f3b0 100644 --- a/llarp/path/pathbuilder.cpp +++ b/llarp/path/pathbuilder.cpp @@ -167,7 +167,7 @@ namespace llarp , numHops(hops) { p_router->paths.AddPathBuilder(this); - p_router->crypto.encryption_keygen(enckey); + p_router->crypto->encryption_keygen(enckey); _run.store(true); keygens.store(0); } @@ -281,7 +281,7 @@ namespace llarp lastBuild = Now(); // async generate keys AsyncPathKeyExchangeContext< Builder >* ctx = - new AsyncPathKeyExchangeContext< Builder >(&router->crypto); + new AsyncPathKeyExchangeContext< Builder >(router->crypto.get()); ctx->router = router; ctx->pathset = this; auto path = new llarp::path::Path(hops, this, roles); diff --git a/llarp/path/transit_hop.cpp b/llarp/path/transit_hop.cpp index 2a4e05983..22bd7adfa 100644 --- a/llarp/path/transit_hop.cpp +++ b/llarp/path/transit_hop.cpp @@ -75,7 +75,7 @@ namespace llarp { dlt = MESSAGE_PAD_SIZE - dlt; // randomize padding - r->crypto.randbytes(buf.cur, dlt); + r->crypto->randbytes(buf.cur, dlt); buf.sz += dlt; } buf.cur = buf.base; @@ -89,7 +89,7 @@ namespace llarp RelayDownstreamMessage msg; msg.pathid = info.rxID; msg.Y = Y ^ nonceXOR; - r->crypto.xchacha20(buf, pathKey, Y); + r->crypto->xchacha20(buf, pathKey, Y); msg.X = buf; llarp::LogDebug("relay ", msg.X.size(), " bytes downstream from ", info.upstream, " to ", info.downstream); @@ -100,7 +100,7 @@ namespace llarp TransitHop::HandleUpstream(llarp_buffer_t buf, const TunnelNonce& Y, llarp::Router* r) { - r->crypto.xchacha20(buf, pathKey, Y); + r->crypto->xchacha20(buf, pathKey, Y); if(IsEndpoint(r->pubkey())) { m_LastActivity = r->Now(); @@ -157,13 +157,13 @@ namespace llarp TransitHop::HandleObtainExitMessage( const llarp::routing::ObtainExitMessage* msg, llarp::Router* r) { - if(msg->Verify(&r->crypto) + if(msg->Verify(r->crypto.get()) && r->exitContext.ObtainNewExit(msg->I, info.rxID, msg->E != 0)) { llarp::routing::GrantExitMessage grant; grant.S = NextSeqNo(); grant.T = msg->T; - if(!grant.Sign(&r->crypto, r->identity)) + if(!grant.Sign(r->crypto.get(), r->identity)) { llarp::LogError("Failed to sign grant exit message"); return false; @@ -175,7 +175,7 @@ namespace llarp llarp::routing::RejectExitMessage reject; reject.S = NextSeqNo(); reject.T = msg->T; - if(!reject.Sign(&r->crypto, r->identity)) + if(!reject.Sign(r->crypto.get(), r->identity)) { llarp::LogError("Failed to sign reject exit message"); return false; @@ -189,13 +189,13 @@ namespace llarp { llarp::routing::DataDiscardMessage discard(info.rxID, msg->S); auto ep = r->exitContext.FindEndpointForPath(info.rxID); - if(ep && msg->Verify(&r->crypto, ep->PubKey())) + if(ep && msg->Verify(r->crypto.get(), ep->PubKey())) { ep->Close(); // ep is now gone af llarp::routing::CloseExitMessage reply; reply.S = NextSeqNo(); - if(reply.Sign(&r->crypto, r->identity)) + if(reply.Sign(r->crypto.get(), r->identity)) return SendRoutingMessage(&reply, r); } return SendRoutingMessage(&discard, r); @@ -218,7 +218,7 @@ namespace llarp auto ep = r->exitContext.FindEndpointForPath(msg->P); if(ep) { - if(!msg->Verify(&r->crypto, ep->PubKey())) + if(!msg->Verify(r->crypto.get(), ep->PubKey())) return false; if(ep->UpdateLocalPath(info.rxID)) diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 582d4280e..cbbf14ae2 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -196,7 +197,7 @@ namespace llarp Router::OnSessionEstablished(llarp::RouterContact rc) { async_verify_RC(rc, nullptr); - llarp::LogInfo("session with ", rc.pubkey, "established"); + llarp::LogInfo("session with ", rc.pubkey, " established"); } Router::Router(struct llarp_threadpool *_tp, struct llarp_ev_loop *_netloop, @@ -205,7 +206,7 @@ namespace llarp , netloop(_netloop) , tp(_tp) , logic(_logic) - , crypto(llarp::Crypto::sodium{}) + , crypto(std::make_unique< sodium::CryptoLibSodium >()) , paths(this) , exitContext(this) , dht(llarp_dht_context_new(this)) @@ -343,7 +344,7 @@ namespace llarp { return; } - if(results[0].Verify(&crypto, Now())) + if(results[0].Verify(crypto.get(), Now())) { nodedb->Insert(results[0]); TryConnectAsync(results[0], 10); @@ -391,7 +392,7 @@ namespace llarp llarp::LogError("failure to decode or verify of remote RC"); return; } - if(remote.Verify(&crypto, Now())) + if(remote.Verify(crypto.get(), Now())) { llarp::LogDebug("verified signature"); // store into filesystem @@ -415,15 +416,16 @@ namespace llarp if(!EnsureEncryptionKey()) return false; if(usingSNSeed) - return llarp_loadServiceNodeIdentityKey(&crypto, ident_keyfile, identity); + return llarp_loadServiceNodeIdentityKey(crypto.get(), ident_keyfile, + identity); else - return llarp_findOrCreateIdentity(&crypto, ident_keyfile, identity); + return llarp_findOrCreateIdentity(crypto.get(), ident_keyfile, identity); } bool Router::EnsureEncryptionKey() { - return llarp_findOrCreateEncryption(&crypto, encryption_keyfile, + return llarp_findOrCreateEncryption(crypto.get(), encryption_keyfile, this->encryption); } @@ -459,7 +461,7 @@ namespace llarp Router::SaveRC() { llarp::LogDebug("verify RC signature"); - if(!_rc.Verify(&crypto, Now())) + if(!_rc.Verify(crypto.get(), Now())) { rc().Dump< MAX_RC_SIZE >(); llarp::LogError("RC is invalid, not saving"); @@ -585,7 +587,7 @@ namespace llarp return; for(const auto &rc : results) { - if(rc.Verify(&crypto, Now())) + if(rc.Verify(crypto.get(), Now())) nodedb->Insert(rc); else return; @@ -672,11 +674,11 @@ namespace llarp llarp::RouterContact nextRC = _rc; if(rotateKeys) { - crypto.encryption_keygen(nextOnionKey); + crypto->encryption_keygen(nextOnionKey); nextRC.enckey = llarp::seckey_topublic(nextOnionKey); } nextRC.last_updated = Now(); - if(!nextRC.Sign(&crypto, identity)) + if(!nextRC.Sign(crypto.get(), identity)) return false; _rc = nextRC; if(rotateKeys) @@ -817,7 +819,7 @@ namespace llarp bool Router::Sign(llarp::Signature &sig, llarp_buffer_t buf) const { - return crypto.sign(sig, identity, buf); + return crypto->sign(sig, identity, buf); } void @@ -967,7 +969,7 @@ namespace llarp job->nodedb = nodedb; job->logic = logic; - // job->crypto = &crypto; // we already have this + // job->crypto = crypto.get(); // we already have this job->cryptoworker = tp; job->diskworker = disk; if(rc.IsPublicRouter()) @@ -1003,6 +1005,21 @@ namespace llarp } llarp::LogInfo("Bound RPC server to ", rpcBindAddr); } + if(whitelistRouters) + { + rpcCaller = std::make_unique(this); + rpcCaller->SetBasicAuth(lokidRPCUser, lokidRPCPassword); + while(!rpcCaller->Start(lokidRPCAddr)) + { + llarp::LogError("failed to start jsonrpc caller to ", lokidRPCAddr); +#if defined(ANDROID) || defined(RPI) + sleep(1); +#else + std::this_thread::sleep_for(std::chrono::seconds(1)); +#endif + } + llarp::LogInfo("RPC Caller to ", lokidRPCAddr, " started"); + } llarp_threadpool_start(tp); llarp_threadpool_start(disk); @@ -1042,8 +1059,7 @@ namespace llarp if(ExitEnabled()) { llarp::nuint32_t a = publicAddr.xtonl(); - // TODO: enable this once the network can serialize xi - //_rc.exits.emplace_back(_rc.pubkey, a); + _rc.exits.emplace_back(_rc.pubkey, a); llarp::LogInfo( "Neato tehl33toh, You are a freaking exit relay. w00t!!!!! your " "exit " @@ -1051,7 +1067,7 @@ namespace llarp a); } llarp::LogInfo("Signing rc..."); - if(!_rc.Sign(&crypto, identity)) + if(!_rc.Sign(crypto.get(), identity)) { llarp::LogError("failed to sign rc"); return false; @@ -1104,11 +1120,11 @@ namespace llarp { // we are a client // regenerate keys and resign rc before everything else - crypto.identity_keygen(identity); - crypto.encryption_keygen(encryption); + crypto->identity_keygen(identity); + crypto->encryption_keygen(encryption); _rc.pubkey = llarp::seckey_topublic(identity); _rc.enckey = llarp::seckey_topublic(encryption); - if(!_rc.Sign(&crypto, identity)) + if(!_rc.Sign(crypto.get(), identity)) { llarp::LogError("failed to regenerate keys and sign RC"); return false; @@ -1408,11 +1424,13 @@ namespace llarp { // fallback defaults // To NeuroScr: why run findFree* here instead of in tun.cpp? + // I think it should be in tun.cpp, better to closer to time of usage + // that way new tun may have grab a range we may have also grabbed here static const std::unordered_map< std::string, std::function< std::string(void) > > netConfigDefaults = { - {"ifname", llarp::findFreeLokiTunIfName}, - {"ifaddr", llarp::findFreePrivateRange}, + {"ifname", []() -> std::string { return "auto"; }}, + {"ifaddr", []() -> std::string { return "auto"; }}, {"local-dns", []() -> std::string { return "127.0.0.1:53"; }}, {"upstream-dns", []() -> std::string { return "1.1.1.1:53"; }}}; // populate with fallback defaults if values not present @@ -1591,6 +1609,14 @@ namespace llarp { self->lokidRPCAddr = val; } + if(StrEq(key, "username")) + { + self->lokidRPCUser = val; + } + if(StrEq(key, "password")) + { + self->lokidRPCPassword = val; + } } else if(StrEq(section, "dns")) { @@ -1608,6 +1634,7 @@ namespace llarp else if(StrEq(section, "connect") || (StrEq(section, "bootstrap") && StrEq(key, "add-node"))) { + //llarp::LogDebug("connect section has ", key, "=", val); self->bootstrapRCList.emplace_back(); auto &rc = self->bootstrapRCList.back(); if(!rc.Read(val)) @@ -1617,7 +1644,7 @@ namespace llarp self->bootstrapRCList.pop_back(); return; } - if(rc.Verify(&self->crypto, self->Now())) + if(rc.Verify(self->crypto.get(), self->Now())) { llarp::LogInfo("Added bootstrap node ", RouterID(rc.pubkey)); } diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index c385e7aa7..23c340963 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -26,9 +26,10 @@ #include #include #include -#include -#include +#include #include +#include +#include namespace llarp { @@ -105,7 +106,7 @@ namespace llarp llarp_ev_loop *netloop; llarp_threadpool *tp; llarp::Logic *logic; - llarp::Crypto crypto; + std::unique_ptr< llarp::Crypto > crypto; llarp::path::PathContext paths; llarp::exit::Context exitContext; llarp::SecretKey identity; @@ -174,7 +175,9 @@ namespace llarp /// lokid caller const std::string DefaultLokidRPCAddr = "127.0.0.1:22023"; std::unique_ptr< llarp::rpc::Caller > rpcCaller; - std::string lokidRPCAddr = DefaultLokidRPCAddr; + std::string lokidRPCAddr = DefaultLokidRPCAddr; + std::string lokidRPCUser = ""; + std::string lokidRPCPassword = ""; std::set< std::unique_ptr< llarp::ILinkLayer >, CompareLinks< llarp::ILinkLayer > > diff --git a/llarp/router_contact.hpp b/llarp/router_contact.hpp index 5e01875f0..3d3b1c077 100644 --- a/llarp/router_contact.hpp +++ b/llarp/router_contact.hpp @@ -8,6 +8,7 @@ #include #include +#include #include #define MAX_RC_SIZE (1024) @@ -188,6 +189,9 @@ namespace llarp bool VerifySignature(llarp::Crypto *crypto) const; }; + + using RouterLookupHandler = + std::function< void(const std::vector< RouterContact >&) >; } // namespace llarp #endif diff --git a/llarp/router_id.hpp b/llarp/router_id.hpp index 0a64f2da7..695af7e4e 100644 --- a/llarp/router_id.hpp +++ b/llarp/router_id.hpp @@ -44,6 +44,13 @@ namespace llarp using Hash = AlignedBuffer< SIZE >::Hash; }; + + inline bool + operator==(const RouterID& lhs, const RouterID& rhs) + { + return lhs.as_array() == rhs.as_array(); + } + } // namespace llarp #endif diff --git a/llarp/rpc/rpc.cpp b/llarp/rpc/rpc.cpp index b94a9b82b..74dd7d874 100644 --- a/llarp/rpc/rpc.cpp +++ b/llarp/rpc/rpc.cpp @@ -3,6 +3,7 @@ #include #ifdef USE_ABYSS +#include #include #endif @@ -14,8 +15,9 @@ namespace llarp struct CallerHandler : public ::abyss::http::IRPCClientHandler { - CallerHandler(::abyss::http::ConnImpl* impl) - : ::abyss::http::IRPCClientHandler(impl) + CallerImpl* m_Parent; + CallerHandler(::abyss::http::ConnImpl* impl, CallerImpl* parent) + : ::abyss::http::IRPCClientHandler(impl), m_Parent(parent) { } @@ -44,16 +46,12 @@ namespace llarp } void - PopulateReqHeaders(abyss::http::Headers_t& hdr) - { - (void)hdr; - // TODO: add http auth (?) - } + PopulateReqHeaders(abyss::http::Headers_t& hdr); }; struct GetServiceNodeListHandler final : public CallerHandler { - using PubkeyList_t = std::vector< PubKey >; + using PubkeyList_t = std::vector< RouterID >; using Callback_t = std::function< void(const PubkeyList_t&, bool) >; ~GetServiceNodeListHandler() @@ -61,8 +59,9 @@ namespace llarp } Callback_t handler; - GetServiceNodeListHandler(::abyss::http::ConnImpl* impl, Callback_t h) - : CallerHandler(impl), handler(h) + GetServiceNodeListHandler(::abyss::http::ConnImpl* impl, + CallerImpl* parent, Callback_t h) + : CallerHandler(impl, parent), handler(h) { } @@ -92,8 +91,12 @@ namespace llarp if(key_itr->IsString()) { keys.emplace_back(); - if(!HexDecode(key_itr->GetString(), keys.back().begin(), - decltype(keys)::value_type::SIZE)) + std::string str = key_itr->GetString(); + if(str.size() != Base32DecodeSize(keys.back().size())) + { + keys.pop_back(); + } + else if(!Base32Decode(str, keys.back())) { keys.pop_back(); } @@ -114,10 +117,13 @@ namespace llarp struct CallerImpl : public ::abyss::http::JSONRPC { Router* router; - llarp_time_t m_NextKeyUpdate; + llarp_time_t m_NextKeyUpdate = 0; const llarp_time_t KeyUpdateInterval = 1000 * 60 * 2; using PubkeyList_t = GetServiceNodeListHandler::PubkeyList_t; + std::string username; + std::string password; + CallerImpl(Router* r) : ::abyss::http::JSONRPC(), router(r) { } @@ -133,13 +139,20 @@ namespace llarp Flush(); } + void + SetBasicAuth(const std::string& user, const std::string& passwd) + { + username = user; + password = passwd; + } + void AsyncUpdatePubkeyList() { LogInfo("Updating service node list"); ::abyss::json::Value params; params.SetObject(); - QueueRPC("/get_all_service_node_keys", std::move(params), + QueueRPC("get_all_service_nodes_keys", std::move(params), std::bind(&CallerImpl::NewAsyncUpdatePubkeyListConn, this, std::placeholders::_1)); } @@ -154,7 +167,7 @@ namespace llarp NewAsyncUpdatePubkeyListConn(abyss::http::ConnImpl* impl) { return new GetServiceNodeListHandler( - impl, + impl, this, std::bind(&CallerImpl::HandleServiceNodeListUpdated, this, std::placeholders::_1, std::placeholders::_2)); } @@ -180,6 +193,18 @@ namespace llarp } }; + void + CallerHandler::PopulateReqHeaders(abyss::http::Headers_t& hdr) + { + if(m_Parent->username.empty() || m_Parent->password.empty()) + return; + std::stringstream ss; + ss << "Basic "; + std::string cred = m_Parent->username + ":" + m_Parent->password; + llarp::Base64Encode(ss, (const byte_t*)cred.c_str(), cred.size()); + hdr.emplace("Authorization", ss.str()); + } + struct Handler : public ::abyss::httpd::IRPCHandler { Router* router; @@ -354,6 +379,11 @@ namespace llarp { (void)now; } + + void + SetBasicAuth(const std::string&, const std::string&) + { + } }; #endif @@ -385,6 +415,12 @@ namespace llarp m_Impl->Tick(now); } + void + Caller::SetBasicAuth(const std::string& user, const std::string& passwd) + { + m_Impl->SetBasicAuth(user, passwd); + } + Server::Server(Router* r) : m_Impl(new ServerImpl(r)) { } diff --git a/llarp/rpc/rpc.hpp b/llarp/rpc/rpc.hpp index 15916d4a4..611962b47 100644 --- a/llarp/rpc/rpc.hpp +++ b/llarp/rpc/rpc.hpp @@ -40,6 +40,10 @@ namespace llarp Caller(Router* r); ~Caller(); + /// set http basic auth for use with remote rpc endpoint + void + SetBasicAuth(const std::string& user, const std::string& password); + /// start with jsonrpc endpoint address bool Start(const std::string& remote); @@ -48,10 +52,6 @@ namespace llarp void Stop(); - /// test if a router is valid - bool - VerifyRouter(const PubKey& pk); - /// do per second tick void Tick(llarp_time_t now); diff --git a/llarp/service/IntroSet.cpp b/llarp/service/IntroSet.cpp index 571733dae..3581f77b8 100644 --- a/llarp/service/IntroSet.cpp +++ b/llarp/service/IntroSet.cpp @@ -122,7 +122,8 @@ namespace llarp if(!A.Verify(crypto, buf, Z)) return false; // validate PoW - if(W && !W->IsValid(crypto->shorthash, now)) + using namespace std::placeholders; + if(W && !W->IsValid(std::bind(&Crypto::shorthash, crypto, _1, _2), now)) return false; // valid timestamps // add max clock skew diff --git a/llarp/service/IntroSet.hpp b/llarp/service/IntroSet.hpp index 17c9fc9b7..46577f085 100644 --- a/llarp/service/IntroSet.hpp +++ b/llarp/service/IntroSet.hpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -153,6 +154,10 @@ namespace llarp bool Verify(llarp::Crypto* crypto, llarp_time_t now) const; }; + + using IntroSetLookupHandler = + std::function< void(const std::vector< IntroSet >&) >; + } // namespace service } // namespace llarp diff --git a/llarp/service/context.cpp b/llarp/service/context.cpp index 243346ec9..7a14a6c12 100644 --- a/llarp/service/context.cpp +++ b/llarp/service/context.cpp @@ -30,6 +30,22 @@ namespace llarp return true; } + void + Context::ForEachService( + std::function< bool(const std::string &, + const std::unique_ptr< Endpoint > &) > + visit) + { + auto itr = m_Endpoints.begin(); + while(itr != m_Endpoints.end()) + { + if(visit(itr->first, itr->second)) + ++itr; + else + return; + } + } + bool Context::RemoveEndpoint(const std::string &name) { @@ -274,17 +290,18 @@ namespace llarp static std::map< std::string, std::function< llarp::service::Endpoint *( - const std::string &, llarp::Router *) > > + const std::string &, llarp::Router *, + llarp::service::Context *) > > endpointConstructors = { {"tun", - [](const std::string &nick, - llarp::Router *r) -> llarp::service::Endpoint * { - return new llarp::handlers::TunEndpoint(nick, r); + [](const std::string &nick, llarp::Router *r, + llarp::service::Context *c) -> llarp::service::Endpoint * { + return new llarp::handlers::TunEndpoint(nick, r, c); }}, {"null", - [](const std::string &nick, - llarp::Router *r) -> llarp::service::Endpoint * { - return new llarp::handlers::NullEndpoint(nick, r); + [](const std::string &nick, llarp::Router *r, + llarp::service::Context *c) -> llarp::service::Endpoint * { + return new llarp::handlers::NullEndpoint(nick, r, c); }}}; { @@ -297,7 +314,7 @@ namespace llarp } // construct - service.reset(itr->second(conf.first, m_Router)); + service.reset(itr->second(conf.first, m_Router, this)); // if ephemeral, then we need to regen key // if privkey file, then set it and load it diff --git a/llarp/service/context.hpp b/llarp/service/context.hpp index 87cde2bc9..de002223f 100644 --- a/llarp/service/context.hpp +++ b/llarp/service/context.hpp @@ -61,6 +61,12 @@ namespace llarp bool iterate(struct endpoint_iter &i); + /// function visitor returns false to prematurely break iteration + void + ForEachService(std::function< bool(const std::string &, + const std::unique_ptr< Endpoint > &) > + visit); + /// hint at possible path usage and trigger building early bool Prefetch(const llarp::service::Address &addr); diff --git a/llarp/service/endpoint.cpp b/llarp/service/endpoint.cpp index 6bafeb3a2..f91ea8d05 100644 --- a/llarp/service/endpoint.cpp +++ b/llarp/service/endpoint.cpp @@ -16,8 +16,10 @@ namespace llarp { namespace service { - Endpoint::Endpoint(const std::string& name, llarp::Router* r) + Endpoint::Endpoint(const std::string& name, llarp::Router* r, + Context* parent) : path::Builder(r, r->dht, 6, DEFAULT_HOP_LENGTH) + , context(parent) , m_Router(r) , m_Name(name) { @@ -116,7 +118,7 @@ namespace llarp return; } m_IntroSet.topic = m_Tag; - if(!m_Identity.SignIntroSet(m_IntroSet, &m_Router->crypto, now)) + if(!m_Identity.SignIntroSet(m_IntroSet, m_Router->crypto.get(), now)) { llarp::LogWarn("failed to sign introset for endpoint ", Name()); return; @@ -352,7 +354,7 @@ namespace llarp bool Endpoint::HandleGotIntroMessage(const llarp::dht::GotIntroMessage* msg) { - auto crypto = &m_Router->crypto; + auto crypto = m_Router->crypto.get(); std::set< IntroSet > remote; for(const auto& introset : msg->I) { @@ -492,7 +494,7 @@ namespace llarp bool Endpoint::LoadKeyFile() { - auto crypto = &m_Router->crypto; + auto crypto = m_Router->crypto.get(); if(m_Keyfile.size()) { if(!m_Identity.EnsureKeys(m_Keyfile, crypto)) @@ -1233,7 +1235,7 @@ namespace llarp f.C.Zero(); transfer.Y.Randomize(); transfer.P = remoteIntro.pathID; - if(!f.EncryptAndSign(&Router()->crypto, m, K, m_Identity)) + if(!f.EncryptAndSign(Router()->crypto.get(), m, K, m_Identity)) { llarp::LogError("failed to encrypt and sign"); return false; @@ -1260,12 +1262,13 @@ namespace llarp } } // no converstation - return EnsurePathToService(remote, - [](Address, OutboundContext* c) { - if(c) - c->UpdateIntroSet(true); - }, - 5000, false); + return EnsurePathToService( + remote, + [](Address, OutboundContext* c) { + if(c) + c->UpdateIntroSet(true); + }, + 5000, false); } bool @@ -1491,9 +1494,11 @@ namespace llarp // compure post handshake session key // PKE (A, B, N) SharedSecret sharedSecret; - if(!self->m_LocalIdentity.KeyExchange(self->crypto->dh_client, - sharedSecret, self->remote, - self->frame.N)) + using namespace std::placeholders; + path_dh_func dh_client = + std::bind(&Crypto::dh_client, self->crypto, _1, _2, _3, _4); + if(!self->m_LocalIdentity.KeyExchange(dh_client, sharedSecret, + self->remote, self->frame.N)) { llarp::LogError("failed to derive x25519 shared key component"); } @@ -1726,7 +1731,7 @@ namespace llarp Endpoint::SendContext::EncryptAndSendTo(llarp_buffer_t payload, ProtocolType t) { - auto crypto = m_Endpoint->Router()->crypto; + auto crypto = m_Endpoint->Router()->crypto.get(); SharedSecret shared; routing::PathTransferMessage msg; ProtocolFrame& f = msg.T; @@ -1762,7 +1767,7 @@ namespace llarp m.PutBuffer(payload); m.tag = f.T; - if(!f.EncryptAndSign(&crypto, m, shared, m_Endpoint->m_Identity)) + if(!f.EncryptAndSign(crypto, m, shared, m_Endpoint->m_Identity)) { llarp::LogError("failed to sign"); return; @@ -1804,7 +1809,7 @@ namespace llarp llarp::Crypto* Endpoint::Crypto() { - return &m_Router->crypto; + return m_Router->crypto.get(); } llarp_threadpool* diff --git a/llarp/service/endpoint.hpp b/llarp/service/endpoint.hpp index e49acf4cf..1e2445723 100644 --- a/llarp/service/endpoint.hpp +++ b/llarp/service/endpoint.hpp @@ -20,6 +20,8 @@ namespace llarp { namespace service { + // foward declare + struct Context; // forward declare struct AsyncKeyExchange; @@ -35,7 +37,7 @@ namespace llarp static const size_t MAX_OUTBOUND_CONTEXT_COUNT = 4; - Endpoint(const std::string& nickname, llarp::Router* r); + Endpoint(const std::string& nickname, llarp::Router* r, Context* parent); ~Endpoint(); void @@ -47,6 +49,20 @@ namespace llarp virtual void Tick(llarp_time_t now); + /// return true if we have a resolvable ip address + virtual bool + HasIfAddr() const + { + return false; + } + + /// get our ifaddr if it is set + virtual huint32_t + GetIfAddr() const + { + return huint32_t{0}; + } + /// router's logic llarp::Logic* RouterLogic(); @@ -381,6 +397,9 @@ namespace llarp IntroSetPublished(); protected: + /// parent context that owns this endpoint + Context* const context; + void RegenAndPublishIntroSet(llarp_time_t now, bool forceRebuild = false); diff --git a/llarp/service/protocol.cpp b/llarp/service/protocol.cpp index 86e85c2da..fc564d7f7 100644 --- a/llarp/service/protocol.cpp +++ b/llarp/service/protocol.cpp @@ -269,7 +269,11 @@ namespace llarp } // PKE (A, B, N) SharedSecret sharedSecret; - if(!self->m_LocalIdentity.KeyExchange(crypto->dh_server, sharedSecret, + using namespace std::placeholders; + path_dh_func dh_server = + std::bind(&Crypto::dh_server, self->crypto, _1, _2, _3, _4); + + if(!self->m_LocalIdentity.KeyExchange(dh_server, sharedSecret, self->msg->sender, self->frame.N)) { llarp::LogError("x25519 key exchange failed"); diff --git a/llarp/util/encode.hpp b/llarp/util/encode.hpp index 376fbc29a..ec024749d 100644 --- a/llarp/util/encode.hpp +++ b/llarp/util/encode.hpp @@ -31,13 +31,19 @@ namespace llarp return b * d.quot; } + static size_t + Base32DecodeSize(size_t sz) + { + return DecodeSize<5, 8>(sz); + } + template < typename Stack, typename V > bool Base32Decode(const Stack& stack, V& value) { int tmp = 0, bits = 0; size_t ret = 0; - size_t len = DecodeSize< 5, 8 >(value.size()); + size_t len = Base32DecodeSize(value.size()); size_t outLen = value.size(); for(size_t i = 0; i < len; i++) { @@ -146,6 +152,68 @@ namespace llarp return sz == 0; } + static const char base64_table[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; + + template < typename OStream_t > + void + Base64Encode(OStream_t& out, const uint8_t* src, size_t len) + { + size_t i = 0; + size_t j = 0; + uint8_t buf[4] = {0}; + uint8_t tmp[3] = {0}; + while(len--) + { + tmp[i++] = *(src++); + if(3 == i) + { + buf[0] = (tmp[0] & 0xfc) >> 2; + buf[1] = ((tmp[0] & 0x03) << 4) + ((tmp[1] & 0xf0) >> 4); + buf[2] = ((tmp[1] & 0x0f) << 2) + ((tmp[2] & 0xc0) >> 6); + buf[3] = tmp[2] & 0x3f; + + // encode + for(i = 0; i < 4; ++i) + { + out << base64_table[buf[i]]; + } + // reset + i = 0; + } + } + + // remainder + if(i > 0) + { + // fill `tmp' with `\0' at most 3 times + for(j = i; j < 3; ++j) + { + tmp[j] = 0; + } + + // encode remainder + buf[0] = (tmp[0] & 0xfc) >> 2; + buf[1] = ((tmp[0] & 0x03) << 4) + ((tmp[1] & 0xf0) >> 4); + buf[2] = ((tmp[1] & 0x0f) << 2) + ((tmp[2] & 0xc0) >> 6); + buf[3] = tmp[2] & 0x3f; + for(j = 0; (j < i + 1); ++j) + { + out << base64_table[buf[j]]; + } + + // pad + while((i++ < 3)) + { + out << '='; + } + } + } + } // namespace llarp #endif diff --git a/test/crypto/test_llarp_crypto.cpp b/test/crypto/test_llarp_crypto.cpp index 6aaf5b3b9..0c3427fd2 100644 --- a/test/crypto/test_llarp_crypto.cpp +++ b/test/crypto/test_llarp_crypto.cpp @@ -1,4 +1,4 @@ -#include +#include #include @@ -6,16 +6,15 @@ namespace llarp { - struct IdentityKeyTest : public ::testing::Test + struct IdentityKeyTest : public ::testing::Test { - llarp::Crypto crypto; + llarp::sodium::CryptoLibSodium crypto; llarp::IdentitySecret seed; - IdentityKeyTest() : crypto(llarp::Crypto::sodium{}) + IdentityKeyTest() { - } - + llarp::Crypto* Crypto() { @@ -27,15 +26,13 @@ namespace llarp { seed.Randomize(); } - - }; TEST_F(IdentityKeyTest, TestSeedToSecretKey) { SecretKey secret; ASSERT_TRUE(crypto.seed_to_secretkey(secret, seed)); - AlignedBuffer<128> random; + AlignedBuffer< 128 > random; random.Randomize(); Signature sig; ASSERT_TRUE(crypto.sign(sig, secret, random.as_buffer())); @@ -47,10 +44,10 @@ namespace llarp struct PQCryptoTest : public ::testing::Test { - llarp::Crypto crypto; + llarp::sodium::CryptoLibSodium crypto; PQKeyPair keys; - PQCryptoTest() : crypto(llarp::Crypto::sodium{}) + PQCryptoTest() { } diff --git a/test/dht/mock_context.cpp b/test/dht/mock_context.cpp new file mode 100644 index 000000000..a0ce0f1ce --- /dev/null +++ b/test/dht/mock_context.cpp @@ -0,0 +1 @@ +#include \ No newline at end of file diff --git a/test/dht/mock_context.hpp b/test/dht/mock_context.hpp new file mode 100644 index 000000000..7953cd3a2 --- /dev/null +++ b/test/dht/mock_context.hpp @@ -0,0 +1,46 @@ +#ifndef TEST_LLARP_MOCK_CONTEXT +#define TEST_LLARP_MOCK_CONTEXT + +#include + +#include + +namespace llarp +{ + namespace test + { + struct MockContext final : public dht::AbstractContext + { + MOCK_METHOD2(LookupRouter, bool(const RouterID&, RouterLookupHandler)); + + MOCK_METHOD6(LookupIntroSetRecursive, + void(const service::Address&, const dht::Key_t&, uint64_t, + const dht::Key_t&, uint64_t, + service::IntroSetLookupHandler)); + + MOCK_METHOD5(LookupIntroSetIterative, + void(const service::Address&, const dht::Key_t&, uint64_t, + const dht::Key_t&, service::IntroSetLookupHandler)); + + MOCK_METHOD3( + FindRandomIntroSetsWithTagExcluding, + std::set< service::IntroSet >(const service::Tag&, size_t, + const std::set< service::IntroSet >&)); + + MOCK_METHOD3(DHTSendTo, void(const RouterID&, dht::IMessage*, bool)); + + MOCK_CONST_METHOD0(Now, llarp_time_t()); + + MOCK_CONST_METHOD0(Crypto, llarp::Crypto*()); + + MOCK_CONST_METHOD0(GetRouter, llarp::Router*()); + + MOCK_CONST_METHOD0(OurKey, const dht::Key_t&()); + + MOCK_CONST_METHOD0(Nodes, dht::Bucket< dht::RCNode >*()); + }; + + } // namespace test +} // namespace llarp + +#endif diff --git a/test/dht/test_llarp_dht_explorenetworkjob.cpp b/test/dht/test_llarp_dht_explorenetworkjob.cpp new file mode 100644 index 000000000..9946cba05 --- /dev/null +++ b/test/dht/test_llarp_dht_explorenetworkjob.cpp @@ -0,0 +1,105 @@ +#include + +#include +#include +#include + +#include + +using namespace llarp; +using namespace ::testing; + +using test::makeBuf; + +struct TestDhtExploreNetworkJob : public ::testing::Test +{ + RouterID peer; + test::MockContext context; + dht::ExploreNetworkJob exploreNetworkJob; + + TestDhtExploreNetworkJob() + : peer(makeBuf< RouterID >(0x01)), exploreNetworkJob(peer, &context) + { + } +}; + +TEST_F(TestDhtExploreNetworkJob, validate) +{ + const RouterID other = makeBuf< RouterID >(0x02); + ASSERT_TRUE(exploreNetworkJob.Validate(other)); +} + +TEST_F(TestDhtExploreNetworkJob, get_next_peer) +{ + dht::Key_t key = makeBuf< dht::Key_t >(0x02); + std::set< dht::Key_t > exclude; + ASSERT_FALSE(exploreNetworkJob.GetNextPeer(key, exclude)); +} + +TEST_F(TestDhtExploreNetworkJob, do_next) +{ + const dht::Key_t key = makeBuf< dht::Key_t >(0x02); + ASSERT_NO_THROW(exploreNetworkJob.DoNextRequest(key)); +} + +TEST_F(TestDhtExploreNetworkJob, start) +{ + // Verify input arguments are passed correctly. + // The actual logic is inside the `dht::AbstractContext` implementation. + + const auto txKey = makeBuf< dht::Key_t >(0x02); + uint64_t txId = 4; + + dht::TXOwner txOwner(txKey, txId); + + // clang-format off + EXPECT_CALL(context, DHTSendTo( + Eq(txKey.as_array()), + WhenDynamicCastTo< dht::FindRouterMessage* >(NotNull()), + true) + ).Times(1); + // clang-format off + + ASSERT_NO_THROW(exploreNetworkJob.Start(txOwner)); +} + +TEST_F(TestDhtExploreNetworkJob, send_reply) +{ + // Concerns: + // - Empty collection + // - Lookup router fails (returns false) + // - Number of calls matches collection size + + { + exploreNetworkJob.valuesFound.clear(); + EXPECT_CALL(context, LookupRouter(_, _)).Times(0); + EXPECT_CALL(context, GetRouter()).WillOnce(Return(nullptr)); + + ASSERT_NO_THROW(exploreNetworkJob.SendReply()); + } + + { + exploreNetworkJob.valuesFound.clear(); + exploreNetworkJob.valuesFound.push_back(makeBuf(0x00)); + exploreNetworkJob.valuesFound.push_back(makeBuf(0x01)); + exploreNetworkJob.valuesFound.push_back(makeBuf(0x02)); + + EXPECT_CALL(context, GetRouter()).WillOnce(Return(nullptr)); + EXPECT_CALL(context, LookupRouter(Ne(makeBuf(0x01)), _)).Times(2).WillRepeatedly(Return(true)); + EXPECT_CALL(context, LookupRouter(Eq(makeBuf(0x01)), _)).WillOnce(Return(false)); + + ASSERT_NO_THROW(exploreNetworkJob.SendReply()); + } + + { + exploreNetworkJob.valuesFound.clear(); + exploreNetworkJob.valuesFound.push_back(makeBuf(0x00)); + exploreNetworkJob.valuesFound.push_back(makeBuf(0x01)); + exploreNetworkJob.valuesFound.push_back(makeBuf(0x02)); + + EXPECT_CALL(context, GetRouter()).WillOnce(Return(nullptr)); + EXPECT_CALL(context, LookupRouter(_, _)).Times(3).WillRepeatedly(Return(true)); + + ASSERT_NO_THROW(exploreNetworkJob.SendReply()); + } +} diff --git a/test/dht/test_llarp_dht_tx.cpp b/test/dht/test_llarp_dht_tx.cpp new file mode 100644 index 000000000..89a90014b --- /dev/null +++ b/test/dht/test_llarp_dht_tx.cpp @@ -0,0 +1,170 @@ +#include + +#include + +#include +#include + +using namespace llarp; +using namespace ::testing; + +using llarp::test::makeBuf; + +// Mock implementation of TX. +struct TestTx final : public dht::TX< dht::Key_t, std::string > +{ + TestTx(const dht::TXOwner& asker, const dht::Key_t& k, + dht::AbstractContext* p) + : dht::TX< dht::Key_t, std::string >(asker, k, p) + { + } + + MOCK_CONST_METHOD1(Validate, bool(const std::string&)); + + MOCK_METHOD1(Start, void(const dht::TXOwner&)); + + MOCK_METHOD2(GetNextPeer, bool(dht::Key_t&, const std::set< dht::Key_t >&)); + + MOCK_METHOD1(DoNextRequest, void(const dht::Key_t&)); + + MOCK_METHOD0(SendReply, void()); +}; + +struct TestDhtTx : public Test +{ + dht::TXOwner asker; + dht::Key_t key; + TestTx tx; + + TestDhtTx() : tx(asker, key, nullptr) + { + } +}; + +TEST_F(TestDhtTx, on_found) +{ + // Concerns + // - Validate returns true + // - Repeated call on success + // - Validate returns false + // - Repeated call on failure + // - Repeated call on success after failure + + const auto key = makeBuf< dht::Key_t >(0x00); + std::string val("good value"); + + // Validate returns true + { + EXPECT_CALL(tx, Validate(val)).WillOnce(Return(true)); + + tx.OnFound(key, val); + + ASSERT_THAT(tx.peersAsked, Contains(key)); + ASSERT_THAT(tx.valuesFound, Contains(val)); + } + + // Repeated call on success + { + EXPECT_CALL(tx, Validate(val)).WillOnce(Return(true)); + tx.OnFound(key, val); + ASSERT_THAT(tx.peersAsked, Contains(key)); + ASSERT_THAT(tx.valuesFound, Contains(val)); + } + + const auto key1 = makeBuf< dht::Key_t >(0x01); + std::string badVal("bad value"); + + // Validate returns false + { + EXPECT_CALL(tx, Validate(badVal)).WillOnce(Return(false)); + + tx.OnFound(key1, badVal); + + ASSERT_THAT(tx.peersAsked, Contains(key1)); + ASSERT_THAT(tx.valuesFound, Not(Contains(badVal))); + } + + // Repeated call on failure + { + EXPECT_CALL(tx, Validate(badVal)).WillOnce(Return(false)); + + tx.OnFound(key1, badVal); + + ASSERT_THAT(tx.peersAsked, Contains(key1)); + ASSERT_THAT(tx.valuesFound, Not(Contains(badVal))); + } + + // Repeated call on success after failure + { + EXPECT_CALL(tx, Validate(badVal)).WillOnce(Return(true)); + + tx.OnFound(key1, badVal); + + ASSERT_THAT(tx.peersAsked, Contains(key1)); + ASSERT_THAT(tx.valuesFound, Contains(badVal)); + } +} + +TEST_F(TestDhtTx, ask_next_peer) +{ + // Concerns: + // - GetNextPeer fails + // - Next Peer is not closer + // - next ptr is null + // - next ptr is not null + + const auto key0 = makeBuf< dht::Key_t >(0x00); + const auto key1 = makeBuf< dht::Key_t >(0x01); + const auto key2 = makeBuf< dht::Key_t >(0x02); + + { + // GetNextPeer fails + EXPECT_CALL(tx, GetNextPeer(_, _)).WillOnce(Return(false)); + + EXPECT_CALL(tx, DoNextRequest(key1)).Times(0); + + ASSERT_FALSE(tx.AskNextPeer(key0, {})); + ASSERT_THAT(tx.peersAsked, Contains(key0)); + + tx.peersAsked.clear(); + } + + { + // Next Peer is not closer + EXPECT_CALL(tx, GetNextPeer(_, _)) + .WillOnce(DoAll(SetArgReferee< 0 >(key1), Return(true))); + + EXPECT_CALL(tx, DoNextRequest(key1)).Times(0); + + ASSERT_FALSE(tx.AskNextPeer(key0, {})); + ASSERT_THAT(tx.peersAsked, Contains(key0)); + + tx.peersAsked.clear(); + } + + { + // next ptr is null + EXPECT_CALL(tx, GetNextPeer(_, _)) + .WillOnce(DoAll(SetArgReferee< 0 >(key1), Return(true))); + + EXPECT_CALL(tx, DoNextRequest(key1)).Times(1); + + ASSERT_TRUE(tx.AskNextPeer(key2, {})); + ASSERT_THAT(tx.peersAsked, Contains(key2)); + + tx.peersAsked.clear(); + } + + { + // next ptr is not null + EXPECT_CALL(tx, GetNextPeer(_, _)).Times(0); + + EXPECT_CALL(tx, DoNextRequest(key1)).Times(1); + + auto ptr = std::make_unique< dht::Key_t >(key1); + ASSERT_TRUE(tx.AskNextPeer(key2, ptr)); + ASSERT_THAT(tx.peersAsked, Contains(key2)); + + tx.peersAsked.clear(); + } +} diff --git a/test/link/test_llarp_link.cpp b/test/link/test_llarp_link.cpp index c0cd9e8a7..de3b4c483 100644 --- a/test/link/test_llarp_link.cpp +++ b/test/link/test_llarp_link.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include struct LinkLayerTest : public ::testing::Test @@ -97,7 +99,7 @@ struct LinkLayerTest : public ::testing::Test } }; - llarp::Crypto crypto; + llarp::sodium::CryptoLibSodium crypto; Context Alice; Context Bob; @@ -109,11 +111,7 @@ struct LinkLayerTest : public ::testing::Test llarp_time_t oldRCLifetime; - LinkLayerTest() - : crypto(llarp::Crypto::sodium{}) - , Alice(crypto) - , Bob(crypto) - , netLoop(nullptr) + LinkLayerTest() : Alice(crypto), Bob(crypto), netLoop(nullptr) { } diff --git a/test/routing/test_llarp_routing_obtainexitmessage.cpp b/test/routing/test_llarp_routing_obtainexitmessage.cpp index dfc8898e3..ff1271d09 100644 --- a/test/routing/test_llarp_routing_obtainexitmessage.cpp +++ b/test/routing/test_llarp_routing_obtainexitmessage.cpp @@ -1,17 +1,19 @@ -#include +#include #include -#include +#include + +#include using ObtainExitMessage = llarp::routing::ObtainExitMessage; class ObtainExitTest : public ::testing::Test { public: - llarp::Crypto crypto; + llarp::sodium::CryptoLibSodium crypto; llarp::SecretKey alice; - ObtainExitTest() : crypto(llarp::Crypto::sodium{}) + ObtainExitTest() { } diff --git a/test/service/test_llarp_service_address.cpp b/test/service/test_llarp_service_address.cpp index e800f86c7..e8d313830 100644 --- a/test/service/test_llarp_service_address.cpp +++ b/test/service/test_llarp_service_address.cpp @@ -1,6 +1,7 @@ -#include #include +#include + struct ServiceAddressTest : public ::testing::Test { const std::string snode = diff --git a/test/service/test_llarp_service_identity.cpp b/test/service/test_llarp_service_identity.cpp index 6872ceecf..f5392d3ec 100644 --- a/test/service/test_llarp_service_identity.cpp +++ b/test/service/test_llarp_service_identity.cpp @@ -1,18 +1,19 @@ -#include - #include +#include #include #include #include #include #include +#include + struct HiddenServiceTest : public ::testing::Test { - llarp::Crypto crypto; + llarp::sodium::CryptoLibSodium crypto; llarp::service::Identity ident; - HiddenServiceTest() : crypto(llarp::Crypto::sodium{}) + HiddenServiceTest() { } diff --git a/test/test_libabyss.cpp b/test/test_libabyss.cpp index 5090ffa16..279e8fb6e 100644 --- a/test/test_libabyss.cpp +++ b/test/test_libabyss.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -9,7 +10,7 @@ struct AbyssTestBase : public ::testing::Test { - llarp::Crypto crypto; + llarp::sodium::CryptoLibSodium crypto; llarp_threadpool* threadpool = nullptr; llarp_ev_loop* loop = nullptr; std::unique_ptr< llarp::Logic > logic; @@ -18,7 +19,7 @@ struct AbyssTestBase : public ::testing::Test const std::string method = "test.method"; bool called = false; - AbyssTestBase() : crypto(llarp::Crypto::sodium{}) + AbyssTestBase() { } diff --git a/test/test_llarp_encrypted_frame.cpp b/test/test_llarp_encrypted_frame.cpp index 478f7fd64..c50de3fa8 100644 --- a/test/test_llarp_encrypted_frame.cpp +++ b/test/test_llarp_encrypted_frame.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include @@ -13,10 +14,10 @@ using LRCR = llarp::LR_CommitRecord; class FrameTest : public ::testing::Test { public: - llarp::Crypto crypto; + llarp::sodium::CryptoLibSodium crypto; SecretKey alice, bob; - FrameTest() : crypto(llarp::Crypto::sodium{}) + FrameTest() { } diff --git a/test/test_llarp_router.cpp b/test/test_llarp_router.cpp index 9edcd0ddc..56fdbbb7d 100644 --- a/test/test_llarp_router.cpp +++ b/test/test_llarp_router.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include @@ -13,11 +14,11 @@ using FindOrCreateFunc = std::function< bool(llarp::Crypto *, const fs::path &, struct FindOrCreate : public ::testing::TestWithParam< FindOrCreateFunc > { - FindOrCreate() : crypto(llarp::Crypto::sodium{}) + FindOrCreate() { } - llarp::Crypto crypto; + llarp::sodium::CryptoLibSodium crypto; }; // Concerns diff --git a/test/test_llarp_router_contact.cpp b/test/test_llarp_router_contact.cpp index 52aca2e66..d4a20a327 100644 --- a/test/test_llarp_router_contact.cpp +++ b/test/test_llarp_router_contact.cpp @@ -1,6 +1,7 @@ #include #include +#include #include static const byte_t DEF_VALUE[] = "unittest"; @@ -10,8 +11,7 @@ struct RCTest : public ::testing::Test using RC_t = llarp::RouterContact; using SecKey_t = llarp::SecretKey; - RCTest() - : crypto(llarp::Crypto::sodium{}), oldval(llarp::NetID::DefaultValue()) + RCTest() : oldval(llarp::NetID::DefaultValue()) { llarp::NetID::DefaultValue() = llarp::NetID(DEF_VALUE); } @@ -21,7 +21,7 @@ struct RCTest : public ::testing::Test llarp::NetID::DefaultValue() = oldval; } - llarp::Crypto crypto; + llarp::sodium::CryptoLibSodium crypto; const llarp::NetID oldval; }; diff --git a/test/test_util.hpp b/test/test_util.hpp index b9d320601..6c18b935b 100644 --- a/test/test_util.hpp +++ b/test/test_util.hpp @@ -2,6 +2,7 @@ #define TEST_UTIL_HPP #include +#include namespace llarp { @@ -10,6 +11,15 @@ namespace llarp std::string randFilename(); + template < typename Buf > + Buf + makeBuf(byte_t val) + { + Buf b; + b.Fill(val); + return b; + } + struct FileGuard { const fs::path &p;