Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .drone.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ local debian_pipeline(name,
'echo "man-db man-db/auto-update boolean false" | debconf-set-selections',
apt_get_quiet + ' update',
apt_get_quiet + ' install -y eatmydata',
'eatmydata ' + apt_get_quiet + ' dist-upgrade -y',
] + (
if std.length(oxen_repo) > 0 then [
'eatmydata ' + apt_get_quiet + ' install --no-install-recommends -y lsb-release',
Expand All @@ -99,7 +100,6 @@ local debian_pipeline(name,
] else []
) + extra_setup
+ [
'eatmydata ' + apt_get_quiet + ' dist-upgrade -y',
'eatmydata ' + apt_get_quiet + ' install --no-install-recommends -y gdb cmake git pkg-config ccache ' + std.join(' ', deps),
'mkdir build',
'cd build',
Expand Down
45 changes: 40 additions & 5 deletions contrib/libsession-router_jank_test.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <session/router.hpp>

#include <chrono>
#include <csignal>
#include <exception>
#include <filesystem>
Expand All @@ -17,7 +18,7 @@ int main(int argc, char** argv)
{
if (argc <= 1)
{
std::cerr << "USAGE: " << argv[0] << " {WHATEVER.loki | WHATEVER.snode}\n";
std::cerr << "USAGE: " << argv[0] << " {PUBKEY.sesh | PUBKEY.snode | ONS.loki}\n";
return 1;
}

Expand Down Expand Up @@ -48,13 +49,47 @@ int main(int argc, char** argv)
{
conn_prom.get_future().get();

//std::this_thread::sleep_for(500ms);
std::cout << "\x1b[33;1mINITIATING SESSION TO " << target << "\x1b[0m\n\n" << std::flush;
auto start = std::chrono::steady_clock::now();
std::cout << "\x1b[33;1mINITIATING SESSION TO \x1b[34;1m" << target << "\x1b[0m\n\n" << std::flush;

std::promise<std::string> resolve_prom;
srouter->resolve(target, [&](std::optional<std::string> a, bool timeout) {
if (a)
{
if (*a != target)
{
auto now = std::chrono::steady_clock::now();
std::cout << "\n\n\x1b[35;1mRESOLVED SNS \x1b[34;1m" << target << "\x1b[35;1m TO \x1b[34;1m" << *a
<< "\x1b[35;1m in " << std::chrono::round<std::chrono::milliseconds>(now - start).count()
<< "ms\x1b[0m\n\n";
start = now;
}
resolve_prom.set_value(std::move(*a));
return;
}

try
{
throw std::runtime_error{
"Failed to resolve ("s + (timeout ? "timeout" : "does not exist") + ") target \x1b[34;1m" + target};
}
catch (...)
{
resolve_prom.set_exception(std::current_exception());
}
});

target = resolve_prom.get_future().get();

srouter->establish_udp(
target,
12345,
[&prom](auto udp_info) {
std::cout << "\n\x1b[32;1mUDP bound to port [::1]:" << udp_info.local_port << "\x1b[0m\n\n" << std::flush;
[&prom, &start](auto udp_info) {
std::cout
<< "\n\x1b[32;1mSession established ("
<< std::chrono::round<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count()
<< "ms); UDP bound to port [::1]:" << udp_info.local_port << "\x1b[0m\n\n"
<< std::flush;
prom.set_value();
},
[&prom]() {
Expand Down
80 changes: 60 additions & 20 deletions include/session/router.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,26 +87,42 @@ namespace session::router
~SessionRouter();

// Schedules the given callback to be fired when Session Router edge connections are mostly
// established (and thus Session Router is ready to start building paths). If Session Router is already
// established, this will schedule an immediate invocation of the callback.
// established (and thus Session Router is ready to start building paths). If Session
// Router is already established, this will schedule an immediate invocation of the
// callback.
//
// If the `with_path` argument is true (or omitted) then the callback instead fires with
// there are edge connections *and* at least one inbound/utility path, which is needed to be
// able to query the network for things like ONS records and client contacts. `false`, on
// the other hand, will fire sooner without requiring a path be completed: it is suitable
// for signalling when Session Router is sufficient connected to start building sessions to
// relays.
//
// If persist is true then the callback will be stored and called *each* time Session Router enters
// the connected state (i.e. it will be called again if Session Router loses all connectivity and
// then regains connections).
void on_connected(std::function<void()> callback, bool persist = false);

// Schedules the given callback to be fired when Session Router becomes fully disconnected, i.e.
// loses all established edge connections. If persist is true then the callback will be
// fired *each* time Session Router transitions from connected to disconnected state. If Session Router
// then regains connections and/or paths).
void on_connected(std::function<void()> callback, bool with_path = true, bool persist = false);

// Schedules the given callback to be fired when Session Router becomes fully disconnected,
// i.e. loses all established edge connections or its last inbound path. When `with_path`
// is given and false, this tracks edge disconnection, which means the instance has lost all
// connectivity; when omitted or true, the callback is also fired if the last inbound path
// is lost, signalling that network querying and client sessions cannot currently work, but
// sessions to relays may still be functional.
//
// If `persist` is true then the callback will be fired *each* time Session Router
// transitions from connected to disconnected state. If Session Router
// is not currently connected then the callback will be scheduled immediately.
void on_disconnected(std::function<void()> callback, bool persist = false);
void on_disconnected(std::function<void()> callback, bool with_path = true, bool persist = false);

// Establishes a session to the given remote (.loki or .snode), with an IPv6 localhost port
// mapped to a port on the remote. A limited number of packets (e.g. to establish a
// connection) can be sent to the mapped port immediately even before the session
// establishes: a few packets will be queued and delivered once (and if) the session
// Establishes a session to the given remote (pubkey.sesh or pubkey.snode), with an IPv6
// localhost port mapped to a port on the remote. A limited number of packets (e.g. to
// establish a connection) can be sent to the mapped port immediately even before the
// session establishes: a few packets will be queued and delivered once (and if) the session
// establishes.
//
// (This method does not accept SNS names: you need to call resolve_sns() first for that).
//
// The returned object contains the port information. The tunnel will remain active until
// drop_udp() is called with the same remote address and port (or the SessionRouter instance
// is destroyed). The caller should track this and drop UDP ports when no longer needed.
Expand All @@ -121,13 +137,14 @@ namespace session::router
// call. Note that `on_established` can be called immediately (i.e. before
// `establish_udp()` returns), if a session to the remote is already established.
//
// `on_timeout` is invoked instead of `on_established` if the session fails to establish for
// whatever reason, with a string giving a descriptive reason. Note that `on_timeout` is
// *not* called if the call throws (such as if given an unparseable address). Note that an
// `on_timeout` call does *not* mean the tunnel has been cancelled: it will remain and future
// attempts to connect to the tunnel port will attempt to (re-)establish the session. If
// you want to cancel it on session initiation failure, call `close_udp()` from within the
// on_timeout callback.
// `on_timeout` is invoked instead of `on_established` if the session fails to establish.
// Note that:
// - `on_timeout` is *not* called if the call throws (such as if given an unparseable
// address).
// - an `on_timeout` call does *not* mean the tunnel has been cancelled: it will remain
// active and future attempts to connect to the tunnel port will attempt to (re-)establish
// the session. If you want to cancel it on session initiation failure, you must call
// `close_udp()` from within the on_timeout callback.
//
// Take care not to use very slow or blocking code inside the callbacks: they are called
// from Session Router's logic thread (and so any blocking will stall Session Router).
Expand All @@ -141,6 +158,29 @@ namespace session::router
// mappings set up from previous connections through the tunnel.
void close_udp(std::string_view remote, uint16_t port);

// Takes an ONS/SNS address such as "blocks.loki" and attempts to resolve it to a Session
// Router client (aka hidden service) address such as
// "kcpyawm9se7trdbzncimdi5t7st4p5mh9i1mg7gkpuubi4k4ku1y.sesh".
//
// This method will also accept a full network address (PUBKEY.sesh or PUBKEY.snode), in
// which case it simply instantly calls the callback with the same address. (This
// capability is designed to allow this method to be used with an address that could be
// either ONS/SNS or direct pubkey).
//
// When the name is resolved, the callback will be invoked with the network address. If the
// name does not exist or a timeout occurs it will be invoked with nullopt and a second
// argument that is true if we timed out (i.e. don't know), false if we got a definitive
// answer from the network that the name does not exist. (The bool should not be used when
// `addr` has a value).
//
// It is possible for the callback to be called instantly (i.e. before resolve_sns returns)
// if the result is already cached, or when given a direct pubkey address rather than a
// resolvable ONS/SNS name.
//
// This method will throw an invalid_argument exception if the given address is neither a
// valid pubkey address nor potentially valid ONS/SNS address.
void resolve(std::string address, std::function<void(std::optional<std::string> addr, bool timeout)> callback);

// If we have a session with the given remote, returns the path we are currently using for
// that session. In the case of a client<->client session, this will be the relay which we
// are using as a pivot.
Expand Down
4 changes: 4 additions & 0 deletions src/handlers/session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,8 @@ namespace srouter::handlers
{
log::debug(logcat, "Successfully built path {}", p);

router.on_inbound_path_change(true);

if (not router.config().network.is_reachable)
return;

Expand All @@ -510,6 +512,8 @@ namespace srouter::handlers
update_and_publish_localcc();
}

void SessionEndpoint::no_established_paths_left() { router.on_inbound_path_change(false); }

void SessionEndpoint::on_path_build_failure(int64_t /*build_id*/, path::Path* path, bool timeout)
{
if (path)
Expand Down
1 change: 1 addition & 0 deletions src/handlers/session.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ namespace srouter

void on_path_build_failure(int64_t build_id, path::Path* path, bool timeout) override;
void on_path_build_success(int64_t build_id, path::Path& p) override;
void no_established_paths_left() override;

void session_post_init(std::shared_ptr<session::InboundSession> new_session);

Expand Down
11 changes: 10 additions & 1 deletion src/path/path_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ namespace srouter::path
{
Lock_t lock{paths_mutex};

int still_established = 0;
int n = 0;
for (auto itr = _paths.begin(); itr != _paths.end();)
{
Expand All @@ -80,11 +81,19 @@ namespace srouter::path
n++;
}
else
{
if (itr->second and itr->second->is_established())
still_established++;
++itr;
}
}

if (n)
{
log::debug(logcat, "{} expired paths dropped", n);
if (!still_established)
no_established_paths_left();
}
}

void PathHandler::invalidate_paths()
Expand Down Expand Up @@ -121,7 +130,7 @@ namespace srouter::path

expire_paths(now);

if (not router.is_service_node and not router.is_connected())
if (not router.is_service_node and not router.is_edge_connected())
// If we are not yet fully connected then we can't initiate path builds. (In theory we
// could whe not yet fully connected, but don't want to because that would bias edge
// router selection towards faster ones).
Expand Down
9 changes: 9 additions & 0 deletions src/path/path_handler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,15 @@ namespace srouter

void expire_paths(sys_ms now);

// Called whenever our last a path gets dropped (expires naturally or was forced to expire
// early because of timeout) if we have no established paths. This is mainly used by
// SessionEndpoint to be able to trigger Router's "on_disconnected" callbacks. Note
// that this is not stateful, i.e. it can potentially fire multiple times without having
// actually established new paths in between calls.
//
// The default does nothing.
virtual void no_established_paths_left() {}

// In case we know none of our paths are still valid, e.g. we received a close on a
// relay session so we assume it's restarting.
void invalidate_paths();
Expand Down
Loading