mirror of
https://github.com/oxen-io/lokinet
synced 2023-12-14 06:53:00 +01:00
Merge pull request #2216 from tewinget/path-messages
Path build and onioned messages
This commit is contained in:
commit
e58e8473f8
|
@ -12,7 +12,6 @@ endfunction()
|
||||||
|
|
||||||
lokinet_add_library(lokinet-cryptography
|
lokinet_add_library(lokinet-cryptography
|
||||||
crypto/crypto.cpp
|
crypto/crypto.cpp
|
||||||
crypto/encrypted_frame.cpp
|
|
||||||
crypto/types.cpp
|
crypto/types.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -207,7 +206,6 @@ lokinet_add_library(lokinet-config
|
||||||
|
|
||||||
# All path objects; link directly to lokinet-core
|
# All path objects; link directly to lokinet-core
|
||||||
lokinet_add_library(lokinet-path
|
lokinet_add_library(lokinet-path
|
||||||
messages/relay.cpp
|
|
||||||
path/abstracthophandler.cpp
|
path/abstracthophandler.cpp
|
||||||
path/path.cpp
|
path/path.cpp
|
||||||
path/path_context.cpp
|
path/path_context.cpp
|
||||||
|
|
|
@ -65,7 +65,7 @@ namespace llarp
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
dh_client_priv(
|
dh_client_priv(
|
||||||
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
|
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const SymmNonce& n)
|
||||||
{
|
{
|
||||||
llarp::SharedSecret dh_result;
|
llarp::SharedSecret dh_result;
|
||||||
|
|
||||||
|
@ -81,13 +81,13 @@ namespace llarp
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
dh_server_priv(
|
dh_server_priv(
|
||||||
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
|
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const SymmNonce& n)
|
||||||
{
|
{
|
||||||
llarp::SharedSecret dh_result;
|
llarp::SharedSecret dh_result;
|
||||||
|
|
||||||
if (dh(dh_result, pk, sk.toPublic(), pk.data(), sk))
|
if (dh(dh_result, pk, sk.toPublic(), pk.data(), sk))
|
||||||
{
|
{
|
||||||
return crypto_generichash_blake2b(shared.data(), 32, n.data(), 32, dh_result.data(), 32)
|
return crypto_generichash_blake2b(shared.data(), 32, n.data(), n.size(), dh_result.data(), 32)
|
||||||
!= -1;
|
!= -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ namespace llarp
|
||||||
|
|
||||||
if (dh(dh_result.data(), pk, sk, pk, sk))
|
if (dh(dh_result.data(), pk, sk, pk, sk))
|
||||||
{
|
{
|
||||||
return crypto_generichash_blake2b(shared, 32, nonce, 32, dh_result.data(), 32) != -1;
|
return crypto_generichash_blake2b(shared, 32, nonce, 24, dh_result.data(), 32) != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
llarp::LogWarn("crypto::dh_server - dh failed");
|
llarp::LogWarn("crypto::dh_server - dh failed");
|
||||||
|
@ -143,7 +143,7 @@ namespace llarp
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
crypto::xchacha20(uint8_t* buf, size_t size, const SharedSecret& k, const TunnelNonce& n)
|
crypto::xchacha20(uint8_t* buf, size_t size, const SharedSecret& k, const SymmNonce& n)
|
||||||
{
|
{
|
||||||
return xchacha20(buf, size, n.data(), k.data());
|
return xchacha20(buf, size, n.data(), k.data());
|
||||||
}
|
}
|
||||||
|
@ -154,16 +154,31 @@ namespace llarp
|
||||||
return crypto_stream_xchacha20_xor(buf, buf, size, nonce, secret) == 0;
|
return crypto_stream_xchacha20_xor(buf, buf, size, nonce, secret) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// do a round of chacha for and return the nonce xor the given xor_factor
|
||||||
|
SymmNonce
|
||||||
|
crypto::onion(
|
||||||
|
unsigned char* buf,
|
||||||
|
size_t size,
|
||||||
|
const SharedSecret& k,
|
||||||
|
const SymmNonce& nonce,
|
||||||
|
const SymmNonce& xor_factor)
|
||||||
|
{
|
||||||
|
if (!crypto::xchacha20(buf, size, k, nonce))
|
||||||
|
throw std::runtime_error{"chacha failed during onion step"};
|
||||||
|
|
||||||
|
return nonce ^ xor_factor;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
crypto::dh_client(
|
crypto::dh_client(
|
||||||
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
|
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const SymmNonce& n)
|
||||||
{
|
{
|
||||||
return dh_client_priv(shared, pk, sk, n);
|
return dh_client_priv(shared, pk, sk, n);
|
||||||
}
|
}
|
||||||
/// path dh relay side
|
/// path dh relay side
|
||||||
bool
|
bool
|
||||||
crypto::dh_server(
|
crypto::dh_server(
|
||||||
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
|
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const SymmNonce& n)
|
||||||
{
|
{
|
||||||
return dh_server_priv(shared, pk, sk, n);
|
return dh_server_priv(shared, pk, sk, n);
|
||||||
}
|
}
|
||||||
|
@ -177,20 +192,6 @@ namespace llarp
|
||||||
{
|
{
|
||||||
return dh_server_priv(shared_secret, other_pk, local_pk, nonce);
|
return dh_server_priv(shared_secret, other_pk, local_pk, nonce);
|
||||||
}
|
}
|
||||||
/// transport dh client side
|
|
||||||
bool
|
|
||||||
crypto::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
|
|
||||||
crypto::transport_dh_server(
|
|
||||||
llarp::SharedSecret& shared, const PubKey& pk, const SecretKey& sk, const TunnelNonce& n)
|
|
||||||
{
|
|
||||||
return dh_server_priv(shared, pk, sk, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
crypto::shorthash(ShortHash& result, uint8_t* buf, size_t size)
|
crypto::shorthash(ShortHash& result, uint8_t* buf, size_t size)
|
||||||
|
|
|
@ -23,28 +23,30 @@ namespace llarp
|
||||||
|
|
||||||
/// xchacha symmetric cipher
|
/// xchacha symmetric cipher
|
||||||
bool
|
bool
|
||||||
xchacha20(uint8_t*, size_t size, const SharedSecret&, const TunnelNonce&);
|
xchacha20(uint8_t*, size_t size, const SharedSecret&, const SymmNonce&);
|
||||||
bool
|
bool
|
||||||
xchacha20(uint8_t*, size_t size, const uint8_t*, const uint8_t*);
|
xchacha20(uint8_t*, size_t size, const uint8_t*, const uint8_t*);
|
||||||
|
|
||||||
|
SymmNonce
|
||||||
|
onion(
|
||||||
|
unsigned char* buf,
|
||||||
|
size_t size,
|
||||||
|
const SharedSecret& k,
|
||||||
|
const SymmNonce& nonce,
|
||||||
|
const SymmNonce& xor_factor);
|
||||||
|
|
||||||
/// path dh creator's side
|
/// path dh creator's side
|
||||||
bool
|
bool
|
||||||
dh_client(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&);
|
dh_client(SharedSecret&, const PubKey&, const SecretKey&, const SymmNonce&);
|
||||||
/// path dh relay side
|
/// path dh relay side
|
||||||
bool
|
bool
|
||||||
dh_server(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&);
|
dh_server(SharedSecret&, const PubKey&, const SecretKey&, const SymmNonce&);
|
||||||
bool
|
bool
|
||||||
dh_server(
|
dh_server(
|
||||||
uint8_t* shared_secret,
|
uint8_t* shared_secret,
|
||||||
const uint8_t* other_pk,
|
const uint8_t* other_pk,
|
||||||
const uint8_t* local_pk,
|
const uint8_t* local_pk,
|
||||||
const uint8_t* nonce);
|
const uint8_t* nonce);
|
||||||
/// transport dh client side
|
|
||||||
bool
|
|
||||||
transport_dh_client(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&);
|
|
||||||
/// transport dh server side
|
|
||||||
bool
|
|
||||||
transport_dh_server(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&);
|
|
||||||
/// blake2b 256 bit
|
/// blake2b 256 bit
|
||||||
bool
|
bool
|
||||||
shorthash(ShortHash&, uint8_t*, size_t size);
|
shorthash(ShortHash&, uint8_t*, size_t size);
|
||||||
|
|
|
@ -1,127 +0,0 @@
|
||||||
#include "encrypted_frame.hpp"
|
|
||||||
|
|
||||||
#include "crypto.hpp"
|
|
||||||
|
|
||||||
#include <llarp/util/logging.hpp>
|
|
||||||
|
|
||||||
namespace llarp
|
|
||||||
{
|
|
||||||
bool
|
|
||||||
EncryptedFrame::DoEncrypt(const SharedSecret& shared, bool noDH)
|
|
||||||
{
|
|
||||||
uint8_t* hash_ptr = data();
|
|
||||||
uint8_t* nonce_ptr = hash_ptr + SHORTHASHSIZE;
|
|
||||||
uint8_t* pubkey_ptr = nonce_ptr + TUNNONCESIZE;
|
|
||||||
uint8_t* body_ptr = pubkey_ptr + PUBKEYSIZE;
|
|
||||||
|
|
||||||
if (noDH)
|
|
||||||
{
|
|
||||||
crypto::randbytes(nonce_ptr, TUNNONCESIZE);
|
|
||||||
crypto::randbytes(pubkey_ptr, PUBKEYSIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
TunnelNonce nonce(nonce_ptr);
|
|
||||||
|
|
||||||
// encrypt body
|
|
||||||
if (!crypto::xchacha20(body_ptr, size() - EncryptedFrameOverheadSize, shared, nonce))
|
|
||||||
{
|
|
||||||
llarp::LogError("encrypt failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!crypto::hmac(hash_ptr, nonce_ptr, size() - SHORTHASHSIZE, shared))
|
|
||||||
{
|
|
||||||
llarp::LogError("Failed to generate message auth");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
EncryptedFrame::EncryptInPlace(const SecretKey& ourSecretKey, const PubKey& otherPubkey)
|
|
||||||
{
|
|
||||||
// format of frame is
|
|
||||||
// <32 bytes keyed hash of following data>
|
|
||||||
// <32 bytes nonce>
|
|
||||||
// <32 bytes pubkey>
|
|
||||||
// <N bytes encrypted payload>
|
|
||||||
//
|
|
||||||
byte_t* hash = data();
|
|
||||||
byte_t* noncePtr = hash + SHORTHASHSIZE;
|
|
||||||
byte_t* pubkey = noncePtr + TUNNONCESIZE;
|
|
||||||
|
|
||||||
SharedSecret shared;
|
|
||||||
|
|
||||||
// set our pubkey
|
|
||||||
memcpy(pubkey, ourSecretKey.toPublic().data(), PUBKEYSIZE);
|
|
||||||
// randomize nonce
|
|
||||||
crypto::randbytes(noncePtr, TUNNONCESIZE);
|
|
||||||
TunnelNonce nonce(noncePtr);
|
|
||||||
|
|
||||||
// derive shared key
|
|
||||||
if (!crypto::dh_client(shared, otherPubkey, ourSecretKey, nonce))
|
|
||||||
{
|
|
||||||
llarp::LogError("DH failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DoEncrypt(shared, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
EncryptedFrame::DoDecrypt(const SharedSecret& shared)
|
|
||||||
{
|
|
||||||
uint8_t* hash_ptr = data();
|
|
||||||
uint8_t* nonce_ptr = hash_ptr + SHORTHASHSIZE;
|
|
||||||
uint8_t* body_ptr = hash_ptr + EncryptedFrameOverheadSize;
|
|
||||||
|
|
||||||
TunnelNonce nonce(nonce_ptr);
|
|
||||||
|
|
||||||
ShortHash digest;
|
|
||||||
if (!crypto::hmac(digest.data(), nonce_ptr, size() - SHORTHASHSIZE, shared))
|
|
||||||
{
|
|
||||||
llarp::LogError("Digest failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!std::equal(digest.begin(), digest.end(), hash_ptr))
|
|
||||||
{
|
|
||||||
llarp::LogError("message authentication failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!crypto::xchacha20(body_ptr, size() - EncryptedFrameOverheadSize, shared, nonce))
|
|
||||||
{
|
|
||||||
llarp::LogError("decrypt failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
EncryptedFrame::DecryptInPlace(const SecretKey& ourSecretKey)
|
|
||||||
{
|
|
||||||
// format of frame is
|
|
||||||
// <32 bytes keyed hash of following data>
|
|
||||||
// <32 bytes nonce>
|
|
||||||
// <32 bytes pubkey>
|
|
||||||
// <N bytes encrypted payload>
|
|
||||||
//
|
|
||||||
byte_t* noncePtr = data() + SHORTHASHSIZE;
|
|
||||||
TunnelNonce nonce(noncePtr);
|
|
||||||
PubKey otherPubkey(noncePtr + TUNNONCESIZE);
|
|
||||||
|
|
||||||
SharedSecret shared;
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DoDecrypt(shared);
|
|
||||||
}
|
|
||||||
} // namespace llarp
|
|
|
@ -1,87 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "encrypted.hpp"
|
|
||||||
#include "types.hpp"
|
|
||||||
|
|
||||||
#include <llarp/util/buffer.hpp>
|
|
||||||
#include <llarp/util/mem.h>
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace llarp
|
|
||||||
{
|
|
||||||
static constexpr size_t EncryptedFrameOverheadSize = PUBKEYSIZE + TUNNONCESIZE + SHORTHASHSIZE;
|
|
||||||
static constexpr size_t EncryptedFrameBodySize = 128 * 6;
|
|
||||||
static constexpr size_t EncryptedFrameSize = EncryptedFrameOverheadSize + EncryptedFrameBodySize;
|
|
||||||
|
|
||||||
struct EncryptedFrame : public Encrypted<EncryptedFrameSize>
|
|
||||||
{
|
|
||||||
EncryptedFrame() : EncryptedFrame(EncryptedFrameBodySize)
|
|
||||||
{}
|
|
||||||
|
|
||||||
EncryptedFrame(size_t sz)
|
|
||||||
: Encrypted<EncryptedFrameSize>(
|
|
||||||
std::min(sz, EncryptedFrameBodySize) + EncryptedFrameOverheadSize)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void
|
|
||||||
Resize(size_t sz)
|
|
||||||
{
|
|
||||||
if (sz <= EncryptedFrameSize)
|
|
||||||
{
|
|
||||||
_sz = sz;
|
|
||||||
UpdateBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
DoEncrypt(const SharedSecret& shared, bool noDH = false);
|
|
||||||
|
|
||||||
bool
|
|
||||||
DecryptInPlace(const SecretKey& seckey);
|
|
||||||
|
|
||||||
bool
|
|
||||||
DoDecrypt(const SharedSecret& shared);
|
|
||||||
|
|
||||||
bool
|
|
||||||
EncryptInPlace(const SecretKey& seckey, const PubKey& other);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename User>
|
|
||||||
struct AsyncFrameDecrypter
|
|
||||||
{
|
|
||||||
using User_ptr = std::shared_ptr<User>;
|
|
||||||
using DecryptHandler = std::function<void(llarp_buffer_t*, User_ptr)>;
|
|
||||||
|
|
||||||
void
|
|
||||||
Decrypt(User_ptr user)
|
|
||||||
{
|
|
||||||
if (target.DecryptInPlace(seckey))
|
|
||||||
{
|
|
||||||
auto buf = target.Buffer();
|
|
||||||
buf->cur = buf->base + EncryptedFrameOverheadSize;
|
|
||||||
result(buf, user);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
result(nullptr, user);
|
|
||||||
}
|
|
||||||
|
|
||||||
AsyncFrameDecrypter(const SecretKey& secretkey, DecryptHandler h)
|
|
||||||
: result(std::move(h)), seckey(secretkey)
|
|
||||||
{}
|
|
||||||
|
|
||||||
DecryptHandler result;
|
|
||||||
const SecretKey& seckey;
|
|
||||||
EncryptedFrame target;
|
|
||||||
|
|
||||||
using WorkFunc_t = std::function<void(void)>;
|
|
||||||
using WorkerFunction_t = std::function<void(WorkFunc_t)>;
|
|
||||||
|
|
||||||
void
|
|
||||||
AsyncDecrypt(const EncryptedFrame& frame, User_ptr u, WorkerFunction_t worker)
|
|
||||||
{
|
|
||||||
target = frame;
|
|
||||||
worker([this, u = std::move(u)]() mutable { Decrypt(std::move(u)); });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace llarp
|
|
|
@ -164,10 +164,6 @@ namespace llarp
|
||||||
/// PKE(result, publickey, secretkey, nonce)
|
/// PKE(result, publickey, secretkey, nonce)
|
||||||
using path_dh_func = bool (*)(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&);
|
using path_dh_func = bool (*)(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&);
|
||||||
|
|
||||||
/// TKE(result, publickey, secretkey, nonce)
|
|
||||||
using transport_dh_func =
|
|
||||||
bool (*)(SharedSecret&, const PubKey&, const SecretKey&, const TunnelNonce&);
|
|
||||||
|
|
||||||
/// SH(result, body)
|
/// SH(result, body)
|
||||||
using shorthash_func = bool (*)(ShortHash&, const llarp_buffer_t&);
|
using shorthash_func = bool (*)(ShortHash&, const llarp_buffer_t&);
|
||||||
} // namespace llarp
|
} // namespace llarp
|
||||||
|
|
|
@ -131,7 +131,7 @@ namespace llarp
|
||||||
llarp_time_t timeout) = 0;
|
llarp_time_t timeout) = 0;
|
||||||
|
|
||||||
virtual void
|
virtual void
|
||||||
lookup_name(std::string name, std::function<void(oxen::quic::message)> func) = 0;
|
lookup_name(std::string name, std::function<void(std::string, bool)> func) = 0;
|
||||||
|
|
||||||
virtual const EventLoop_ptr&
|
virtual const EventLoop_ptr&
|
||||||
Loop() = 0;
|
Loop() = 0;
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
#include "ev.hpp"
|
#include "ev.hpp"
|
||||||
#include "udp_handle.hpp"
|
#include "udp_handle.hpp"
|
||||||
|
|
||||||
#include <llarp/util/meta/memfn.hpp>
|
|
||||||
#include <llarp/util/thread/queue.hpp>
|
#include <llarp/util/thread/queue.hpp>
|
||||||
|
|
||||||
// #include <uvw.hpp>
|
// #include <uvw.hpp>
|
||||||
|
|
|
@ -57,7 +57,8 @@ namespace llarp::exit
|
||||||
if (!parent->UpdateEndpointPath(remote_signkey, nextPath))
|
if (!parent->UpdateEndpointPath(remote_signkey, nextPath))
|
||||||
return false;
|
return false;
|
||||||
const RouterID us{parent->GetRouter()->pubkey()};
|
const RouterID us{parent->GetRouter()->pubkey()};
|
||||||
current_path = parent->GetRouter()->path_context().GetByUpstream(us, nextPath);
|
// TODO: is this getting a Path or a TransitHop?
|
||||||
|
// current_path = parent->GetRouter()->path_context().GetByUpstream(us, nextPath);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,9 @@ namespace llarp::handlers
|
||||||
ExitEndpoint::~ExitEndpoint() = default;
|
ExitEndpoint::~ExitEndpoint() = default;
|
||||||
|
|
||||||
void
|
void
|
||||||
ExitEndpoint::lookup_name(std::string, std::function<void(oxen::quic::message)>)
|
ExitEndpoint::lookup_name(std::string, std::function<void(std::string, bool)>)
|
||||||
{
|
{
|
||||||
// TODO: implement me
|
// TODO: implement me (or does EndpointBase having this method as virtual even make sense?)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -766,7 +766,9 @@ namespace llarp::handlers
|
||||||
{
|
{
|
||||||
if (wantInternet && !permit_exit)
|
if (wantInternet && !permit_exit)
|
||||||
return false;
|
return false;
|
||||||
path::HopHandler_ptr handler = router->path_context().GetByUpstream(router->pubkey(), path);
|
// TODO: is this getting a path or a transit hop or...somehow possibly either?
|
||||||
|
// path::HopHandler_ptr handler = router->path_context().GetByUpstream(router->pubkey(), path);
|
||||||
|
path::HopHandler_ptr handler{};
|
||||||
if (handler == nullptr)
|
if (handler == nullptr)
|
||||||
return false;
|
return false;
|
||||||
auto ip = GetIPForIdent(pk);
|
auto ip = GetIPForIdent(pk);
|
||||||
|
|
|
@ -49,7 +49,7 @@ namespace llarp
|
||||||
llarp_time_t timeout) override;
|
llarp_time_t timeout) override;
|
||||||
|
|
||||||
void
|
void
|
||||||
lookup_name(std::string name, std::function<void(oxen::quic::message)> func) override;
|
lookup_name(std::string name, std::function<void(std::string, bool)> func) override;
|
||||||
|
|
||||||
const EventLoop_ptr&
|
const EventLoop_ptr&
|
||||||
Loop() override;
|
Loop() override;
|
||||||
|
|
|
@ -683,28 +683,17 @@ namespace llarp::handlers
|
||||||
}
|
}
|
||||||
else if (service::is_valid_name(ons_name))
|
else if (service::is_valid_name(ons_name))
|
||||||
{
|
{
|
||||||
lookup_name(ons_name, [msg, ons_name, reply](oxen::quic::message m) mutable {
|
lookup_name(
|
||||||
if (m)
|
ons_name, [msg, ons_name, reply](std::string name_result, bool success) mutable {
|
||||||
{
|
if (success)
|
||||||
std::string result;
|
{
|
||||||
try
|
msg.AddMXReply(name_result, 1);
|
||||||
{
|
}
|
||||||
oxenc::bt_dict_consumer btdc{m.body()};
|
else
|
||||||
result = btdc.require<std::string>("NAME");
|
msg.AddNXReply();
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
log::warning(logcat, "Failed to parse find name response!");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.AddMXReply(result, 1);
|
reply(msg);
|
||||||
}
|
});
|
||||||
else
|
|
||||||
msg.AddNXReply();
|
|
||||||
|
|
||||||
reply(msg);
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -837,29 +826,15 @@ namespace llarp::handlers
|
||||||
ons_name,
|
ons_name,
|
||||||
isV6,
|
isV6,
|
||||||
reply,
|
reply,
|
||||||
ReplyToDNSWhenReady](oxen::quic::message m) mutable {
|
ReplyToDNSWhenReady](std::string name_result, bool success) mutable {
|
||||||
if (m)
|
if (not success)
|
||||||
{
|
|
||||||
std::string name;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
oxenc::bt_dict_consumer btdc{m.body()};
|
|
||||||
name = btdc.require<std::string>("NAME");
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
log::warning(logcat, "Failed to parse find name response!");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReplyToDNSWhenReady(name, msg, isV6);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
log::warning(logcat, "{} (ONS name: {}) not resolved", name, ons_name);
|
log::warning(logcat, "{} (ONS name: {}) not resolved", name, ons_name);
|
||||||
msg->AddNXReply();
|
msg->AddNXReply();
|
||||||
reply(*msg);
|
reply(*msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReplyToDNSWhenReady(name_result, msg, isV6);
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <llarp/messages/exit.hpp>
|
#include <llarp/messages/exit.hpp>
|
||||||
#include <llarp/messages/path.hpp>
|
#include <llarp/messages/path.hpp>
|
||||||
#include <llarp/nodedb.hpp>
|
#include <llarp/nodedb.hpp>
|
||||||
|
#include <llarp/path/path.hpp>
|
||||||
#include <llarp/router/rc_lookup_handler.hpp>
|
#include <llarp/router/rc_lookup_handler.hpp>
|
||||||
#include <llarp/router/router.hpp>
|
#include <llarp/router/router.hpp>
|
||||||
|
|
||||||
|
@ -123,6 +124,8 @@ namespace llarp
|
||||||
|
|
||||||
} // namespace link
|
} // namespace link
|
||||||
|
|
||||||
|
using messages::serialize_response;
|
||||||
|
|
||||||
void
|
void
|
||||||
LinkManager::for_each_connection(std::function<void(link::Connection&)> func)
|
LinkManager::for_each_connection(std::function<void(link::Connection&)> func)
|
||||||
{
|
{
|
||||||
|
@ -135,13 +138,31 @@ namespace llarp
|
||||||
void
|
void
|
||||||
LinkManager::register_commands(std::shared_ptr<oxen::quic::BTRequestStream>& s)
|
LinkManager::register_commands(std::shared_ptr<oxen::quic::BTRequestStream>& s)
|
||||||
{
|
{
|
||||||
for (const auto& [name, func] : rpc_commands)
|
assert(ep.connid_map.count(s->conn_id()));
|
||||||
|
const RouterID& rid = ep.connid_map[s->conn_id()];
|
||||||
|
|
||||||
|
s->register_command("path_build"s, [this, rid](oxen::quic::message m) {
|
||||||
|
_router.loop()->call(
|
||||||
|
[this, &rid, msg = std::move(m)]() mutable { handle_path_build(std::move(msg), rid); });
|
||||||
|
});
|
||||||
|
|
||||||
|
s->register_command("path_control"s, [this, rid](oxen::quic::message m) {
|
||||||
|
_router.loop()->call(
|
||||||
|
[this, &rid, msg = std::move(m)]() mutable { handle_path_control(std::move(msg), rid); });
|
||||||
|
});
|
||||||
|
|
||||||
|
for (auto& method : direct_requests)
|
||||||
{
|
{
|
||||||
s->register_command(name, [this, f = func](oxen::quic::message m) {
|
s->register_command(
|
||||||
_router.loop()->call([this, func = f, msg = std::move(m)]() mutable {
|
std::string{method.first}, [this, func = method.second](oxen::quic::message m) {
|
||||||
std::invoke(func, this, std::move(msg));
|
_router.loop()->call([this, msg = std::move(m), func = std::move(func)]() mutable {
|
||||||
});
|
auto body = msg.body_str();
|
||||||
});
|
auto respond = [m = std::move(msg)](std::string response) mutable {
|
||||||
|
m.respond(std::move(response));
|
||||||
|
};
|
||||||
|
std::invoke(func, this, body, std::move(respond));
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,12 +215,7 @@ namespace llarp
|
||||||
std::string body,
|
std::string body,
|
||||||
std::function<void(oxen::quic::message m)> func)
|
std::function<void(oxen::quic::message m)> func)
|
||||||
{
|
{
|
||||||
if (not func and rpc_responses.count(endpoint))
|
assert(func); // makes no sense to send control message and ignore response
|
||||||
{
|
|
||||||
func = [&](oxen::quic::message m) {
|
|
||||||
std::invoke(rpc_responses[endpoint], this, std::move(m));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (func)
|
if (func)
|
||||||
{
|
{
|
||||||
|
@ -416,8 +432,6 @@ namespace llarp
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
util::Lock l(m);
|
|
||||||
|
|
||||||
LogInfo("stopping links");
|
LogInfo("stopping links");
|
||||||
is_stopping = true;
|
is_stopping = true;
|
||||||
|
|
||||||
|
@ -430,8 +444,6 @@ namespace llarp
|
||||||
if (is_stopping)
|
if (is_stopping)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
util::Lock l(m);
|
|
||||||
|
|
||||||
persisting_conns[remote] = std::max(until, persisting_conns[remote]);
|
persisting_conns[remote] = std::max(until, persisting_conns[remote]);
|
||||||
if (have_client_connection_to(remote))
|
if (have_client_connection_to(remote))
|
||||||
{
|
{
|
||||||
|
@ -513,29 +525,30 @@ namespace llarp
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LinkManager::handle_find_name(oxen::quic::message m)
|
LinkManager::handle_find_name(std::string_view body, std::function<void(std::string)> respond)
|
||||||
{
|
{
|
||||||
std::string name_hash;
|
std::string name_hash;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
oxenc::bt_dict_consumer btdp{m.body()};
|
oxenc::bt_dict_consumer btdp{body};
|
||||||
|
|
||||||
name_hash = btdp.require<std::string>("H");
|
name_hash = btdp.require<std::string>("H");
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
log::warning(link_cat, "Exception: {}", e.what());
|
log::warning(link_cat, "Exception: {}", e.what());
|
||||||
m.respond(serialize_response({{"STATUS", FindNameMessage::EXCEPTION}}), true);
|
respond(messages::ERROR_RESPONSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
_router.rpc_client()->lookup_ons_hash(
|
_router.rpc_client()->lookup_ons_hash(
|
||||||
name_hash,
|
name_hash,
|
||||||
[msg = std::move(m)]([[maybe_unused]] std::optional<service::EncryptedName> maybe) mutable {
|
[respond = std::move(respond)](
|
||||||
|
[[maybe_unused]] std::optional<service::EncryptedName> maybe) mutable {
|
||||||
if (maybe)
|
if (maybe)
|
||||||
msg.respond(serialize_response({{"NAME", maybe->ciphertext}}));
|
respond(serialize_response({{"NAME", maybe->ciphertext}}));
|
||||||
else
|
else
|
||||||
msg.respond(serialize_response({{"STATUS", FindNameMessage::NOT_FOUND}}), true);
|
respond(serialize_response({{messages::STATUS_KEY, FindNameMessage::NOT_FOUND}}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -553,7 +566,7 @@ namespace llarp
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
oxenc::bt_dict_consumer btdc{m.body()};
|
oxenc::bt_dict_consumer btdc{m.body()};
|
||||||
payload = btdc.require<std::string>(m ? "NAME" : "STATUS");
|
payload = btdc.require<std::string>(m ? "NAME" : messages::STATUS_KEY);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
|
@ -567,7 +580,7 @@ namespace llarp
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (payload == FindNameMessage::EXCEPTION)
|
if (payload == "ERROR")
|
||||||
{
|
{
|
||||||
log::info(link_cat, "FindNameMessage failed with unkown error!");
|
log::info(link_cat, "FindNameMessage failed with unkown error!");
|
||||||
|
|
||||||
|
@ -583,17 +596,15 @@ namespace llarp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add callback to relayed messages (calls to send_control_message so the
|
|
||||||
// response finds its way back)
|
|
||||||
void
|
void
|
||||||
LinkManager::handle_find_router(oxen::quic::message m)
|
LinkManager::handle_find_router(std::string_view body, std::function<void(std::string)> respond)
|
||||||
{
|
{
|
||||||
std::string target_key;
|
std::string target_key;
|
||||||
bool is_exploratory, is_iterative;
|
bool is_exploratory, is_iterative;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
oxenc::bt_dict_consumer btdc{m.body()};
|
oxenc::bt_dict_consumer btdc{body};
|
||||||
|
|
||||||
is_exploratory = btdc.require<bool>("E");
|
is_exploratory = btdc.require<bool>("E");
|
||||||
is_iterative = btdc.require<bool>("I");
|
is_iterative = btdc.require<bool>("I");
|
||||||
|
@ -602,8 +613,7 @@ namespace llarp
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
log::warning(link_cat, "Exception: {}", e.what());
|
log::warning(link_cat, "Exception: {}", e.what());
|
||||||
m.respond(
|
respond(messages::ERROR_RESPONSE);
|
||||||
serialize_response({{"STATUS", FindRouterMessage::EXCEPTION}, {"TARGET", ""}}), true);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,9 +643,8 @@ namespace llarp
|
||||||
neighbors += rid.bt_encode();
|
neighbors += rid.bt_encode();
|
||||||
}
|
}
|
||||||
|
|
||||||
m.respond(
|
respond(serialize_response(
|
||||||
serialize_response({{"STATUS", FindRouterMessage::RETRY_EXP}, {"TARGET", neighbors}}),
|
{{messages::STATUS_KEY, FindRouterMessage::RETRY_EXP}, {"TARGET", neighbors}}));
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -651,13 +660,13 @@ namespace llarp
|
||||||
target_rid,
|
target_rid,
|
||||||
"find_router",
|
"find_router",
|
||||||
FindRouterMessage::serialize(target_rid, false, false),
|
FindRouterMessage::serialize(target_rid, false, false),
|
||||||
[original = std::move(m)](oxen::quic::message msg) mutable {
|
[respond = std::move(respond)](oxen::quic::message msg) mutable {
|
||||||
original.respond(msg.body_str(), not msg);
|
respond(msg.body_str());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m.respond(serialize_response({{"RC", closest_rc.view()}}));
|
respond(serialize_response({{"RC", closest_rc.view()}}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (not is_iterative)
|
else if (not is_iterative)
|
||||||
|
@ -668,26 +677,22 @@ namespace llarp
|
||||||
closest_rid,
|
closest_rid,
|
||||||
"find_router",
|
"find_router",
|
||||||
FindRouterMessage::serialize(closest_rid, false, false),
|
FindRouterMessage::serialize(closest_rid, false, false),
|
||||||
[original = std::move(m)](oxen::quic::message msg) mutable {
|
[respond = std::move(respond)](oxen::quic::message msg) mutable {
|
||||||
original.respond(msg.body_str(), not msg);
|
respond(msg.body_str());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m.respond(
|
respond(serialize_response(
|
||||||
serialize_response(
|
{{messages::STATUS_KEY, FindRouterMessage::RETRY_ITER},
|
||||||
{{"STATUS", FindRouterMessage::RETRY_ITER},
|
{"TARGET", reinterpret_cast<const char*>(target_addr.data())}}));
|
||||||
{"TARGET", reinterpret_cast<const char*>(target_addr.data())}}),
|
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m.respond(
|
respond(serialize_response(
|
||||||
serialize_response(
|
{{messages::STATUS_KEY, FindRouterMessage::RETRY_NEW},
|
||||||
{{"STATUS", FindRouterMessage::RETRY_NEW},
|
{"TARGET", reinterpret_cast<const char*>(closest_rid.data())}}));
|
||||||
{"TARGET", reinterpret_cast<const char*>(closest_rid.data())}}),
|
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -727,7 +732,7 @@ namespace llarp
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (status == FindRouterMessage::EXCEPTION)
|
if (status == "ERROR")
|
||||||
{
|
{
|
||||||
log::info(link_cat, "FindRouterMessage failed with remote exception!");
|
log::info(link_cat, "FindRouterMessage failed with remote exception!");
|
||||||
// Do something smart here probably
|
// Do something smart here probably
|
||||||
|
@ -781,7 +786,7 @@ namespace llarp
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == FindRouterMessage::EXCEPTION)
|
if (status == "ERROR")
|
||||||
{
|
{
|
||||||
log::info(link_cat, "FindRouterMessage failed with remote exception!");
|
log::info(link_cat, "FindRouterMessage failed with remote exception!");
|
||||||
// Do something smart here probably
|
// Do something smart here probably
|
||||||
|
@ -811,7 +816,7 @@ namespace llarp
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LinkManager::handle_publish_intro(oxen::quic::message m)
|
LinkManager::handle_publish_intro(std::string_view body, std::function<void(std::string)> respond)
|
||||||
{
|
{
|
||||||
std::string introset, derived_signing_key, payload, sig, nonce;
|
std::string introset, derived_signing_key, payload, sig, nonce;
|
||||||
uint64_t is_relayed, relay_order;
|
uint64_t is_relayed, relay_order;
|
||||||
|
@ -819,7 +824,7 @@ namespace llarp
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
oxenc::bt_dict_consumer btdc_a{m.body()};
|
oxenc::bt_dict_consumer btdc_a{body};
|
||||||
|
|
||||||
introset = btdc_a.require<std::string>("I");
|
introset = btdc_a.require<std::string>("I");
|
||||||
relay_order = btdc_a.require<uint64_t>("O");
|
relay_order = btdc_a.require<uint64_t>("O");
|
||||||
|
@ -836,7 +841,7 @@ namespace llarp
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
log::warning(link_cat, "Exception: {}", e.what());
|
log::warning(link_cat, "Exception: {}", e.what());
|
||||||
m.respond(serialize_response({{"STATUS", PublishIntroMessage::EXCEPTION}}), true);
|
respond(messages::ERROR_RESPONSE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -847,14 +852,14 @@ namespace llarp
|
||||||
if (not service::EncryptedIntroSet::verify(introset, derived_signing_key, sig))
|
if (not service::EncryptedIntroSet::verify(introset, derived_signing_key, sig))
|
||||||
{
|
{
|
||||||
log::error(link_cat, "Received PublishIntroMessage with invalid introset: {}", introset);
|
log::error(link_cat, "Received PublishIntroMessage with invalid introset: {}", introset);
|
||||||
m.respond(serialize_response({{"STATUS", PublishIntroMessage::INVALID_INTROSET}}), true);
|
respond(serialize_response({{messages::STATUS_KEY, PublishIntroMessage::INVALID_INTROSET}}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (now + service::MAX_INTROSET_TIME_DELTA > signed_at + path::DEFAULT_LIFETIME)
|
if (now + service::MAX_INTROSET_TIME_DELTA > signed_at + path::DEFAULT_LIFETIME)
|
||||||
{
|
{
|
||||||
log::error(link_cat, "Received PublishIntroMessage with expired introset: {}", introset);
|
log::error(link_cat, "Received PublishIntroMessage with expired introset: {}", introset);
|
||||||
m.respond(serialize_response({{"STATUS", PublishIntroMessage::EXPIRED}}), true);
|
respond(serialize_response({{messages::STATUS_KEY, PublishIntroMessage::EXPIRED}}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -864,7 +869,7 @@ namespace llarp
|
||||||
{
|
{
|
||||||
log::error(
|
log::error(
|
||||||
link_cat, "Received PublishIntroMessage but only know {} nodes", closest_rcs.size());
|
link_cat, "Received PublishIntroMessage but only know {} nodes", closest_rcs.size());
|
||||||
m.respond(serialize_response({{"STATUS", PublishIntroMessage::INSUFFICIENT}}), true);
|
respond(serialize_response({{messages::STATUS_KEY, PublishIntroMessage::INSUFFICIENT}}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -876,7 +881,7 @@ namespace llarp
|
||||||
{
|
{
|
||||||
log::error(
|
log::error(
|
||||||
link_cat, "Received PublishIntroMessage with invalide relay order: {}", relay_order);
|
link_cat, "Received PublishIntroMessage with invalide relay order: {}", relay_order);
|
||||||
m.respond(serialize_response({{"STATUS", PublishIntroMessage::INVALID_ORDER}}), true);
|
respond(serialize_response({{messages::STATUS_KEY, PublishIntroMessage::INVALID_ORDER}}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -893,7 +898,7 @@ namespace llarp
|
||||||
relay_order);
|
relay_order);
|
||||||
|
|
||||||
_router.contacts()->services()->PutNode(dht::ISNode{std::move(enc)});
|
_router.contacts()->services()->PutNode(dht::ISNode{std::move(enc)});
|
||||||
m.respond(serialize_response({{"STATUS", ""}}));
|
respond(serialize_response({{messages::STATUS_KEY, ""}}));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -903,7 +908,12 @@ namespace llarp
|
||||||
send_control_message(
|
send_control_message(
|
||||||
peer_key,
|
peer_key,
|
||||||
"publish_intro",
|
"publish_intro",
|
||||||
PublishIntroMessage::serialize(introset, relay_order, is_relayed));
|
PublishIntroMessage::serialize(introset, relay_order, is_relayed),
|
||||||
|
[respond = std::move(respond)](oxen::quic::message m) {
|
||||||
|
if (m.timed_out)
|
||||||
|
return; // drop if timed out; requester will have timed out as well
|
||||||
|
respond(m.body_str());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -926,7 +936,7 @@ namespace llarp
|
||||||
log::info(link_cat, "Received PublishIntroMessage for {} (TXID: {}); we are candidate {}");
|
log::info(link_cat, "Received PublishIntroMessage for {} (TXID: {}); we are candidate {}");
|
||||||
|
|
||||||
_router.contacts()->services()->PutNode(dht::ISNode{std::move(enc)});
|
_router.contacts()->services()->PutNode(dht::ISNode{std::move(enc)});
|
||||||
m.respond(serialize_response());
|
respond(serialize_response({{messages::STATUS_KEY, ""}}));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
log::warning(
|
log::warning(
|
||||||
|
@ -949,7 +959,7 @@ namespace llarp
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
oxenc::bt_dict_consumer btdc{m.body()};
|
oxenc::bt_dict_consumer btdc{m.body()};
|
||||||
payload = btdc.require<std::string>("STATUS");
|
payload = btdc.require<std::string>(messages::STATUS_KEY);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
|
@ -963,7 +973,7 @@ namespace llarp
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (payload == PublishIntroMessage::EXCEPTION)
|
if (payload == "ERROR")
|
||||||
{
|
{
|
||||||
log::info(link_cat, "PublishIntroMessage failed with remote exception!");
|
log::info(link_cat, "PublishIntroMessage failed with remote exception!");
|
||||||
// Do something smart here probably
|
// Do something smart here probably
|
||||||
|
@ -984,14 +994,14 @@ namespace llarp
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LinkManager::handle_find_intro(oxen::quic::message m)
|
LinkManager::handle_find_intro(std::string_view body, std::function<void(std::string)> respond)
|
||||||
{
|
{
|
||||||
ustring location;
|
ustring location;
|
||||||
uint64_t relay_order, is_relayed;
|
uint64_t relay_order, is_relayed;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
oxenc::bt_dict_consumer btdc{m.body()};
|
oxenc::bt_dict_consumer btdc{body};
|
||||||
|
|
||||||
relay_order = btdc.require<uint64_t>("O");
|
relay_order = btdc.require<uint64_t>("O");
|
||||||
is_relayed = btdc.require<uint64_t>("R");
|
is_relayed = btdc.require<uint64_t>("R");
|
||||||
|
@ -1000,7 +1010,7 @@ namespace llarp
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
log::warning(link_cat, "Exception: {}", e.what());
|
log::warning(link_cat, "Exception: {}", e.what());
|
||||||
m.respond(serialize_response({{"STATUS", FindIntroMessage::EXCEPTION}}), true);
|
respond(messages::ERROR_RESPONSE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1012,7 +1022,7 @@ namespace llarp
|
||||||
{
|
{
|
||||||
log::warning(
|
log::warning(
|
||||||
link_cat, "Received FindIntroMessage with invalid relay order: {}", relay_order);
|
link_cat, "Received FindIntroMessage with invalid relay order: {}", relay_order);
|
||||||
m.respond(serialize_response({{"STATUS", FindIntroMessage::INVALID_ORDER}}), true);
|
respond(serialize_response({{messages::STATUS_KEY, FindIntroMessage::INVALID_ORDER}}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1022,7 +1032,7 @@ namespace llarp
|
||||||
{
|
{
|
||||||
log::error(
|
log::error(
|
||||||
link_cat, "Received FindIntroMessage but only know {} nodes", closest_rcs.size());
|
link_cat, "Received FindIntroMessage but only know {} nodes", closest_rcs.size());
|
||||||
m.respond(serialize_response({{"STATUS", FindIntroMessage::INSUFFICIENT_NODES}}), true);
|
respond(serialize_response({{messages::STATUS_KEY, FindIntroMessage::INSUFFICIENT_NODES}}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1035,7 +1045,7 @@ namespace llarp
|
||||||
peer_key,
|
peer_key,
|
||||||
"find_intro",
|
"find_intro",
|
||||||
FindIntroMessage::serialize(dht::Key_t{peer_key}, is_relayed, relay_order),
|
FindIntroMessage::serialize(dht::Key_t{peer_key}, is_relayed, relay_order),
|
||||||
[original_msg = std::move(m)](oxen::quic::message relay_response) mutable {
|
[respond = std::move(respond)](oxen::quic::message relay_response) mutable {
|
||||||
if (relay_response)
|
if (relay_response)
|
||||||
log::info(
|
log::info(
|
||||||
link_cat,
|
link_cat,
|
||||||
|
@ -1048,19 +1058,19 @@ namespace llarp
|
||||||
log::critical(
|
log::critical(
|
||||||
link_cat, "Relayed FindIntroMessage failed! Notifying initial requester");
|
link_cat, "Relayed FindIntroMessage failed! Notifying initial requester");
|
||||||
|
|
||||||
original_msg.respond(relay_response.body_str(), not relay_response);
|
respond(relay_response.body_str());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (auto maybe_intro = _router.contacts()->get_introset_by_location(addr))
|
if (auto maybe_intro = _router.contacts()->get_introset_by_location(addr))
|
||||||
m.respond(serialize_response({{"INTROSET", maybe_intro->bt_encode()}}));
|
respond(serialize_response({{"INTROSET", maybe_intro->bt_encode()}}));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
log::warning(
|
log::warning(
|
||||||
link_cat,
|
link_cat,
|
||||||
"Received FindIntroMessage with relayed == false and no local introset entry");
|
"Received FindIntroMessage with relayed == false and no local introset entry");
|
||||||
m.respond(serialize_response({{"STATUS", FindIntroMessage::NOT_FOUND}}), true);
|
respond(serialize_response({{messages::STATUS_KEY, FindIntroMessage::NOT_FOUND}}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1079,7 +1089,7 @@ namespace llarp
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
oxenc::bt_dict_consumer btdc{m.body()};
|
oxenc::bt_dict_consumer btdc{m.body()};
|
||||||
payload = btdc.require<std::string>((m) ? "INTROSET" : "STATUS");
|
payload = btdc.require<std::string>((m) ? "INTROSET" : messages::STATUS_KEY);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
|
@ -1101,12 +1111,12 @@ namespace llarp
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LinkManager::handle_path_build(oxen::quic::message m)
|
LinkManager::handle_path_build(oxen::quic::message m, const RouterID& from)
|
||||||
{
|
{
|
||||||
if (!_router.path_context().AllowingTransit())
|
if (!_router.path_context().AllowingTransit())
|
||||||
{
|
{
|
||||||
log::warning(link_cat, "got path build request when not permitting transit");
|
log::warning(link_cat, "got path build request when not permitting transit");
|
||||||
m.respond(serialize_response({{"STATUS", PathBuildMessage::NO_TRANSIT}}), true);
|
m.respond(serialize_response({{messages::STATUS_KEY, PathBuildMessage::NO_TRANSIT}}), true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try
|
try
|
||||||
|
@ -1136,7 +1146,8 @@ namespace llarp
|
||||||
shared.data(), other_pubkey.data(), _router.pubkey(), inner_nonce.data()))
|
shared.data(), other_pubkey.data(), _router.pubkey(), inner_nonce.data()))
|
||||||
{
|
{
|
||||||
log::info(link_cat, "DH server initialization failed during path build");
|
log::info(link_cat, "DH server initialization failed during path build");
|
||||||
m.respond(serialize_response({{"STATUS", PathBuildMessage::BAD_CRYPTO}}), true);
|
m.respond(
|
||||||
|
serialize_response({{messages::STATUS_KEY, PathBuildMessage::BAD_CRYPTO}}), true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1149,14 +1160,16 @@ namespace llarp
|
||||||
shared))
|
shared))
|
||||||
{
|
{
|
||||||
log::error(link_cat, "HMAC failed on path build request");
|
log::error(link_cat, "HMAC failed on path build request");
|
||||||
m.respond(serialize_response({{"STATUS", PathBuildMessage::BAD_CRYPTO}}), true);
|
m.respond(
|
||||||
|
serialize_response({{messages::STATUS_KEY, PathBuildMessage::BAD_CRYPTO}}), true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!std::equal(
|
if (!std::equal(
|
||||||
digest.begin(), digest.end(), reinterpret_cast<const unsigned char*>(hash.data())))
|
digest.begin(), digest.end(), reinterpret_cast<const unsigned char*>(hash.data())))
|
||||||
{
|
{
|
||||||
log::info(link_cat, "HMAC mismatch on path build request");
|
log::info(link_cat, "HMAC mismatch on path build request");
|
||||||
m.respond(serialize_response({{"STATUS", PathBuildMessage::BAD_CRYPTO}}), true);
|
m.respond(
|
||||||
|
serialize_response({{messages::STATUS_KEY, PathBuildMessage::BAD_CRYPTO}}), true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1168,7 +1181,8 @@ namespace llarp
|
||||||
outer_nonce.data()))
|
outer_nonce.data()))
|
||||||
{
|
{
|
||||||
log::info(link_cat, "Decrypt failed on path build request");
|
log::info(link_cat, "Decrypt failed on path build request");
|
||||||
m.respond(serialize_response({{"STATUS", PathBuildMessage::BAD_CRYPTO}}), true);
|
m.respond(
|
||||||
|
serialize_response({{messages::STATUS_KEY, PathBuildMessage::BAD_CRYPTO}}), true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1189,17 +1203,14 @@ namespace llarp
|
||||||
if (frame.empty())
|
if (frame.empty())
|
||||||
{
|
{
|
||||||
log::info(link_cat, "Path build request received invalid frame");
|
log::info(link_cat, "Path build request received invalid frame");
|
||||||
m.respond(serialize_response({{"STATUS", PathBuildMessage::BAD_FRAMES}}), true);
|
m.respond(serialize_response({{messages::STATUS_KEY, PathBuildMessage::BAD_FRAMES}}), true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// populate transit hop object with hop info
|
// populate transit hop object with hop info
|
||||||
// TODO: how to get downstream hop RouterID from here (all we have is oxen::quic::message)
|
// TODO: IP / path build limiting clients
|
||||||
// could do message->btstream->stream->connection_interface->connectionid
|
|
||||||
// and check our mapping, but that feels ugly as sin (and message->stream is private)
|
|
||||||
// TODO: also need downstream for IP / path build limiting clients
|
|
||||||
auto hop = std::make_shared<path::TransitHop>();
|
auto hop = std::make_shared<path::TransitHop>();
|
||||||
// hop->info.downstream = m.from(); // TODO: RouterID m.from() or similar
|
hop->info.downstream = from;
|
||||||
|
|
||||||
// extract pathIDs and check if zero or used
|
// extract pathIDs and check if zero or used
|
||||||
auto& hop_info = hop->info;
|
auto& hop_info = hop->info;
|
||||||
|
@ -1209,22 +1220,16 @@ namespace llarp
|
||||||
if (hop_info.txID.IsZero() || hop_info.rxID.IsZero())
|
if (hop_info.txID.IsZero() || hop_info.rxID.IsZero())
|
||||||
{
|
{
|
||||||
log::warning(link_cat, "Invalid PathID; PathIDs must be non-zero");
|
log::warning(link_cat, "Invalid PathID; PathIDs must be non-zero");
|
||||||
m.respond(serialize_response({{"STATUS", PathBuildMessage::BAD_PATHID}}), true);
|
m.respond(serialize_response({{messages::STATUS_KEY, PathBuildMessage::BAD_PATHID}}), true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
hop_info.upstream.from_string(upstream);
|
hop_info.upstream.from_string(upstream);
|
||||||
|
|
||||||
// TODO: need downstream (above), and also the whole transit hop container is garbage.
|
|
||||||
// namely the PathID uniqueness checking uses the PathIDs and upstream/downstream
|
|
||||||
// but if someone made a path with txid, rxid, and downstream the same but
|
|
||||||
// a different upstream, that would be "unique" but we wouldn't know where
|
|
||||||
// to route messages (nevermind that messages don't currently know the RouterID
|
|
||||||
// they came from).
|
|
||||||
if (_router.path_context().HasTransitHop(hop_info))
|
if (_router.path_context().HasTransitHop(hop_info))
|
||||||
{
|
{
|
||||||
log::warning(link_cat, "Invalid PathID; PathIDs must be unique");
|
log::warning(link_cat, "Invalid PathID; PathIDs must be unique");
|
||||||
m.respond(serialize_response({{"STATUS", PathBuildMessage::BAD_PATHID}}), true);
|
m.respond(serialize_response({{messages::STATUS_KEY, PathBuildMessage::BAD_PATHID}}), true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1232,11 +1237,13 @@ namespace llarp
|
||||||
hop->pathKey.data(), other_pubkey.data(), _router.pubkey(), inner_nonce.data()))
|
hop->pathKey.data(), other_pubkey.data(), _router.pubkey(), inner_nonce.data()))
|
||||||
{
|
{
|
||||||
log::warning(link_cat, "DH failed during path build.");
|
log::warning(link_cat, "DH failed during path build.");
|
||||||
m.respond(serialize_response({{"STATUS", PathBuildMessage::BAD_CRYPTO}}), true);
|
m.respond(serialize_response({{messages::STATUS_KEY, PathBuildMessage::BAD_CRYPTO}}), true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// generate hash of hop key for nonce mutation
|
// generate hash of hop key for nonce mutation
|
||||||
crypto::shorthash(hop->nonceXOR, hop->pathKey.data(), hop->pathKey.size());
|
ShortHash xor_hash;
|
||||||
|
crypto::shorthash(xor_hash, hop->pathKey.data(), hop->pathKey.size());
|
||||||
|
hop->nonceXOR = xor_hash.data(); // nonceXOR is 24 bytes, ShortHash is 32; this will truncate
|
||||||
|
|
||||||
// set and check path lifetime
|
// set and check path lifetime
|
||||||
hop->lifetime = 1ms * lifetime;
|
hop->lifetime = 1ms * lifetime;
|
||||||
|
@ -1244,7 +1251,8 @@ namespace llarp
|
||||||
if (hop->lifetime >= path::DEFAULT_LIFETIME)
|
if (hop->lifetime >= path::DEFAULT_LIFETIME)
|
||||||
{
|
{
|
||||||
log::warning(link_cat, "Path build attempt with too long of a lifetime.");
|
log::warning(link_cat, "Path build attempt with too long of a lifetime.");
|
||||||
m.respond(serialize_response({{"STATUS", PathBuildMessage::BAD_LIFETIME}}), true);
|
m.respond(
|
||||||
|
serialize_response({{messages::STATUS_KEY, PathBuildMessage::BAD_LIFETIME}}), true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1253,9 +1261,10 @@ namespace llarp
|
||||||
|
|
||||||
if (hop_info.upstream == _router.pubkey())
|
if (hop_info.upstream == _router.pubkey())
|
||||||
{
|
{
|
||||||
|
hop->terminal_hop = true;
|
||||||
// we are terminal hop and everything is okay
|
// we are terminal hop and everything is okay
|
||||||
_router.path_context().PutTransitHop(hop);
|
_router.path_context().PutTransitHop(hop);
|
||||||
m.respond(serialize_response({{"STATUS", PathBuildMessage::OK}}), false);
|
m.respond(messages::OK_RESPONSE, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1287,52 +1296,7 @@ namespace llarp
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
log::warning(link_cat, "Exception: {}", e.what());
|
log::warning(link_cat, "Exception: {}", e.what());
|
||||||
m.respond(serialize_response({{"STATUS", PathBuildMessage::EXCEPTION}}), true);
|
m.respond(messages::ERROR_RESPONSE, true);
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
LinkManager::handle_path_build_response(oxen::quic::message m)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
oxenc::bt_dict_consumer btdc{m.body()};
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
log::warning(link_cat, "Exception: {}", e.what());
|
|
||||||
// m.respond(serialize_response({{"STATUS", "EXCEPTION"}}), true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
LinkManager::handle_path_confirm(oxen::quic::message m)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
oxenc::bt_dict_consumer btdc{m.body()};
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
log::warning(link_cat, "Exception: {}", e.what());
|
|
||||||
m.respond(serialize_response({{"STATUS", "EXCEPTION"}}), true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
LinkManager::handle_path_confirm_response(oxen::quic::message m)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
oxenc::bt_dict_consumer btdc{m.body()};
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
log::warning(link_cat, "Exception: {}", e.what());
|
|
||||||
// m.respond(serialize_response({{"STATUS", "EXCEPTION"}}), true);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1347,7 +1311,7 @@ namespace llarp
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
log::warning(link_cat, "Exception: {}", e.what());
|
log::warning(link_cat, "Exception: {}", e.what());
|
||||||
m.respond(serialize_response({{"STATUS", "EXCEPTION"}}), true);
|
m.respond(messages::ERROR_RESPONSE, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1362,7 +1326,7 @@ namespace llarp
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
log::warning(link_cat, "Exception: {}", e.what());
|
log::warning(link_cat, "Exception: {}", e.what());
|
||||||
// m.respond(serialize_response({{"STATUS", "EXCEPTION"}}), true);
|
// m.respond(serialize_response({{messages::STATUS_KEY, "EXCEPTION"}}), true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1377,7 +1341,7 @@ namespace llarp
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
log::warning(link_cat, "Exception: {}", e.what());
|
log::warning(link_cat, "Exception: {}", e.what());
|
||||||
m.respond(serialize_response({{"STATUS", "EXCEPTION"}}), true);
|
m.respond(messages::ERROR_RESPONSE, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1392,7 +1356,7 @@ namespace llarp
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
log::warning(link_cat, "Exception: {}", e.what());
|
log::warning(link_cat, "Exception: {}", e.what());
|
||||||
m.respond(serialize_response({{"STATUS", "EXCEPTION"}}), true);
|
m.respond(messages::ERROR_RESPONSE, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1416,8 +1380,8 @@ namespace llarp
|
||||||
tx_id = btdc.require<std::string_view>("T");
|
tx_id = btdc.require<std::string_view>("T");
|
||||||
|
|
||||||
RouterID target{pubkey.data()};
|
RouterID target{pubkey.data()};
|
||||||
auto transit_hop = std::static_pointer_cast<path::TransitHop>(
|
auto transit_hop =
|
||||||
_router.path_context().GetByUpstream(target, PathID_t{to_usv(tx_id).data()}));
|
_router.path_context().GetTransitHop(target, PathID_t{to_usv(tx_id).data()});
|
||||||
|
|
||||||
const auto rx_id = transit_hop->info.rxID;
|
const auto rx_id = transit_hop->info.rxID;
|
||||||
|
|
||||||
|
@ -1431,7 +1395,7 @@ namespace llarp
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
log::warning(link_cat, "Exception: {}", e.what());
|
log::warning(link_cat, "Exception: {}", e.what());
|
||||||
m.respond(serialize_response({{"STATUS", ObtainExitMessage::EXCEPTION}}), true);
|
m.respond(messages::ERROR_RESPONSE, true);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1461,8 +1425,7 @@ namespace llarp
|
||||||
sig = to_usv(btlc.consume_string_view());
|
sig = to_usv(btlc.consume_string_view());
|
||||||
tx_id = btdc.require<std::string_view>("T");
|
tx_id = btdc.require<std::string_view>("T");
|
||||||
|
|
||||||
auto path_ptr = std::static_pointer_cast<path::Path>(
|
auto path_ptr = _router.path_context().GetPath(PathID_t{to_usv(tx_id).data()});
|
||||||
_router.path_context().GetByDownstream(_router.pubkey(), PathID_t{to_usv(tx_id).data()}));
|
|
||||||
|
|
||||||
if (crypto::verify(_router.pubkey(), to_usv(dict_data), sig))
|
if (crypto::verify(_router.pubkey(), to_usv(dict_data), sig))
|
||||||
path_ptr->enable_exit_traffic();
|
path_ptr->enable_exit_traffic();
|
||||||
|
@ -1490,8 +1453,8 @@ namespace llarp
|
||||||
path_id = btdc.require<std::string_view>("P");
|
path_id = btdc.require<std::string_view>("P");
|
||||||
tx_id = btdc.require<std::string_view>("T");
|
tx_id = btdc.require<std::string_view>("T");
|
||||||
|
|
||||||
auto transit_hop = std::static_pointer_cast<path::TransitHop>(
|
auto transit_hop =
|
||||||
_router.path_context().GetByUpstream(_router.pubkey(), PathID_t{to_usv(tx_id).data()}));
|
_router.path_context().GetTransitHop(_router.pubkey(), PathID_t{to_usv(tx_id).data()});
|
||||||
|
|
||||||
if (auto exit_ep =
|
if (auto exit_ep =
|
||||||
_router.exitContext().FindEndpointForPath(PathID_t{to_usv(path_id).data()}))
|
_router.exitContext().FindEndpointForPath(PathID_t{to_usv(path_id).data()}))
|
||||||
|
@ -1500,7 +1463,9 @@ namespace llarp
|
||||||
{
|
{
|
||||||
(exit_ep->UpdateLocalPath(transit_hop->info.rxID))
|
(exit_ep->UpdateLocalPath(transit_hop->info.rxID))
|
||||||
? m.respond(UpdateExitMessage::sign_and_serialize_response(_router.identity(), tx_id))
|
? m.respond(UpdateExitMessage::sign_and_serialize_response(_router.identity(), tx_id))
|
||||||
: m.respond(serialize_response({{"STATUS", UpdateExitMessage::UPDATE_FAILED}}), true);
|
: m.respond(
|
||||||
|
serialize_response({{messages::STATUS_KEY, UpdateExitMessage::UPDATE_FAILED}}),
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
// If we fail to verify the message, no-op
|
// If we fail to verify the message, no-op
|
||||||
}
|
}
|
||||||
|
@ -1508,7 +1473,7 @@ namespace llarp
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
log::warning(link_cat, "Exception: {}", e.what());
|
log::warning(link_cat, "Exception: {}", e.what());
|
||||||
m.respond(serialize_response({{"STATUS", UpdateExitMessage::EXCEPTION}}), true);
|
m.respond(messages::ERROR_RESPONSE, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1538,8 +1503,7 @@ namespace llarp
|
||||||
sig = to_usv(btlc.consume_string_view());
|
sig = to_usv(btlc.consume_string_view());
|
||||||
tx_id = btdc.require<std::string_view>("T");
|
tx_id = btdc.require<std::string_view>("T");
|
||||||
|
|
||||||
auto path_ptr = std::static_pointer_cast<path::Path>(
|
auto path_ptr = _router.path_context().GetPath(PathID_t{to_usv(tx_id).data()});
|
||||||
_router.path_context().GetByDownstream(_router.pubkey(), PathID_t{to_usv(tx_id).data()}));
|
|
||||||
|
|
||||||
if (crypto::verify(_router.pubkey(), to_usv(dict_data), sig))
|
if (crypto::verify(_router.pubkey(), to_usv(dict_data), sig))
|
||||||
{
|
{
|
||||||
|
@ -1574,8 +1538,8 @@ namespace llarp
|
||||||
sig = to_usv(btlc.consume_string_view());
|
sig = to_usv(btlc.consume_string_view());
|
||||||
tx_id = btdc.require<std::string_view>("T");
|
tx_id = btdc.require<std::string_view>("T");
|
||||||
|
|
||||||
auto transit_hop = std::static_pointer_cast<path::TransitHop>(
|
auto transit_hop =
|
||||||
_router.path_context().GetByUpstream(_router.pubkey(), PathID_t{to_usv(tx_id).data()}));
|
_router.path_context().GetTransitHop(_router.pubkey(), PathID_t{to_usv(tx_id).data()});
|
||||||
|
|
||||||
const auto rx_id = transit_hop->info.rxID;
|
const auto rx_id = transit_hop->info.rxID;
|
||||||
|
|
||||||
|
@ -1588,12 +1552,13 @@ namespace llarp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m.respond(serialize_response({{"STATUS", CloseExitMessage::UPDATE_FAILED}}), true);
|
m.respond(
|
||||||
|
serialize_response({{messages::STATUS_KEY, CloseExitMessage::UPDATE_FAILED}}), true);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
log::warning(link_cat, "Exception: {}", e.what());
|
log::warning(link_cat, "Exception: {}", e.what());
|
||||||
m.respond(serialize_response({{"STATUS", CloseExitMessage::EXCEPTION}}), true);
|
m.respond(messages::ERROR_RESPONSE, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1624,8 +1589,7 @@ namespace llarp
|
||||||
tx_id = btdc.require<std::string_view>("T");
|
tx_id = btdc.require<std::string_view>("T");
|
||||||
nonce = btdc.require<std::string_view>("Y");
|
nonce = btdc.require<std::string_view>("Y");
|
||||||
|
|
||||||
auto path_ptr = std::static_pointer_cast<path::Path>(
|
auto path_ptr = _router.path_context().GetPath(PathID_t{to_usv(tx_id).data()});
|
||||||
_router.path_context().GetByDownstream(_router.pubkey(), PathID_t{to_usv(tx_id).data()}));
|
|
||||||
|
|
||||||
if (path_ptr->SupportsAnyRoles(path::ePathRoleExit | path::ePathRoleSVC)
|
if (path_ptr->SupportsAnyRoles(path::ePathRoleExit | path::ePathRoleSVC)
|
||||||
and crypto::verify(_router.pubkey(), to_usv(dict_data), sig))
|
and crypto::verify(_router.pubkey(), to_usv(dict_data), sig))
|
||||||
|
@ -1639,16 +1603,51 @@ namespace llarp
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LinkManager::handle_path_control(oxen::quic::message m)
|
LinkManager::handle_path_control(oxen::quic::message m, const RouterID& from)
|
||||||
{
|
{
|
||||||
if (m.timed_out)
|
|
||||||
{
|
|
||||||
log::info(link_cat, "Path control message timed out!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{}
|
{
|
||||||
|
oxenc::bt_dict_consumer btdc{m.body()};
|
||||||
|
auto nonce = SymmNonce{btdc.require<ustring_view>("NONCE").data()};
|
||||||
|
auto path_id_str = btdc.require<ustring_view>("PATHID");
|
||||||
|
auto payload = btdc.require<std::string>("PAYLOAD");
|
||||||
|
auto path_id = PathID_t{path_id_str.data()};
|
||||||
|
auto hop = _router.path_context().GetTransitHop(from, path_id);
|
||||||
|
|
||||||
|
// TODO: use "path_control" for both directions? If not, drop message on
|
||||||
|
// floor if we don't have the path_id in question; if we decide to make this
|
||||||
|
// bidirectional, will need to check if we have a Path with path_id.
|
||||||
|
if (not hop)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// if terminal hop, payload should contain a request (e.g. "find_router"); handle and respond.
|
||||||
|
if (hop->terminal_hop)
|
||||||
|
{
|
||||||
|
hop->onion(payload, nonce, false);
|
||||||
|
handle_inner_request(std::move(m), std::move(payload), std::move(hop));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto next_id = path_id == hop->info.rxID ? hop->info.txID : hop->info.rxID;
|
||||||
|
auto next_router = path_id == hop->info.rxID ? hop->info.upstream : hop->info.downstream;
|
||||||
|
auto new_payload = hop->onion_and_payload(payload, next_id, nonce);
|
||||||
|
send_control_message(
|
||||||
|
next_router,
|
||||||
|
"path_control"s,
|
||||||
|
std::move(new_payload),
|
||||||
|
[hop_weak = hop->weak_from_this(), path_id, prev_message = std::move(m)](
|
||||||
|
oxen::quic::message response) mutable {
|
||||||
|
auto hop = hop_weak.lock();
|
||||||
|
if (not hop)
|
||||||
|
return;
|
||||||
|
|
||||||
|
oxenc::bt_dict_consumer resp_btdc{response.body()};
|
||||||
|
auto nonce = SymmNonce{resp_btdc.require<ustring_view>("NONCE").data()};
|
||||||
|
auto payload = resp_btdc.require<std::string>("PAYLOAD");
|
||||||
|
auto resp_payload = hop->onion_and_payload(payload, path_id, nonce);
|
||||||
|
prev_message.respond(std::move(resp_payload), false);
|
||||||
|
});
|
||||||
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
log::warning(link_cat, "Exception: {}", e.what());
|
log::warning(link_cat, "Exception: {}", e.what());
|
||||||
|
@ -1656,6 +1655,34 @@ namespace llarp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LinkManager::handle_inner_request(
|
||||||
|
oxen::quic::message m, std::string payload, std::shared_ptr<path::TransitHop> hop)
|
||||||
|
{
|
||||||
|
oxenc::bt_dict_consumer btdc{payload};
|
||||||
|
auto body = btdc.require<std::string_view>("BODY");
|
||||||
|
auto method = btdc.require<std::string_view>("METHOD");
|
||||||
|
|
||||||
|
// If a handler exists for "method", call it; else drop request on the floor.
|
||||||
|
auto itr = path_requests.find(method);
|
||||||
|
if (itr == path_requests.end())
|
||||||
|
{
|
||||||
|
log::info(link_cat, "Received path control request \"{}\", which has no handler.", method);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto respond = [m = std::move(m),
|
||||||
|
hop_weak = hop->weak_from_this()](std::string response) mutable {
|
||||||
|
auto hop = hop_weak.lock();
|
||||||
|
if (not hop)
|
||||||
|
return; // transit hop gone, drop response
|
||||||
|
|
||||||
|
m.respond(hop->onion_and_payload(response, hop->info.rxID), false);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::invoke(itr->second, this, std::move(body), std::move(respond));
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LinkManager::handle_convo_intro(oxen::quic::message m)
|
LinkManager::handle_convo_intro(oxen::quic::message m)
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
#include <llarp/constants/path.hpp>
|
#include <llarp/constants/path.hpp>
|
||||||
#include <llarp/crypto/crypto.hpp>
|
#include <llarp/crypto/crypto.hpp>
|
||||||
|
#include <llarp/messages/common.hpp>
|
||||||
|
#include <llarp/path/transit_hop.hpp>
|
||||||
#include <llarp/router/rc_lookup_handler.hpp>
|
#include <llarp/router/rc_lookup_handler.hpp>
|
||||||
#include <llarp/router_contact.hpp>
|
#include <llarp/router_contact.hpp>
|
||||||
#include <llarp/util/compare_ptr.hpp>
|
#include <llarp/util/compare_ptr.hpp>
|
||||||
|
@ -26,12 +28,6 @@ namespace llarp
|
||||||
{
|
{
|
||||||
struct LinkManager;
|
struct LinkManager;
|
||||||
|
|
||||||
inline std::string
|
|
||||||
serialize_response(oxenc::bt_dict supplement = {})
|
|
||||||
{
|
|
||||||
return oxenc::bt_serialize(supplement);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace link
|
namespace link
|
||||||
{
|
{
|
||||||
struct Connection;
|
struct Connection;
|
||||||
|
@ -171,8 +167,6 @@ namespace llarp
|
||||||
friend struct link::Endpoint;
|
friend struct link::Endpoint;
|
||||||
|
|
||||||
std::atomic<bool> is_stopping;
|
std::atomic<bool> is_stopping;
|
||||||
// DISCUSS: is this necessary? can we reduce the amount of locking and nuke this
|
|
||||||
mutable util::Mutex m; // protects persisting_conns
|
|
||||||
|
|
||||||
// sessions to persist -> timestamp to end persist at
|
// sessions to persist -> timestamp to end persist at
|
||||||
std::unordered_map<RouterID, llarp_time_t> persisting_conns;
|
std::unordered_map<RouterID, llarp_time_t> persisting_conns;
|
||||||
|
@ -288,18 +282,21 @@ namespace llarp
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// DHT messages
|
// DHT messages
|
||||||
void handle_find_name(oxen::quic::message); // relay
|
void
|
||||||
void handle_find_intro(oxen::quic::message); // relay
|
handle_find_name(std::string_view body, std::function<void(std::string)> respond); // relay
|
||||||
void handle_publish_intro(oxen::quic::message); // relay
|
void
|
||||||
void handle_find_router(oxen::quic::message); // relay + path
|
handle_find_intro(std::string_view body, std::function<void(std::string)> respond); // relay
|
||||||
|
void
|
||||||
|
handle_publish_intro(std::string_view body, std::function<void(std::string)> respond); // relay
|
||||||
|
void
|
||||||
|
handle_find_router(
|
||||||
|
std::string_view body, std::function<void(std::string)> respond); // relay + path
|
||||||
|
|
||||||
// Path messages
|
// Path messages
|
||||||
void handle_path_build(oxen::quic::message); // relay
|
void
|
||||||
void handle_path_confirm(oxen::quic::message); // relay
|
handle_path_build(oxen::quic::message, const RouterID& from); // relay
|
||||||
void handle_path_latency(oxen::quic::message); // relay
|
void handle_path_latency(oxen::quic::message); // relay
|
||||||
void handle_path_transfer(oxen::quic::message); // relay
|
void handle_path_transfer(oxen::quic::message); // relay
|
||||||
void handle_relay_commit(oxen::quic::message); // relay
|
|
||||||
void handle_relay_status(oxen::quic::message); // relay
|
|
||||||
|
|
||||||
// Exit messages
|
// Exit messages
|
||||||
void handle_obtain_exit(oxen::quic::message); // relay
|
void handle_obtain_exit(oxen::quic::message); // relay
|
||||||
|
@ -309,21 +306,45 @@ namespace llarp
|
||||||
// Misc
|
// Misc
|
||||||
void handle_convo_intro(oxen::quic::message);
|
void handle_convo_intro(oxen::quic::message);
|
||||||
|
|
||||||
std::unordered_map<std::string, void (LinkManager::*)(oxen::quic::message)> rpc_commands = {
|
// These requests come over a path (as a "path_control" request),
|
||||||
{"find_name", &LinkManager::handle_find_name},
|
// may or may not need to make a request to another relay,
|
||||||
{"find_router", &LinkManager::handle_find_router},
|
// then respond (onioned) back along the path.
|
||||||
{"publish_intro", &LinkManager::handle_publish_intro},
|
std::unordered_map<
|
||||||
{"find_intro", &LinkManager::handle_find_intro},
|
std::string_view,
|
||||||
{"path_build", &LinkManager::handle_path_build},
|
void (LinkManager::*)(std::string_view body, std::function<void(std::string)> respond)>
|
||||||
{"path_confirm", &LinkManager::handle_path_confirm},
|
path_requests = {
|
||||||
{"path_latency", &LinkManager::handle_path_latency},
|
{"find_name"sv, &LinkManager::handle_find_name},
|
||||||
{"update_exit", &LinkManager::handle_update_exit},
|
{"find_router"sv, &LinkManager::handle_find_router},
|
||||||
{"obtain_exit", &LinkManager::handle_obtain_exit},
|
{"publish_intro"sv, &LinkManager::handle_publish_intro},
|
||||||
{"close_exit", &LinkManager::handle_close_exit},
|
{"find_intro"sv, &LinkManager::handle_find_intro}};
|
||||||
{"convo_intro", &LinkManager::handle_convo_intro}};
|
/*
|
||||||
|
{"path_confirm", &LinkManager::handle_path_confirm},
|
||||||
|
{"path_latency", &LinkManager::handle_path_latency},
|
||||||
|
{"update_exit", &LinkManager::handle_update_exit},
|
||||||
|
{"obtain_exit", &LinkManager::handle_obtain_exit},
|
||||||
|
{"close_exit", &LinkManager::handle_close_exit},
|
||||||
|
{"convo_intro", &LinkManager::handle_convo_intro}};
|
||||||
|
*/
|
||||||
|
|
||||||
|
// these requests are direct, i.e. not over a path;
|
||||||
|
// only "find_router" makes sense client->relay,
|
||||||
|
// the rest are relay->relay
|
||||||
|
// TODO: new RC fetch endpoint (which will be both client->relay and relay->relay)
|
||||||
|
std::unordered_map<
|
||||||
|
std::string_view,
|
||||||
|
void (LinkManager::*)(std::string_view body, std::function<void(std::string)> respond)>
|
||||||
|
direct_requests = {
|
||||||
|
{"find_router"sv, &LinkManager::handle_find_router},
|
||||||
|
{"publish_intro"sv, &LinkManager::handle_publish_intro},
|
||||||
|
{"find_intro"sv, &LinkManager::handle_find_intro}};
|
||||||
|
|
||||||
// Path relaying
|
// Path relaying
|
||||||
void handle_path_control(oxen::quic::message);
|
void
|
||||||
|
handle_path_control(oxen::quic::message, const RouterID& from);
|
||||||
|
|
||||||
|
void
|
||||||
|
handle_inner_request(
|
||||||
|
oxen::quic::message m, std::string payload, std::shared_ptr<path::TransitHop> hop);
|
||||||
|
|
||||||
// DHT responses
|
// DHT responses
|
||||||
void handle_find_name_response(oxen::quic::message);
|
void handle_find_name_response(oxen::quic::message);
|
||||||
|
@ -332,10 +353,6 @@ namespace llarp
|
||||||
void handle_find_router_response(oxen::quic::message);
|
void handle_find_router_response(oxen::quic::message);
|
||||||
|
|
||||||
// Path responses
|
// Path responses
|
||||||
void handle_path_build_response(oxen::quic::message);
|
|
||||||
void handle_relay_commit_response(oxen::quic::message);
|
|
||||||
void handle_relay_status_response(oxen::quic::message);
|
|
||||||
void handle_path_confirm_response(oxen::quic::message);
|
|
||||||
void handle_path_latency_response(oxen::quic::message);
|
void handle_path_latency_response(oxen::quic::message);
|
||||||
void handle_path_transfer_response(oxen::quic::message);
|
void handle_path_transfer_response(oxen::quic::message);
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,22 @@ namespace
|
||||||
|
|
||||||
namespace llarp
|
namespace llarp
|
||||||
{
|
{
|
||||||
|
namespace messages
|
||||||
|
{
|
||||||
|
|
||||||
|
inline std::string
|
||||||
|
serialize_response(oxenc::bt_dict supplement = {})
|
||||||
|
{
|
||||||
|
return oxenc::bt_serialize(supplement);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ideally STATUS is the first key in a bt-dict, so use a single, early ascii char
|
||||||
|
inline const auto STATUS_KEY = "!"s;
|
||||||
|
inline const auto TIMEOUT_RESPONSE = serialize_response({{STATUS_KEY, "TIMEOUT"}});
|
||||||
|
inline const auto ERROR_RESPONSE = serialize_response({{STATUS_KEY, "ERROR"}});
|
||||||
|
inline const auto OK_RESPONSE = serialize_response({{STATUS_KEY, "OK"}});
|
||||||
|
} // namespace messages
|
||||||
|
|
||||||
/// abstract base class for serialized messages
|
/// abstract base class for serialized messages
|
||||||
struct AbstractSerializable
|
struct AbstractSerializable
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,7 +6,6 @@ namespace llarp
|
||||||
{
|
{
|
||||||
namespace FindRouterMessage
|
namespace FindRouterMessage
|
||||||
{
|
{
|
||||||
inline auto EXCEPTION = "EXCEPTION"sv;
|
|
||||||
inline auto RETRY_EXP = "RETRY AS EXPLORATORY"sv;
|
inline auto RETRY_EXP = "RETRY AS EXPLORATORY"sv;
|
||||||
inline auto RETRY_ITER = "RETRY AS ITERATIVE"sv;
|
inline auto RETRY_ITER = "RETRY AS ITERATIVE"sv;
|
||||||
inline auto RETRY_NEW = "RETRY WITH NEW RECIPIENT"sv;
|
inline auto RETRY_NEW = "RETRY WITH NEW RECIPIENT"sv;
|
||||||
|
@ -52,9 +51,7 @@ namespace llarp
|
||||||
|
|
||||||
namespace FindIntroMessage
|
namespace FindIntroMessage
|
||||||
{
|
{
|
||||||
inline auto EXCEPTION = "EXCEPTION"sv;
|
|
||||||
inline auto NOT_FOUND = "NOT FOUND"sv;
|
inline auto NOT_FOUND = "NOT FOUND"sv;
|
||||||
inline auto TIMED_OUT = "TIMED OUT"sv;
|
|
||||||
inline auto INVALID_ORDER = "INVALID ORDER"sv;
|
inline auto INVALID_ORDER = "INVALID ORDER"sv;
|
||||||
inline auto INSUFFICIENT_NODES = "INSUFFICIENT NODES"sv;
|
inline auto INSUFFICIENT_NODES = "INSUFFICIENT NODES"sv;
|
||||||
|
|
||||||
|
@ -80,7 +77,6 @@ namespace llarp
|
||||||
|
|
||||||
namespace FindNameMessage
|
namespace FindNameMessage
|
||||||
{
|
{
|
||||||
inline auto EXCEPTION = "EXCEPTION"sv;
|
|
||||||
inline auto NOT_FOUND = "NOT FOUND"sv;
|
inline auto NOT_FOUND = "NOT FOUND"sv;
|
||||||
|
|
||||||
inline static std::string
|
inline static std::string
|
||||||
|
@ -120,7 +116,6 @@ namespace llarp
|
||||||
|
|
||||||
namespace PublishIntroMessage
|
namespace PublishIntroMessage
|
||||||
{
|
{
|
||||||
inline auto EXCEPTION = "EXCEPTION"sv;
|
|
||||||
inline auto INVALID_INTROSET = "INVALID INTROSET"sv;
|
inline auto INVALID_INTROSET = "INVALID INTROSET"sv;
|
||||||
inline auto EXPIRED = "EXPIRED INTROSET"sv;
|
inline auto EXPIRED = "EXPIRED INTROSET"sv;
|
||||||
inline auto INSUFFICIENT = "INSUFFICIENT NODES"sv;
|
inline auto INSUFFICIENT = "INSUFFICIENT NODES"sv;
|
||||||
|
|
|
@ -12,7 +12,6 @@ namespace llarp
|
||||||
|
|
||||||
namespace ObtainExitMessage
|
namespace ObtainExitMessage
|
||||||
{
|
{
|
||||||
inline auto EXCEPTION = "EXCEPTION"sv;
|
|
||||||
|
|
||||||
// flag: 0 = Exit, 1 = Snode
|
// flag: 0 = Exit, 1 = Snode
|
||||||
inline std::string
|
inline std::string
|
||||||
|
@ -63,7 +62,6 @@ namespace llarp
|
||||||
|
|
||||||
namespace UpdateExitMessage
|
namespace UpdateExitMessage
|
||||||
{
|
{
|
||||||
inline auto EXCEPTION = "EXCEPTION"sv;
|
|
||||||
inline auto UPDATE_FAILED = "EXIT UPDATE FAILED"sv;
|
inline auto UPDATE_FAILED = "EXIT UPDATE FAILED"sv;
|
||||||
|
|
||||||
inline std::string
|
inline std::string
|
||||||
|
@ -113,7 +111,6 @@ namespace llarp
|
||||||
|
|
||||||
namespace CloseExitMessage
|
namespace CloseExitMessage
|
||||||
{
|
{
|
||||||
inline auto EXCEPTION = "EXCEPTION"sv;
|
|
||||||
inline auto UPDATE_FAILED = "CLOSE EXIT FAILED"sv;
|
inline auto UPDATE_FAILED = "CLOSE EXIT FAILED"sv;
|
||||||
|
|
||||||
inline std::string
|
inline std::string
|
||||||
|
|
|
@ -6,8 +6,6 @@ namespace llarp
|
||||||
{
|
{
|
||||||
namespace PathBuildMessage
|
namespace PathBuildMessage
|
||||||
{
|
{
|
||||||
inline auto OK = "OK"sv;
|
|
||||||
inline auto EXCEPTION = "EXCEPTION"sv;
|
|
||||||
inline auto BAD_FRAMES = "BAD_FRAMES"sv;
|
inline auto BAD_FRAMES = "BAD_FRAMES"sv;
|
||||||
inline auto BAD_CRYPTO = "BAD_CRYPTO"sv;
|
inline auto BAD_CRYPTO = "BAD_CRYPTO"sv;
|
||||||
inline auto NO_TRANSIT = "NOT ALLOWING TRANSIT"sv;
|
inline auto NO_TRANSIT = "NOT ALLOWING TRANSIT"sv;
|
||||||
|
@ -29,7 +27,9 @@ namespace llarp
|
||||||
throw std::runtime_error{std::move(err)};
|
throw std::runtime_error{std::move(err)};
|
||||||
}
|
}
|
||||||
// generate nonceXOR value self->hop->pathKey
|
// generate nonceXOR value self->hop->pathKey
|
||||||
crypto::shorthash(hop.nonceXOR, hop.shared.data(), hop.shared.size());
|
ShortHash hash;
|
||||||
|
crypto::shorthash(hash, hop.shared.data(), hop.shared.size());
|
||||||
|
hop.nonceXOR = hash.data(); // nonceXOR is 24 bytes, ShortHash is 32; this will truncate
|
||||||
|
|
||||||
hop.upstream = nextHop;
|
hop.upstream = nextHop;
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ namespace llarp
|
||||||
crypto::encryption_keygen(framekey);
|
crypto::encryption_keygen(framekey);
|
||||||
|
|
||||||
SharedSecret shared;
|
SharedSecret shared;
|
||||||
TunnelNonce outer_nonce;
|
SymmNonce outer_nonce;
|
||||||
outer_nonce.Randomize();
|
outer_nonce.Randomize();
|
||||||
|
|
||||||
// derive (outer) shared key
|
// derive (outer) shared key
|
||||||
|
|
|
@ -1,121 +0,0 @@
|
||||||
#include "relay.hpp"
|
|
||||||
|
|
||||||
#include <llarp/path/path_context.hpp>
|
|
||||||
#include <llarp/router/router.hpp>
|
|
||||||
#include <llarp/util/bencode.hpp>
|
|
||||||
|
|
||||||
namespace llarp
|
|
||||||
{
|
|
||||||
void
|
|
||||||
RelayUpstreamMessage::clear()
|
|
||||||
{
|
|
||||||
pathid.Zero();
|
|
||||||
enc.Clear();
|
|
||||||
nonce.Zero();
|
|
||||||
version = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
RelayUpstreamMessage::bt_encode() const
|
|
||||||
{
|
|
||||||
oxenc::bt_dict_producer btdp;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
btdp.append("a", "u");
|
|
||||||
btdp.append("p", pathid.ToView());
|
|
||||||
btdp.append("v", llarp::constants::proto_version);
|
|
||||||
btdp.append("x", std::string_view{reinterpret_cast<const char*>(enc.data()), enc.size()});
|
|
||||||
btdp.append("y", std::string_view{reinterpret_cast<const char*>(nonce.data()), nonce.size()});
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
log::critical(link_cat, "Error: RelayUpstreamMessage failed to bt encode contents!");
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::move(btdp).str();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
RelayUpstreamMessage::decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf)
|
|
||||||
{
|
|
||||||
bool read = false;
|
|
||||||
if (!BEncodeMaybeReadDictEntry("p", pathid, read, key, buf))
|
|
||||||
return false;
|
|
||||||
if (!BEncodeMaybeVerifyVersion("v", version, llarp::constants::proto_version, read, key, buf))
|
|
||||||
return false;
|
|
||||||
if (!BEncodeMaybeReadDictEntry("x", enc, read, key, buf))
|
|
||||||
return false;
|
|
||||||
if (!BEncodeMaybeReadDictEntry("y", nonce, read, key, buf))
|
|
||||||
return false;
|
|
||||||
return read;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
RelayUpstreamMessage::handle_message(Router* r) const
|
|
||||||
{
|
|
||||||
auto path = r->path_context().GetByDownstream(conn->remote_rc.router_id(), pathid);
|
|
||||||
if (path)
|
|
||||||
{
|
|
||||||
return path->HandleUpstream(llarp_buffer_t(enc), nonce, r);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RelayDownstreamMessage::clear()
|
|
||||||
{
|
|
||||||
pathid.Zero();
|
|
||||||
enc.Clear();
|
|
||||||
nonce.Zero();
|
|
||||||
version = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
RelayDownstreamMessage::bt_encode() const
|
|
||||||
{
|
|
||||||
oxenc::bt_dict_producer btdp;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
btdp.append("a", "d");
|
|
||||||
btdp.append("p", pathid.ToView());
|
|
||||||
btdp.append("v", llarp::constants::proto_version);
|
|
||||||
btdp.append("x", std::string_view{reinterpret_cast<const char*>(enc.data()), enc.size()});
|
|
||||||
btdp.append("y", std::string_view{reinterpret_cast<const char*>(nonce.data()), nonce.size()});
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
log::critical(link_cat, "Error: RelayDownstreamMessage failed to bt encode contents!");
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::move(btdp).str();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
RelayDownstreamMessage::decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf)
|
|
||||||
{
|
|
||||||
bool read = false;
|
|
||||||
if (!BEncodeMaybeReadDictEntry("p", pathid, read, key, buf))
|
|
||||||
return false;
|
|
||||||
if (!BEncodeMaybeVerifyVersion("v", version, llarp::constants::proto_version, read, key, buf))
|
|
||||||
return false;
|
|
||||||
if (!BEncodeMaybeReadDictEntry("x", enc, read, key, buf))
|
|
||||||
return false;
|
|
||||||
if (!BEncodeMaybeReadDictEntry("y", nonce, read, key, buf))
|
|
||||||
return false;
|
|
||||||
return read;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
RelayDownstreamMessage::handle_message(Router* r) const
|
|
||||||
{
|
|
||||||
auto path = r->path_context().GetByUpstream(conn->remote_rc.router_id(), pathid);
|
|
||||||
if (path)
|
|
||||||
{
|
|
||||||
return path->HandleDownstream(llarp_buffer_t(enc), nonce, r);
|
|
||||||
}
|
|
||||||
llarp::LogWarn("no path for downstream message id=", pathid);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} // namespace llarp
|
|
|
@ -1,75 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "link_message.hpp"
|
|
||||||
|
|
||||||
#include <llarp/crypto/encrypted.hpp>
|
|
||||||
#include <llarp/crypto/types.hpp>
|
|
||||||
#include <llarp/path/path_types.hpp>
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace llarp
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
Data messages to be sent via quic datagrams
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct RelayUpstreamMessage final : public AbstractLinkMessage
|
|
||||||
{
|
|
||||||
Encrypted<MAX_LINK_MSG_SIZE - 128> enc;
|
|
||||||
TunnelNonce nonce;
|
|
||||||
|
|
||||||
bool
|
|
||||||
decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf) override;
|
|
||||||
|
|
||||||
std::string
|
|
||||||
bt_encode() const override;
|
|
||||||
|
|
||||||
bool
|
|
||||||
handle_message(Router* router) const override;
|
|
||||||
|
|
||||||
void
|
|
||||||
clear() override;
|
|
||||||
|
|
||||||
const char*
|
|
||||||
name() const override
|
|
||||||
{
|
|
||||||
return "RelayUpstream";
|
|
||||||
}
|
|
||||||
uint16_t
|
|
||||||
priority() const override
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RelayDownstreamMessage final : public AbstractLinkMessage
|
|
||||||
{
|
|
||||||
Encrypted<MAX_LINK_MSG_SIZE - 128> enc;
|
|
||||||
TunnelNonce nonce;
|
|
||||||
|
|
||||||
bool
|
|
||||||
decode_key(const llarp_buffer_t& key, llarp_buffer_t* buf) override;
|
|
||||||
|
|
||||||
std::string
|
|
||||||
bt_encode() const override;
|
|
||||||
|
|
||||||
bool
|
|
||||||
handle_message(Router* router) const override;
|
|
||||||
|
|
||||||
void
|
|
||||||
clear() override;
|
|
||||||
|
|
||||||
const char*
|
|
||||||
name() const override
|
|
||||||
{
|
|
||||||
return "RelayDownstream";
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t
|
|
||||||
priority() const override
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace llarp
|
|
|
@ -4,34 +4,26 @@
|
||||||
|
|
||||||
namespace llarp::path
|
namespace llarp::path
|
||||||
{
|
{
|
||||||
// handle data in upstream direction
|
std::string
|
||||||
bool
|
make_onion_payload(
|
||||||
AbstractHopHandler::HandleUpstream(const llarp_buffer_t& X, const TunnelNonce& Y, Router* r)
|
const SymmNonce& nonce, const PathID_t& path_id, const std::string_view& inner_payload)
|
||||||
{
|
{
|
||||||
auto& pkt = m_UpstreamQueue.emplace_back();
|
return make_onion_payload(
|
||||||
pkt.first.resize(X.sz);
|
nonce,
|
||||||
std::copy_n(X.base, X.sz, pkt.first.begin());
|
path_id,
|
||||||
pkt.second = Y;
|
ustring_view{
|
||||||
r->TriggerPump();
|
reinterpret_cast<const unsigned char*>(inner_payload.data()), inner_payload.size()});
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle data in downstream direction
|
std::string
|
||||||
bool
|
make_onion_payload(
|
||||||
AbstractHopHandler::HandleDownstream(const llarp_buffer_t& X, const TunnelNonce& Y, Router* r)
|
const SymmNonce& nonce, const PathID_t& path_id, const ustring_view& inner_payload)
|
||||||
{
|
{
|
||||||
auto& pkt = m_DownstreamQueue.emplace_back();
|
oxenc::bt_dict_producer next_dict;
|
||||||
pkt.first.resize(X.sz);
|
next_dict.append("NONCE", nonce.ToView());
|
||||||
std::copy_n(X.base, X.sz, pkt.first.begin());
|
next_dict.append("PATHID", path_id.ToView());
|
||||||
pkt.second = Y;
|
next_dict.append("PAYLOAD", inner_payload);
|
||||||
r->TriggerPump();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
return std::move(next_dict).str();
|
||||||
AbstractHopHandler::DecayFilters(llarp_time_t now)
|
|
||||||
{
|
|
||||||
m_UpstreamReplayFilter.Decay(now);
|
|
||||||
m_DownstreamReplayFilter.Decay(now);
|
|
||||||
}
|
}
|
||||||
} // namespace llarp::path
|
} // namespace llarp::path
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <llarp/crypto/encrypted_frame.hpp>
|
#include "path_types.hpp"
|
||||||
|
|
||||||
#include <llarp/crypto/types.hpp>
|
#include <llarp/crypto/types.hpp>
|
||||||
#include <llarp/messages/relay.hpp>
|
|
||||||
#include <llarp/util/decaying_hashset.hpp>
|
#include <llarp/util/decaying_hashset.hpp>
|
||||||
#include <llarp/util/types.hpp>
|
#include <llarp/util/types.hpp>
|
||||||
|
|
||||||
|
@ -22,41 +22,38 @@ namespace llarp
|
||||||
|
|
||||||
namespace path
|
namespace path
|
||||||
{
|
{
|
||||||
|
|
||||||
|
std::string
|
||||||
|
make_onion_payload(
|
||||||
|
const SymmNonce& nonce, const PathID_t& path_id, const std::string_view& inner_payload);
|
||||||
|
std::string
|
||||||
|
make_onion_payload(
|
||||||
|
const SymmNonce& nonce, const PathID_t& path_id, const ustring_view& inner_payload);
|
||||||
|
|
||||||
struct AbstractHopHandler
|
struct AbstractHopHandler
|
||||||
{
|
{
|
||||||
using TrafficEvent_t = std::pair<std::vector<byte_t>, TunnelNonce>;
|
|
||||||
using TrafficQueue_t = std::list<TrafficEvent_t>;
|
|
||||||
|
|
||||||
virtual ~AbstractHopHandler() = default;
|
virtual ~AbstractHopHandler() = default;
|
||||||
|
|
||||||
virtual PathID_t
|
virtual PathID_t
|
||||||
RXID() const = 0;
|
RXID() const = 0;
|
||||||
|
|
||||||
void
|
|
||||||
DecayFilters(llarp_time_t now);
|
|
||||||
|
|
||||||
virtual bool
|
virtual bool
|
||||||
Expired(llarp_time_t now) const = 0;
|
Expired(llarp_time_t now) const = 0;
|
||||||
|
|
||||||
virtual bool
|
virtual bool
|
||||||
ExpiresSoon(llarp_time_t now, llarp_time_t dlt) const = 0;
|
ExpiresSoon(llarp_time_t now, llarp_time_t dlt) const = 0;
|
||||||
|
|
||||||
|
/// sends a control request along a path
|
||||||
|
///
|
||||||
|
/// performs the necessary onion encryption before sending.
|
||||||
|
/// func will be called when a timeout occurs or a response is received.
|
||||||
|
/// if a response is received, onion decryption is performed before func is called.
|
||||||
|
///
|
||||||
|
/// func is called with a bt-encoded response string (if applicable), and
|
||||||
|
/// a timeout flag (if set, response string will be empty)
|
||||||
virtual bool
|
virtual bool
|
||||||
send_path_control_message(
|
send_path_control_message(
|
||||||
std::string method,
|
std::string method, std::string body, std::function<void(std::string)> func) = 0;
|
||||||
std::string body,
|
|
||||||
std::function<void(oxen::quic::message m)> func) = 0;
|
|
||||||
|
|
||||||
/// send routing message and increment sequence number
|
|
||||||
virtual bool
|
|
||||||
SendRoutingMessage(std::string payload, Router* r) = 0;
|
|
||||||
|
|
||||||
// handle data in upstream direction
|
|
||||||
virtual bool
|
|
||||||
HandleUpstream(const llarp_buffer_t& X, const TunnelNonce& Y, Router*);
|
|
||||||
// handle data in downstream direction
|
|
||||||
virtual bool
|
|
||||||
HandleDownstream(const llarp_buffer_t& X, const TunnelNonce& Y, Router*);
|
|
||||||
|
|
||||||
/// return timestamp last remote activity happened at
|
/// return timestamp last remote activity happened at
|
||||||
virtual llarp_time_t
|
virtual llarp_time_t
|
||||||
|
@ -68,29 +65,8 @@ namespace llarp
|
||||||
return m_SequenceNum++;
|
return m_SequenceNum++;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void
|
|
||||||
FlushUpstream(Router* r) = 0;
|
|
||||||
|
|
||||||
virtual void
|
|
||||||
FlushDownstream(Router* r) = 0;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint64_t m_SequenceNum = 0;
|
uint64_t m_SequenceNum = 0;
|
||||||
TrafficQueue_t m_UpstreamQueue;
|
|
||||||
TrafficQueue_t m_DownstreamQueue;
|
|
||||||
util::DecayingHashSet<TunnelNonce> m_UpstreamReplayFilter;
|
|
||||||
util::DecayingHashSet<TunnelNonce> m_DownstreamReplayFilter;
|
|
||||||
|
|
||||||
virtual void
|
|
||||||
UpstreamWork(TrafficQueue_t queue, Router* r) = 0;
|
|
||||||
|
|
||||||
virtual void
|
|
||||||
DownstreamWork(TrafficQueue_t queue, Router* r) = 0;
|
|
||||||
|
|
||||||
virtual void
|
|
||||||
HandleAllUpstream(std::vector<RelayUpstreamMessage> msgs, Router* r) = 0;
|
|
||||||
virtual void
|
|
||||||
HandleAllDownstream(std::vector<RelayDownstreamMessage> msgs, Router* r) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using HopHandler_ptr = std::shared_ptr<AbstractHopHandler>;
|
using HopHandler_ptr = std::shared_ptr<AbstractHopHandler>;
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
namespace llarp::path
|
namespace llarp::path
|
||||||
{
|
{
|
||||||
|
|
||||||
Path::Path(
|
Path::Path(
|
||||||
Router* rtr,
|
Router* rtr,
|
||||||
const std::vector<RemoteRC>& h,
|
const std::vector<RemoteRC>& h,
|
||||||
|
@ -48,10 +49,7 @@ namespace llarp::path
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Path::obtain_exit(
|
Path::obtain_exit(
|
||||||
SecretKey sk,
|
SecretKey sk, uint64_t flag, std::string tx_id, std::function<void(std::string)> func)
|
||||||
uint64_t flag,
|
|
||||||
std::string tx_id,
|
|
||||||
std::function<void(oxen::quic::message m)> func)
|
|
||||||
{
|
{
|
||||||
return send_path_control_message(
|
return send_path_control_message(
|
||||||
"obtain_exit",
|
"obtain_exit",
|
||||||
|
@ -60,7 +58,7 @@ namespace llarp::path
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Path::close_exit(SecretKey sk, std::string tx_id, std::function<void(oxen::quic::message m)> func)
|
Path::close_exit(SecretKey sk, std::string tx_id, std::function<void(std::string)> func)
|
||||||
{
|
{
|
||||||
return send_path_control_message(
|
return send_path_control_message(
|
||||||
"close_exit", CloseExitMessage::sign_and_serialize(sk, std::move(tx_id)), std::move(func));
|
"close_exit", CloseExitMessage::sign_and_serialize(sk, std::move(tx_id)), std::move(func));
|
||||||
|
@ -71,21 +69,21 @@ namespace llarp::path
|
||||||
const dht::Key_t& location,
|
const dht::Key_t& location,
|
||||||
bool is_relayed,
|
bool is_relayed,
|
||||||
uint64_t order,
|
uint64_t order,
|
||||||
std::function<void(oxen::quic::message m)> func)
|
std::function<void(std::string)> func)
|
||||||
{
|
{
|
||||||
return send_path_control_message(
|
return send_path_control_message(
|
||||||
"find_intro", FindIntroMessage::serialize(location, is_relayed, order), std::move(func));
|
"find_intro", FindIntroMessage::serialize(location, is_relayed, order), std::move(func));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Path::find_name(std::string name, std::function<void(oxen::quic::message m)> func)
|
Path::find_name(std::string name, std::function<void(std::string)> func)
|
||||||
{
|
{
|
||||||
return send_path_control_message(
|
return send_path_control_message(
|
||||||
"find_name", FindNameMessage::serialize(std::move(name)), std::move(func));
|
"find_name", FindNameMessage::serialize(std::move(name)), std::move(func));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Path::find_router(std::string rid, std::function<void(oxen::quic::message m)> func)
|
Path::find_router(std::string rid, std::function<void(std::string)> func)
|
||||||
{
|
{
|
||||||
return send_path_control_message(
|
return send_path_control_message(
|
||||||
"find_router", FindRouterMessage::serialize(std::move(rid), false, false), std::move(func));
|
"find_router", FindRouterMessage::serialize(std::move(rid), false, false), std::move(func));
|
||||||
|
@ -93,62 +91,79 @@ namespace llarp::path
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Path::send_path_control_message(
|
Path::send_path_control_message(
|
||||||
std::string method, std::string body, std::function<void(oxen::quic::message m)> func)
|
std::string method, std::string body, std::function<void(std::string)> func)
|
||||||
{
|
{
|
||||||
std::string payload;
|
oxenc::bt_dict_producer btdp;
|
||||||
|
btdp.append("BODY", body);
|
||||||
|
btdp.append("METHOD", method);
|
||||||
|
auto payload = std::move(btdp).str();
|
||||||
|
|
||||||
{
|
// TODO: old impl padded messages if smaller than a certain size; do we still want to?
|
||||||
oxenc::bt_dict_producer btdp;
|
SymmNonce nonce;
|
||||||
btdp.append("BODY", body);
|
|
||||||
btdp.append("METHOD", method);
|
|
||||||
payload = std::move(btdp).str();
|
|
||||||
}
|
|
||||||
|
|
||||||
TunnelNonce nonce;
|
|
||||||
nonce.Randomize();
|
nonce.Randomize();
|
||||||
|
|
||||||
|
// chacha and mutate nonce for each hop
|
||||||
for (const auto& hop : hops)
|
for (const auto& hop : hops)
|
||||||
{
|
{
|
||||||
// do a round of chacha for each hop and mutate the nonce with that hop's nonce
|
nonce = crypto::onion(
|
||||||
crypto::xchacha20(
|
reinterpret_cast<unsigned char*>(payload.data()),
|
||||||
reinterpret_cast<unsigned char*>(payload.data()), payload.size(), hop.shared, nonce);
|
payload.size(),
|
||||||
|
hop.shared,
|
||||||
nonce ^= hop.nonceXOR;
|
nonce,
|
||||||
|
hop.nonceXOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
oxenc::bt_dict_producer outer_dict;
|
auto outer_payload = make_onion_payload(nonce, TXID(), payload);
|
||||||
outer_dict.append("NONCE", nonce.ToView());
|
|
||||||
outer_dict.append("PATHID", TXID().ToView());
|
|
||||||
outer_dict.append("PAYLOAD", payload);
|
|
||||||
|
|
||||||
return router.send_control_message(
|
return router.send_control_message(
|
||||||
upstream(),
|
upstream(),
|
||||||
"path_control",
|
"path_control",
|
||||||
std::move(outer_dict).str(),
|
std::move(outer_payload),
|
||||||
[response_cb = std::move(func)](oxen::quic::message m) {
|
[response_cb = std::move(func), weak = weak_from_this()](oxen::quic::message m) {
|
||||||
if (m)
|
auto self = weak.lock();
|
||||||
|
// TODO: do we want to allow empty callback here?
|
||||||
|
if ((not self) or (not response_cb))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m.timed_out)
|
||||||
{
|
{
|
||||||
// do path hop logic here
|
response_cb(messages::TIMEOUT_RESPONSE);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SymmNonce nonce{};
|
||||||
|
std::string payload;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
oxenc::bt_dict_consumer btdc{m.body()};
|
||||||
|
|
||||||
|
auto nonce = SymmNonce{btdc.require<ustring_view>("NONCE").data()};
|
||||||
|
auto payload = btdc.require<std::string>("PAYLOAD");
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
log::warning(path_cat, "Error parsing path control message response: {}", e.what());
|
||||||
|
response_cb(messages::ERROR_RESPONSE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& hop : self->hops)
|
||||||
|
{
|
||||||
|
nonce = crypto::onion(
|
||||||
|
reinterpret_cast<unsigned char*>(payload.data()),
|
||||||
|
payload.size(),
|
||||||
|
hop.shared,
|
||||||
|
nonce,
|
||||||
|
hop.nonceXOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: should we do anything (even really simple) here to check if the decrypted
|
||||||
|
// response is sensible (e.g. is a bt dict)? Parsing and handling of the
|
||||||
|
// contents (errors or otherwise) is the currently responsibility of the callback.
|
||||||
|
response_cb(payload);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
Path::HandleUpstream(const llarp_buffer_t& X, const TunnelNonce& Y, Router* r)
|
|
||||||
{
|
|
||||||
if (not m_UpstreamReplayFilter.Insert(Y))
|
|
||||||
return false;
|
|
||||||
return AbstractHopHandler::HandleUpstream(X, Y, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
Path::HandleDownstream(const llarp_buffer_t& X, const TunnelNonce& Y, Router* r)
|
|
||||||
{
|
|
||||||
if (not m_DownstreamReplayFilter.Insert(Y))
|
|
||||||
return false;
|
|
||||||
return AbstractHopHandler::HandleDownstream(X, Y, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
RouterID
|
RouterID
|
||||||
Path::Endpoint() const
|
Path::Endpoint() const
|
||||||
{
|
{
|
||||||
|
@ -216,6 +231,9 @@ namespace llarp::path
|
||||||
void
|
void
|
||||||
Path::EnterState(PathStatus st, llarp_time_t now)
|
Path::EnterState(PathStatus st, llarp_time_t now)
|
||||||
{
|
{
|
||||||
|
if (now == 0s)
|
||||||
|
now = router.now();
|
||||||
|
|
||||||
if (st == ePathFailed)
|
if (st == ePathFailed)
|
||||||
{
|
{
|
||||||
_status = st;
|
_status = st;
|
||||||
|
@ -286,8 +304,6 @@ namespace llarp::path
|
||||||
{"ready", IsReady()},
|
{"ready", IsReady()},
|
||||||
{"txRateCurrent", m_LastTXRate},
|
{"txRateCurrent", m_LastTXRate},
|
||||||
{"rxRateCurrent", m_LastRXRate},
|
{"rxRateCurrent", m_LastRXRate},
|
||||||
{"replayTX", m_UpstreamReplayFilter.Size()},
|
|
||||||
{"replayRX", m_DownstreamReplayFilter.Size()},
|
|
||||||
{"hasExit", SupportsAnyRoles(ePathRoleExit)}};
|
{"hasExit", SupportsAnyRoles(ePathRoleExit)}};
|
||||||
|
|
||||||
std::vector<util::StatusObject> hopsObj;
|
std::vector<util::StatusObject> hopsObj;
|
||||||
|
@ -421,73 +437,6 @@ namespace llarp::path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
Path::HandleAllUpstream(std::vector<RelayUpstreamMessage> msgs, Router* r)
|
|
||||||
{
|
|
||||||
for (const auto& msg : msgs)
|
|
||||||
{
|
|
||||||
if (r->send_data_message(upstream(), msg.bt_encode()))
|
|
||||||
{
|
|
||||||
m_TXRate += msg.enc.size();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogDebug("failed to send upstream to ", upstream());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
r->TriggerPump();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Path::UpstreamWork(TrafficQueue_t msgs, Router* r)
|
|
||||||
{
|
|
||||||
std::vector<RelayUpstreamMessage> sendmsgs(msgs.size());
|
|
||||||
size_t idx = 0;
|
|
||||||
for (auto& ev : msgs)
|
|
||||||
{
|
|
||||||
TunnelNonce n = ev.second;
|
|
||||||
|
|
||||||
uint8_t* buf = ev.first.data();
|
|
||||||
size_t sz = ev.first.size();
|
|
||||||
|
|
||||||
for (const auto& hop : hops)
|
|
||||||
{
|
|
||||||
crypto::xchacha20(buf, sz, hop.shared, n);
|
|
||||||
n ^= hop.nonceXOR;
|
|
||||||
}
|
|
||||||
auto& msg = sendmsgs[idx];
|
|
||||||
std::memcpy(msg.enc.data(), buf, sz);
|
|
||||||
msg.nonce = ev.second;
|
|
||||||
msg.pathid = TXID();
|
|
||||||
++idx;
|
|
||||||
}
|
|
||||||
r->loop()->call([self = shared_from_this(), data = std::move(sendmsgs), r]() mutable {
|
|
||||||
self->HandleAllUpstream(std::move(data), r);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Path::FlushUpstream(Router* r)
|
|
||||||
{
|
|
||||||
if (not m_UpstreamQueue.empty())
|
|
||||||
{
|
|
||||||
r->queue_work([self = shared_from_this(),
|
|
||||||
data = std::exchange(m_UpstreamQueue, {}),
|
|
||||||
r]() mutable { self->UpstreamWork(std::move(data), r); });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Path::FlushDownstream(Router* r)
|
|
||||||
{
|
|
||||||
if (not m_DownstreamQueue.empty())
|
|
||||||
{
|
|
||||||
r->queue_work([self = shared_from_this(),
|
|
||||||
data = std::exchange(m_DownstreamQueue, {}),
|
|
||||||
r]() mutable { self->DownstreamWork(std::move(data), r); });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// how long we wait for a path to become active again after it times out
|
/// how long we wait for a path to become active again after it times out
|
||||||
constexpr auto PathReanimationTimeout = 45s;
|
constexpr auto PathReanimationTimeout = 45s;
|
||||||
|
|
||||||
|
@ -515,47 +464,6 @@ namespace llarp::path
|
||||||
return fmt::format("TX={} RX={}", TXID(), RXID());
|
return fmt::format("TX={} RX={}", TXID(), RXID());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
Path::DownstreamWork(TrafficQueue_t msgs, Router* r)
|
|
||||||
{
|
|
||||||
std::vector<RelayDownstreamMessage> sendMsgs(msgs.size());
|
|
||||||
size_t idx = 0;
|
|
||||||
for (auto& ev : msgs)
|
|
||||||
{
|
|
||||||
sendMsgs[idx].nonce = ev.second;
|
|
||||||
|
|
||||||
uint8_t* buf = ev.first.data();
|
|
||||||
size_t sz = ev.first.size();
|
|
||||||
|
|
||||||
for (const auto& hop : hops)
|
|
||||||
{
|
|
||||||
sendMsgs[idx].nonce ^= hop.nonceXOR;
|
|
||||||
crypto::xchacha20(buf, sz, hop.shared, sendMsgs[idx].nonce);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::memcpy(sendMsgs[idx].enc.data(), buf, sz);
|
|
||||||
++idx;
|
|
||||||
}
|
|
||||||
r->loop()->call([self = shared_from_this(), msgs = std::move(sendMsgs), r]() mutable {
|
|
||||||
self->HandleAllDownstream(std::move(msgs), r);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Path::HandleAllDownstream(std::vector<RelayDownstreamMessage> msgs, Router* /* r */)
|
|
||||||
{
|
|
||||||
for (const auto& msg : msgs)
|
|
||||||
{
|
|
||||||
const llarp_buffer_t buf{msg.enc};
|
|
||||||
m_RXRate += buf.sz;
|
|
||||||
// if (HandleRoutingMessage(buf, r))
|
|
||||||
// {
|
|
||||||
// r->TriggerPump();
|
|
||||||
// m_LastRecvMessage = r->now();
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Note: this is one of two places where AbstractRoutingMessage::bt_encode() is called, the
|
/** Note: this is one of two places where AbstractRoutingMessage::bt_encode() is called, the
|
||||||
other of which is llarp/path/transit_hop.cpp in TransitHop::SendRoutingMessage(). For now,
|
other of which is llarp/path/transit_hop.cpp in TransitHop::SendRoutingMessage(). For now,
|
||||||
we will default to the override of ::bt_encode() that returns an std::string. The role that
|
we will default to the override of ::bt_encode() that returns an std::string. The role that
|
||||||
|
@ -571,6 +479,7 @@ namespace llarp::path
|
||||||
functions it calls and so on) will need to be modified to take an std::string that we can
|
functions it calls and so on) will need to be modified to take an std::string that we can
|
||||||
std::move around.
|
std::move around.
|
||||||
*/
|
*/
|
||||||
|
/* TODO: replace this with sending an onion-ed data message
|
||||||
bool
|
bool
|
||||||
Path::SendRoutingMessage(std::string payload, Router*)
|
Path::SendRoutingMessage(std::string payload, Router*)
|
||||||
{
|
{
|
||||||
|
@ -594,6 +503,7 @@ namespace llarp::path
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
template <typename Samples_t>
|
template <typename Samples_t>
|
||||||
static llarp_time_t
|
static llarp_time_t
|
||||||
|
|
|
@ -5,9 +5,7 @@
|
||||||
#include "pathset.hpp"
|
#include "pathset.hpp"
|
||||||
|
|
||||||
#include <llarp/constants/path.hpp>
|
#include <llarp/constants/path.hpp>
|
||||||
#include <llarp/crypto/encrypted_frame.hpp>
|
|
||||||
#include <llarp/crypto/types.hpp>
|
#include <llarp/crypto/types.hpp>
|
||||||
#include <llarp/messages/relay.hpp>
|
|
||||||
#include <llarp/router_id.hpp>
|
#include <llarp/router_id.hpp>
|
||||||
#include <llarp/service/intro.hpp>
|
#include <llarp/service/intro.hpp>
|
||||||
#include <llarp/util/aligned.hpp>
|
#include <llarp/util/aligned.hpp>
|
||||||
|
@ -33,8 +31,6 @@ namespace llarp
|
||||||
struct TransitHopInfo;
|
struct TransitHopInfo;
|
||||||
struct PathHopConfig;
|
struct PathHopConfig;
|
||||||
|
|
||||||
using TransitHop_ptr = std::shared_ptr<TransitHop>;
|
|
||||||
|
|
||||||
struct Ptr_hash;
|
struct Ptr_hash;
|
||||||
|
|
||||||
struct Endpoint_Hash;
|
struct Endpoint_Hash;
|
||||||
|
@ -122,14 +118,6 @@ namespace llarp
|
||||||
return _status;
|
return _status;
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle data in upstream direction
|
|
||||||
bool
|
|
||||||
HandleUpstream(const llarp_buffer_t& X, const TunnelNonce& Y, Router*) override;
|
|
||||||
// handle data in downstream direction
|
|
||||||
|
|
||||||
bool
|
|
||||||
HandleDownstream(const llarp_buffer_t& X, const TunnelNonce& Y, Router*) override;
|
|
||||||
|
|
||||||
const std::string&
|
const std::string&
|
||||||
ShortName() const;
|
ShortName() const;
|
||||||
|
|
||||||
|
@ -143,7 +131,7 @@ namespace llarp
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
EnterState(PathStatus st, llarp_time_t now);
|
EnterState(PathStatus st, llarp_time_t now = 0s);
|
||||||
|
|
||||||
llarp_time_t
|
llarp_time_t
|
||||||
ExpireTime() const
|
ExpireTime() const
|
||||||
|
@ -186,39 +174,41 @@ namespace llarp
|
||||||
Tick(llarp_time_t now, Router* r);
|
Tick(llarp_time_t now, Router* r);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
find_name(std::string name, std::function<void(oxen::quic::message m)> func = nullptr);
|
find_name(std::string name, std::function<void(std::string)> func = nullptr);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
find_router(std::string rid, std::function<void(oxen::quic::message m)> func = nullptr);
|
find_router(std::string rid, std::function<void(std::string)> func = nullptr);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
find_intro(
|
find_intro(
|
||||||
const dht::Key_t& location,
|
const dht::Key_t& location,
|
||||||
bool is_relayed = false,
|
bool is_relayed = false,
|
||||||
uint64_t order = 0,
|
uint64_t order = 0,
|
||||||
std::function<void(oxen::quic::message m)> func = nullptr);
|
std::function<void(std::string)> func = nullptr);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
close_exit(
|
close_exit(SecretKey sk, std::string tx_id, std::function<void(std::string)> func = nullptr);
|
||||||
SecretKey sk,
|
|
||||||
std::string tx_id,
|
|
||||||
std::function<void(oxen::quic::message m)> func = nullptr);
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
obtain_exit(
|
obtain_exit(
|
||||||
SecretKey sk,
|
SecretKey sk,
|
||||||
uint64_t flag,
|
uint64_t flag,
|
||||||
std::string tx_id,
|
std::string tx_id,
|
||||||
std::function<void(oxen::quic::message m)> func = nullptr);
|
std::function<void(std::string)> func = nullptr);
|
||||||
|
|
||||||
|
/// sends a control request along a path
|
||||||
|
///
|
||||||
|
/// performs the necessary onion encryption before sending.
|
||||||
|
/// func will be called when a timeout occurs or a response is received.
|
||||||
|
/// if a response is received, onion decryption is performed before func is called.
|
||||||
|
///
|
||||||
|
/// func is called with a bt-encoded response string (if applicable), and
|
||||||
|
/// a timeout flag (if set, response string will be empty)
|
||||||
bool
|
bool
|
||||||
send_path_control_message(
|
send_path_control_message(
|
||||||
std::string method,
|
std::string method,
|
||||||
std::string body,
|
std::string body,
|
||||||
std::function<void(oxen::quic::message m)> func = nullptr) override;
|
std::function<void(std::string)> func = nullptr) override;
|
||||||
|
|
||||||
bool
|
|
||||||
SendRoutingMessage(std::string payload, Router* r) override;
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IsReady() const;
|
IsReady() const;
|
||||||
|
@ -246,25 +236,6 @@ namespace llarp
|
||||||
std::string
|
std::string
|
||||||
name() const;
|
name() const;
|
||||||
|
|
||||||
void
|
|
||||||
FlushUpstream(Router* r) override;
|
|
||||||
|
|
||||||
void
|
|
||||||
FlushDownstream(Router* r) override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void
|
|
||||||
UpstreamWork(TrafficQueue_t queue, Router* r) override;
|
|
||||||
|
|
||||||
void
|
|
||||||
DownstreamWork(TrafficQueue_t queue, Router* r) override;
|
|
||||||
|
|
||||||
void
|
|
||||||
HandleAllUpstream(std::vector<RelayUpstreamMessage> msgs, Router* r) override;
|
|
||||||
|
|
||||||
void
|
|
||||||
HandleAllDownstream(std::vector<RelayDownstreamMessage> msgs, Router* r) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool
|
bool
|
||||||
SendLatencyMessage(Router* r);
|
SendLatencyMessage(Router* r);
|
||||||
|
|
|
@ -69,195 +69,80 @@ namespace llarp::path
|
||||||
bool
|
bool
|
||||||
PathContext::HopIsUs(const RouterID& k) const
|
PathContext::HopIsUs(const RouterID& k) const
|
||||||
{
|
{
|
||||||
return std::equal(_router->pubkey(), _router->pubkey() + PUBKEYSIZE, k.begin());
|
return _router->pubkey() == k;
|
||||||
}
|
}
|
||||||
|
|
||||||
PathContext::EndpointPathPtrSet
|
std::vector<std::shared_ptr<Path>>
|
||||||
PathContext::FindOwnedPathsWithEndpoint(const RouterID& r)
|
PathContext::FindOwnedPathsWithEndpoint(const RouterID& r)
|
||||||
{
|
{
|
||||||
EndpointPathPtrSet found;
|
std::vector<std::shared_ptr<Path>> found;
|
||||||
m_OurPaths.ForEach([&](const Path_ptr& p) {
|
for (const auto& [pathid, path] : own_paths)
|
||||||
if (p->Endpoint() == r && p->IsReady())
|
{
|
||||||
found.insert(p);
|
// each path is stored in this map twice, once for each pathid at the first hop
|
||||||
});
|
// This will make the output deduplicated without needing a std::set
|
||||||
|
// TODO: we should only need to map one pathid; as the path owner we only send/receive
|
||||||
|
// packets with the first hop's RXID; its TXID is for packets between it and hop 2.
|
||||||
|
// TODO: Also, perhaps we want a bit of data duplication here, e.g. a map from
|
||||||
|
// RouterID (terminal hop) to shared_ptr<Path>.
|
||||||
|
if (path->TXID() == pathid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (path->Endpoint() == r && path->IsReady())
|
||||||
|
found.push_back(path);
|
||||||
|
}
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <
|
|
||||||
typename Lock_t,
|
|
||||||
typename Map_t,
|
|
||||||
typename Key_t,
|
|
||||||
typename CheckValue_t,
|
|
||||||
typename GetFunc_t,
|
|
||||||
typename Return_ptr = HopHandler_ptr>
|
|
||||||
Return_ptr
|
|
||||||
MapGet(Map_t& map, const Key_t& k, CheckValue_t check, GetFunc_t get)
|
|
||||||
{
|
|
||||||
Lock_t lock(map.first);
|
|
||||||
auto range = map.second.equal_range(k);
|
|
||||||
for (auto i = range.first; i != range.second; ++i)
|
|
||||||
{
|
|
||||||
if (check(i->second))
|
|
||||||
return get(i->second);
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Lock_t, typename Map_t, typename Key_t, typename CheckValue_t>
|
|
||||||
bool
|
|
||||||
MapHas(Map_t& map, const Key_t& k, CheckValue_t check)
|
|
||||||
{
|
|
||||||
Lock_t lock(map.first);
|
|
||||||
auto range = map.second.equal_range(k);
|
|
||||||
for (auto i = range.first; i != range.second; ++i)
|
|
||||||
{
|
|
||||||
if (check(i->second))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Lock_t, typename Map_t, typename Key_t, typename Value_t>
|
|
||||||
void
|
|
||||||
MapPut(Map_t& map, const Key_t& k, const Value_t& v)
|
|
||||||
{
|
|
||||||
Lock_t lock(map.first);
|
|
||||||
map.second.emplace(k, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Lock_t, typename Map_t, typename Visit_t>
|
|
||||||
void
|
|
||||||
MapIter(Map_t& map, Visit_t v)
|
|
||||||
{
|
|
||||||
Lock_t lock(map.first);
|
|
||||||
for (const auto& item : map.second)
|
|
||||||
v(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Lock_t, typename Map_t, typename Key_t, typename Check_t>
|
|
||||||
void
|
|
||||||
MapDel(Map_t& map, const Key_t& k, Check_t check)
|
|
||||||
{
|
|
||||||
Lock_t lock(map.first);
|
|
||||||
auto range = map.second.equal_range(k);
|
|
||||||
for (auto i = range.first; i != range.second;)
|
|
||||||
{
|
|
||||||
if (check(i->second))
|
|
||||||
i = map.second.erase(i);
|
|
||||||
else
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
PathContext::AddOwnPath(PathSet_ptr set, Path_ptr path)
|
PathContext::AddOwnPath(PathSet_ptr set, Path_ptr path)
|
||||||
{
|
{
|
||||||
set->AddPath(path);
|
set->AddPath(path);
|
||||||
MapPut<util::Lock>(m_OurPaths, path->TXID(), path);
|
own_paths[path->TXID()] = path;
|
||||||
MapPut<util::Lock>(m_OurPaths, path->RXID(), path);
|
own_paths[path->RXID()] = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
PathContext::HasTransitHop(const TransitHopInfo& info)
|
PathContext::HasTransitHop(const TransitHopInfo& info)
|
||||||
{
|
{
|
||||||
return MapHas<SyncTransitMap_t::Lock_t>(
|
TransitHopID downstream{info.downstream, info.rxID};
|
||||||
m_TransitPaths, info.txID, [info](const std::shared_ptr<TransitHop>& hop) -> bool {
|
if (transit_hops.count(downstream))
|
||||||
return info == hop->info;
|
return true;
|
||||||
});
|
|
||||||
|
TransitHopID upstream{info.upstream, info.txID};
|
||||||
|
if (transit_hops.count(upstream))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::weak_ptr<TransitHop>>
|
std::shared_ptr<TransitHop>
|
||||||
PathContext::TransitHopByInfo(const TransitHopInfo& info)
|
PathContext::GetTransitHop(const RouterID& rid, const PathID_t& path_id)
|
||||||
{
|
{
|
||||||
// this is ugly as sin
|
if (auto itr = transit_hops.find({rid, path_id}); itr != transit_hops.end())
|
||||||
auto own = MapGet<
|
return itr->second;
|
||||||
SyncTransitMap_t::Lock_t,
|
|
||||||
decltype(m_TransitPaths),
|
return nullptr;
|
||||||
PathID_t,
|
|
||||||
std::function<bool(const std::shared_ptr<TransitHop>&)>,
|
|
||||||
std::function<TransitHop*(const std::shared_ptr<TransitHop>&)>,
|
|
||||||
TransitHop*>(
|
|
||||||
m_TransitPaths,
|
|
||||||
info.txID,
|
|
||||||
[info](const auto& hop) -> bool { return hop->info == info; },
|
|
||||||
[](const auto& hop) -> TransitHop* { return hop.get(); });
|
|
||||||
if (own)
|
|
||||||
return own->weak_from_this();
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::weak_ptr<TransitHop>>
|
Path_ptr
|
||||||
PathContext::TransitHopByUpstream(const RouterID& upstream, const PathID_t& id)
|
PathContext::GetPath(const PathID_t& path_id)
|
||||||
{
|
{
|
||||||
// this is ugly as sin as well
|
if (auto itr = own_paths.find(path_id); itr != own_paths.end())
|
||||||
auto own = MapGet<
|
return itr->second;
|
||||||
SyncTransitMap_t::Lock_t,
|
|
||||||
decltype(m_TransitPaths),
|
|
||||||
PathID_t,
|
|
||||||
std::function<bool(const std::shared_ptr<TransitHop>&)>,
|
|
||||||
std::function<TransitHop*(const std::shared_ptr<TransitHop>&)>,
|
|
||||||
TransitHop*>(
|
|
||||||
m_TransitPaths,
|
|
||||||
id,
|
|
||||||
[upstream](const auto& hop) -> bool { return hop->info.upstream == upstream; },
|
|
||||||
[](const auto& hop) -> TransitHop* { return hop.get(); });
|
|
||||||
if (own)
|
|
||||||
return own->weak_from_this();
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
HopHandler_ptr
|
return nullptr;
|
||||||
PathContext::GetByUpstream(const RouterID& remote, const PathID_t& id)
|
|
||||||
{
|
|
||||||
auto own = MapGet<util::Lock>(
|
|
||||||
m_OurPaths,
|
|
||||||
id,
|
|
||||||
[](const Path_ptr) -> bool {
|
|
||||||
// TODO: is this right?
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
[](Path_ptr p) -> HopHandler_ptr { return p; });
|
|
||||||
if (own)
|
|
||||||
return own;
|
|
||||||
|
|
||||||
return MapGet<SyncTransitMap_t::Lock_t>(
|
|
||||||
m_TransitPaths,
|
|
||||||
id,
|
|
||||||
[remote](const std::shared_ptr<TransitHop>& hop) -> bool {
|
|
||||||
return hop->info.upstream == remote;
|
|
||||||
},
|
|
||||||
[](const std::shared_ptr<TransitHop>& h) -> HopHandler_ptr { return h; });
|
|
||||||
}
|
|
||||||
|
|
||||||
HopHandler_ptr
|
|
||||||
PathContext::GetByDownstream(const RouterID& remote, const PathID_t& id)
|
|
||||||
{
|
|
||||||
return MapGet<SyncTransitMap_t::Lock_t>(
|
|
||||||
m_TransitPaths,
|
|
||||||
id,
|
|
||||||
[remote](const std::shared_ptr<TransitHop>& hop) -> bool {
|
|
||||||
return hop->info.downstream == remote;
|
|
||||||
},
|
|
||||||
[](const std::shared_ptr<TransitHop>& h) -> HopHandler_ptr { return h; });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
PathContext::TransitHopPreviousIsRouter(const PathID_t& path, const RouterID& otherRouter)
|
PathContext::TransitHopPreviousIsRouter(const PathID_t& path_id, const RouterID& otherRouter)
|
||||||
{
|
{
|
||||||
SyncTransitMap_t::Lock_t lock(m_TransitPaths.first);
|
return transit_hops.count({otherRouter, path_id});
|
||||||
auto itr = m_TransitPaths.second.find(path);
|
|
||||||
if (itr == m_TransitPaths.second.end())
|
|
||||||
return false;
|
|
||||||
return itr->second->info.downstream == otherRouter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PathSet_ptr
|
PathSet_ptr
|
||||||
PathContext::GetLocalPathSet(const PathID_t& id)
|
PathContext::GetLocalPathSet(const PathID_t& id)
|
||||||
{
|
{
|
||||||
auto& map = m_OurPaths;
|
if (auto itr = own_paths.find(id); itr != own_paths.end())
|
||||||
util::Lock lock(map.first);
|
|
||||||
auto itr = map.second.find(id);
|
|
||||||
if (itr != map.second.end())
|
|
||||||
{
|
{
|
||||||
if (auto parent = itr->second->m_PathSet.lock())
|
if (auto parent = itr->second->m_PathSet.lock())
|
||||||
return parent;
|
return parent;
|
||||||
|
@ -271,54 +156,30 @@ namespace llarp::path
|
||||||
return _router->pubkey();
|
return _router->pubkey();
|
||||||
}
|
}
|
||||||
|
|
||||||
TransitHop_ptr
|
std::shared_ptr<TransitHop>
|
||||||
PathContext::GetPathForTransfer(const PathID_t& id)
|
PathContext::GetPathForTransfer(const PathID_t& id)
|
||||||
{
|
{
|
||||||
const RouterID us(OurRouterID());
|
if (auto itr = transit_hops.find({OurRouterID(), id}); itr != transit_hops.end())
|
||||||
auto& map = m_TransitPaths;
|
|
||||||
{
|
{
|
||||||
SyncTransitMap_t::Lock_t lock(map.first);
|
return itr->second;
|
||||||
auto range = map.second.equal_range(id);
|
|
||||||
for (auto i = range.first; i != range.second; ++i)
|
|
||||||
{
|
|
||||||
if (i->second->info.upstream == us)
|
|
||||||
return i->second;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
PathContext::PumpUpstream()
|
|
||||||
{
|
|
||||||
m_TransitPaths.ForEach([&](auto& ptr) { ptr->FlushUpstream(_router); });
|
|
||||||
m_OurPaths.ForEach([&](auto& ptr) { ptr->FlushUpstream(_router); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
PathContext::PumpDownstream()
|
|
||||||
{
|
|
||||||
m_TransitPaths.ForEach([&](auto& ptr) { ptr->FlushDownstream(_router); });
|
|
||||||
m_OurPaths.ForEach([&](auto& ptr) { ptr->FlushDownstream(_router); });
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t
|
uint64_t
|
||||||
PathContext::CurrentTransitPaths()
|
PathContext::CurrentTransitPaths()
|
||||||
{
|
{
|
||||||
SyncTransitMap_t::Lock_t lock(m_TransitPaths.first);
|
return transit_hops.size() / 2;
|
||||||
const auto& map = m_TransitPaths.second;
|
|
||||||
return map.size() / 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t
|
uint64_t
|
||||||
PathContext::CurrentOwnedPaths(path::PathStatus st)
|
PathContext::CurrentOwnedPaths(path::PathStatus st)
|
||||||
{
|
{
|
||||||
uint64_t num{};
|
uint64_t num{};
|
||||||
util::Lock lock{m_OurPaths.first};
|
for (auto& own_path : own_paths)
|
||||||
auto& map = m_OurPaths.second;
|
|
||||||
for (auto itr = map.begin(); itr != map.end(); ++itr)
|
|
||||||
{
|
{
|
||||||
if (itr->second->Status() == st)
|
if (own_path.second->Status() == st)
|
||||||
num++;
|
num++;
|
||||||
}
|
}
|
||||||
return num / 2;
|
return num / 2;
|
||||||
|
@ -327,8 +188,10 @@ namespace llarp::path
|
||||||
void
|
void
|
||||||
PathContext::PutTransitHop(std::shared_ptr<TransitHop> hop)
|
PathContext::PutTransitHop(std::shared_ptr<TransitHop> hop)
|
||||||
{
|
{
|
||||||
MapPut<SyncTransitMap_t::Lock_t>(m_TransitPaths, hop->info.txID, hop);
|
TransitHopID downstream{hop->info.downstream, hop->info.rxID};
|
||||||
MapPut<SyncTransitMap_t::Lock_t>(m_TransitPaths, hop->info.rxID, hop);
|
TransitHopID upstream{hop->info.upstream, hop->info.txID};
|
||||||
|
transit_hops.emplace(std::move(downstream), hop);
|
||||||
|
transit_hops.emplace(std::move(upstream), hop);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -338,37 +201,30 @@ namespace llarp::path
|
||||||
path_limits.Decay(now);
|
path_limits.Decay(now);
|
||||||
|
|
||||||
{
|
{
|
||||||
SyncTransitMap_t::Lock_t lock(m_TransitPaths.first);
|
auto itr = transit_hops.begin();
|
||||||
auto& map = m_TransitPaths.second;
|
while (itr != transit_hops.end())
|
||||||
auto itr = map.begin();
|
|
||||||
while (itr != map.end())
|
|
||||||
{
|
{
|
||||||
if (itr->second->Expired(now))
|
if (itr->second->Expired(now))
|
||||||
{
|
{
|
||||||
// TODO: this
|
// TODO: this
|
||||||
// _router->outboundMessageHandler().RemovePath(itr->first);
|
// _router->outboundMessageHandler().RemovePath(itr->first);
|
||||||
itr = map.erase(itr);
|
itr = transit_hops.erase(itr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
itr->second->DecayFilters(now);
|
|
||||||
++itr;
|
++itr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
util::Lock lock(m_OurPaths.first);
|
for (auto itr = own_paths.begin(); itr != own_paths.end();)
|
||||||
auto& map = m_OurPaths.second;
|
|
||||||
auto itr = map.begin();
|
|
||||||
while (itr != map.end())
|
|
||||||
{
|
{
|
||||||
if (itr->second->Expired(now))
|
if (itr->second->Expired(now))
|
||||||
{
|
{
|
||||||
itr = map.erase(itr);
|
itr = own_paths.erase(itr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
itr->second->DecayFilters(now);
|
|
||||||
++itr;
|
++itr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
#include "pathset.hpp"
|
#include "pathset.hpp"
|
||||||
#include "transit_hop.hpp"
|
#include "transit_hop.hpp"
|
||||||
|
|
||||||
#include <llarp/crypto/encrypted_frame.hpp>
|
|
||||||
#include <llarp/ev/ev.hpp>
|
#include <llarp/ev/ev.hpp>
|
||||||
#include <llarp/net/ip_address.hpp>
|
#include <llarp/net/ip_address.hpp>
|
||||||
#include <llarp/util/compare_ptr.hpp>
|
#include <llarp/util/compare_ptr.hpp>
|
||||||
|
@ -25,149 +24,121 @@ namespace llarp
|
||||||
struct TransitHop;
|
struct TransitHop;
|
||||||
struct TransitHopInfo;
|
struct TransitHopInfo;
|
||||||
|
|
||||||
using TransitHop_ptr = std::shared_ptr<TransitHop>;
|
struct TransitHopID
|
||||||
|
|
||||||
struct PathContext
|
|
||||||
{
|
{
|
||||||
explicit PathContext(Router* router);
|
RouterID rid;
|
||||||
|
PathID_t path_id;
|
||||||
/// called from router tick function
|
|
||||||
void
|
|
||||||
ExpirePaths(llarp_time_t now);
|
|
||||||
|
|
||||||
void
|
|
||||||
PumpUpstream();
|
|
||||||
|
|
||||||
void
|
|
||||||
PumpDownstream();
|
|
||||||
|
|
||||||
void
|
|
||||||
AllowTransit();
|
|
||||||
|
|
||||||
void
|
|
||||||
RejectTransit();
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
CheckPathLimitHitByIP(const IpAddress& ip);
|
operator==(const TransitHopID& other) const
|
||||||
|
|
||||||
bool
|
|
||||||
CheckPathLimitHitByIP(const std::string& ip);
|
|
||||||
|
|
||||||
bool
|
|
||||||
AllowingTransit() const;
|
|
||||||
|
|
||||||
bool
|
|
||||||
HasTransitHop(const TransitHopInfo& info);
|
|
||||||
|
|
||||||
void
|
|
||||||
PutTransitHop(std::shared_ptr<TransitHop> hop);
|
|
||||||
|
|
||||||
HopHandler_ptr
|
|
||||||
GetByUpstream(const RouterID& id, const PathID_t& path);
|
|
||||||
|
|
||||||
bool
|
|
||||||
TransitHopPreviousIsRouter(const PathID_t& path, const RouterID& r);
|
|
||||||
|
|
||||||
TransitHop_ptr
|
|
||||||
GetPathForTransfer(const PathID_t& topath);
|
|
||||||
|
|
||||||
HopHandler_ptr
|
|
||||||
GetByDownstream(const RouterID& id, const PathID_t& path);
|
|
||||||
|
|
||||||
std::optional<std::weak_ptr<TransitHop>>
|
|
||||||
TransitHopByInfo(const TransitHopInfo&);
|
|
||||||
|
|
||||||
std::optional<std::weak_ptr<TransitHop>>
|
|
||||||
TransitHopByUpstream(const RouterID&, const PathID_t&);
|
|
||||||
|
|
||||||
PathSet_ptr
|
|
||||||
GetLocalPathSet(const PathID_t& id);
|
|
||||||
|
|
||||||
using EndpointPathPtrSet = std::set<Path_ptr, ComparePtr<Path_ptr>>;
|
|
||||||
/// get a set of all paths that we own who's endpoint is r
|
|
||||||
EndpointPathPtrSet
|
|
||||||
FindOwnedPathsWithEndpoint(const RouterID& r);
|
|
||||||
|
|
||||||
bool
|
|
||||||
HopIsUs(const RouterID& k) const;
|
|
||||||
|
|
||||||
void
|
|
||||||
AddOwnPath(PathSet_ptr set, Path_ptr p);
|
|
||||||
|
|
||||||
void
|
|
||||||
RemovePathSet(PathSet_ptr set);
|
|
||||||
|
|
||||||
using TransitHopsMap_t = std::unordered_multimap<PathID_t, TransitHop_ptr>;
|
|
||||||
|
|
||||||
struct SyncTransitMap_t
|
|
||||||
{
|
{
|
||||||
using Mutex_t = util::NullMutex;
|
return rid == other.rid && path_id == other.path_id;
|
||||||
using Lock_t = util::NullLock;
|
|
||||||
|
|
||||||
Mutex_t first; // protects second
|
|
||||||
TransitHopsMap_t second;
|
|
||||||
|
|
||||||
/// Invokes a callback for each transit path; visit must be invokable with a `const
|
|
||||||
/// TransitHop_ptr&` argument.
|
|
||||||
template <typename TransitHopVisitor>
|
|
||||||
void
|
|
||||||
ForEach(TransitHopVisitor&& visit)
|
|
||||||
{
|
|
||||||
Lock_t lock(first);
|
|
||||||
for (const auto& item : second)
|
|
||||||
visit(item.second);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// maps path id -> pathset owner of path
|
|
||||||
using OwnedPathsMap_t = std::unordered_map<PathID_t, Path_ptr>;
|
|
||||||
|
|
||||||
struct SyncOwnedPathsMap_t
|
|
||||||
{
|
|
||||||
util::Mutex first; // protects second
|
|
||||||
OwnedPathsMap_t second;
|
|
||||||
|
|
||||||
/// Invokes a callback for each owned path; visit must be invokable with a `const Path_ptr&`
|
|
||||||
/// argument.
|
|
||||||
template <typename OwnedHopVisitor>
|
|
||||||
void
|
|
||||||
ForEach(OwnedHopVisitor&& visit)
|
|
||||||
{
|
|
||||||
util::Lock lock(first);
|
|
||||||
for (const auto& item : second)
|
|
||||||
visit(item.second);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const EventLoop_ptr&
|
|
||||||
loop();
|
|
||||||
|
|
||||||
const SecretKey&
|
|
||||||
EncryptionSecretKey();
|
|
||||||
|
|
||||||
const byte_t*
|
|
||||||
OurRouterID() const;
|
|
||||||
|
|
||||||
/// current number of transit paths we have
|
|
||||||
uint64_t
|
|
||||||
CurrentTransitPaths();
|
|
||||||
|
|
||||||
/// current number of paths we created in status
|
|
||||||
uint64_t
|
|
||||||
CurrentOwnedPaths(path::PathStatus status = path::PathStatus::ePathEstablished);
|
|
||||||
|
|
||||||
Router*
|
|
||||||
router() const
|
|
||||||
{
|
|
||||||
return _router;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
Router* _router;
|
|
||||||
SyncTransitMap_t m_TransitPaths;
|
|
||||||
SyncOwnedPathsMap_t m_OurPaths;
|
|
||||||
bool m_AllowTransit;
|
|
||||||
util::DecayingHashSet<IpAddress> path_limits;
|
|
||||||
};
|
};
|
||||||
} // namespace path
|
} // namespace path
|
||||||
} // namespace llarp
|
} // namespace llarp
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template <>
|
||||||
|
struct hash<llarp::path::TransitHopID>
|
||||||
|
{
|
||||||
|
size_t
|
||||||
|
operator()(const llarp::path::TransitHopID& obj) const noexcept
|
||||||
|
{
|
||||||
|
return std::hash<llarp::PathID_t>{}(obj.path_id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
namespace llarp::path
|
||||||
|
{
|
||||||
|
struct PathContext
|
||||||
|
{
|
||||||
|
explicit PathContext(Router* router);
|
||||||
|
|
||||||
|
/// called from router tick function
|
||||||
|
void
|
||||||
|
ExpirePaths(llarp_time_t now);
|
||||||
|
|
||||||
|
void
|
||||||
|
AllowTransit();
|
||||||
|
|
||||||
|
void
|
||||||
|
RejectTransit();
|
||||||
|
|
||||||
|
bool
|
||||||
|
CheckPathLimitHitByIP(const IpAddress& ip);
|
||||||
|
|
||||||
|
bool
|
||||||
|
CheckPathLimitHitByIP(const std::string& ip);
|
||||||
|
|
||||||
|
bool
|
||||||
|
AllowingTransit() const;
|
||||||
|
|
||||||
|
bool
|
||||||
|
HasTransitHop(const TransitHopInfo& info);
|
||||||
|
|
||||||
|
void
|
||||||
|
PutTransitHop(std::shared_ptr<TransitHop> hop);
|
||||||
|
|
||||||
|
Path_ptr
|
||||||
|
GetPath(const PathID_t& path_id);
|
||||||
|
|
||||||
|
bool
|
||||||
|
TransitHopPreviousIsRouter(const PathID_t& path, const RouterID& r);
|
||||||
|
|
||||||
|
std::shared_ptr<TransitHop>
|
||||||
|
GetPathForTransfer(const PathID_t& topath);
|
||||||
|
|
||||||
|
std::shared_ptr<TransitHop>
|
||||||
|
GetTransitHop(const RouterID&, const PathID_t&);
|
||||||
|
|
||||||
|
PathSet_ptr
|
||||||
|
GetLocalPathSet(const PathID_t& id);
|
||||||
|
|
||||||
|
/// get a set of all paths that we own who's endpoint is r
|
||||||
|
std::vector<std::shared_ptr<Path>>
|
||||||
|
FindOwnedPathsWithEndpoint(const RouterID& r);
|
||||||
|
|
||||||
|
bool
|
||||||
|
HopIsUs(const RouterID& k) const;
|
||||||
|
|
||||||
|
void
|
||||||
|
AddOwnPath(PathSet_ptr set, Path_ptr p);
|
||||||
|
|
||||||
|
void
|
||||||
|
RemovePathSet(PathSet_ptr set);
|
||||||
|
|
||||||
|
const EventLoop_ptr&
|
||||||
|
loop();
|
||||||
|
|
||||||
|
const SecretKey&
|
||||||
|
EncryptionSecretKey();
|
||||||
|
|
||||||
|
const byte_t*
|
||||||
|
OurRouterID() const;
|
||||||
|
|
||||||
|
/// current number of transit paths we have
|
||||||
|
uint64_t
|
||||||
|
CurrentTransitPaths();
|
||||||
|
|
||||||
|
/// current number of paths we created in status
|
||||||
|
uint64_t
|
||||||
|
CurrentOwnedPaths(path::PathStatus status = path::PathStatus::ePathEstablished);
|
||||||
|
|
||||||
|
Router*
|
||||||
|
router() const
|
||||||
|
{
|
||||||
|
return _router;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Router* _router;
|
||||||
|
|
||||||
|
std::unordered_map<TransitHopID, std::shared_ptr<TransitHop>> transit_hops;
|
||||||
|
std::unordered_map<PathID_t, Path_ptr> own_paths;
|
||||||
|
bool m_AllowTransit;
|
||||||
|
util::DecayingHashSet<IpAddress> path_limits;
|
||||||
|
};
|
||||||
|
} // namespace llarp::path
|
||||||
|
|
|
@ -32,11 +32,11 @@ namespace llarp
|
||||||
/// shared secret at this hop
|
/// shared secret at this hop
|
||||||
SharedSecret shared;
|
SharedSecret shared;
|
||||||
/// hash of shared secret used for nonce mutation
|
/// hash of shared secret used for nonce mutation
|
||||||
ShortHash nonceXOR;
|
SymmNonce nonceXOR;
|
||||||
/// next hop's router id
|
/// next hop's router id
|
||||||
RouterID upstream;
|
RouterID upstream;
|
||||||
/// nonce for key exchange
|
/// nonce for key exchange
|
||||||
TunnelNonce nonce;
|
SymmNonce nonce;
|
||||||
// lifetime
|
// lifetime
|
||||||
llarp_time_t lifetime = DEFAULT_LIFETIME;
|
llarp_time_t lifetime = DEFAULT_LIFETIME;
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,14 @@
|
||||||
#include <llarp/link/link_manager.hpp>
|
#include <llarp/link/link_manager.hpp>
|
||||||
#include <llarp/messages/path.hpp>
|
#include <llarp/messages/path.hpp>
|
||||||
#include <llarp/nodedb.hpp>
|
#include <llarp/nodedb.hpp>
|
||||||
|
#include <llarp/path/pathset.hpp>
|
||||||
#include <llarp/profiling.hpp>
|
#include <llarp/profiling.hpp>
|
||||||
#include <llarp/router/rc_lookup_handler.hpp>
|
#include <llarp/router/rc_lookup_handler.hpp>
|
||||||
#include <llarp/router/router.hpp>
|
#include <llarp/router/router.hpp>
|
||||||
#include <llarp/util/logging.hpp>
|
#include <llarp/util/logging.hpp>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
namespace llarp
|
namespace llarp
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
|
@ -90,7 +93,9 @@ namespace llarp
|
||||||
throw std::runtime_error{std::move(err)};
|
throw std::runtime_error{std::move(err)};
|
||||||
}
|
}
|
||||||
// generate nonceXOR value self->hop->pathKey
|
// generate nonceXOR value self->hop->pathKey
|
||||||
crypto::shorthash(hop.nonceXOR, hop.shared.data(), hop.shared.size());
|
ShortHash hash;
|
||||||
|
crypto::shorthash(hash, hop.shared.data(), hop.shared.size());
|
||||||
|
hop.nonceXOR = hash.data(); // nonceXOR is 24 bytes, ShortHash is 32; this will truncate
|
||||||
|
|
||||||
hop.upstream = nextHop;
|
hop.upstream = nextHop;
|
||||||
}
|
}
|
||||||
|
@ -117,7 +122,7 @@ namespace llarp
|
||||||
crypto::encryption_keygen(framekey);
|
crypto::encryption_keygen(framekey);
|
||||||
|
|
||||||
SharedSecret shared;
|
SharedSecret shared;
|
||||||
TunnelNonce outer_nonce;
|
SymmNonce outer_nonce;
|
||||||
outer_nonce.Randomize();
|
outer_nonce.Randomize();
|
||||||
|
|
||||||
// derive (outer) shared key
|
// derive (outer) shared key
|
||||||
|
@ -460,8 +465,7 @@ namespace llarp
|
||||||
frames.append(dummy);
|
frames.append(dummy);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto self = GetSelf();
|
router->path_context().AddOwnPath(GetSelf(), path);
|
||||||
router->path_context().AddOwnPath(self, path);
|
|
||||||
PathBuildStarted(path);
|
PathBuildStarted(path);
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
|
@ -469,30 +473,33 @@ namespace llarp
|
||||||
// handle these responses as well as how we store and use Paths as a whole might
|
// handle these responses as well as how we store and use Paths as a whole might
|
||||||
// be worth doing sooner rather than later. Leaving some TODOs below where fail
|
// be worth doing sooner rather than later. Leaving some TODOs below where fail
|
||||||
// and success live.
|
// and success live.
|
||||||
auto response_cb = [self](oxen::quic::message m) {
|
auto response_cb = [path](oxen::quic::message m) {
|
||||||
if (m)
|
try
|
||||||
{
|
{
|
||||||
std::string status;
|
if (m)
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
oxenc::bt_dict_consumer btdc{m.body()};
|
// TODO: inform success (what this means needs revisiting, badly)
|
||||||
status = btdc.require<std::string>("STATUS");
|
path->EnterState(path::ePathEstablished);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
catch (...)
|
if (m.timed_out)
|
||||||
{
|
{
|
||||||
log::warning(path_cat, "Error: Failed to parse path build response!", status);
|
log::warning(path_cat, "Path build timed out");
|
||||||
m.respond(serialize_response({{"STATUS", "EXCEPTION"}}), true);
|
}
|
||||||
throw;
|
else
|
||||||
|
{
|
||||||
|
oxenc::bt_dict_consumer d{m.body()};
|
||||||
|
auto status = d.require<std::string_view>(messages::STATUS_KEY);
|
||||||
|
log::warning(path_cat, "Path build returned failure status: {}", status);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: success logic
|
|
||||||
}
|
}
|
||||||
else
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
log::warning(path_cat, "Path build request returned failure {}");
|
log::warning(path_cat, "Failed parsing path build response.");
|
||||||
// TODO: failure logic
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: inform failure (what this means needs revisiting, badly)
|
||||||
|
path->EnterState(path::ePathFailed);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (not router->send_control_message(
|
if (not router->send_control_message(
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include "path.hpp"
|
#include "path.hpp"
|
||||||
|
|
||||||
|
#include <llarp/crypto/crypto.hpp>
|
||||||
|
|
||||||
namespace llarp::path
|
namespace llarp::path
|
||||||
{
|
{
|
||||||
PathSet::PathSet(size_t num) : numDesiredPaths(num)
|
PathSet::PathSet(size_t num) : numDesiredPaths(num)
|
||||||
|
@ -441,16 +443,4 @@ namespace llarp::path
|
||||||
return chosen;
|
return chosen;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
PathSet::UpstreamFlush(Router* r)
|
|
||||||
{
|
|
||||||
ForEachPath([r](const Path_ptr& p) { p->FlushUpstream(r); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
PathSet::DownstreamFlush(Router* r)
|
|
||||||
{
|
|
||||||
ForEachPath([r](const Path_ptr& p) { p->FlushDownstream(r); });
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace llarp::path
|
} // namespace llarp::path
|
||||||
|
|
|
@ -12,21 +12,43 @@ namespace llarp::path
|
||||||
"[TransitHopInfo tx={} rx={} upstream={} downstream={}]", txID, rxID, upstream, downstream);
|
"[TransitHopInfo tx={} rx={} upstream={} downstream={}]", txID, rxID, upstream, downstream);
|
||||||
}
|
}
|
||||||
|
|
||||||
TransitHop::TransitHop()
|
TransitHop::TransitHop() : AbstractHopHandler{}
|
||||||
: AbstractHopHandler{}
|
{}
|
||||||
, m_UpstreamGather{TRANSIT_HOP_QUEUE_SIZE}
|
|
||||||
, m_DownstreamGather{TRANSIT_HOP_QUEUE_SIZE}
|
void
|
||||||
|
TransitHop::onion(ustring& data, SymmNonce& nonce, bool randomize) const
|
||||||
{
|
{
|
||||||
m_UpstreamGather.enable();
|
if (randomize)
|
||||||
m_DownstreamGather.enable();
|
nonce.Randomize();
|
||||||
m_UpstreamWorkCounter = 0;
|
nonce = crypto::onion(data.data(), data.size(), pathKey, nonce, nonceXOR);
|
||||||
m_DownstreamWorkCounter = 0;
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TransitHop::onion(std::string& data, SymmNonce& nonce, bool randomize) const
|
||||||
|
{
|
||||||
|
if (randomize)
|
||||||
|
nonce.Randomize();
|
||||||
|
nonce = crypto::onion(
|
||||||
|
reinterpret_cast<unsigned char*>(data.data()), data.size(), pathKey, nonce, nonceXOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
TransitHop::onion_and_payload(
|
||||||
|
std::string& payload, PathID_t next_id, std::optional<SymmNonce> nonce) const
|
||||||
|
{
|
||||||
|
SymmNonce n;
|
||||||
|
auto& nref = nonce ? *nonce : n;
|
||||||
|
onion(payload, nref, not nonce);
|
||||||
|
|
||||||
|
return path::make_onion_payload(nref, next_id, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
TransitHop::send_path_control_message(
|
TransitHop::send_path_control_message(std::string, std::string, std::function<void(std::string)>)
|
||||||
std::string, std::string, std::function<void(oxen::quic::message m)>)
|
|
||||||
{
|
{
|
||||||
|
// TODO: if we want terminal/pivot hops to be able to *initiate* a request rather than
|
||||||
|
// simply responding/reacting to the client end's requests, this will need
|
||||||
|
// an implementation.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +82,7 @@ namespace llarp::path
|
||||||
functions it calls and so on) will need to be modified to take an std::string that we can
|
functions it calls and so on) will need to be modified to take an std::string that we can
|
||||||
std::move around.
|
std::move around.
|
||||||
*/
|
*/
|
||||||
|
/* TODO: replace this with layer of onion + send data message
|
||||||
bool
|
bool
|
||||||
TransitHop::SendRoutingMessage(std::string payload, Router* r)
|
TransitHop::SendRoutingMessage(std::string payload, Router* r)
|
||||||
{
|
{
|
||||||
|
@ -82,157 +105,7 @@ namespace llarp::path
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
void
|
|
||||||
TransitHop::DownstreamWork(TrafficQueue_t msgs, Router* r)
|
|
||||||
{
|
|
||||||
auto flushIt = [self = shared_from_this(), r]() {
|
|
||||||
std::vector<RelayDownstreamMessage> msgs;
|
|
||||||
while (auto maybe = self->m_DownstreamGather.tryPopFront())
|
|
||||||
{
|
|
||||||
msgs.push_back(*maybe);
|
|
||||||
}
|
|
||||||
self->HandleAllDownstream(std::move(msgs), r);
|
|
||||||
};
|
|
||||||
for (auto& ev : msgs)
|
|
||||||
{
|
|
||||||
RelayDownstreamMessage msg;
|
|
||||||
|
|
||||||
// const llarp_buffer_t buf(ev.first);
|
|
||||||
uint8_t* buf = ev.first.data();
|
|
||||||
size_t sz = ev.first.size();
|
|
||||||
|
|
||||||
msg.pathid = info.rxID;
|
|
||||||
msg.nonce = ev.second ^ nonceXOR;
|
|
||||||
|
|
||||||
crypto::xchacha20(buf, sz, pathKey, ev.second);
|
|
||||||
std::memcpy(msg.enc.data(), buf, sz);
|
|
||||||
|
|
||||||
llarp::LogDebug(
|
|
||||||
"relay ",
|
|
||||||
msg.enc.size(),
|
|
||||||
" bytes downstream from ",
|
|
||||||
info.upstream,
|
|
||||||
" to ",
|
|
||||||
info.downstream);
|
|
||||||
if (m_DownstreamGather.full())
|
|
||||||
{
|
|
||||||
r->loop()->call(flushIt);
|
|
||||||
}
|
|
||||||
if (m_DownstreamGather.enabled())
|
|
||||||
m_DownstreamGather.pushBack(msg);
|
|
||||||
}
|
|
||||||
r->loop()->call(flushIt);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TransitHop::UpstreamWork(TrafficQueue_t msgs, Router* r)
|
|
||||||
{
|
|
||||||
for (auto& ev : msgs)
|
|
||||||
{
|
|
||||||
RelayUpstreamMessage msg;
|
|
||||||
|
|
||||||
uint8_t* buf = ev.first.data();
|
|
||||||
size_t sz = ev.first.size();
|
|
||||||
|
|
||||||
crypto::xchacha20(buf, sz, pathKey, ev.second);
|
|
||||||
|
|
||||||
msg.pathid = info.txID;
|
|
||||||
msg.nonce = ev.second ^ nonceXOR;
|
|
||||||
std::memcpy(msg.enc.data(), buf, sz);
|
|
||||||
|
|
||||||
if (m_UpstreamGather.tryPushBack(msg) != thread::QueueReturn::Success)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush it:
|
|
||||||
r->loop()->call([self = shared_from_this(), r] {
|
|
||||||
std::vector<RelayUpstreamMessage> msgs;
|
|
||||||
while (auto maybe = self->m_UpstreamGather.tryPopFront())
|
|
||||||
{
|
|
||||||
msgs.push_back(*maybe);
|
|
||||||
}
|
|
||||||
self->HandleAllUpstream(std::move(msgs), r);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TransitHop::HandleAllUpstream(std::vector<RelayUpstreamMessage> msgs, Router* r)
|
|
||||||
{
|
|
||||||
if (IsEndpoint(r->pubkey()))
|
|
||||||
{
|
|
||||||
for (const auto& msg : msgs)
|
|
||||||
{
|
|
||||||
const llarp_buffer_t buf(msg.enc);
|
|
||||||
if (!r->ParseRoutingMessageBuffer(buf, *this, info.rxID))
|
|
||||||
{
|
|
||||||
LogWarn("invalid upstream data on endpoint ", info);
|
|
||||||
}
|
|
||||||
m_LastActivity = r->now();
|
|
||||||
}
|
|
||||||
FlushDownstream(r);
|
|
||||||
for (const auto& other : m_FlushOthers)
|
|
||||||
{
|
|
||||||
other->FlushDownstream(r);
|
|
||||||
}
|
|
||||||
m_FlushOthers.clear();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (const auto& msg : msgs)
|
|
||||||
{
|
|
||||||
llarp::LogDebug(
|
|
||||||
"relay ",
|
|
||||||
msg.enc.size(),
|
|
||||||
" bytes upstream from ",
|
|
||||||
info.downstream,
|
|
||||||
" to ",
|
|
||||||
info.upstream);
|
|
||||||
r->send_data_message(info.upstream, msg.bt_encode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
r->TriggerPump();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TransitHop::HandleAllDownstream(std::vector<RelayDownstreamMessage> msgs, Router* r)
|
|
||||||
{
|
|
||||||
for (const auto& msg : msgs)
|
|
||||||
{
|
|
||||||
log::debug(
|
|
||||||
path_cat,
|
|
||||||
"Relaying {} bytes downstream from {} to {}",
|
|
||||||
msg.enc.size(),
|
|
||||||
info.upstream,
|
|
||||||
info.downstream);
|
|
||||||
// TODO: is this right?
|
|
||||||
r->send_data_message(info.downstream, msg.bt_encode());
|
|
||||||
}
|
|
||||||
|
|
||||||
r->TriggerPump();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TransitHop::FlushUpstream(Router* r)
|
|
||||||
{
|
|
||||||
if (not m_UpstreamQueue.empty())
|
|
||||||
{
|
|
||||||
r->queue_work([self = shared_from_this(),
|
|
||||||
data = std::exchange(m_UpstreamQueue, {}),
|
|
||||||
r]() mutable { self->UpstreamWork(std::move(data), r); });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TransitHop::FlushDownstream(Router* r)
|
|
||||||
{
|
|
||||||
if (not m_DownstreamQueue.empty())
|
|
||||||
{
|
|
||||||
r->queue_work([self = shared_from_this(),
|
|
||||||
data = std::exchange(m_DownstreamQueue, {}),
|
|
||||||
r]() mutable { self->DownstreamWork(std::move(data), r); });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
TransitHop::ToString() const
|
TransitHop::ToString() const
|
||||||
|
@ -244,8 +117,7 @@ namespace llarp::path
|
||||||
void
|
void
|
||||||
TransitHop::Stop()
|
TransitHop::Stop()
|
||||||
{
|
{
|
||||||
m_UpstreamGather.disable();
|
// TODO: still need this concept?
|
||||||
m_DownstreamGather.disable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -55,12 +55,29 @@ namespace llarp
|
||||||
|
|
||||||
TransitHopInfo info;
|
TransitHopInfo info;
|
||||||
SharedSecret pathKey;
|
SharedSecret pathKey;
|
||||||
ShortHash nonceXOR;
|
SymmNonce nonceXOR;
|
||||||
llarp_time_t started = 0s;
|
llarp_time_t started = 0s;
|
||||||
// 10 minutes default
|
// 10 minutes default
|
||||||
llarp_time_t lifetime = DEFAULT_LIFETIME;
|
llarp_time_t lifetime = DEFAULT_LIFETIME;
|
||||||
llarp_proto_version_t version;
|
llarp_proto_version_t version;
|
||||||
llarp_time_t m_LastActivity = 0s;
|
llarp_time_t m_LastActivity = 0s;
|
||||||
|
bool terminal_hop{false};
|
||||||
|
|
||||||
|
// If randomize is given, first randomizes `nonce`
|
||||||
|
//
|
||||||
|
// Does xchacha20 on `data` in-place with `nonce` and `pathKey`, then
|
||||||
|
// mutates `nonce` = `nonce` ^ `nonceXOR` in-place.
|
||||||
|
void
|
||||||
|
onion(ustring& data, SymmNonce& nonce, bool randomize = false) const;
|
||||||
|
|
||||||
|
void
|
||||||
|
onion(std::string& data, SymmNonce& nonce, bool randomize = false) const;
|
||||||
|
|
||||||
|
std::string
|
||||||
|
onion_and_payload(
|
||||||
|
std::string& payload,
|
||||||
|
PathID_t next_id,
|
||||||
|
std::optional<SymmNonce> nonce = std::nullopt) const;
|
||||||
|
|
||||||
PathID_t
|
PathID_t
|
||||||
RXID() const override
|
RXID() const override
|
||||||
|
@ -106,47 +123,29 @@ namespace llarp
|
||||||
return now >= ExpireTime() - dlt;
|
return now >= ExpireTime() - dlt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: should this be a separate method indicating directionality?
|
||||||
|
// Most control messages won't make sense to be sent to a client,
|
||||||
|
// so perhaps control messages from a terminal relay to a client (rather than
|
||||||
|
// the other way around) should be their own message type.
|
||||||
|
//
|
||||||
|
/// sends a control request along a path
|
||||||
|
///
|
||||||
|
/// performs the necessary onion encryption before sending.
|
||||||
|
/// func will be called when a timeout occurs or a response is received.
|
||||||
|
/// if a response is received, onion decryption is performed before func is called.
|
||||||
|
///
|
||||||
|
/// func is called with a bt-encoded response string (if applicable), and
|
||||||
|
/// a timeout flag (if set, response string will be empty)
|
||||||
bool
|
bool
|
||||||
send_path_control_message(
|
send_path_control_message(
|
||||||
std::string method,
|
std::string method, std::string body, std::function<void(std::string)> func) override;
|
||||||
std::string body,
|
|
||||||
std::function<void(oxen::quic::message m)> func) override;
|
|
||||||
|
|
||||||
// send routing message when end of path
|
|
||||||
bool
|
|
||||||
SendRoutingMessage(std::string payload, Router* r) override;
|
|
||||||
|
|
||||||
void
|
|
||||||
FlushUpstream(Router* r) override;
|
|
||||||
|
|
||||||
void
|
|
||||||
FlushDownstream(Router* r) override;
|
|
||||||
|
|
||||||
void
|
void
|
||||||
QueueDestroySelf(Router* r);
|
QueueDestroySelf(Router* r);
|
||||||
|
|
||||||
protected:
|
|
||||||
void
|
|
||||||
UpstreamWork(TrafficQueue_t queue, Router* r) override;
|
|
||||||
|
|
||||||
void
|
|
||||||
DownstreamWork(TrafficQueue_t queue, Router* r) override;
|
|
||||||
|
|
||||||
void
|
|
||||||
HandleAllUpstream(std::vector<RelayUpstreamMessage> msgs, Router* r) override;
|
|
||||||
|
|
||||||
void
|
|
||||||
HandleAllDownstream(std::vector<RelayDownstreamMessage> msgs, Router* r) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void
|
void
|
||||||
SetSelfDestruct();
|
SetSelfDestruct();
|
||||||
|
|
||||||
std::set<std::shared_ptr<TransitHop>, ComparePtr<std::shared_ptr<TransitHop>>> m_FlushOthers;
|
|
||||||
thread::Queue<RelayUpstreamMessage> m_UpstreamGather;
|
|
||||||
thread::Queue<RelayDownstreamMessage> m_DownstreamGather;
|
|
||||||
std::atomic<uint32_t> m_UpstreamWorkCounter;
|
|
||||||
std::atomic<uint32_t> m_DownstreamWorkCounter;
|
|
||||||
};
|
};
|
||||||
} // namespace path
|
} // namespace path
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,12 @@ namespace llarp
|
||||||
void
|
void
|
||||||
RCLookupHandler::get_rc(const RouterID& rid, RCRequestCallback callback, bool forceLookup)
|
RCLookupHandler::get_rc(const RouterID& rid, RCRequestCallback callback, bool forceLookup)
|
||||||
{
|
{
|
||||||
|
(void)rid;
|
||||||
|
(void)callback;
|
||||||
|
(void)forceLookup;
|
||||||
|
/* RC refactor pending, this will likely go away entirely
|
||||||
|
*
|
||||||
|
*
|
||||||
RemoteRC remoteRC;
|
RemoteRC remoteRC;
|
||||||
|
|
||||||
if (not forceLookup)
|
if (not forceLookup)
|
||||||
|
@ -83,58 +89,28 @@ namespace llarp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto lookup_cb = [this, callback, rid](oxen::quic::message m) mutable {
|
auto lookup_cb = [this, callback, rid](RemoteRC rc, bool success) mutable {
|
||||||
auto& r = link_manager->router();
|
auto& r = link_manager->router();
|
||||||
|
|
||||||
if (m)
|
if (not success)
|
||||||
{
|
|
||||||
std::string payload;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
oxenc::bt_dict_consumer btdc{m.body()};
|
|
||||||
payload = btdc.require<std::string>("RC");
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
log::warning(link_cat, "Failed to parse Find Router response!");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
RemoteRC result{std::move(payload)};
|
|
||||||
|
|
||||||
if (callback)
|
|
||||||
callback(result.router_id(), result, true);
|
|
||||||
else
|
|
||||||
r.node_db()->put_rc_if_newer(result);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (callback)
|
if (callback)
|
||||||
callback(rid, std::nullopt, false);
|
callback(rid, std::nullopt, false);
|
||||||
else
|
return;
|
||||||
link_manager->handle_find_router_error(std::move(m));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r.node_db()->put_rc_if_newer(rc);
|
||||||
|
if (callback)
|
||||||
|
callback(rc.router_id(), rc, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
// if we are a client try using the hidden service endpoints
|
// TODO: RC fetching and gossiping in general is being refactored, and the old method
|
||||||
|
// of look it up over a path or directly but using the same method but called
|
||||||
|
// differently is going away. It's a mess. The below will do a lookup via a path,
|
||||||
|
// relays will need a different implementation TBD.
|
||||||
if (!isServiceNode)
|
if (!isServiceNode)
|
||||||
{
|
hidden_service_context->GetDefault()->lookup_router(rid, std::move(lookup_cb));
|
||||||
bool sent = false;
|
*/
|
||||||
LogInfo("Lookup ", rid, " anonymously");
|
|
||||||
hidden_service_context->ForEachService(
|
|
||||||
[&, cb = lookup_cb](
|
|
||||||
const std::string&, const std::shared_ptr<service::Endpoint>& ep) -> bool {
|
|
||||||
const bool success = ep->lookup_router(rid, cb);
|
|
||||||
sent = sent || success;
|
|
||||||
return !success;
|
|
||||||
});
|
|
||||||
if (sent)
|
|
||||||
return;
|
|
||||||
LogWarn("cannot lookup ", rid, " anonymously");
|
|
||||||
}
|
|
||||||
|
|
||||||
contacts->lookup_router(rid, lookup_cb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
#include <llarp/net/net.hpp>
|
#include <llarp/net/net.hpp>
|
||||||
#include <llarp/nodedb.hpp>
|
#include <llarp/nodedb.hpp>
|
||||||
#include <llarp/util/logging.hpp>
|
#include <llarp/util/logging.hpp>
|
||||||
#include <llarp/util/meta/memfn.hpp>
|
|
||||||
#include <llarp/util/status.hpp>
|
#include <llarp/util/status.hpp>
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
@ -75,8 +74,6 @@ namespace llarp
|
||||||
llarp::LogTrace("Router::PumpLL() start");
|
llarp::LogTrace("Router::PumpLL() start");
|
||||||
if (is_stopping.load())
|
if (is_stopping.load())
|
||||||
return;
|
return;
|
||||||
paths.PumpDownstream();
|
|
||||||
paths.PumpUpstream();
|
|
||||||
_hidden_service_context.Pump();
|
_hidden_service_context.Pump();
|
||||||
llarp::LogTrace("Router::PumpLL() end");
|
llarp::LogTrace("Router::PumpLL() end");
|
||||||
}
|
}
|
||||||
|
@ -693,7 +690,7 @@ namespace llarp
|
||||||
_contacts,
|
_contacts,
|
||||||
_node_db,
|
_node_db,
|
||||||
_loop,
|
_loop,
|
||||||
util::memFn(&Router::queue_work, this),
|
[this](std::function<void(void)> work) { queue_work(std::move(work)); },
|
||||||
&_link_manager,
|
&_link_manager,
|
||||||
&_hidden_service_context,
|
&_hidden_service_context,
|
||||||
strictConnectPubkeys,
|
strictConnectPubkeys,
|
||||||
|
@ -1290,7 +1287,6 @@ namespace llarp
|
||||||
_exit_context.Stop();
|
_exit_context.Stop();
|
||||||
llarp::sys::service_manager->stopping();
|
llarp::sys::service_manager->stopping();
|
||||||
log::debug(logcat, "final upstream pump");
|
log::debug(logcat, "final upstream pump");
|
||||||
paths.PumpUpstream();
|
|
||||||
llarp::sys::service_manager->stopping();
|
llarp::sys::service_manager->stopping();
|
||||||
log::debug(logcat, "final links pump");
|
log::debug(logcat, "final links pump");
|
||||||
_loop->call_later(200ms, [this] { AfterStopIssued(); });
|
_loop->call_later(200ms, [this] { AfterStopIssued(); });
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
#include <llarp/crypto/crypto.hpp>
|
#include <llarp/crypto/crypto.hpp>
|
||||||
#include <llarp/crypto/types.hpp>
|
#include <llarp/crypto/types.hpp>
|
||||||
#include <llarp/util/meta/memfn.hpp>
|
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
@ -43,6 +42,9 @@ namespace llarp::service
|
||||||
AsyncKeyExchange::Encrypt(
|
AsyncKeyExchange::Encrypt(
|
||||||
std::shared_ptr<AsyncKeyExchange> self, std::shared_ptr<ProtocolFrameMessage> frame)
|
std::shared_ptr<AsyncKeyExchange> self, std::shared_ptr<ProtocolFrameMessage> frame)
|
||||||
{
|
{
|
||||||
|
(void)self;
|
||||||
|
(void)frame;
|
||||||
|
/* TODO: client<->client session ("conversation"/"convo") key exchange
|
||||||
// derive ntru session key component
|
// derive ntru session key component
|
||||||
SharedSecret secret;
|
SharedSecret secret;
|
||||||
crypto::pqe_encrypt(frame->cipher, secret, self->introPubKey);
|
crypto::pqe_encrypt(frame->cipher, secret, self->introPubKey);
|
||||||
|
@ -73,5 +75,6 @@ namespace llarp::service
|
||||||
{
|
{
|
||||||
LogError("failed to encrypt and sign");
|
LogError("failed to encrypt and sign");
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
} // namespace llarp::service
|
} // namespace llarp::service
|
||||||
|
|
|
@ -139,63 +139,50 @@ namespace llarp::service
|
||||||
auth = AuthInfo{token},
|
auth = AuthInfo{token},
|
||||||
ranges,
|
ranges,
|
||||||
result_handler,
|
result_handler,
|
||||||
poker = router()->route_poker()](oxen::quic::message m) mutable {
|
poker = router()->route_poker()](std::string name_result, bool success) mutable {
|
||||||
if (m)
|
if (not success)
|
||||||
{
|
|
||||||
std::string name;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
oxenc::bt_dict_consumer btdc{m.body()};
|
|
||||||
name = btdc.require<std::string>("NAME");
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
log::warning(link_cat, "Failed to parse find name response!");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto saddr = service::Address(); saddr.FromString(name))
|
|
||||||
{
|
|
||||||
ptr->SetAuthInfoForEndpoint(saddr, auth);
|
|
||||||
ptr->MarkAddressOutbound(saddr);
|
|
||||||
|
|
||||||
auto result = ptr->EnsurePathToService(
|
|
||||||
saddr,
|
|
||||||
[ptr, name, ranges, result_handler, poker](auto addr, OutboundContext* ctx) {
|
|
||||||
if (ctx == nullptr)
|
|
||||||
{
|
|
||||||
result_handler(false, "could not establish flow to {}"_format(name));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make a lambda that sends the reply after doing auth
|
|
||||||
auto apply_result = [ptr, poker, addr, result_handler, ranges](
|
|
||||||
std::string result, bool success) {
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
for (const auto& range : ranges)
|
|
||||||
ptr->MapExitRange(range, addr);
|
|
||||||
|
|
||||||
if (poker)
|
|
||||||
poker->put_up();
|
|
||||||
|
|
||||||
result_handler(true, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
result_handler(false, result);
|
|
||||||
};
|
|
||||||
|
|
||||||
ctx->send_auth_async(apply_result);
|
|
||||||
},
|
|
||||||
ptr->PathAlignmentTimeout());
|
|
||||||
|
|
||||||
if (not result)
|
|
||||||
result_handler(false, "Could not build path to {}"_format(name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
result_handler(false, "Exit {} not found!"_format(name));
|
result_handler(false, "Exit {} not found!"_format(name));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto saddr = service::Address(); saddr.FromString(name_result))
|
||||||
|
{
|
||||||
|
ptr->SetAuthInfoForEndpoint(saddr, auth);
|
||||||
|
ptr->MarkAddressOutbound(saddr);
|
||||||
|
|
||||||
|
auto result = ptr->EnsurePathToService(
|
||||||
|
saddr,
|
||||||
|
[ptr, name, name_result, ranges, result_handler, poker](
|
||||||
|
auto addr, OutboundContext* ctx) {
|
||||||
|
if (ctx == nullptr)
|
||||||
|
{
|
||||||
|
result_handler(
|
||||||
|
false, "could not establish flow to {} ({})"_format(name_result, name));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make a lambda that sends the reply after doing auth
|
||||||
|
auto apply_result = [ptr, poker, addr, result_handler, ranges](
|
||||||
|
std::string result, bool success) {
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
for (const auto& range : ranges)
|
||||||
|
ptr->MapExitRange(range, addr);
|
||||||
|
|
||||||
|
if (poker)
|
||||||
|
poker->put_up();
|
||||||
|
}
|
||||||
|
|
||||||
|
result_handler(success, result);
|
||||||
|
};
|
||||||
|
|
||||||
|
ctx->send_auth_async(apply_result);
|
||||||
|
},
|
||||||
|
ptr->PathAlignmentTimeout());
|
||||||
|
|
||||||
|
if (not result)
|
||||||
|
result_handler(false, "Could not build path to {} ({})"_format(name_result, name));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -213,20 +200,22 @@ namespace llarp::service
|
||||||
// If we fail along the way (e.g. it's a .snode, we can't build a path, or whatever else) then
|
// If we fail along the way (e.g. it's a .snode, we can't build a path, or whatever else) then
|
||||||
// we invoke the resultHandler with an empty vector.
|
// we invoke the resultHandler with an empty vector.
|
||||||
lookup_name(
|
lookup_name(
|
||||||
name, [this, resultHandler, service = std::move(service)](oxen::quic::message m) mutable {
|
name,
|
||||||
if (!m)
|
[this, resultHandler, service = std::move(service)](
|
||||||
|
std::string name_result, bool success) mutable {
|
||||||
|
if (!success)
|
||||||
return resultHandler({});
|
return resultHandler({});
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
oxenc::bt_dict_consumer btdc{m.body()};
|
oxenc::bt_dict_consumer btdc{name_result};
|
||||||
name = btdc.require<std::string>("NAME");
|
name = btdc.require<std::string>("NAME");
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
log::warning(link_cat, "Failed to parse find name response!");
|
log::warning(link_cat, "Failed to parse find name response!");
|
||||||
throw;
|
return resultHandler({});
|
||||||
}
|
}
|
||||||
|
|
||||||
auto saddr = service::Address();
|
auto saddr = service::Address();
|
||||||
|
@ -316,35 +305,24 @@ namespace llarp::service
|
||||||
{
|
{
|
||||||
auto& name = item.first;
|
auto& name = item.first;
|
||||||
|
|
||||||
lookup_name(name, [this, name, info = item.second](oxen::quic::message m) mutable {
|
lookup_name(
|
||||||
if (m)
|
name, [this, name, info = item.second](std::string name_result, bool success) mutable {
|
||||||
{
|
if (not success)
|
||||||
std::string result;
|
return;
|
||||||
try
|
|
||||||
{
|
|
||||||
oxenc::bt_dict_consumer btdc{m.body()};
|
|
||||||
result = btdc.require<std::string>("NAME");
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
log::warning(link_cat, "Failed to parse find name response!");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto maybe_range = info.first;
|
const auto maybe_range = info.first;
|
||||||
const auto maybe_auth = info.second;
|
const auto maybe_auth = info.second;
|
||||||
|
|
||||||
_startup_ons_mappings.erase(name);
|
_startup_ons_mappings.erase(name);
|
||||||
|
|
||||||
if (auto saddr = service::Address(); saddr.FromString(result))
|
if (auto saddr = service::Address(); saddr.FromString(name_result))
|
||||||
{
|
{
|
||||||
if (maybe_range.has_value())
|
if (maybe_range.has_value())
|
||||||
_exit_map.Insert(*maybe_range, saddr);
|
_exit_map.Insert(*maybe_range, saddr);
|
||||||
if (maybe_auth.has_value())
|
if (maybe_auth.has_value())
|
||||||
SetAuthInfoForEndpoint(saddr, *maybe_auth);
|
SetAuthInfoForEndpoint(saddr, *maybe_auth);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -798,7 +776,7 @@ namespace llarp::service
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Endpoint::lookup_name(std::string name, std::function<void(oxen::quic::message)> func)
|
Endpoint::lookup_name(std::string name, std::function<void(std::string, bool)> func)
|
||||||
{
|
{
|
||||||
// TODO: so fuck all this?
|
// TODO: so fuck all this?
|
||||||
|
|
||||||
|
@ -838,10 +816,33 @@ namespace llarp::service
|
||||||
std::shuffle(chosenpaths.begin(), chosenpaths.end(), llarp::csrng);
|
std::shuffle(chosenpaths.begin(), chosenpaths.end(), llarp::csrng);
|
||||||
chosenpaths.resize(std::min(paths.size(), MAX_ONS_LOOKUP_ENDPOINTS));
|
chosenpaths.resize(std::min(paths.size(), MAX_ONS_LOOKUP_ENDPOINTS));
|
||||||
|
|
||||||
|
// TODO: only want one successful response to call the callback, or failed if all fail
|
||||||
|
auto response_cb = [func = std::move(func)](std::string resp) {
|
||||||
|
std::string name{};
|
||||||
|
try
|
||||||
|
{
|
||||||
|
oxenc::bt_dict_consumer btdc{resp};
|
||||||
|
auto status = btdc.require<std::string_view>(messages::STATUS_KEY);
|
||||||
|
if (status != "OK"sv)
|
||||||
|
{
|
||||||
|
log::info(link_cat, "Error on ONS lookup: {}", status);
|
||||||
|
func(std::string{status}, false);
|
||||||
|
}
|
||||||
|
name = btdc.require<std::string>("NAME");
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
log::warning(link_cat, "Failed to parse find name response!");
|
||||||
|
func("ERROR"s, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
func(std::move(name), true);
|
||||||
|
};
|
||||||
|
|
||||||
for (const auto& path : chosenpaths)
|
for (const auto& path : chosenpaths)
|
||||||
{
|
{
|
||||||
log::info(link_cat, "{} lookup {} from {}", Name(), name, path->Endpoint());
|
log::info(link_cat, "{} lookup {} from {}", Name(), name, path->Endpoint());
|
||||||
path->find_name(name, func);
|
path->find_name(name, response_cb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -857,18 +858,42 @@ namespace llarp::service
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Endpoint::lookup_router(RouterID rid, std::function<void(oxen::quic::message)> func)
|
Endpoint::lookup_router(RouterID rid, std::function<void(RouterContact rc, bool success)> func)
|
||||||
{
|
{
|
||||||
const auto& routers = _state->pending_routers;
|
(void)rid;
|
||||||
|
(void)func;
|
||||||
if (routers.find(rid) == routers.end())
|
|
||||||
{
|
|
||||||
auto path = GetEstablishedPathClosestTo(rid);
|
|
||||||
path->find_router("find_router", func);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
/* RC refactor pending, this will likely go away entirely
|
||||||
|
*
|
||||||
|
*
|
||||||
|
auto path = GetEstablishedPathClosestTo(rid);
|
||||||
|
|
||||||
|
auto response_cb = [func = std::move(func)](std::string resp, bool timeout) {
|
||||||
|
if (timeout)
|
||||||
|
func(RouterContact{}, false);
|
||||||
|
|
||||||
|
std::string payload;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
oxenc::bt_dict_consumer btdc{resp};
|
||||||
|
payload = btdc.require<std::string>("RC");
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
log::warning(link_cat, "Failed to parse Find Router response!");
|
||||||
|
func(RouterContact{}, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RouterContact result{std::move(payload)};
|
||||||
|
|
||||||
|
func(result, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
path->find_router("find_router", std::move(response_cb));
|
||||||
|
return true;
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1327,29 +1352,43 @@ namespace llarp::service
|
||||||
// address once.
|
// address once.
|
||||||
bool hookAdded = false;
|
bool hookAdded = false;
|
||||||
|
|
||||||
|
auto got_it = std::make_shared<bool>(false);
|
||||||
|
|
||||||
|
// TODO: if all requests fail, call callback with failure?
|
||||||
for (const auto& path : paths)
|
for (const auto& path : paths)
|
||||||
{
|
{
|
||||||
path->find_intro(location, false, 0, [this, hook](oxen::quic::message m) mutable {
|
path->find_intro(location, false, 0, [this, hook, got_it](std::string resp) mutable {
|
||||||
if (m)
|
// asking many, use only first successful
|
||||||
|
if (*got_it)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string introset;
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
std::string introset;
|
oxenc::bt_dict_consumer btdc{resp};
|
||||||
|
auto status = btdc.require<std::string_view>(messages::STATUS_KEY);
|
||||||
try
|
if (status != "OK"sv)
|
||||||
{
|
{
|
||||||
oxenc::bt_dict_consumer btdc{m.body()};
|
log::info(link_cat, "Error in find intro set response: {}", status);
|
||||||
introset = btdc.require<std::string>("INTROSET");
|
return;
|
||||||
}
|
}
|
||||||
catch (...)
|
introset = btdc.require<std::string>("INTROSET");
|
||||||
{
|
|
||||||
log::warning(link_cat, "Failed to parse find name response!");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
service::EncryptedIntroSet enc{introset};
|
|
||||||
router()->contacts()->services()->PutNode(std::move(enc));
|
|
||||||
|
|
||||||
// TODO: finish this
|
|
||||||
}
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
log::warning(link_cat, "Failed to parse find name response!");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
service::EncryptedIntroSet enc{introset};
|
||||||
|
router()->contacts()->services()->PutNode(std::move(enc));
|
||||||
|
|
||||||
|
// TODO: finish this
|
||||||
|
/*
|
||||||
|
if (good)
|
||||||
|
*got_it = true;
|
||||||
|
*/
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return hookAdded;
|
return hookAdded;
|
||||||
|
@ -1432,7 +1471,7 @@ namespace llarp::service
|
||||||
queue.pop();
|
queue.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto r = router();
|
// auto r = router();
|
||||||
|
|
||||||
// TODO: locking on this container
|
// TODO: locking on this container
|
||||||
// for (const auto& [addr, outctx] : _state->remote_sessions)
|
// for (const auto& [addr, outctx] : _state->remote_sessions)
|
||||||
|
@ -1452,8 +1491,6 @@ namespace llarp::service
|
||||||
// if (item.second->SendRoutingMessage(*item.first, r))
|
// if (item.second->SendRoutingMessage(*item.first, r))
|
||||||
// ConvoTagTX(item.first->protocol_frame_msg.convo_tag);
|
// ConvoTagTX(item.first->protocol_frame_msg.convo_tag);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
UpstreamFlush(r);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<ConvoTag>
|
std::optional<ConvoTag>
|
||||||
|
|
|
@ -235,12 +235,11 @@ namespace llarp
|
||||||
|
|
||||||
// "find router" via closest path
|
// "find router" via closest path
|
||||||
bool
|
bool
|
||||||
lookup_router(RouterID router, std::function<void(oxen::quic::message)> func = nullptr);
|
lookup_router(RouterID router, std::function<void(RouterContact, bool)> func = nullptr);
|
||||||
|
|
||||||
// "find name"
|
// "find name"
|
||||||
void
|
void
|
||||||
lookup_name(
|
lookup_name(std::string name, std::function<void(std::string, bool)> func = nullptr) override;
|
||||||
std::string name, std::function<void(oxen::quic::message)> func = nullptr) override;
|
|
||||||
|
|
||||||
// "find introset?"
|
// "find introset?"
|
||||||
void
|
void
|
||||||
|
|
|
@ -167,7 +167,7 @@ namespace llarp::service
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
IntroSet i{other_i};
|
IntroSet i{other_i};
|
||||||
encrypted.nounce.Randomize();
|
encrypted.nonce.Randomize();
|
||||||
// set timestamp
|
// set timestamp
|
||||||
// TODO: round to nearest 1000 ms
|
// TODO: round to nearest 1000 ms
|
||||||
i.time_signed = now;
|
i.time_signed = now;
|
||||||
|
@ -180,7 +180,7 @@ namespace llarp::service
|
||||||
auto bte = i.bt_encode();
|
auto bte = i.bt_encode();
|
||||||
|
|
||||||
const SharedSecret k{i.address_keys.Addr()};
|
const SharedSecret k{i.address_keys.Addr()};
|
||||||
crypto::xchacha20(reinterpret_cast<uint8_t*>(bte.data()), bte.size(), k, encrypted.nounce);
|
crypto::xchacha20(reinterpret_cast<uint8_t*>(bte.data()), bte.size(), k, encrypted.nonce);
|
||||||
|
|
||||||
std::memcpy(encrypted.introsetPayload.data(), bte.data(), bte.size());
|
std::memcpy(encrypted.introsetPayload.data(), bte.data(), bte.size());
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace llarp::service
|
||||||
std::string s)
|
std::string s)
|
||||||
: signedAt{signed_at}
|
: signedAt{signed_at}
|
||||||
, introsetPayload{reinterpret_cast<uint8_t*>(enc_payload.data()), enc_payload.size()}
|
, introsetPayload{reinterpret_cast<uint8_t*>(enc_payload.data()), enc_payload.size()}
|
||||||
, nounce{reinterpret_cast<uint8_t*>(nonce.data())}
|
, nonce{reinterpret_cast<uint8_t*>(nonce.data())}
|
||||||
{
|
{
|
||||||
derivedSigningKey = PubKey::from_string(signing_key);
|
derivedSigningKey = PubKey::from_string(signing_key);
|
||||||
sig.from_string(std::move(s));
|
sig.from_string(std::move(s));
|
||||||
|
@ -27,7 +27,7 @@ namespace llarp::service
|
||||||
oxenc::bt_dict_consumer btdc{bt_payload};
|
oxenc::bt_dict_consumer btdc{bt_payload};
|
||||||
|
|
||||||
derivedSigningKey = PubKey::from_string(btdc.require<std::string>("d"));
|
derivedSigningKey = PubKey::from_string(btdc.require<std::string>("d"));
|
||||||
nounce.from_string(btdc.require<std::string>("n"));
|
nonce.from_string(btdc.require<std::string>("n"));
|
||||||
signedAt = std::chrono::milliseconds{btdc.require<uint64_t>("s")};
|
signedAt = std::chrono::milliseconds{btdc.require<uint64_t>("s")};
|
||||||
introsetPayload = btdc.require<ustring>("x");
|
introsetPayload = btdc.require<ustring>("x");
|
||||||
sig.from_string(btdc.require<std::string>("z"));
|
sig.from_string(btdc.require<std::string>("z"));
|
||||||
|
@ -54,7 +54,7 @@ namespace llarp::service
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
btdp.append("d", derivedSigningKey.ToView());
|
btdp.append("d", derivedSigningKey.ToView());
|
||||||
btdp.append("n", nounce.ToView());
|
btdp.append("n", nonce.ToView());
|
||||||
btdp.append("s", signedAt.count());
|
btdp.append("s", signedAt.count());
|
||||||
btdp.append(
|
btdp.append(
|
||||||
"x",
|
"x",
|
||||||
|
@ -88,7 +88,7 @@ namespace llarp::service
|
||||||
if (not BEncodeMaybeReadDictEntry("d", derivedSigningKey, read, key, buf))
|
if (not BEncodeMaybeReadDictEntry("d", derivedSigningKey, read, key, buf))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (not BEncodeMaybeReadDictEntry("n", nounce, read, key, buf))
|
if (not BEncodeMaybeReadDictEntry("n", nonce, read, key, buf))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (not BEncodeMaybeReadDictInt("s", signedAt, read, key, buf))
|
if (not BEncodeMaybeReadDictInt("s", signedAt, read, key, buf))
|
||||||
|
@ -111,7 +111,7 @@ namespace llarp::service
|
||||||
return fmt::format(
|
return fmt::format(
|
||||||
"[EncIntroSet d={} n={} s={} x=[{} bytes] z={}]",
|
"[EncIntroSet d={} n={} s={} x=[{} bytes] z={}]",
|
||||||
derivedSigningKey,
|
derivedSigningKey,
|
||||||
nounce,
|
nonce,
|
||||||
signedAt.count(),
|
signedAt.count(),
|
||||||
introsetPayload.size(),
|
introsetPayload.size(),
|
||||||
sig);
|
sig);
|
||||||
|
@ -124,7 +124,7 @@ namespace llarp::service
|
||||||
std::string payload{
|
std::string payload{
|
||||||
reinterpret_cast<const char*>(introsetPayload.data()), introsetPayload.size()};
|
reinterpret_cast<const char*>(introsetPayload.data()), introsetPayload.size()};
|
||||||
|
|
||||||
crypto::xchacha20(reinterpret_cast<uint8_t*>(payload.data()), payload.size(), k, nounce);
|
crypto::xchacha20(reinterpret_cast<uint8_t*>(payload.data()), payload.size(), k, nonce);
|
||||||
|
|
||||||
return IntroSet{payload};
|
return IntroSet{payload};
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,7 +138,7 @@ namespace llarp::service
|
||||||
PubKey derivedSigningKey;
|
PubKey derivedSigningKey;
|
||||||
llarp_time_t signedAt = 0s;
|
llarp_time_t signedAt = 0s;
|
||||||
ustring introsetPayload;
|
ustring introsetPayload;
|
||||||
TunnelNonce nounce;
|
SymmNonce nonce;
|
||||||
std::optional<Tag> topic;
|
std::optional<Tag> topic;
|
||||||
Signature sig;
|
Signature sig;
|
||||||
|
|
||||||
|
@ -203,8 +203,8 @@ namespace llarp::service
|
||||||
inline bool
|
inline bool
|
||||||
operator==(const EncryptedIntroSet& lhs, const EncryptedIntroSet& rhs)
|
operator==(const EncryptedIntroSet& lhs, const EncryptedIntroSet& rhs)
|
||||||
{
|
{
|
||||||
return std::tie(lhs.signedAt, lhs.derivedSigningKey, lhs.nounce, lhs.sig)
|
return std::tie(lhs.signedAt, lhs.derivedSigningKey, lhs.nonce, lhs.sig)
|
||||||
== std::tie(rhs.signedAt, rhs.derivedSigningKey, rhs.nounce, rhs.sig);
|
== std::tie(rhs.signedAt, rhs.derivedSigningKey, rhs.nonce, rhs.sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
|
|
|
@ -157,6 +157,7 @@ namespace llarp::service
|
||||||
return "OBContext:" + current_intro.address_keys.Addr().ToString();
|
return "OBContext:" + current_intro.address_keys.Addr().ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: it seems a lot of this logic is duplicated in service/endpoint
|
||||||
void
|
void
|
||||||
OutboundContext::UpdateIntroSet()
|
OutboundContext::UpdateIntroSet()
|
||||||
{
|
{
|
||||||
|
@ -173,7 +174,7 @@ namespace llarp::service
|
||||||
|
|
||||||
for (const auto& path : paths)
|
for (const auto& path : paths)
|
||||||
{
|
{
|
||||||
path->find_intro(location, false, relayOrder, [this](oxen::quic::message m) mutable {
|
path->find_intro(location, false, relayOrder, [this](std::string resp) mutable {
|
||||||
if (marked_bad)
|
if (marked_bad)
|
||||||
{
|
{
|
||||||
log::info(link_cat, "Outbound context has been marked bad (whatever that means)");
|
log::info(link_cat, "Outbound context has been marked bad (whatever that means)");
|
||||||
|
@ -182,43 +183,51 @@ namespace llarp::service
|
||||||
|
|
||||||
updatingIntroSet = false;
|
updatingIntroSet = false;
|
||||||
|
|
||||||
if (m)
|
// TODO: this parsing is probably elsewhere, may need DRYed
|
||||||
|
std::string introset;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
std::string introset;
|
oxenc::bt_dict_consumer btdc{resp};
|
||||||
|
auto status = btdc.require<std::string_view>(messages::STATUS_KEY);
|
||||||
try
|
if (status != "OK"sv)
|
||||||
{
|
{
|
||||||
oxenc::bt_dict_consumer btdc{m.body()};
|
log::info(link_cat, "Error in find intro set response: {}", status);
|
||||||
introset = btdc.require<std::string>("INTROSET");
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
log::warning(link_cat, "Failed to parse find name response!");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
service::EncryptedIntroSet enc{introset};
|
|
||||||
const auto intro = enc.decrypt(PubKey{addr.as_array()});
|
|
||||||
|
|
||||||
if (intro.time_signed == 0s)
|
|
||||||
{
|
|
||||||
log::warning(link_cat, "{} recieved introset with zero timestamp");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (current_intro.time_signed > intro.time_signed)
|
introset = btdc.require<std::string>("INTROSET");
|
||||||
{
|
|
||||||
log::info(link_cat, "{} received outdated introset; dropping", Name());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (intro.IsExpired(llarp::time_now_ms()))
|
|
||||||
{
|
|
||||||
log::warning(link_cat, "{} received expired introset", Name());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
current_intro = intro;
|
|
||||||
ShiftIntroRouter();
|
|
||||||
}
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
log::warning(link_cat, "Failed to parse find name response!");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
service::EncryptedIntroSet enc{introset};
|
||||||
|
const auto intro = enc.decrypt(PubKey{addr.as_array()});
|
||||||
|
|
||||||
|
if (intro.time_signed == 0s)
|
||||||
|
{
|
||||||
|
log::warning(link_cat, "{} recieved introset with zero timestamp");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (current_intro.time_signed > intro.time_signed)
|
||||||
|
{
|
||||||
|
log::info(link_cat, "{} received outdated introset; dropping", Name());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't "shift" to the same intro we're already using...
|
||||||
|
if (current_intro == intro)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (intro.IsExpired(llarp::time_now_ms()))
|
||||||
|
{
|
||||||
|
log::warning(link_cat, "{} received expired introset", Name());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_intro = intro;
|
||||||
|
ShiftIntroRouter();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -534,8 +543,9 @@ namespace llarp::service
|
||||||
ex->msg.proto = ProtocolType::Auth;
|
ex->msg.proto = ProtocolType::Auth;
|
||||||
|
|
||||||
ex->hook = [this, path, cb = std::move(func)](auto frame) mutable {
|
ex->hook = [this, path, cb = std::move(func)](auto frame) mutable {
|
||||||
auto hook = [&, frame, path](oxen::quic::message) {
|
auto hook = [&, frame, path](std::string resp) {
|
||||||
// TODO: revisit this
|
// TODO: revisit this
|
||||||
|
(void)resp;
|
||||||
ep.HandleHiddenServiceFrame(path, *frame.get());
|
ep.HandleHiddenServiceFrame(path, *frame.get());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -302,8 +302,11 @@ namespace llarp::service
|
||||||
|
|
||||||
// PKE (A, B, N)
|
// PKE (A, B, N)
|
||||||
SharedSecret shared_secret;
|
SharedSecret shared_secret;
|
||||||
if (!self->m_LocalIdentity.KeyExchange(
|
if (!crypto::dh_server(
|
||||||
crypto::dh_server, shared_secret, self->msg->sender, self->frame.nonce))
|
shared_secret,
|
||||||
|
self->msg->sender.EncryptionPublicKey(),
|
||||||
|
self->m_LocalIdentity.enckey,
|
||||||
|
self->frame.nonce))
|
||||||
{
|
{
|
||||||
LogError("x25519 key exchange failed");
|
LogError("x25519 key exchange failed");
|
||||||
Dump<MAX_PROTOCOL_MESSAGE_SIZE>(self->frame);
|
Dump<MAX_PROTOCOL_MESSAGE_SIZE>(self->frame);
|
||||||
|
|
|
@ -75,7 +75,7 @@ namespace llarp
|
||||||
PQCipherBlock cipher;
|
PQCipherBlock cipher;
|
||||||
Encrypted<2048> enc;
|
Encrypted<2048> enc;
|
||||||
uint64_t flag; // set to indicate in plaintext a nack, aka "dont try again"
|
uint64_t flag; // set to indicate in plaintext a nack, aka "dont try again"
|
||||||
KeyExchangeNonce nonce;
|
SymmNonce nonce;
|
||||||
Signature sig;
|
Signature sig;
|
||||||
PathID_t path_id;
|
PathID_t path_id;
|
||||||
service::ConvoTag convo_tag;
|
service::ConvoTag convo_tag;
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
#ifndef LLARP_UTIL_MEMFN
|
|
||||||
#define LLARP_UTIL_MEMFN
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace llarp::util
|
|
||||||
{
|
|
||||||
// Wraps a member function and instance into a callable object that invokes
|
|
||||||
// the method (non-const overload).
|
|
||||||
template <
|
|
||||||
typename Return,
|
|
||||||
typename Class,
|
|
||||||
typename Derived,
|
|
||||||
typename... Arg,
|
|
||||||
typename = std::enable_if_t<std::is_base_of<Class, Derived>::value>>
|
|
||||||
auto
|
|
||||||
memFn(Return (Class::*f)(Arg...), Derived* self)
|
|
||||||
{
|
|
||||||
return [f, self](Arg... args) -> Return { return (self->*f)(std::forward<Arg>(args)...); };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wraps a member function and instance into a lambda that invokes the
|
|
||||||
// method (const overload).
|
|
||||||
template <
|
|
||||||
typename Return,
|
|
||||||
typename Class,
|
|
||||||
typename Derived,
|
|
||||||
typename... Arg,
|
|
||||||
typename = std::enable_if_t<std::is_base_of<Class, Derived>::value>>
|
|
||||||
auto
|
|
||||||
memFn(Return (Class::*f)(Arg...) const, const Derived* self)
|
|
||||||
{
|
|
||||||
return [f, self](Arg... args) -> Return { return (self->*f)(std::forward<Arg>(args)...); };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wraps a member function and shared pointer to an instance into a lambda
|
|
||||||
// that invokes the method.
|
|
||||||
template <
|
|
||||||
typename Return,
|
|
||||||
typename Class,
|
|
||||||
typename Derived,
|
|
||||||
typename... Arg,
|
|
||||||
typename = std::enable_if_t<std::is_base_of<Class, Derived>::value>>
|
|
||||||
auto
|
|
||||||
memFn(Return (Class::*f)(Arg...), std::shared_ptr<Derived> self)
|
|
||||||
{
|
|
||||||
return [f, self = std::move(self)](Arg... args) -> Return {
|
|
||||||
return (self.get()->*f)(std::forward<Arg>(args)...);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wraps a member function and shared pointer to an instance into a lambda
|
|
||||||
// that invokes the method (const method overload).
|
|
||||||
template <
|
|
||||||
typename Return,
|
|
||||||
typename Class,
|
|
||||||
typename Derived,
|
|
||||||
typename... Arg,
|
|
||||||
typename = std::enable_if_t<std::is_base_of<Class, Derived>::value>>
|
|
||||||
auto
|
|
||||||
memFn(Return (Class::*f)(Arg...) const, std::shared_ptr<Derived> self)
|
|
||||||
{
|
|
||||||
return [f, self = std::move(self)](Arg... args) -> Return {
|
|
||||||
return (self.get()->*f)(std::forward<Arg>(args)...);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace llarp::util
|
|
||||||
|
|
||||||
#endif
|
|
Loading…
Reference in a new issue